{"id":381346,"date":"2024-06-29T03:37:02","date_gmt":"2024-06-29T03:37:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=381346"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=381346","title":{"rendered":"<span>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043d\u0430 React.js<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/645\/a3e\/f67\/645a3ef6736c81397c82d1124b173a4b.png\" width=\"1400\" height=\"600\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/645\/a3e\/f67\/645a3ef6736c81397c82d1124b173a4b.png\"\/><\/figure>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b\u0430, \u0438 \u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0<a href=\"https:\/\/kts.studio\/\">KTS<\/a>.<\/p>\n<p>\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043c\u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043d\u0430 React.js. \u0417\u0430\u0434\u0430\u0447\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043c\u043d\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439, \u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e \u0441\u0432\u043e\u0435\u043c \u043e\u043f\u044b\u0442\u0435. \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u044d\u0442\u0430\u043f\u043d\u043e \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0438 \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043a\u043e\u043c\u0443-\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0438 \u0441\u0438\u043b\u044b.<\/p>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<ol>\n<li>\n<p><a href=\"#1\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Draft.js?<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043a\u043e\u0434\u0438\u0442\u044c<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#5\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0431\u043b\u043e\u043a\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">Inline-\u0441\u0442\u0438\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#7\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#8\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0439 \u043a\u043b\u0430\u0432\u0438\u0448<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u042d\u043a\u0441\u043f\u043e\u0440\u0442-\u0438\u043c\u043f\u043e\u0440\u0442 HTML \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#10\">\u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<\/ol>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>1. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u041e\u043d \u0434\u043e\u043b\u0436\u0435\u043d:<\/p>\n<ol>\n<li>\n<p>\u0418\u043c\u0435\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u2014 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u0442.\u0434<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0438\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u2014 \u0436\u0438\u0440\u043d\u043e\u0441\u0442\u044c, \u043a\u0443\u0440\u0441\u0438\u0432 \u0438 \u0442.\u0434<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0441\u044b\u043b\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435\u043c \u043a\u043b\u0430\u0432\u0438\u0448<\/p>\n<\/li>\n<li>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 HTML<\/p>\n<\/li>\n<\/ol>\n<p>\u042f \u043f\u043e\u043d\u0438\u043c\u0430\u043b, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0441 \u043d\u0443\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u043e \u0438 \u0434\u043e\u043b\u0433\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u0447\u0430\u043b \u0438\u0441\u043a\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0440\u0435\u0448\u0430\u043b\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u043c\u0438 \u043a\u0430\u043c\u043d\u044f\u043c\u0438 \u043e\u043d\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c. \u0412\u044b\u044f\u0441\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0430\u043a\u0435\u0442 Draft.js.<\/p>\n<p><a class=\"anchor\" name=\"2\" id=\"2\"><\/a><\/p>\n<h2>2. \u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Draft.js?<\/h2>\n<p>\u0412 2016 \u0433\u043e\u0434\u0443 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u044b Facebook \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c \u043d\u0430\u00a0<a href=\"https:\/\/conf2016.reactjs.org\/schedule#rich-text-editing-with-react\">React Conf<\/a>. Draft.js \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c \u043d\u0430 React.js. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435, \u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u0438 \u043c\u043d\u043e\u0433\u043e\u043c \u0434\u0440\u0443\u0433\u043e\u043c. \u0410 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u0440\u043e\u0441\u0441\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. Draft.js \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438\u00a0<a href=\"https:\/\/immutable-js.com\/\">immutable.js<\/a>. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043c\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439.<\/p>\n<p>\u041a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u043d\u0430 Draft.js \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0438\u0437\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-content-block\/\">\u0431\u043b\u043e\u043a\u043e\u0432<\/a>. \u0411\u043b\u043e\u043a \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u0435\u043a\u0441\u0442\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e. \u041a\u0430\u0436\u0434\u043e\u043c\u0443 \u0431\u043b\u043e\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0435\u0433\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0430: \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0442\u0435\u0433, \u0441\u0442\u0438\u043b\u0438, \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442. \u041a \u043b\u044e\u0431\u043e\u043c\u0443 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0443 \u0442\u0435\u043a\u0441\u0442\u0430 \u0431\u043b\u043e\u043a\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-inline-styles\">inline-\u0441\u0442\u0438\u043b\u0438<\/a>\u00a0(\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0436\u0438\u0440\u043d\u044b\u043c). \u0410 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 Draft.js \u0435\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u044b\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-entities\/\">Entities<\/a>\u00a0\u0438\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-decorators\/\">Decorators<\/a>. \u0421 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u0441 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<p>\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430\u00a0<a href=\"https:\/\/draftjs.org\/\">\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0430\u0439\u0442\u0435<\/a>\u00a0\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/091\/85d\/dbc\/09185ddbc13b26c731440aea17441687.png\" width=\"1400\" height=\"600\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/091\/85d\/dbc\/09185ddbc13b26c731440aea17441687.png\"\/><\/figure>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<h2>3. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h2>\n<p>\u041d\u0430\u0448 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439:<\/p>\n<ol>\n<li>\n<p>\u041e\u043a\u043d\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u042d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0435\u0441\u044c API \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 React-hook useEditor.<\/p>\n<p>\u0414\u0438\u0437\u0430\u0439\u043d \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c, \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u0442 \u0441\u0430\u043c\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0440\u043e\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0442\u044c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 props-\u044b \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f React Context. C \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0441\u043c\u043e\u0433\u0443\u0442 \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0441\u0445\u0435\u043c\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/15b\/fe0\/f49\/15bfe0f499a1c58dd22abebc5171f4b2.jpg\" width=\"242\" height=\"442\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/15b\/fe0\/f49\/15bfe0f499a1c58dd22abebc5171f4b2.jpg\" data-blurred=\"true\"\/><\/figure>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h3>4. \u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043a\u043e\u0434\u0438\u0442\u044c<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u043d\u0430\u0441\u0442\u043e\u0439\u043a\u0443 \u0441\u0431\u043e\u0440\u043a\u0438, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f\u00a0<a href=\"https:\/\/create-react-app.dev\/\">Create React App<\/a>. \u041a\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Typescript, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u043c:<\/p>\n<pre><code class=\"bash\">$ npx create-react-app editor --template typescript <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"bash\">$ yarn add draft-js draft-convert <\/code><\/pre>\n<ul>\n<li>\n<p><em>draft-js<\/em>\u00a0\u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Draft.js<\/p>\n<\/li>\n<li>\n<p><em>draft-convert<\/em>\u00a0\u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 Draft.js. \u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e \u043d\u0435\u0439 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u0431\u043b\u043e\u043a\u0435 <a href=\"#9\">\u00ab\u042d\u043a\u0441\u043f\u043e\u0440\u0442-\u0438\u043c\u043f\u043e\u0440\u0442 html \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u00bb.<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0442\u0438\u043f\u044b \u0434\u043b\u044f \u0432\u044b\u0448\u0435\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a:<\/p>\n<pre><code class=\"bash\">$ yarn add -D @types\/draft-js @types\/draft-convert  <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<em>useEditor.tsx.<\/em>\u00a0\u0414\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c React-hook, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { EditorState } from 'draft-js'; import * as React from 'react';   export type EditorApi = {   state: EditorState;   onChange: (state: EditorState) => void; }  export const useEditor = (): EditorApi => {   const [state, setState] = React.useState(() => EditorState.createEmpty());      return React.useMemo(() => ({     state,     onChange: setState   }), [state]) } <\/code><\/pre>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430\u00a0<em>EditorState.createEmpty()<\/em>\u00a0\u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0435 \u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438. \u041f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u043b\u043e\u0433\u0438\u043a\u0443.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c React Context, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/context.tsx import * as React from 'react'; import { EditorApi, useEditor } from '.\/useEditor';  const TextEditorContext = React.createContext&lt;EditorApi | undefined>(undefined);  export const useEditorApi = () => {   const context = React.useContext(TextEditorContext);   if (context === undefined) {     throw new Error('useEditorApi must be used within TextEditorProvider');   }      return context; }  export const TextEditorProvider: React.FC = ({ children }) => {   const editorApi = useEditor();      return (     &lt;TextEditorContext.Provider value={editorApi}>       {children}     &lt;\/TextEditorContext.Provider>   ) } <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u0430\u00a0<em>useEditorApi:<\/em><\/p>\n<pre><code class=\"javascript\">const editorApi = useEditorApi(); <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/App.tsx function App() {   return (     &lt;TextEditorProvider >       &lt;ToolPanel \/>       &lt;TextEditor \/>     &lt;\/TextEditorProvider>   ); } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043e\u043a\u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u00a0<em>TextEditor:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/index.ts export { default } from '.\/TextEditor'; <\/code><\/pre>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx import * as React from 'react'; import { Editor } from 'draft-js'; import { useEditorApi } from '.\/context'; import cn from 'classnames'; import '.\/TextEditor.scss';  export type TextEditorProps = {   className?: string; }  const TextEditor: React.FC&lt;TextEditorProps> = ({ className }) => {   const { state, onChange } = useEditorApi();      return (     &lt;div className={cn(\"text-editor\", className)}>       &lt;Editor         placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448 \u0442\u0435\u043a\u0441\u0442\"         editorState={state}         onChange={onChange}       \/>     &lt;\/div>   ); }  export default TextEditor; <\/code><\/pre>\n<p>\u041c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u00a0<em>Editor<\/em>\u00a0\u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430\u00a0<em>Draft.js.<\/em>\u00a0\u0418\u043c\u0435\u043d\u043d\u043e \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u043e\u043b\u0435 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c. \u0421\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e c \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u043c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/index.ts export { default } from '.\/ToolPanel'; <\/code><\/pre>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/ToolPanel.tsx import * as React from 'react'; import { EditorApi } from '..\/useEditor'; import '.\/ToolPanel.scss';  const ToolPanel:React.FC&lt;ToolPanelProps> = ({ className } ) => {   return (     &lt;div className={cn('tool-panel', className)}>       {\/* \u0417\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0434 \u0434\u043b\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f *\/}     &lt;\/div>   ); }  export default ToolPanel; <\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h2>5. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0431\u043b\u043e\u043a\u043e\u0432<\/h2>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440, \u043d\u043e \u043f\u043e\u043a\u0430 \u043e\u043d \u043d\u0438 \u0447\u0435\u043c \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e\u00a0<em>textarea<\/em>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u043e\u0432.<\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Draft.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0431\u043b\u043e\u043a\u043e\u0432. \u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-custom-block-render-map\">\u0442\u0443\u0442<\/a>.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438\u00a0<em>config.ts:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts export enum BlockType {   \/* \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 *\/   h1 = 'header-one',   h2 = 'header-two',   h3 = 'header-three',   h4 = 'header-four',   h5 = 'header-five',   h6 = 'header-six',   \/* \u0426\u0438\u0442\u0430\u0442\u0430 *\/   blockquote = 'blockquote',   \/* \u0411\u043b\u043e\u043a \u0441 \u043a\u043e\u0434\u043e\u043c *\/   code = 'code-block',   \/* \u0421\u043f\u0438\u0441\u043e\u043a *\/   list = 'unordered-list-item',   \/* \u041d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a *\/   orderList = 'ordered-list-item',   \/* \u0421\u043d\u043e\u0441\u043a\u0430 *\/   cite = 'cite',   \/* \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0442\u0435\u043a\u0441\u0442 *\/   default = 'unstyled', } <\/code><\/pre>\n<p>\u041c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 enum \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u0442\u0438\u043f\u043e\u0432 \u0431\u043b\u043e\u043a\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u0431\u043b\u043e\u043a \u0441\u043d\u043e\u0441\u043a\u0438\u00a0<em>cite.<\/em>\u00a0\u041d\u043e \u043f\u043e\u043a\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0442\u0438\u043f\u044b \u0431\u043b\u043e\u043a\u043e\u0432. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0432 Draft.js, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-custom-block-render-map#configuring-block-render-map\">\u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0431\u043b\u043e\u043a\u043e\u0432<\/a>. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c\u00a0<em>Immutable.Map<\/em>\u00a0\u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430\u00a0<em>immutable<\/em>\u00a0(\u043e\u043d \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Draft.js). \u041c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e \u0431\u043b\u043e\u043a\u0430 (<em>cite<\/em>) \u0438 \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u0433\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 DOM. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u043a\u0430\u0440\u0442\u0443 \u0431\u043b\u043e\u043a\u043e\u0432 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u00a0\u00a0<em>DefaultDraftBlockRenderMap.merge:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts import Immutable from 'immutable'; import { DefaultDraftBlockRenderMap } from 'draft-js';  ...  const CUSTOM_BLOCK_RENDER_MAP = Immutable.Map({   [BlockType.cite]: {     element: 'cite', &lt;-- \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u0433\u0430   }, });  export const BLOCK_RENDER_MAP = DefaultDraftBlockRenderMap.merge(CUSTOM_BLOCK_RENDER_MAP); <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0443\u043a\u0430\u0436\u0435\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0443 \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0431\u043b\u043e\u043a\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx import { BLOCK_RENDER_MAP } from '.\/config'; ... &lt;Editor     ...     blockRenderMap={BLOCK_RENDER_MAP} \/> <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0443\u043c\u0435\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b \u0431\u043b\u043e\u043a\u043e\u0432. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u043c \u0431\u043b\u043e\u043a\u043e\u0432, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0445\u0443\u043a\u00a0<em>useEditor<\/em>\u00a0\u0434\u0432\u0430 \u043f\u043e\u043b\u044f:<\/p>\n<ul>\n<li>\n<p>toggleBlockType \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 \u0431\u043b\u043e\u043a\u0430;<\/p>\n<\/li>\n<li>\n<p>currentBlockType \u2014 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0442\u0438\u043f\u0430 \u0431\u043b\u043e\u043a\u0430, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0443 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>Draft.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u043b\u0430\u0441\u0441 RichUtils \u0441\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430. \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<em>toggleBlockType<\/em>\u00a0\u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c RichUtils.<em>toggleBlockType<\/em>, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u0431\u043b\u043e\u043a\u0430 \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u043c\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx export type EditorApi = {   ...   toggleBlockType: (blockType: BlockType) => void; }  export const useEditor = (html?: string): EditorApi => {     ...     const toggleBlockType = React.useCallback((blockType: BlockType) => {       setState((currentState) => RichUtils.toggleBlockType(currentState, blockType))     }, []);     ... } <\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\u00a0<em>currentBlockType<\/em>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { BlockType } from '.\/config';  export type EditorApi = {   ...   currentBlockType: BlockType; }  export const useEditor = (html?: string): EditorApi => {     ...     const currentBlockType = React.useMemo(() => {       \/* \u0428\u0430\u0433 1 *\/       const selection = state.getSelection();       \/* \u0428\u0430\u0433 2 *\/       const content = state.getCurrentContent();       \/* \u0428\u0430\u0433 3 *\/       const block = content.getBlockForKey(selection.getStartKey());       \/* \u0428\u0430\u0433 4 *\/       return block.getType() as BlockType;     }, [state]);     ... } <\/code><\/pre>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043a\u043e\u0434 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<p><strong>\u0428\u0430\u0433 1:<\/strong>\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u0443, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-selection-state\/#internaldocs-banner\">\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f<\/a>\u00a0\u043e \u0442\u043e\u043c, \u0433\u0434\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043a\u0430\u0440\u0435\u0442\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041d\u0430\u043f\u043e\u043c\u043d\u044e \u0447\u0442\u043e Draft.js \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u0438 \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0447\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432\u00a0<em>selection,<\/em>\u00a0\u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c\u00a0<em>toJS .<\/em><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5a3\/e98\/4a4\/5a3e984a42578eb7c46dbcc8218812fb.png\" width=\"197\" height=\"129\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5a3\/e98\/4a4\/5a3e984a42578eb7c46dbcc8218812fb.png\"\/><\/figure>\n<p><strong>\u0428\u0430\u0433 2:<\/strong>\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u0443\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-content-state\/#internaldocs-banner\">\u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430<\/a>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/879\/7ae\/bcc\/8797aebccad3d54a85a0fb90a7a92204.png\" width=\"847\" height=\"516\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/879\/7ae\/bcc\/8797aebccad3d54a85a0fb90a7a92204.png\"\/><\/figure>\n<p><strong>\u0428\u0430\u0433 3:<\/strong>\u00a0\u043f\u043e \u043a\u043b\u044e\u0447\u0443 \u043d\u0430\u0445\u043e\u0434\u0438\u043c \u0431\u043b\u043e\u043a, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0445\u043e\u0434\u0438\u043c\u0441\u044f. \u041a\u043b\u044e\u0447 \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0445\u0435\u0448, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b Draft.js.<\/p>\n<p><strong>\u0428\u0430\u0433 4:<\/strong>\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0438\u043f \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u0431\u043b\u043e\u043a\u0430.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430\u043c\u0438 \u0431\u043b\u043e\u043a\u043e\u0432.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/49b\/293\/8a2\/49b2938a267733b220af531e1371064c.gif\" width=\"740\" height=\"195\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/49b\/293\/8a2\/49b2938a267733b220af531e1371064c.gif\"\/><\/figure>\n<p><a class=\"anchor\" name=\"6\" id=\"6\"><\/a><\/p>\n<h2>6. Inline-c\u0442\u0438\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0430<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0438\u043b\u0435\u0439 \u043a \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0442\u0435\u043a\u0441\u0442\u0443.<\/p>\n<p>Draft.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0441\u0442\u0438\u043b\u0435\u0439 \u0434\u043b\u044f inline-c\u0442\u0438\u043b\u0435\u0439. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0438\u043c\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f. \u041d\u0430\u0448 \u0441\u0442\u0438\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c \u0446\u0432\u0435\u0442 \u0444\u043e\u043d\u0430 \u0438 \u0446\u0432\u0435\u0442 \u0448\u0440\u0438\u0444\u0442\u0430 \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043f\u0438\u0448\u0435\u043c \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0435 enum \u0434\u043b\u044f inline-c\u0442\u0438\u043b\u0435\u0439:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts export enum InlineStyle {   BOLD = 'BOLD',   ITALIC = 'ITALIC',   UNDERLINE = 'UNDERLINE',   ACCENT = 'ACCENT' \/\/ \u043a\u043e\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0438\u043b\u044f } <\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0441\u0442\u0438\u043b\u0438 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u0430\u0440\u0442\u0443:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts export const CUSTOM_STYLE_MAP = {   [InlineStyle.ACCENT]: {     backgroundColor: '#F7F6F3',     color: '#A41E68',   }, }; <\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0443 \u0432\u00a0<em>Editor:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx &lt;Editor   customStyleMap={CUSTOM_STYLE_MAP}   ... \/> <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0441\u0442\u0438\u043b\u0438. \u0414\u0430\u043b\u0435\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f inline-c\u0442\u0438\u043b\u044f\u043c\u0438:<\/p>\n<ul>\n<li>\n<p><em>toggleInlineStyle \u2014<\/em>\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\/\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f inline-c\u0442\u0438\u043b\u044f;<\/p>\n<\/li>\n<li>\n<p>hasInlineStyle \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u043a\u0430\u0436\u0435\u0442, \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d \u043b\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c \u0434\u043b\u044f \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043c\u044b \u0441\u043d\u043e\u0432\u0430 \u043c\u043e\u0436\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f\u00a0<em>RichUtils:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.ts const toggleInlineStyle = React.useCallback((inlineStyle: InlineStyle) => {   setState((currentState) => RichUtils.toggleInlineStyle(currentState, inlineStyle)) }, []);  const hasInlineStyle = React.useCallback((inlineStyle: InlineStyle) => {   \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043c\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u0439 Set \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u0441\u0442\u0438\u043b\u0435\u0439 *\/   const currentStyle = state.getCurrentInlineStyle();   \/* \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u043b\u0438 \u0442\u0430\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c *\/   return currentStyle.has(inlineStyle); }, [state]); <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f inline-c\u0442\u0438\u043b\u044f\u043c\u0438:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/ToolPanel.tsx const INLINE_STYLES_CODES = Object.values(InlineStyle);  const ToolPanel: React.FC = () => {   const { toggleInlineStyle, hasInlineStyle } = useEditorApi();      return (     &lt;div className=\"tool-panel\">       ...       {INLINE_STYLES_CODES.map((code) => {         const onMouseDown = (e) => {           e.preventDefault();           toggleInlineStyle(code);         };              return (           &lt;button             key={code}             className={cn(               \"tool-panel__item\",               hasInlineStyle(code) &amp;&amp; \"tool-panel__item_active\"             )}             onMouseDown={onMouseDown}           >             {code}           &lt;\/button>         );       })}     &lt;\/div>   ); };<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3f9\/02f\/c48\/3f902fc48f3d5bc8623da642c356a6cb.gif\" width=\"740\" height=\"195\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3f9\/02f\/c48\/3f902fc48f3d5bc8623da642c356a6cb.gif\"\/><\/figure>\n<p><a class=\"anchor\" name=\"7\" id=\"7\"><\/a><\/p>\n<h2>7. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0441\u0441\u044b\u043b\u043e\u043a. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-entity\/#internaldocs-banner\">Entities<\/a>. Entity \u2014 \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u0440\u0430\u043d\u0438\u0442 \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u043a\u0441\u0442\u0430. \u0423 \u043d\u0435\u0433\u043e \u0435\u0441\u0442\u044c \u0442\u0440\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430:<\/p>\n<ul>\n<li>\n<p>type \u2014 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 Entity<\/p>\n<\/li>\n<li>\n<p>mutability \u2014 \u0442\u0438\u043f \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0442\u0435\u043a\u0441\u0442\u0443 (\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043d\u0438\u0436\u0435)<\/p>\n<\/li>\n<li>\n<p>data \u2014 \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u043e\u0432\u00a0<em>Entity:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts export enum EntityType {   link = 'link', } <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u00a0<em>Link,<\/em>\u00a0\u0438\u043c\u0435\u043d\u043d\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 \u0441\u0441\u044b\u043b\u043e\u043a:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/Link\/Link.tsx import { ContentState } from 'draft-js'; import * as React from 'react';  type LinkProps = {   children: React.ReactNode;   contentState: ContentState;   entityKey: string; }  const Link: React.FC&lt;LinkProps> = ({ contentState, entityKey, children }) => {   \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c url \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 Entity *\/   const { url } = contentState.getEntity(entityKey).getData();      return (     &lt;a href={url}>       {children}     &lt;\/a>   ); }  export default Link; <\/code><\/pre>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043a\u0430\u0436\u0434\u043e\u0439\u00a0<em>Entity<\/em>\u00a0\u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0445\u0435\u0448-\u043a\u043b\u044e\u0447. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0435\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u043c \u043c\u0435\u0442\u0430-\u0434\u0430\u043d\u043d\u044b\u043c, \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a \u2014\u00a0<em>url.<\/em>\u00a0\u0415\u0433\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 Entity-\u0441\u0441\u044b\u043b\u043a\u0438.<\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b Draft.js \u043f\u043e\u043d\u0438\u043c\u0430\u043b, \u043a \u043a\u0430\u043a\u043e\u043c\u0443 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0443 \u0442\u0435\u043a\u0441\u0442\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u0430 Entity, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-decorators\/\">Decorators<\/a>.<\/p>\n<p>\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0440\u0435\u0445 \u0447\u0430\u0441\u0442\u0435\u0439:<\/p>\n<ul>\n<li>\n<p><em>strategy<\/em>\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u043a\u0441\u0442\u0430 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/p>\n<\/li>\n<li>\n<p><em>component<\/em>\u00a0\u2014 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c<\/p>\n<\/li>\n<li>\n<p>props \u2014 \u043f\u0440\u043e\u043f\u0441\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443.<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u0441\u0441\u044b\u043b\u043e\u043a:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/Link\/index.ts import Link from \".\/Link\"; import { EntityType } from \"..\/config\"; import { ContentBlock, ContentState, DraftDecorator } from \"draft-js\";  function findLinkEntities(   \/* \u0411\u043b\u043e\u043a \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 *\/   contentBlock: ContentBlock,   \/* \u0424\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0441 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u043c\u0438 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u043a\u0441\u0442\u0430 *\/   callback: (start: number, end: number) => void,   \/* \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043a\u0430\u0440\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 *\/   contentState: ContentState ): void {   \/* \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u0432 \u0431\u043b\u043e\u043a\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 *\/   contentBlock.findEntityRanges((character) => {     \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u043b\u044e\u0447 Entity *\/     const entityKey = character.getEntity();     \/* \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0447\u0442\u043e Entity \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u0442\u0438\u043f\u0443 Entity-\u0441\u0441\u044b\u043b\u043e\u043a *\/     return (       entityKey !== null &amp;&amp;       contentState.getEntity(entityKey).getType() === EntityType.link     );   }, callback); }  const decorator: DraftDecorator = {   strategy: findLinkEntities,   component: Link, };  export default decorator; <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0443:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { CompositeDecorator } from 'draft-js'; import LinkDecorator from '.\/Link';  \/* \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u043c \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0432 \u043e\u0434\u0438\u043d *\/ const decorator = new CompositeDecorator([LinkDecorator]);  export const useEditor = (): EditorApi => {     const [state, setState] = React.useState(() => EditorState.createEmpty(decorator));     ... } <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e! \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0443\u043c\u0435\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0438. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0443\u0447\u0438\u043c \u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0438\u043f\u043e\u0432 Entity, c\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043e\u0431\u0449\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u00a0<em>Entity.<\/em>\u00a0\u041e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0445\u0443\u043a\u0430 \u0438 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445. \u0414\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 \u0441\u0441\u044b\u043b\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e addLink, \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { EditorState, RichUtils } from 'draft-js';  export const useEditor = (): EditorApi => {     ...     const addEntity = React.useCallback((entityType: EntityType, data: Record&lt;string, string>, mutability: DraftEntityMutability) => {     setState((currentState) => {         \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 *\/       const contentState = currentState.getCurrentContent();         \/* \u0421\u043e\u0437\u0434\u0430\u0435\u043c Entity \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 *\/       const contentStateWithEntity = contentState.createEntity(entityType, mutability, data);         \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 Entity *\/       const entityKey = contentStateWithEntity.getLastCreatedEntityKey();         \/* \u041e\u0431\u044c\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u043d\u043e\u0432\u044b\u043c *\/       const newState = EditorState.set(currentState, { currentContent: contentStateWithEntity });       \/* \u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e *\/         return RichUtils.toggleLink(newState, newState.getSelection(), entityKey);     })   }, []);    const addLink = React.useCallback((url) => { addEntity(EntityType.link, { url }, 'MUTABLE')   }, [addEntity]);  return {     ...     addLink } } <\/code><\/pre>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e\u00a0<em>Entity<\/em>\u00a0\u0438\u043c\u0435\u0435\u0442 \u0442\u0440\u0438 \u0440\u0435\u0436\u0438\u043c\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0442\u0435\u043a\u0441\u0442\u0443:<\/p>\n<ol>\n<li>\n<p><em>MUTABLE \u2014<\/em>\u00a0\u0442\u0435\u043a\u0441\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d<\/p>\n<\/li>\n<li>\n<p><em>IMMUTABLE \u2014<\/em>\u00a0\u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0442\u0435\u043a\u0441\u0442\u0430\u00a0<em>Entity<\/em>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d<\/p>\n<\/li>\n<li>\n<p><em>SEGMENTED \u2014<\/em>\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a\u00a0<em>IMMUTABLE,<\/em>\u00a0\u0441 \u0442\u043e\u0439 \u043b\u0438\u0448\u044c \u0440\u0430\u0437\u043d\u0438\u0446\u0435\u0439, \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u043b\u043e\u0432, \u0442\u043e \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0441\u0438\u043c\u0432\u043e\u043b\u0430, \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0432\u043e, \u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u0441\u043b\u043e\u0432\u0430 \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043a\u00a0<em>Entity.<\/em>\u00a0(\u041f\u0440\u0438\u043c\u0435\u0440: &#171;\u041c\u0430\u0448\u0430 \u043c\u044b|\u043b\u0430 \u0440\u0430\u043c\u0443&#187; \u2192 [backspace] \u2192 &#171;\u041c\u0430\u0448\u0430 \u0440\u0430\u043c\u0443&#187;)<\/p>\n<\/li>\n<\/ol>\n<p>\u041c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438\u00a0<em>MUTABLE,<\/em>\u00a0\u0442\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442 \u0441\u0441\u044b\u043b\u043a\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u043c.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0441\u044b\u043b\u043a\u0438, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f\u00a0<em>prompt<\/em>:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/ToolPanel.tsx const ToolPanel: React.FC = () => {   const { addLink } = useEditorApi();    const handlerAddLink = () => {    const url = prompt('URL:');      if (url) {       addLink(url);     }   }  return (     ...     &lt;button onClick={handlerAddLink}>       \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443     &lt;\/button>     ... ); } <\/code><\/pre>\n<p>\u0421\u0441\u044b\u043b\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00a0<em>url<\/em>\u00a0\u0443\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0438.<\/p>\n<p>\u0414\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u043d\u0430 \u0441\u0441\u044b\u043b\u043a\u0443 \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u0441\u043d\u043e\u0432\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u043b\u0441\u044f\u00a0<em>prompt<\/em>\u00a0\u0441 \u043f\u0440\u0435\u0434\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c\u00a0<em>url<\/em>.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043a\u043b\u0438\u043a \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/Link\/Link.tsx import { useEditorApi } from '..\/context';  const Link: React.FC&lt;LinkProps> = ({ contentState, entityKey, children }) => {   const { setEntityData } = useEditorApi();   const { url} = contentState.getEntity(entityKey).getData();      const handlerClick = () => {     const newUrl = prompt('URL:', url);     if (newUrl) {       setEntityData(entityKey, { url: newUrl });     }   }      return (     &lt;a href={url} onClick={handlerClick}>       {children}     &lt;\/a>   ); } <\/code><\/pre>\n<p>\u0418 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435\u00a0<em>Entity:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx export const useEditor = (): EditorApi => {     ...     const setEntityData = React.useCallback((entityKey, data) => {     setState((currentState) => {       \/* \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 *\/       const content = currentState.getCurrentContent();       \/* \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 Entity \u0441 \u043d\u043e\u0432\u044b\u043c\u0438 *\/       const contentStateUpdated = content.mergeEntityData(         entityKey,         data,       )       \/* \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c \u0442\u0438\u043f\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f *\/       return EditorState.push(currentState, contentStateUpdated, 'apply-entity');     })   }, []);    export {     ...     setEntityData } } <\/code><\/pre>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-editor-change-type\/\">\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0438\u043f\u043e\u0432<\/a>\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u041a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b Draft.js \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u043b, \u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u0432\u0442\u043e\u0440 \u0438 \u043e\u0442\u043c\u0435\u043d\u0430.<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0435. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435\u00a0<em>Entity<\/em>\u00a0\u0432 \u043d\u0430\u0448\u0435\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c2c\/dff\/2e7\/c2cdff2e7592182fa842721391ca8b75.gif\" width=\"740\" height=\"195\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c2c\/dff\/2e7\/c2cdff2e7592182fa842721391ca8b75.gif\"\/><\/figure>\n<p><a class=\"anchor\" name=\"8\" id=\"8\"><\/a><\/p>\n<h2>8. \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0439 \u043a\u043b\u0430\u0432\u0438\u0448<\/h2>\n<p>\u0412\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448. \u0412 Draft.js \u0435\u0441\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f. \u0421\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448 \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u2014 \u043a\u043e\u043c\u0430\u043d\u0434\u0443. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044d\u0442\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434.<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx export const useEditor = (): EditorApi => { ...   const handleKeyCommand = React.useCallback((command: DraftEditorCommand, editorState: EditorState) => {   const newState = RichUtils.handleKeyCommand(editorState, command);      if (newState) {  setState(newState);   return 'handled'; }  return 'not-handled';   }, []);    export {     ...     handleKeyCommand  } } <\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430, \u0438 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0434\u043d\u043e \u0438\u0437 \u0434\u0432\u0443\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:<\/p>\n<ul>\n<li>\n<p><em>handled \u2014<\/em>\u00a0\u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0430<\/p>\n<\/li>\n<li>\n<p><em>not-handled<\/em>\u00a0\u2014 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 \u0433\u043e\u043d\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0441\u0442\u0430\u0440\u0430\u0442\u044c\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-editorstate-race-conditions\/\">\u0442\u0443\u0442<\/a>.<\/p>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<em>RichUtils.handleKeyCommand<\/em>\u00a0\u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0439 \u043a\u043b\u0430\u0432\u0438\u0448. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,\u00a0<em>ctrl + I<\/em>\u00a0\u0438\u043b\u0438\u00a0<em>cmd + I<\/em>\u00a0\u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0442\u0438\u043b\u044f\u00a0<em>ITALIC.<\/em><\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u043a\u0438\u043d\u0443\u0442\u044c \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx &lt;Editor   handleKeyCommand={editorApi.handleKeyCommand}   ... \/> <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0439 \u043a\u043b\u0430\u0432\u0438\u0448. \u041d\u043e \u043c\u044b \u043f\u043e\u0439\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0432\u043e\u044e \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0438\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0430\u00a0<em>ACCENT.<\/em>\u00a0\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u043c \u0442\u0438\u043f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434, \u0447\u0442\u043e\u0431\u044b ts \u043d\u0430 \u043d\u0430\u0441 \u043d\u0435 \u0440\u0443\u0433\u0430\u043b\u0441\u044f:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts import { DraftEditorCommand} from 'draft-js';  export type KeyCommand = DraftEditorCommand | 'accent'; \/\/ \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { KeyBindingUtil, getDefaultKeyBinding } from 'draft-js';  export const useEditor = (): EditorApi => { ...   const handlerKeyBinding = React.useCallback((e: React.KeyboardEvent) => {   \/* \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u0430 \u043b\u0438 \u043a\u043b\u0430\u0432\u0438\u0448\u0430 q + ctrl\/cmd *\/     if (e.keyCode === 81 &amp;&amp; KeyBindingUtil.hasCommandModifier(e)) {     return 'accent';     }        return getDefaultKeyBinding(e); }, []);  return {     ...     handlerKeyBinding  } } <\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u00a0<em>getDefaultKeyBinding.<\/em>\u00a0\u0418\u043c\u0435\u043d\u043d\u043e \u043e\u043d\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f\u043c \u043a\u043b\u0430\u0432\u0438\u0448. \u0415\u0449\u0435 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0443\u0442\u0438\u043b\u0438\u0442\u043e\u0439\u00a0<em>KeyBindingUtil.hasCommandModifier<\/em>\u00a0\u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0436\u0430\u043b \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u043a\u043b\u0430\u0432\u0438\u0448\u0443: ctrl \u0438\u043b\u0438 cmd. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043a\u043b\u0430\u0432\u0438\u0448\u00a0<em>q + ctrl<\/em>\u00a0\u0438\u043b\u0438\u00a0\u00a0<em>q + cmd<\/em>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u00a0<em>accent.<\/em>\u00a0\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0435\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u00a0<em>handleKeyCommand:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx const handleKeyCommand = React.useCallback((command: DraftEditorCommand, editorState: EditorState) => {    if (command === 'accent') {     toggleInlineStyle(InlineStyle.ACCENT);     return 'handled';   }   ... }, []); <\/code><\/pre>\n<p>\u0422\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448.<\/p>\n<p><a class=\"anchor\" name=\"9\" id=\"9\"><\/a><\/p>\n<h2>9. \u042d\u043a\u0441\u043f\u043e\u0440\u0442\/ \u0418\u043c\u043f\u043e\u0440\u0442 HTML \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440<\/h2>\n<p>\u041d\u0435\u043e\u0442\u044a\u0435\u043c\u043b\u0435\u043c\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430 \u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 Draft.js \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-data-conversion\/#converttoraw\">convertToRaw<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c json \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e \u0432\u0441\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dd2\/1a2\/e5b\/dd21a2e5b64f62ccc43726c8a1ea868e.png\" width=\"387\" height=\"503\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dd2\/1a2\/e5b\/dd21a2e5b64f62ccc43726c8a1ea868e.png\"\/><\/figure>\n<p>\u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c HTML. \u041f\u043e\u0431\u0440\u043e\u0434\u0438\u0432 \u043f\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0440\u0430\u043c \u0441\u0435\u0442\u0438 \u0438 npm, \u044f \u043d\u0430\u0448\u0435\u043b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u043b\u0438 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 HTML \u0438 \u0438\u0437 HTML. \u0412\u0437\u0432\u0435\u0441\u0438\u0432 \u0432\u0441\u0435 \u0437\u0430 \u0438 \u043f\u0440\u043e\u0442\u0438\u0432, \u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u043d\u0430 \u043f\u0430\u043a\u0435\u0442\u0435\u00a0<a href=\"https:\/\/github.com\/HubSpot\/draft-convert\"><em>draft-convert<\/em><\/a><em>\u00a0(\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u043f\u0440\u0438 \u0441\u0435\u0442\u0430\u043f\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430),<\/em>\u00a0\u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u043b \u0443\u0434\u043e\u0431\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<em>convert.tsx \u2014<\/em>\u00a0\u0432 \u043d\u0435\u043c \u043c\u044b \u043e\u0431\u044a\u044f\u0432\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0432 HTML \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0432 HTML. \u0412 \u043d\u0435\u0439 \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0445\u043e\u0442\u0438\u043c \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0432 \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/convert.tsx import { CUSTOM_STYLE_MAP, BlockType, EntityType, InlineStyle } from '.\/config';  export const stateToHTML = convertToHTML&lt;InlineStyle, BlockType>({   styleToHTML: (style) => {     switch (style) {       case InlineStyle.BOLD:         return &lt;strong \/>;       ...       case InlineStyle.ACCENT:         return &lt;span className=\"accent\" style={CUSTOM_STYLE_MAP[InlineStyle.ACCENT]} \/>;       default:         return null;     }   },   blockToHTML: (block) => {     switch (block.type) {       case BlockType.h1:           return &lt;h1 \/>;         ...       case BlockType.default:         return &lt;p \/>;       default:         return null;     }   },   entityToHTML: (entity, originalText) => {     if (entity.type === EntityType.link) {       return (         &lt;a href={entity.data.url}>           {originalText}         &lt;\/a>       );     }     return originalText;   }, }); <\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438\u043c \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0435\u0440\u043e\u043c, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u00a0<em>toHtml:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx export const useEditor = (): EditorApi => { ...   const toHtml = React.useCallback(() => {   return stateToHTML(state.getCurrentContent());   }, [state]);    return {     ...     toHtml  } } <\/code><\/pre>\n<p>\u0418 \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043d\u043e\u043f\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 HTML \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c:<\/p>\n<pre><code class=\"javascript\">&lt;button onClick={toHtml}>Print&lt;\/button> <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c HTML \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430. \u041e\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 HTML \u0432 \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/convert.tsx export const HTMLtoState = convertFromHTML&lt;DOMStringMap, BlockType>({   htmlToStyle: (nodeName, node, currentStyle) => {     if (nodeName === 'strong') {         return currentStyle.add(InlineStyle.BOLD);     }     ...     if (nodeName === 'span' &amp;&amp; node.classList.contains('accent')) {       return currentStyle.add(InlineStyle.ACCENT);     }      return currentStyle;   },   htmlToBlock(nodeName, node) {     switch (nodeName) {       case 'h1':         return BlockType.h1;       ...       case 'div':       case 'p':         return BlockType.default;       default:         return null;     }   },   htmlToEntity: (nodeName, node, createEntity) => {     if (nodeName === 'a' &amp;&amp; node.href) {       return createEntity(EntityType.link, 'MUTABLE', { url: node.href });     }      return undefined;    }, }); <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0435\u0440 \u0432 HTML \u043a \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c\u00a0<em>EditorState.createWithContent<\/em>\u00a0\u0438 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c\u00a0<em>EditorApi:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx ... export const useEditor = (html?: string): EditorApi => {   const [state, setState] = React.useState(() =>     html       ? EditorState.createWithContent(HTMLtoState(html), decorator)       : EditorState.createEmpty(decorator)   );   ... } <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c HTML \u0438 \u0443\u0440\u0430! \u041d\u0430\u0448 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0443\u043c\u0435\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c.<\/p>\n<p><a class=\"anchor\" name=\"10\" id=\"10\"><\/a><\/p>\n<h2>10. \u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/h2>\n<p>\u041f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435, \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0434\u043d\u0435\u043c \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u0441\u0435 \u0431\u043e\u043b\u0435\u0435 \u043d\u043e\u0432\u044b\u0435 \u0438 \u043d\u0430\u0432\u043e\u0440\u043e\u0447\u0435\u043d\u043d\u044b\u0435 api \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0432 \u0441\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u044f \u0440\u0435\u0448\u0430\u043b \u0434\u0430\u043d\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u043a\u0443. \u041f\u0440\u0430\u0432\u0434\u0430, \u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0431\u044b\u043b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d Mobx, \u0438 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0445\u0443\u043a\u0430 useEditor \u0431\u044b\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u043a\u043b\u0430\u0441\u0441\u0430. \u041d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u0448\u0438\u0440\u043d\u0430\u044f, \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0435 \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0438 \u0432\u0441\u0435\u043c\u0438 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0445\u0443\u043a\u0438 React.js.<\/p>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043f\u043e\u00a0<a href=\"https:\/\/github.com\/ktsstudio\/editor-react\">\u044d\u0442\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0442\u043e\u0436\u0435 \u0440\u0435\u0448\u0430\u043b\u0438 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u2014 \u043a\u0430\u043a\u0438\u043c\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0432\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c?<\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<hr\/>\n<p><strong>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u0440\u043e frontend \u0434\u043b\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/companies\/kts\/articles\/775948\/\">\u0420\u043e\u0430\u0434\u043c\u044d\u043f \u043f\u043e \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c\u0443 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0443 <\/a><\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b: <a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/669784\/\">\u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/a>, <a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/678034\/\">\u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 JS<\/a>, <a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/683240\/\">\u0434\u0435\u0440\u0435\u0432\u044c\u044f \u0441\u043f\u0435\u0446 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0438 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430<\/a><\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0440\u0443\u0433\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u0440\u043e frontend \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/569448\/\">\u041a\u0430\u043a \u043c\u044b \u0432\u044b\u0431\u0438\u0440\u0430\u043b\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u043c\u0438\u043a\u0440\u043e\u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u0432 \u0432 \u041b\u041a \u0434\u043b\u044f 260 000 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432 \u041f\u044f\u0442\u0451\u0440\u043e\u0447\u043a\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/560058\/\">\u041a\u0430\u043a \u043c\u044b \u0441\u0435\u0442\u0430\u043f\u0438\u043b\u0438 \u043c\u043e\u043d\u043e\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 SSR \u0438 SPA \u0434\u043b\u044f Otus.ru<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/kts\/blog\/566176\/\">\u041a\u0430\u043a \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f SSR \u043d\u0430 React<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/articles\/576682\/\"> https:\/\/habr.com\/ru\/articles\/576682\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b\u0430, \u0438 \u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0<a href=\"https:\/\/kts.studio\/\">KTS<\/a>.<\/p>\n<p>\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043c\u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043d\u0430 React.js. \u0417\u0430\u0434\u0430\u0447\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043c\u043d\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439, \u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e \u0441\u0432\u043e\u0435\u043c \u043e\u043f\u044b\u0442\u0435. \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u044d\u0442\u0430\u043f\u043d\u043e \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0438 \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043a\u043e\u043c\u0443-\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u0438 \u0441\u0438\u043b\u044b.<\/p>\n<p>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435:<\/p>\n<ol>\n<li>\n<p><a href=\"#1\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#2\">\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Draft.js?<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#3\">\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#4\">\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043a\u043e\u0434\u0438\u0442\u044c<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#5\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0431\u043b\u043e\u043a\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#6\">Inline-\u0441\u0442\u0438\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#7\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#8\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0439 \u043a\u043b\u0430\u0432\u0438\u0448<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#9\">\u042d\u043a\u0441\u043f\u043e\u0440\u0442-\u0438\u043c\u043f\u043e\u0440\u0442 HTML \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#10\">\u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/a><\/p>\n<\/li>\n<\/ol>\n<p><a class=\"anchor\" name=\"1\" id=\"1\"><\/a><\/p>\n<h2>1. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0421\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u041e\u043d \u0434\u043e\u043b\u0436\u0435\u043d:<\/p>\n<ol>\n<li>\n<p>\u0418\u043c\u0435\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0438\u043b\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u2014 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u0442.\u0434<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0438\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0430 \u2014 \u0436\u0438\u0440\u043d\u043e\u0441\u0442\u044c, \u043a\u0443\u0440\u0441\u0438\u0432 \u0438 \u0442.\u0434<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0441\u044b\u043b\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435\u043c \u043a\u043b\u0430\u0432\u0438\u0448<\/p>\n<\/li>\n<li>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 HTML<\/p>\n<\/li>\n<\/ol>\n<p>\u042f \u043f\u043e\u043d\u0438\u043c\u0430\u043b, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0441 \u043d\u0443\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u043e \u0438 \u0434\u043e\u043b\u0433\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u0447\u0430\u043b \u0438\u0441\u043a\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0440\u0435\u0448\u0430\u043b\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u043c\u0438 \u043a\u0430\u043c\u043d\u044f\u043c\u0438 \u043e\u043d\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c. \u0412\u044b\u044f\u0441\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0430\u043a\u0435\u0442 Draft.js.<\/p>\n<p><a class=\"anchor\" name=\"2\" id=\"2\"><\/a><\/p>\n<h2>2. \u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Draft.js?<\/h2>\n<p>\u0412 2016 \u0433\u043e\u0434\u0443 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u044b Facebook \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u043f\u0430\u043a\u0435\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c \u043d\u0430\u00a0<a href=\"https:\/\/conf2016.reactjs.org\/schedule#rich-text-editing-with-react\">React Conf<\/a>. Draft.js \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c \u043d\u0430 React.js. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435, \u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u0438 \u043c\u043d\u043e\u0433\u043e\u043c \u0434\u0440\u0443\u0433\u043e\u043c. \u0410 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u0440\u043e\u0441\u0441\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. Draft.js \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438\u00a0<a href=\"https:\/\/immutable-js.com\/\">immutable.js<\/a>. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043c\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439.<\/p>\n<p>\u041a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u043d\u0430 Draft.js \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0438\u0437\u00a0<a href=\"https:\/\/draftjs.org\/docs\/api-reference-content-block\/\">\u0431\u043b\u043e\u043a\u043e\u0432<\/a>. \u0411\u043b\u043e\u043a \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u0435\u043a\u0441\u0442\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e. \u041a\u0430\u0436\u0434\u043e\u043c\u0443 \u0431\u043b\u043e\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0435\u0433\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0430: \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0442\u0435\u0433, \u0441\u0442\u0438\u043b\u0438, \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442. \u041a \u043b\u044e\u0431\u043e\u043c\u0443 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0443 \u0442\u0435\u043a\u0441\u0442\u0430 \u0431\u043b\u043e\u043a\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-inline-styles\">inline-\u0441\u0442\u0438\u043b\u0438<\/a>\u00a0(\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0436\u0438\u0440\u043d\u044b\u043c). \u0410 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 Draft.js \u0435\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u044b\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-entities\/\">Entities<\/a>\u00a0\u0438\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-decorators\/\">Decorators<\/a>. \u0421 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 React-\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u0441 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<p>\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430\u00a0<a href=\"https:\/\/draftjs.org\/\">\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0430\u0439\u0442\u0435<\/a>\u00a0\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p><a class=\"anchor\" name=\"3\" id=\"3\"><\/a><\/p>\n<h2>3. \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h2>\n<p>\u041d\u0430\u0448 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439:<\/p>\n<ol>\n<li>\n<p>\u041e\u043a\u043d\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u042d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0435\u0441\u044c API \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 React-hook useEditor.<\/p>\n<p>\u0414\u0438\u0437\u0430\u0439\u043d \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c, \u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u0442 \u0441\u0430\u043c\u043e\u0433\u043e \u043e\u043a\u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0440\u043e\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0442\u044c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 props-\u044b \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f React Context. C \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0441\u043c\u043e\u0433\u0443\u0442 \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0441\u0445\u0435\u043c\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<figure class=\"\"><\/figure>\n<p><a class=\"anchor\" name=\"4\" id=\"4\"><\/a><\/p>\n<h3>4. \u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043a\u043e\u0434\u0438\u0442\u044c<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u043d\u0430\u0441\u0442\u043e\u0439\u043a\u0443 \u0441\u0431\u043e\u0440\u043a\u0438, \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f\u00a0<a href=\"https:\/\/create-react-app.dev\/\">Create React App<\/a>. \u041a\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 Typescript, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u043c:<\/p>\n<pre><code class=\"bash\">$ npx create-react-app editor --template typescript <\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"bash\">$ yarn add draft-js draft-convert <\/code><\/pre>\n<ul>\n<li>\n<p><em>draft-js<\/em>\u00a0\u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Draft.js<\/p>\n<\/li>\n<li>\n<p><em>draft-convert<\/em>\u00a0\u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 Draft.js. \u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e \u043d\u0435\u0439 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u0431\u043b\u043e\u043a\u0435 <a href=\"#9\">\u00ab\u042d\u043a\u0441\u043f\u043e\u0440\u0442-\u0438\u043c\u043f\u043e\u0440\u0442 html \u0432 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u00bb.<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0442\u0438\u043f\u044b \u0434\u043b\u044f \u0432\u044b\u0448\u0435\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a:<\/p>\n<pre><code class=\"bash\">$ yarn add -D @types\/draft-js @types\/draft-convert  <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b\u00a0<em>useEditor.tsx.<\/em>\u00a0\u0414\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c React-hook, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { EditorState } from 'draft-js'; import * as React from 'react';   export type EditorApi = {   state: EditorState;   onChange: (state: EditorState) => void; }  export const useEditor = (): EditorApi => {   const [state, setState] = React.useState(() => EditorState.createEmpty());      return React.useMemo(() => ({     state,     onChange: setState   }), [state]) } <\/code><\/pre>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430\u00a0<em>EditorState.createEmpty()<\/em>\u00a0\u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0435 \u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438. \u041f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u043b\u043e\u0433\u0438\u043a\u0443.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c React Context, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/context.tsx import * as React from 'react'; import { EditorApi, useEditor } from '.\/useEditor';  const TextEditorContext = React.createContext&lt;EditorApi | undefined>(undefined);  export const useEditorApi = () => {   const context = React.useContext(TextEditorContext);   if (context === undefined) {     throw new Error('useEditorApi must be used within TextEditorProvider');   }      return context; }  export const TextEditorProvider: React.FC = ({ children }) => {   const editorApi = useEditor();      return (     &lt;TextEditorContext.Provider value={editorApi}>       {children}     &lt;\/TextEditorContext.Provider>   ) } <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u0430\u00a0<em>useEditorApi:<\/em><\/p>\n<pre><code class=\"javascript\">const editorApi = useEditorApi(); <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/App.tsx function App() {   return (     &lt;TextEditorProvider >       &lt;ToolPanel \/>       &lt;TextEditor \/>     &lt;\/TextEditorProvider>   ); } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043e\u043a\u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u00a0<em>TextEditor:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/index.ts export { default } from '.\/TextEditor'; <\/code><\/pre>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx import * as React from 'react'; import { Editor } from 'draft-js'; import { useEditorApi } from '.\/context'; import cn from 'classnames'; import '.\/TextEditor.scss';  export type TextEditorProps = {   className?: string; }  const TextEditor: React.FC&lt;TextEditorProps> = ({ className }) => {   const { state, onChange } = useEditorApi();      return (     &lt;div className={cn(\"text-editor\", className)}>       &lt;Editor         placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0430\u0448 \u0442\u0435\u043a\u0441\u0442\"         editorState={state}         onChange={onChange}       \/>     &lt;\/div>   ); }  export default TextEditor; <\/code><\/pre>\n<p>\u041c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u00a0<em>Editor<\/em>\u00a0\u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430\u00a0<em>Draft.js.<\/em>\u00a0\u0418\u043c\u0435\u043d\u043d\u043e \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u043f\u043e\u043b\u0435 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c. \u0421\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e c \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u043c API \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/index.ts export { default } from '.\/ToolPanel'; <\/code><\/pre>\n<pre><code class=\"javascript\">\/\/ src\/ToolPanel\/ToolPanel.tsx import * as React from 'react'; import { EditorApi } from '..\/useEditor'; import '.\/ToolPanel.scss';  const ToolPanel:React.FC&lt;ToolPanelProps> = ({ className } ) => {   return (     &lt;div className={cn('tool-panel', className)}>       {\/* \u0417\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0434 \u0434\u043b\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f *\/}     &lt;\/div>   ); }  export default ToolPanel; <\/code><\/pre>\n<p><a class=\"anchor\" name=\"5\" id=\"5\"><\/a><\/p>\n<h2>5. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0431\u043b\u043e\u043a\u043e\u0432<\/h2>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440, \u043d\u043e \u043f\u043e\u043a\u0430 \u043e\u043d \u043d\u0438 \u0447\u0435\u043c \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e\u00a0<em>textarea<\/em>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u043e\u0432.<\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Draft.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0431\u043b\u043e\u043a\u043e\u0432. \u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-custom-block-render-map\">\u0442\u0443\u0442<\/a>.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438\u00a0<em>config.ts:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts export enum BlockType {   \/* \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 *\/   h1 = 'header-one',   h2 = 'header-two',   h3 = 'header-three',   h4 = 'header-four',   h5 = 'header-five',   h6 = 'header-six',   \/* \u0426\u0438\u0442\u0430\u0442\u0430 *\/   blockquote = 'blockquote',   \/* \u0411\u043b\u043e\u043a \u0441 \u043a\u043e\u0434\u043e\u043c *\/   code = 'code-block',   \/* \u0421\u043f\u0438\u0441\u043e\u043a *\/   list = 'unordered-list-item',   \/* \u041d\u0443\u043c\u0435\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a *\/   orderList = 'ordered-list-item',   \/* \u0421\u043d\u043e\u0441\u043a\u0430 *\/   cite = 'cite',   \/* \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0442\u0435\u043a\u0441\u0442 *\/   default = 'unstyled', } <\/code><\/pre>\n<p>\u041c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 enum \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u0442\u0438\u043f\u043e\u0432 \u0431\u043b\u043e\u043a\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u0431\u043b\u043e\u043a \u0441\u043d\u043e\u0441\u043a\u0438\u00a0<em>cite.<\/em>\u00a0\u041d\u043e \u043f\u043e\u043a\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0442\u0438\u043f\u044b \u0431\u043b\u043e\u043a\u043e\u0432. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0432 Draft.js, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c\u00a0<a href=\"https:\/\/draftjs.org\/docs\/advanced-topics-custom-block-render-map#configuring-block-render-map\">\u0438\u043c\u0443\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0431\u043b\u043e\u043a\u043e\u0432<\/a>. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c\u00a0<em>Immutable.Map<\/em>\u00a0\u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430\u00a0<em>immutable<\/em>\u00a0(\u043e\u043d \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Draft.js). \u041c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e \u0431\u043b\u043e\u043a\u0430 (<em>cite<\/em>) \u0438 \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u0433\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 DOM. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043c \u043a\u0430\u0440\u0442\u0443 \u0431\u043b\u043e\u043a\u043e\u0432 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u00a0\u00a0<em>DefaultDraftBlockRenderMap.merge:<\/em><\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/config.ts import Immutable from 'immutable'; import { DefaultDraftBlockRenderMap } from 'draft-js';  ...  const CUSTOM_BLOCK_RENDER_MAP = Immutable.Map({   [BlockType.cite]: {     element: 'cite', &lt;-- \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u0433\u0430   }, });  export const BLOCK_RENDER_MAP = DefaultDraftBlockRenderMap.merge(CUSTOM_BLOCK_RENDER_MAP); <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0443\u043a\u0430\u0436\u0435\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0443 \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0431\u043b\u043e\u043a\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/TextEditor.tsx import { BLOCK_RENDER_MAP } from '.\/config'; ... &lt;Editor     ...     blockRenderMap={BLOCK_RENDER_MAP} \/> <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0443\u043c\u0435\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0442\u0438\u043f\u044b \u0431\u043b\u043e\u043a\u043e\u0432. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u043c \u0431\u043b\u043e\u043a\u043e\u0432, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0445\u0443\u043a\u00a0<em>useEditor<\/em>\u00a0\u0434\u0432\u0430 \u043f\u043e\u043b\u044f:<\/p>\n<ul>\n<li>\n<p>toggleBlockType \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 \u0431\u043b\u043e\u043a\u0430;<\/p>\n<\/li>\n<li>\n<p>currentBlockType \u2014 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0442\u0438\u043f\u0430 \u0431\u043b\u043e\u043a\u0430, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0443 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>Draft.js \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043a\u043b\u0430\u0441\u0441 RichUtils \u0441\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430. \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<em>toggleBlockType<\/em>\u00a0\u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c RichUtils.<em>toggleBlockType<\/em>, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u0431\u043b\u043e\u043a\u0430 \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u043c\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx export type EditorApi = {   ...   toggleBlockType: (blockType: BlockType) => void; }  export const useEditor = (html?: string): EditorApi => {     ...     const toggleBlockType = React.useCallback((blockType: BlockType) => {       setState((currentState) => RichUtils.toggleBlockType(currentState, blockType))     }, []);     ... } <\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\u00a0<em>currentBlockType<\/em>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">\/\/ src\/TextEditor\/useEditor.tsx import { BlockType } from '.\/config';  export type EditorApi = {   ...   currentBlockType: BlockType; }  export const useEditor = (html?: string): EditorApi => {     ...     const currentBlockType = React.useMemo(() => {       \/* \u0428\u0430\u0433 1 *\/       const selection = state.getSelection();       \/* \u0428\u0430\u0433 2 *\/       const content = state.getCurrentContent();       \/* \u0428\u0430\u0433 3 *\/       const block =<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\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-381346","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381346","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=381346"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381346\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=381346"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=381346"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=381346"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}