{"id":305407,"date":"2020-06-16T09:01:48","date_gmt":"2020-06-16T09:01:48","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=305407"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=305407","title":{"rendered":"\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f React Stockcharts \u0434\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0438 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/auriga\/blog\/505638\/\">\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438\u0437\u043b\u043e\u0436\u0435\u043d \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f React \u0434\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0441 \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0445 \u0440\u044b\u043d\u043a\u043e\u0432. \u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0438 <a href=\"https:\/\/alpari.com\/ru\/beginner\/articles\/forex-trend-indicators\/\">\u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430\u043c\u0438<\/a>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0442\u043e\u0440\u0433\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438. \u0421\u0442\u0430\u0442\u044c\u044f \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u0442\u044c frontend \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0445 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ai\/pw\/yj\/aipwyjti1iiijr1gni8dhyo1hwk.png\" alt=\"image\"><br \/>  <a name=\"habracut\"><\/a><br \/>  \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u0432\u0430\u043c \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u0440\u0430\u0444\u0438\u043a, \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/ru.tradingview.com\/chart\/?symbol=MOEX:USDRUB_TOM\">tradingview.com<\/a>. \u0422\u0430\u043a\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430\u0445. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u043d\u0443\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u043e\u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0436\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043d\u0430\u0440\u0430\u0431\u043e\u0442\u043a\u0438. <\/p>\n<p>  \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u043f\u043e\u0438\u0441\u043a \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435, \u0432\u044b\u044f\u0441\u043d\u0438\u0442\u0441\u044f, \u0447\u0442\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u043d\u0430 React <a href=\"https:\/\/www.overloop.io\/blog\/top-5-react-chart-libraries\">\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e<\/a>. \u041d\u043e \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439, \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0432 \u0441\u0432\u043e\u0435\u043c \u0440\u043e\u0434\u0435 \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u0437\u0430\u0442\u0440\u0430\u0442\u0430\u043c\u0438 \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443, \u2013 \u043f\u0440\u043e\u0435\u043a\u0442 <a href=\"http:\/\/rrag.github.io\/react-stockcharts\/documentation.html\">React Stockcharts<\/a>. \u042d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043d\u0430 React \u0438 \u0443\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439 \u0431\u0430\u0440\u043e\u0432. \u0414\u0440\u0443\u0433\u0438\u0435 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041d\u0430\u043c \u0436\u0435 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c React Stockcharts \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043d\u043e\u0432\u044b\u0445 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f. \u0426\u0435\u043b\u044c \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2013 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b. <\/p>\n<p>  React Stockcharts \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/d3js.org\/\">d3js<\/a>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 canvas \u0438 SVG, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b. \u042d\u0442\u043e \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0435\u0442 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u0432\u0435\u0440\u0441\u0438\u044f React 16.8.6, \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f babel \u0438 webpack.<\/p>\n<h4>\u0421 \u0447\u0435\u0433\u043e \u043d\u0430\u0447\u0430\u0442\u044c?<\/h4>\n<p>  \u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u2013 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441 <a href=\"https:\/\/github.com\/rrag\/react-stockcharts\">github<\/a>. \u041f\u0440\u043e\u0438\u043d\u0441\u0442\u0430\u043b\u043b\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432 npm install &#8212;save react-stockcharts, \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 npm run watch.<br \/>  \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0430\u043f\u043e\u043a \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d \u043f\u0440\u043e\u0435\u043a\u0442. \u0412\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0433\u0440\u0430\u0444\u0438\u043a\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/vs\/eg\/6x\/vseg6xwu2qdphq5emi63ccoqh6s.png\" alt=\"image\"><\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430<\/h4>\n<p>  \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 <a href=\"https:\/\/www.metatrader5.com\/ru\/terminal\/help\/indicators\/volume_indicators\/mfi\">Money Flow Index<\/a>. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f: <\/p>\n<p>  1. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b mfi.js \u0432 \u043f\u0430\u043f\u043a\u0435 indicator. \u0412 \u043d\u0435\u043c \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430, \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u043a \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0443.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">indicator\/mfi.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { rebind, merge } from &quot;..\/utils&quot;;  import { mfi } from &quot;..\/calculator&quot;; import baseIndicator from &quot;.\/baseIndicator&quot;;  const ALGORITHM_TYPE = &quot;MFI&quot;;  export default function() {  \tconst base = baseIndicator() \t\t.type(ALGORITHM_TYPE) \t\t.accessor(d =&gt; d.mfi);  \tconst underlyingAlgorithm = mfi();  \tconst mergedAlgorithm = merge() \t\t.algorithm(underlyingAlgorithm) \t\t.merge((datum, indicator) =&gt; { datum.mfi = indicator; });  \tconst indicator = function(data, options = { merge: true }) { \t\tif (options.merge) { \t\t\tif (!base.accessor()) throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); \t\t\treturn mergedAlgorithm(data); \t\t} \t\treturn underlyingAlgorithm(data); \t}; \trebind(indicator, base, &quot;id&quot;, &quot;accessor&quot;, &quot;stroke&quot;, &quot;fill&quot;, &quot;echo&quot;, &quot;type&quot;); \trebind(indicator, underlyingAlgorithm, &quot;undefinedLength&quot;); \trebind(indicator, underlyingAlgorithm, &quot;options&quot;); \trebind(indicator, mergedAlgorithm, &quot;merge&quot;, &quot;skipUndefined&quot;); \treturn indicator; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  2. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b mfi.js \u0432 \u043f\u0430\u043f\u043a\u0435 calculator. \u0417\u0434\u0435\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430. <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">indicator\/mfi.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import { mean } from &quot;d3-array&quot;;  import { slidingWindow } from &quot;..\/utils&quot;; import { MFI as defaultOptions } from &quot;.\/defaultOptionsForComputation&quot;;  export default function() {  \tlet options = defaultOptions;  \tfunction calculator(data) { \t\tconst { windowSize } = options; \t\tlet typical_price, typical_price_privious, money_flow, flow_ratio, flow_index, val_positive_minus, val_negative_minus, money_flow_privious; \t\tlet val_positive = 0, val_negative = 0, ind = 0; \t\tconst arr_positive = [], arr_negative = []; \t\t \t\treturn data.map(function(d,i){ \t\t\tif(i === 0){ \t\t\t\ttypical_price_privious = (d.high + d.low + d.close) \/ 3; \t\t\t\tind++; \t\t\t} else { \t\t\t\ttypical_price = (d.high + d.low + d.close) \/ 3; \t\t\t\tmoney_flow = typical_price * d.volume; \t\t\t\tif(typical_price &gt;= typical_price_privious){ \t\t\t\t\tval_positive += money_flow; \t\t\t\t\tarr_positive.push(money_flow); \t\t\t\t\tarr_negative.push(0); \t\t\t\t} else { \t\t\t\t\tval_negative += money_flow; \t\t\t\t\tarr_negative.push(money_flow); \t\t\t\t\tarr_positive.push(0);\t\t\t\t\t \t\t\t\t} \t\t\t\t \t\t\t\tif(ind &gt;= windowSize ){ \t\t\t\t\tif(i !== windowSize){ \t\t\t\t\t\tval_positive = val_positive - val_positive_minus; \t\t\t\t\t\tval_negative = val_negative - val_negative_minus; \t\t\t\t\t} \t\t\t\t\tval_positive_minus = arr_positive[0]; \t\t\t\t\tval_negative_minus = arr_negative[0]; \t\t\t\t\tarr_positive.shift(); \t\t\t\t\tarr_negative.shift(); \t\t\t\t} \t\t\t\t \t\t\t\ttypical_price_privious = typical_price; \t\t\t\tmoney_flow_privious = money_flow; \t\t\t\tif(ind &gt;= windowSize){ \t\t\t\t\tflow_ratio = val_positive \/ val_negative; \t\t\t\t\tflow_index = 100 - (100 \/ (1 + flow_ratio)); \t\t\t\t\tind++; \t\t\t\t\treturn flow_index; \t\t\t\t} else { \t\t\t\t\tind++; \t\t\t\t\treturn undefined; \t\t\t\t}\t\t\t\t \t\t\t} \t\t}); \t} \tcalculator.undefinedLength = function() { \t\tconst { windowSize } = options; \t\treturn windowSize - 1; \t}; \tcalculator.options = function(x) { \t\tif (!arguments.length) { \t\t\treturn options; \t\t} \t\toptions = { ...defaultOptions, ...x }; \t\treturn calculator; \t}; \treturn calculator; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  3. \u0424\u0430\u0439\u043b calculator\/defaultOptionsForComputation.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0434\u043b\u044f \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439 \u0438 \u0434\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0430.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">calculator\/defaultOptionsForComputation.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">... export const MFI = { \tsource: d =&gt; ({volume: d.volume, high: d.high, low: d.low}), \/\/ &quot;high&quot;, &quot;low&quot;, &quot;open&quot;, &quot;close&quot; \tsourcePath: &quot;volume\/high\/low&quot;, \twindowSize: 10, }; ... <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0414\u043b\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 tooltip \u0438\u0437 tooltip\/MovingAverageTooltip.js. \u0414\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u043d\u0438\u0438 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 LineSeries \u0438\u0437 series\/LineSeries.js. \u0411\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u0441\u043e\u0441\u0442\u043e\u044f\u0442 \u0438\u0437 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 LineSeries, CircleMarker \u0438 \u0442.\u0434.<\/p>\n<p>  \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 MFI \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/t-\/2b\/h-\/t-2bh-0ybdwfhbpb7dhds0fu6ly.png\"><\/p>\n<h4>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f.<\/h4>\n<p>  \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0438\u0436\u0435 \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u2013 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a. <\/p>\n<p>  1. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b RectangleSimple.js \u0432 \u043f\u0430\u043f\u043a\u0435 interactive\/components. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043a\u0443\u0440\u0441\u043e\u0440 \u043c\u044b\u0448\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0434 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c, \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e isHovering. <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">interactive\/components\/RectangleSimple.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import React, { Component } from &quot;react&quot;; import PropTypes from &quot;prop-types&quot;;  import GenericChartComponent from &quot;..\/..\/GenericChartComponent&quot;; import { getMouseCanvas } from &quot;..\/..\/GenericComponent&quot;;  import { \tisDefined, \tnoop, \thexToRGBA, \tgetStrokeDasharray, \tstrokeDashTypes, } from &quot;..\/..\/utils&quot;;  class RectangleSimple extends Component { \tconstructor(props) { \t\tsuper(props);  \t\tthis.renderSVG = this.renderSVG.bind(this); \t\tthis.drawOnCanvas = this.drawOnCanvas.bind(this); \t\tthis.isHover = this.isHover.bind(this); \t} \tisHover(moreProps) { \t\tconst { tolerance, onHover } = this.props;  \t\tif (isDefined(onHover)) { \t\t\tconst { x1Value, x2Value, y1Value, y2Value, type } = this.props; \t\t\tconst { mouseXY, xScale } = moreProps; \t\t\tconst { chartConfig: { yScale } } = moreProps;  \t\t\tconst hovering = isHovering({ \t\t\t\tx1Value, y1Value, \t\t\t\tx2Value, y2Value, \t\t\t\tmouseXY, \t\t\t\ttype, \t\t\t\ttolerance, \t\t\t\txScale, \t\t\t\tyScale, \t\t\t});  \t\t\t\/\/ console.log(&quot;hovering -&gt;&quot;, hovering);  \t\t\treturn hovering; \t\t} \t\treturn false; \t} \tdrawOnCanvas(ctx, moreProps) { \t\tconst { stroke, strokeWidth, strokeOpacity, strokeDasharray, type, fill, fillOpacity, isFill } = this.props; \t\tconst { x1, y1, x2, y2 } = helper(this.props, moreProps);          const width = x2 - x1;         const height = y2 - y1;          \t\tctx.beginPath(); \t\tctx.rect(x1, y1, width, height); \t\tctx.stroke();           if(isFill){             ctx.fillStyle = hexToRGBA(fill, fillOpacity);             ctx.fill();         } \t} \trenderSVG(moreProps) { \t\tconst { stroke, strokeWidth, strokeOpacity, strokeDasharray } = this.props;  \t\tconst lineWidth = strokeWidth;  \t\tconst { x1, y1, x2, y2 } = helper(this.props, moreProps); \t\treturn ( \t\t\t \t\t); \t} \trender() { \t\tconst { selected, interactiveCursorClass } = this.props; \t\tconst { onDragStart, onDrag, onDragComplete, onHover, onUnHover } = this.props;  \t\treturn ; \t} }  export function isHovering2(start, end, [mouseX, mouseY], tolerance) { \tconst m = getSlope(start, end);  \tif (isDefined(m)) { \t\tconst b = getYIntercept(m, end); \t\tconst y = m * mouseX + b; \t\treturn (mouseY &lt; y + tolerance) \t\t\t&amp;&amp; mouseY &gt; (y - tolerance) \t\t\t&amp;&amp; mouseX &gt; Math.min(start[0], end[0]) - tolerance \t\t\t&amp;&amp; mouseX &lt; Math.max(start[0], end[0]) + tolerance; \t} else { \t\treturn mouseY &gt;= Math.min(start[1], end[1]) \t\t\t&amp;&amp; mouseY &lt;= Math.max(start[1], end[1]) \t\t\t&amp;&amp; mouseX &lt; start[0] + tolerance \t\t\t&amp;&amp; mouseX &gt; start[0] - tolerance; \t} }  export function isHovering({ \tx1Value, y1Value, \tx2Value, y2Value, \tmouseXY, \ttype, \ttolerance, \txScale, \tyScale, }) {  \tconst line = generateLine({ \t\ttype, \t\tstart: [x1Value, y1Value], \t\tend: [x2Value, y2Value], \t\txScale, \t\tyScale, \t});  \tconst start = [xScale(line.x1), yScale(line.y1)]; \tconst end = [xScale(line.x2), yScale(line.y2)];  \tconst m = getSlope(start, end); \tconst [mouseX, mouseY] = mouseXY;  \tif (isDefined(m)) { \t\tconst b = getYIntercept(m, end); \t\tconst y = m * mouseX + b;  \t\treturn mouseY &lt; (y + tolerance) \t\t\t&amp;&amp; mouseY &gt; (y - tolerance) \t\t\t&amp;&amp; mouseX &gt; Math.min(start[0], end[0]) - tolerance \t\t\t&amp;&amp; mouseX &lt; Math.max(start[0], end[0]) + tolerance; \t} else { \t\treturn mouseY &gt;= Math.min(start[1], end[1]) \t\t\t&amp;&amp; mouseY &lt;= Math.max(start[1], end[1]) \t\t\t&amp;&amp; mouseX &lt; start[0] + tolerance \t\t\t&amp;&amp; mouseX &gt; start[0] - tolerance; \t} }  function helper(props, moreProps) { \tconst { x1Value, x2Value, y1Value, y2Value, type } = props;  \tconst { xScale, chartConfig: { yScale } } = moreProps;  \tconst modLine = generateLine({ \t\ttype, \t\tstart: [x1Value, y1Value], \t\tend: [x2Value, y2Value], \t\txScale, \t\tyScale, \t});  \tconst x1 = xScale(modLine.x1); \tconst y1 = yScale(modLine.y1); \tconst x2 = xScale(modLine.x2); \tconst y2 = yScale(modLine.y2);  \treturn { \t\tx1, y1, x2, y2 \t}; }  export function getSlope(start, end) { \tconst m \/* slope *\/ = end[0] === start[0] \t\t? undefined \t\t: (end[1] - start[1]) \/ (end[0] - start[0]); \treturn m; } export function getYIntercept(m, end) { \tconst b \/* y intercept *\/ = -1 * m * end[0] + end[1]; \treturn b; }  export function generateLine({ \ttype, start, end, xScale, yScale }) { \tconst m \/* slope *\/ = getSlope(start, end); \t\/\/ console.log(end[0] - start[0], m) \tconst b \/* y intercept *\/ = getYIntercept(m, start);  \tswitch (type) { \t\tcase &quot;XLINE&quot;: \t\t\treturn getXLineCoordinates({ \t\t\t\ttype, start, end, xScale, yScale, m, b \t\t\t}); \t\tcase &quot;RAY&quot;: \t\t\treturn getRayCoordinates({ \t\t\t\ttype, start, end, xScale, yScale, m, b \t\t\t}); \t\tcase &quot;LINE&quot;: \t\t\treturn getLineCoordinates({ \t\t\t\ttype, start, end, xScale, yScale, m, b \t\t\t}); \t} }  function getXLineCoordinates({ \tstart, end, xScale, yScale, m, b }) { \tconst [xBegin, xFinish] = xScale.domain(); \tconst [yBegin, yFinish] = yScale.domain();  \tif (end[0] === start[0]) { \t\treturn { \t\t\tx1: end[0], y1: yBegin, \t\t\tx2: end[0], y2: yFinish, \t\t}; \t} \tconst [x1, x2] = end[0] &gt; start[0] \t\t? [xBegin, xFinish] \t\t: [xFinish, xBegin];  \treturn { \t\tx1, y1: m * x1 + b, \t\tx2, y2: m * x2 + b \t}; }  function getRayCoordinates({ \tstart, end, xScale, yScale, m, b }) { \tconst [xBegin, xFinish] = xScale.domain(); \tconst [yBegin, yFinish] = yScale.domain();  \tconst x1 = start[0]; \tif (end[0] === start[0]) { \t\treturn { \t\t\tx1, \t\t\ty1: start[1], \t\t\tx2: x1, \t\t\ty2: end[1] &gt; start[1] ? yFinish : yBegin, \t\t}; \t}  \tconst x2 = end[0] &gt; start[0] \t\t? xFinish \t\t: xBegin;  \treturn { \t\tx1, y1: m * x1 + b, \t\tx2, y2: m * x2 + b \t}; }  function getLineCoordinates({ \tstart, end }) {  \tconst [x1, y1] = start; \tconst [x2, y2] = end; \tif (end[0] === start[0]) { \t\treturn { \t\t\tx1, \t\t\ty1: start[1], \t\t\tx2: x1, \t\t\ty2: end[1], \t\t}; \t}  \treturn { \t\tx1, y1, \t\tx2, y2, \t}; }  RectangleSimple.propTypes = { \tx1Value: PropTypes.any.isRequired, \tx2Value: PropTypes.any.isRequired, \ty1Value: PropTypes.any.isRequired, \ty2Value: PropTypes.any.isRequired,  \tinteractiveCursorClass: PropTypes.string, \tstroke: PropTypes.string.isRequired, \tstrokeWidth: PropTypes.number.isRequired, \tstrokeOpacity: PropTypes.number.isRequired, \tstrokeDasharray: PropTypes.oneOf(strokeDashTypes),  \ttype: PropTypes.oneOf([ \t\t&quot;XLINE&quot;, \/\/ extends from -Infinity to +Infinity \t\t&quot;RAY&quot;, \/\/ extends to +\/-Infinity in one direction \t\t&quot;LINE&quot;, \/\/ extends between the set bounds \t]).isRequired,  \tonEdge1Drag: PropTypes.func.isRequired, \tonEdge2Drag: PropTypes.func.isRequired, \tonDragStart: PropTypes.func.isRequired, \tonDrag: PropTypes.func.isRequired, \tonDragComplete: PropTypes.func.isRequired, \tonHover: PropTypes.func, \tonUnHover: PropTypes.func,  \tdefaultClassName: PropTypes.string,  \tr: PropTypes.number.isRequired, \tedgeFill: PropTypes.string.isRequired, \tedgeStroke: PropTypes.string.isRequired, \tedgeStrokeWidth: PropTypes.number.isRequired, \twithEdge: PropTypes.bool.isRequired, \tchildren: PropTypes.func.isRequired, \ttolerance: PropTypes.number.isRequired, \tselected: PropTypes.bool.isRequired, };  RectangleSimple.defaultProps = { \tonEdge1Drag: noop, \tonEdge2Drag: noop, \tonDragStart: noop, \tonDrag: noop, \tonDragComplete: noop,  \tedgeStrokeWidth: 3, \tedgeStroke: &quot;#000000&quot;, \tedgeFill: &quot;#FFFFFF&quot;, \tr: 10, \twithEdge: false, \tstrokeWidth: 1, \tstrokeDasharray: &quot;Solid&quot;, \tchildren: noop, \ttolerance: 7, \tselected: false, };  export default RectangleSimple; <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  2. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b EachRectangle.js \u0432 \u043f\u0430\u043f\u043a\u0435 interactive\/wrapper. \u0417\u0434\u0435\u0441\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u043e\u0432. <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">interactive\/wrapper\/EachRectangle.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import React, { Component } from &quot;react&quot;; import PropTypes from &quot;prop-types&quot;;  import { ascending as d3Ascending } from &quot;d3-array&quot;; import { noop, strokeDashTypes } from &quot;..\/..\/utils&quot;; import { saveNodeType, isHover } from &quot;..\/utils&quot;; import { getXValue } from &quot;..\/..\/utils\/ChartDataUtil&quot;;  import Rectangle from &quot;..\/components\/Rectangle&quot;; import ClickableCircle from &quot;..\/components\/ClickableCircle&quot;; import HoverTextNearMouse from &quot;..\/components\/HoverTextNearMouse&quot;;  class EachRectangle extends Component { \tconstructor(props) { \t\tsuper(props);  \t\tthis.handleEdge1Drag = this.handleEdge1Drag.bind(this); \t\tthis.handleEdge2Drag = this.handleEdge2Drag.bind(this); \t\tthis.handleLineDragStart = this.handleLineDragStart.bind(this); \t\tthis.handleLineDrag = this.handleLineDrag.bind(this);  \t\tthis.handleEdge1DragStart = this.handleEdge1DragStart.bind(this); \t\tthis.handleEdge2DragStart = this.handleEdge2DragStart.bind(this);  \t\tthis.handleDragComplete = this.handleDragComplete.bind(this);  \t\tthis.handleHover = this.handleHover.bind(this);  \t\tthis.isHover = isHover.bind(this); \t\tthis.saveNodeType = saveNodeType.bind(this); \t\tthis.nodes = {};  \t\tthis.state = { \t\t\thover: false, \t\t}; \t} \thandleLineDragStart() { \t\tconst { \t\t\tx1Value, y1Value, \t\t\tx2Value, y2Value, \t\t} = this.props;  \t\tthis.dragStart = { \t\t\tx1Value, y1Value, \t\t\tx2Value, y2Value, \t\t}; \t} \thandleLineDrag(moreProps) { \t\tconst { index, onDrag } = this.props;  \t\tconst { \t\t\tx1Value, y1Value, \t\t\tx2Value, y2Value, \t\t} = this.dragStart;  \t\tconst { xScale, chartConfig: { yScale }, xAccessor, fullData } = moreProps; \t\tconst { startPos, mouseXY } = moreProps;  \t\tconst x1 = xScale(x1Value); \t\tconst y1 = yScale(y1Value); \t\tconst x2 = xScale(x2Value); \t\tconst y2 = yScale(y2Value);  \t\tconst dx = startPos[0] - mouseXY[0]; \t\tconst dy = startPos[1] - mouseXY[1];  \t\tconst newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData); \t\tconst newY1Value = yScale.invert(y1 - dy); \t\tconst newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData); \t\tconst newY2Value = yScale.invert(y2 - dy);  \t\tonDrag(index, { \t\t\tx1Value: newX1Value, \t\t\ty1Value: newY1Value, \t\t\tx2Value: newX2Value, \t\t\ty2Value: newY2Value, \t\t}); \t} \thandleEdge1DragStart() { \t\tthis.setState({ \t\t\tanchor: &quot;edge2&quot; \t\t}); \t} \thandleEdge2DragStart() { \t\tthis.setState({ \t\t\tanchor: &quot;edge1&quot; \t\t}); \t} \thandleDragComplete(...rest) { \t\tthis.setState({ \t\t\tanchor: undefined \t\t}); \t\tthis.props.onDragComplete(...rest); \t} \thandleEdge1Drag(moreProps) { \t\tconst { index, onDrag } = this.props; \t\tconst { \t\t\tx2Value, y2Value, \t\t} = this.props;  \t\tconst [x1Value, y1Value] = getNewXY(moreProps);  \t\tonDrag(index, { \t\t\tx1Value, \t\t\ty1Value, \t\t\tx2Value, \t\t\ty2Value, \t\t}); \t} \thandleEdge2Drag(moreProps) { \t\tconst { index, onDrag } = this.props; \t\tconst { \t\t\tx1Value, y1Value, \t\t} = this.props;  \t\tconst [x2Value, y2Value] = getNewXY(moreProps);  \t\tonDrag(index, { \t\t\tx1Value, \t\t\ty1Value, \t\t\tx2Value, \t\t\ty2Value, \t\t}); \t} \thandleHover(moreProps) { \t\tif (this.state.hover !== moreProps.hovering) { \t\t\tthis.setState({ \t\t\t\thover: moreProps.hovering \t\t\t}); \t\t} \t} \trender() { \t\tconst { \t\t\tx1Value, \t\t\ty1Value, \t\t\tx2Value, \t\t\ty2Value, \t\t\ttype, \t\t\tstroke, \t\t\tstrokeWidth, \t\t\tstrokeOpacity, \t\t\tstrokeDasharray, \t\t\tr, \t\t\tedgeStrokeWidth, \t\t\tedgeFill, \t\t\tedgeStroke, \t\t\tedgeInteractiveCursor, \t\t\tlineInteractiveCursor, \t\t\thoverText, \t\t\tselected,  \t\t\tonDragComplete, \t\t} = this.props;  \t\tconst { \t\t\tenable: hoverTextEnabled, \t\t\tselectedText: hoverTextSelected, \t\t\ttext: hoverTextUnselected, \t\t\t...restHoverTextProps \t\t} = hoverText;  \t\tconst { hover, anchor } = this.state;  \t\treturn  \t\t\t \t\t\t \t\t\t \t\t\t \t\t; \t} }  export function getNewXY(moreProps) { \tconst { xScale, chartConfig: { yScale }, xAccessor, plotData, mouseXY } = moreProps; \tconst mouseY = mouseXY[1];  \tconst x = getXValue(xScale, xAccessor, mouseXY, plotData);  \tconst [small, big] = yScale.domain().slice().sort(d3Ascending); \tconst y = yScale.invert(mouseY); \tconst newY = Math.min(Math.max(y, small), big);  \treturn [x, newY]; }  EachRectangle.propTypes = { \tx1Value: PropTypes.any.isRequired, \tx2Value: PropTypes.any.isRequired, \ty1Value: PropTypes.any.isRequired, \ty2Value: PropTypes.any.isRequired,  \tindex: PropTypes.number,  \ttype: PropTypes.oneOf([ \t\t&quot;XLINE&quot;, \/\/ extends from -Infinity to +Infinity \t\t&quot;RAY&quot;, \/\/ extends to +\/-Infinity in one direction \t\t&quot;LINE&quot;, \/\/ extends between the set bounds \t]).isRequired,  \tonDrag: PropTypes.func.isRequired, \tonEdge1Drag: PropTypes.func.isRequired, \tonEdge2Drag: PropTypes.func.isRequired, \tonDragComplete: PropTypes.func.isRequired, \tonSelect: PropTypes.func.isRequired, \tonUnSelect: PropTypes.func.isRequired,  \tr: PropTypes.number.isRequired, \tstrokeOpacity: PropTypes.number.isRequired, \tdefaultClassName: PropTypes.string,  \tselected: PropTypes.bool,  \tstroke: PropTypes.string.isRequired, \tstrokeWidth: PropTypes.number.isRequired, \tstrokeDasharray: PropTypes.oneOf(strokeDashTypes),  \tedgeStrokeWidth: PropTypes.number.isRequired, \tedgeStroke: PropTypes.string.isRequired, \tedgeInteractiveCursor: PropTypes.string.isRequired, \tlineInteractiveCursor: PropTypes.string.isRequired, \tedgeFill: PropTypes.string.isRequired, \thoverText: PropTypes.object.isRequired, };  EachRectangle.defaultProps = { \tonDrag: noop, \tonEdge1Drag: noop, \tonEdge2Drag: noop, \tonDragComplete: noop, \tonSelect: noop, \tonUnSelect: noop,  \tselected: false,  \tedgeStroke: &quot;#000000&quot;, \tedgeFill: &quot;#FFFFFF&quot;, \tedgeStrokeWidth: 2, \tr: 5, \tstrokeWidth: 1, \tstrokeOpacity: 1, \tstrokeDasharray: &quot;Solid&quot;, \thoverText: { \t\tenable: false, \t} };  export default EachRectangle; <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  3. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b Rectangle.js \u0432 \u043f\u0430\u043f\u043a\u0435 interactive. \u042d\u0442\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 rectangle \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">interactive\/Rectangle.js<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">import React, { Component } from &quot;react&quot;; import PropTypes from &quot;prop-types&quot;;  import { isDefined, isNotDefined, noop, strokeDashTypes } from &quot;..\/utils&quot;;  import { \tgetValueFromOverride, \tterminate, \tsaveNodeType, \tisHoverForInteractiveType, } from &quot;.\/utils&quot;;  import EachRectangle from &quot;.\/wrapper\/EachRectangle&quot;; import MouseLocationIndicator from &quot;.\/components\/MouseLocationIndicator&quot;; import HoverTextNearMouse from &quot;.\/components\/HoverTextNearMouse&quot;;  class Rectangle extends Component { \tconstructor(props) { \t\tsuper(props);  \t\tthis.handleStart = this.handleStart.bind(this); \t\tthis.handleEnd = this.handleEnd.bind(this); \t\tthis.handleDrawLine = this.handleDrawLine.bind(this); \t\tthis.handleDragLine = this.handleDragLine.bind(this); \t\tthis.handleDragLineComplete = this.handleDragLineComplete.bind(this);  \t\tthis.terminate = terminate.bind(this); \t\tthis.saveNodeType = saveNodeType.bind(this);  \t\tthis.getSelectionState = isHoverForInteractiveType(&quot;trends&quot;) \t\t\t.bind(this);  \t\tthis.state = { \t\t}; \t\tthis.nodes = []; \t} \thandleDragLine(index, newXYValue) { \t\tthis.setState({ \t\t\toverride: { \t\t\t\tindex, \t\t\t\t...newXYValue \t\t\t} \t\t}); \t} \thandleDragLineComplete(moreProps) { \t\tconst { override } = this.state; \t\tif (isDefined(override)) { \t\t\tconst { trends } = this.props; \t\t\tconst newTrends = trends \t\t\t\t.map((each, idx) =&gt; idx === override.index \t\t\t\t\t? { \t\t\t\t\t\t...each, \t\t\t\t\t\tstart: [override.x1Value, override.y1Value], \t\t\t\t\t\tend: [override.x2Value, override.y2Value], \t\t\t\t\t\tselected: true, \t\t\t\t\t} \t\t\t\t\t: { \t\t\t\t\t\t...each, \t\t\t\t\t\tselected: false, \t\t\t\t\t});  \t\t\tthis.setState({ \t\t\t\toverride: null, \t\t\t}, () =&gt; { \t\t\t\tthis.props.onComplete(newTrends, moreProps); \t\t\t}); \t\t} \t} \thandleDrawLine(xyValue) { \t\tconst { current } = this.state; \t\tif (isDefined(current) &amp;&amp; isDefined(current.start)) { \t\t\tthis.mouseMoved = true; \t\t\tthis.setState({ \t\t\t\tcurrent: { \t\t\t\t\tstart: current.start, \t\t\t\t\tend: xyValue, \t\t\t\t} \t\t\t}); \t\t} \t} \thandleStart(xyValue, moreProps, e) { \t\tconst { current } = this.state;  \t\tif (isNotDefined(current) || isNotDefined(current.start)) { \t\t\tthis.mouseMoved = false;  \t\t\tthis.setState({ \t\t\t\tcurrent: { \t\t\t\t\tstart: xyValue, \t\t\t\t\tend: null, \t\t\t\t}, \t\t\t}, () =&gt; { \t\t\t\tthis.props.onStart(moreProps, e); \t\t\t}); \t\t} \t} \thandleEnd(xyValue, moreProps, e) { \t\tconst { current } = this.state; \t\tconst { trends, appearance, type } = this.props;  \t\tif (this.mouseMoved \t\t\t&amp;&amp; isDefined(current) \t\t\t&amp;&amp; isDefined(current.start) \t\t) { \t\t\tconst newTrends = [ \t\t\t\t...trends.map(d =&gt; ({ ...d, selected: false })), \t\t\t\t{ \t\t\t\t\tstart: current.start, \t\t\t\t\tend: xyValue, \t\t\t\t\tselected: true, \t\t\t\t\tappearance, \t\t\t\t\ttype, \t\t\t\t} \t\t\t]; \t\t\tthis.setState({ \t\t\t\tcurrent: null, \t\t\t\ttrends: newTrends \t\t\t}, () =&gt; { \t\t\t\tthis.props.onComplete(newTrends, moreProps, e); \t\t\t}); \t\t} \t} \trender() { \t\tconst { appearance } = this.props; \t\tconst { enabled, snap, shouldDisableSnap, snapTo, type } = this.props; \t\tconst { currentPositionRadius, currentPositionStroke } = this.props; \t\tconst { currentPositionstrokeOpacity, currentPositionStrokeWidth } = this.props; \t\tconst { hoverText, trends } = this.props; \t\tconst { current, override } = this.state;  \t\tconst tempLine = isDefined(current) &amp;&amp; isDefined(current.end) \t\t\t?  \t\t\t: null;  \t\treturn  \t\t\t{trends.map((each, idx) =&gt; { \t\t\t\tconst eachAppearance = isDefined(each.appearance) \t\t\t\t\t? { ...appearance, ...each.appearance } \t\t\t\t\t: appearance;  \t\t\t\tconst hoverTextWithDefault = { \t\t\t\t\t...Rectangle.defaultProps.hoverText, \t\t\t\t\t...hoverText \t\t\t\t};  \t\t\t\treturn ; \t\t\t})} \t\t\t{tempLine} \t\t\t \t\t; \t} }   Rectangle.propTypes = { \tsnap: PropTypes.bool.isRequired, \tenabled: PropTypes.bool.isRequired, \tsnapTo: PropTypes.func, \tshouldDisableSnap: PropTypes.func.isRequired,  \tonStart: PropTypes.func.isRequired, \tonComplete: PropTypes.func.isRequired, \tonSelect: PropTypes.func,  \tcurrentPositionStroke: PropTypes.string, \tcurrentPositionStrokeWidth: PropTypes.number, \tcurrentPositionstrokeOpacity: PropTypes.number, \tcurrentPositionRadius: PropTypes.number, \ttype: PropTypes.oneOf(['RECTANGLE']), \thoverText: PropTypes.object.isRequired,  \ttrends: PropTypes.array.isRequired,  \tappearance: PropTypes.shape({         isFill: true, \t\tstroke: PropTypes.string.isRequired, \t\tstrokeOpacity: PropTypes.number.isRequired, \t\tstrokeWidth: PropTypes.number.isRequired, \t\tstrokeDasharray: PropTypes.oneOf(strokeDashTypes), \t\tedgeStrokeWidth: PropTypes.number.isRequired, \t\tedgeFill: PropTypes.string.isRequired, \t\tedgeStroke: PropTypes.string.isRequired, \t}).isRequired };  Rectangle.defaultProps = { \ttype: &quot;RECTANGLE&quot;,  \tonStart: noop, \tonComplete: noop, \tonSelect: noop,  \tcurrentPositionStroke: &quot;#000000&quot;, \tcurrentPositionstrokeOpacity: 1, \tcurrentPositionStrokeWidth: 3, \tcurrentPositionRadius: 0,  \tshouldDisableSnap: e =&gt; (e.button === 2 || e.shiftKey), \thoverText: { \t\t...HoverTextNearMouse.defaultProps, \t\tenable: true, \t\tbgHeight: &quot;auto&quot;, \t\tbgWidth: &quot;auto&quot;, \t\ttext: &quot;Click to select object&quot;, \t\tselectedText: &quot;&quot;, \t}, \ttrends: [],  \tappearance: { \t\tstroke: &quot;#000000&quot;, \t\tstrokeOpacity: 1, \t\tstrokeWidth: 1, \t\tstrokeDasharray: &quot;Solid&quot;, \t\tedgeStrokeWidth: 1, \t\tedgeFill: &quot;#FFFFFF&quot;, \t\tedgeStroke: &quot;#000000&quot;, \t\tr: 6,                 fill: '#8AAFE2',                 fillOpacity: 0.7,                 text: '',         \t} };  export default Rectangle; <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/-3\/cm\/bc\/-3cmbcc9wsaxqramp8jc8m7ukf8.png\"><\/p>\n<p>  \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0438<br \/>  \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u044b. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u044b\u0435 \u043e\u043a\u043d\u0430 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<br \/>  \u0433\u0440\u0430\u0444\u0438\u043a\u0430. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043f\u0440\u043e\u0435\u043a\u0442, \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0435\u0433\u043e<br \/>  \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0438.<\/p>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435<\/h4>\n<p>  \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0440\u044f\u0434 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,<br \/>  componentWillReceiveProps. \u041a\u0430\u043a \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445<br \/>  \u0432\u0435\u0440\u0441\u0438\u044f\u0445 React (\u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 17). \u041f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438, \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0439 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442<br \/>  \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0442\u0440\u0443\u0434\u043e\u0437\u0430\u0442\u0440\u0430\u0442 \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h4>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h4>\n<p>  \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 React Stockcharts \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u0430 \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0443, \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430 \u0432\u044b\u0441\u043e\u043a\u0443\u044e<br \/>  \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432. \u041d\u0435 \u0431\u044b\u043b\u043e \u0437\u0430\u043c\u0435\u0447\u0435\u043d\u043e<br \/>  \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a \u043f\u0440\u0438 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430<br \/>  \u0433\u0440\u0430\u0444\u0438\u043a\u0435, \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u043d\u043b\u0430\u0439\u043d \u0440\u0435\u0436\u0438\u043c\u0435. \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0448\u0438\u0440\u043e\u043a\u043e\u043c\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0443,<br \/>  \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0430 \u043a\u0430\u043a \u0433\u043e\u0442\u043e\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u043e\u0432,<br \/>  \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u0430\u0439\u0442\u043e\u0432, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0449\u0438\u0445 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0441 \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0445 \u0440\u044b\u043d\u043a\u043e\u0432.<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/auriga\/blog\/505638\/\"> https:\/\/habr.com\/ru\/company\/auriga\/blog\/505638\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/company\/auriga\/blog\/505638\/\">\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438\u0437\u043b\u043e\u0436\u0435\u043d \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f React \u0434\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0441 \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0445 \u0440\u044b\u043d\u043a\u043e\u0432. \u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0432 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u0438 <a href=\"https:\/\/alpari.com\/ru\/beginner\/articles\/forex-trend-indicators\/\">\u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430\u043c\u0438<\/a>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0442\u043e\u0440\u0433\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438. \u0421\u0442\u0430\u0442\u044c\u044f \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u0442\u044c frontend \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0445 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ai\/pw\/yj\/aipwyjti1iiijr1gni8dhyo1hwk.png\" alt=\"image\">  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-305407","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/305407","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=305407"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/305407\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=305407"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=305407"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=305407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}