diff --git a/.eslintrc.js b/.eslintrc.js index 5d7c6b40890510..f4928df58f8f93 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -76,6 +76,31 @@ const restrictedImports = [ message: "edit-widgets is a WordPress top level package that shouldn't be imported into other packages", }, + { + name: 'codemirror', + message: + 'Please use dynamic import (`import()`) instead since it is a large dependency.', + }, + { + name: '@codemirror/commands', + message: + 'Please use dynamic import (`import()`) instead since it is a large dependency.', + }, + { + name: '@codemirror/lang-css', + message: + 'Please use dynamic import (`import()`) instead since it is a large dependency.', + }, + { + name: '@codemirror/lang-html', + message: + 'Please use dynamic import (`import()`) instead since it is a large dependency.', + }, + { + name: '@codemirror/view', + message: + 'Please use dynamic import (`import()`) instead since it is a large dependency.', + }, ]; module.exports = { diff --git a/package-lock.json b/package-lock.json index aa35cf655f0195..ebaa6965d679eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,11 @@ "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { + "@codemirror/commands": "6.3.3", + "@codemirror/lang-css": "6.2.1", + "@codemirror/lang-html": "6.4.8", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.0", "@wordpress/a11y": "file:packages/a11y", "@wordpress/annotations": "file:packages/annotations", "@wordpress/api-fetch": "file:packages/api-fetch", @@ -80,6 +85,7 @@ "@wordpress/warning": "file:packages/warning", "@wordpress/widgets": "file:packages/widgets", "@wordpress/wordcount": "file:packages/wordcount", + "codemirror": "6.0.1", "es-module-shims": "^1.8.2", "wicg-inert": "3.1.2" }, @@ -4076,6 +4082,124 @@ "react": "*" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz", + "integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", + "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.8.tgz", + "integrity": "sha512-tE2YK7wDlb9ZpAH6mpTPiYm6rhfdQKVDa5r9IwIFlwwgvVaKsCfuKKZoJGWsmMZIf3FQAuJ5CHMPLymOtg1hXw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", + "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz", + "integrity": "sha512-+5YyicIaaAZKU8K43IQi8TBy6mF6giGeWAH7N96Z5LC30Wm5JMjqxOYIE9mxwMG1NbhT2mA3l9hA4uuKUM3E5g==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz", + "integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + }, + "node_modules/@codemirror/view": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.0.tgz", + "integrity": "sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -5945,6 +6069,57 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/css": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.8.tgz", + "integrity": "sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.9.tgz", + "integrity": "sha512-MXxeCMPyrcemSLGaTQEZx0dBUH0i+RPl8RN5GwMAzo53nTsd/Unc/t5ZxACeQoyPUM5/GkPLRUs2WliOImzkRA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", + "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -22198,6 +22373,20 @@ "node": ">=0.10.0" } }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/collapse-white-space": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", @@ -23676,6 +23865,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-env": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-3.2.4.tgz", @@ -48071,6 +48265,11 @@ "webpack": "^5.0.0" } }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -50654,6 +50853,11 @@ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -53251,6 +53455,11 @@ "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", + "@codemirror/commands": "6.3.3", + "@codemirror/lang-css": "6.2.1", + "@codemirror/lang-html": "6.4.8", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.0", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@react-spring/web": "^9.4.5", @@ -53285,6 +53494,7 @@ "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", "classnames": "^2.3.1", + "codemirror": "6.0.1", "colord": "^2.7.0", "deepmerge": "^4.3.0", "diff": "^4.0.2", @@ -58601,6 +58811,118 @@ "mathjs": "^10.1.1" } }, + "@codemirror/autocomplete": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz", + "integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "@codemirror/commands": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", + "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "@codemirror/lang-css": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", + "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.0.0" + } + }, + "@codemirror/lang-html": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.8.tgz", + "integrity": "sha512-tE2YK7wDlb9ZpAH6mpTPiYm6rhfdQKVDa5r9IwIFlwwgvVaKsCfuKKZoJGWsmMZIf3FQAuJ5CHMPLymOtg1hXw==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "@codemirror/lang-javascript": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "@codemirror/language": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", + "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "@codemirror/lint": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz", + "integrity": "sha512-+5YyicIaaAZKU8K43IQi8TBy6mF6giGeWAH7N96Z5LC30Wm5JMjqxOYIE9mxwMG1NbhT2mA3l9hA4uuKUM3E5g==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/search": { + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz", + "integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/state": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + }, + "@codemirror/view": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.0.tgz", + "integrity": "sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==", + "requires": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -59925,6 +60247,57 @@ } } }, + "@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "@lezer/css": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.8.tgz", + "integrity": "sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/html": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.9.tgz", + "integrity": "sha512-MXxeCMPyrcemSLGaTQEZx0dBUH0i+RPl8RN5GwMAzo53nTsd/Unc/t5ZxACeQoyPUM5/GkPLRUs2WliOImzkRA==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/javascript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", + "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/lr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, "@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -68584,6 +68957,11 @@ "version": "file:packages/block-editor", "requires": { "@babel/runtime": "^7.16.0", + "@codemirror/commands": "6.3.3", + "@codemirror/lang-css": "6.2.1", + "@codemirror/lang-html": "6.4.8", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.0", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@react-spring/web": "^9.4.5", @@ -68618,6 +68996,7 @@ "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", "classnames": "^2.3.1", + "codemirror": "6.0.1", "colord": "^2.7.0", "deepmerge": "^4.3.0", "diff": "^4.0.2", @@ -73300,6 +73679,20 @@ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", "dev": true }, + "codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "collapse-white-space": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", @@ -74480,6 +74873,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "cross-env": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-3.2.4.tgz", @@ -93063,6 +93461,11 @@ "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", "dev": true }, + "style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, "style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -95006,6 +95409,11 @@ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, + "w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", diff --git a/package.json b/package.json index e87baf3215cb5c..1a4687ddbfedac 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,11 @@ "IS_GUTENBERG_PLUGIN": true }, "dependencies": { + "@codemirror/commands": "6.3.3", + "@codemirror/lang-css": "6.2.1", + "@codemirror/lang-html": "6.4.8", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.0", "@wordpress/a11y": "file:packages/a11y", "@wordpress/annotations": "file:packages/annotations", "@wordpress/api-fetch": "file:packages/api-fetch", @@ -92,6 +97,7 @@ "@wordpress/warning": "file:packages/warning", "@wordpress/widgets": "file:packages/widgets", "@wordpress/wordcount": "file:packages/wordcount", + "codemirror": "6.0.1", "es-module-shims": "^1.8.2", "wicg-inert": "3.1.2" }, diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index dd4a58f1c91e3e..54eb6c87dae904 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -32,6 +32,11 @@ ], "dependencies": { "@babel/runtime": "^7.16.0", + "@codemirror/commands": "6.3.3", + "@codemirror/lang-css": "6.2.1", + "@codemirror/lang-html": "6.4.8", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.0", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@react-spring/web": "^9.4.5", @@ -66,6 +71,7 @@ "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", "classnames": "^2.3.1", + "codemirror": "6.0.1", "colord": "^2.7.0", "deepmerge": "^4.3.0", "diff": "^4.0.2", diff --git a/packages/block-editor/src/components/global-styles/advanced-panel.js b/packages/block-editor/src/components/global-styles/advanced-panel.js index bbb756cd78247d..c61b4a76fef68f 100644 --- a/packages/block-editor/src/components/global-styles/advanced-panel.js +++ b/packages/block-editor/src/components/global-styles/advanced-panel.js @@ -1,18 +1,75 @@ /** * WordPress dependencies */ -import { - TextareaControl, - Notice, - __experimentalVStack as VStack, -} from '@wordpress/components'; +import { Notice, __experimentalVStack as VStack, BaseControl } from '@wordpress/components'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import { useInstanceId } from '@wordpress/compose'; /** * Internal dependencies */ import { default as transformStyles } from '../../utils/transform-styles'; +import EditorView from './editor-view'; + +/** + * Returns the value that should be set for the code editor height + */ +function getEditorHeight() { + /** + * (height of all the elements in the sidebar except the editor) + (height of the header) + (height of the footer) + * Currently, it's desktop-optimized. + */ + const MARGIN = 234 + 60 + 25; + const editorHeight = window.innerHeight - MARGIN; + return editorHeight; +} + +/** + * Ensure the editor has at least min lines of code, as the editor will shrink to fit the content. + * + * @param {string} content - The content to ensure min lines for. + * @return {string} The content with at least min lines. + */ +function ensureMinLines( content ) { + const MIN_LINES = 10; + const LINE_HEIGHT = 18.2; // Height of one line in the editor + // const MARGIN = 53.4; + let requiredLines = MIN_LINES; + + const lines = content.split( '\n' ); + const contentLineCount = lines.length; + + const editorHeight = getEditorHeight(); + if ( editorHeight !== 0 ) { + // Calculate the minimum number of lines that should be displayed + const calcMinLineCount = Math.ceil( editorHeight / LINE_HEIGHT ); + requiredLines = Math.max( MIN_LINES, calcMinLineCount ); + } + + let result = content; + for ( let i = contentLineCount; i < requiredLines; i++ ) { + result += '\n'; + } + + return result; +} + +/** + * Ensure the editor has at most max height to allow scrolling by `overflow-y: scroll`. + * It needs to run after the editor DOM is mounted. + * + * @param {string} editorId - The id of the editor. + */ +function ensureMaxHeight(editorId) { + const editorHeight = getEditorHeight(); + if ( editorHeight !== 0 ) { + const editor = document.getElementById( editorId ); + if ( editor ) { + editor.style.height = `${ editorHeight }px`; + } + } +} export default function AdvancedPanel( { value, @@ -21,7 +78,8 @@ export default function AdvancedPanel( { } ) { // Custom CSS const [ cssError, setCSSError ] = useState( null ); - const customCSS = inheritedValue?.css; + const instanceId = useInstanceId( AdvancedPanel ); + function handleOnChange( newValue ) { onChange( { ...value, @@ -40,20 +98,18 @@ export default function AdvancedPanel( { } } } - function handleOnBlur( event ) { - if ( ! event?.target?.value ) { + function handleOnBlur( newValue ) { + if ( ! newValue ) { setCSSError( null ); return; } - // Check if the new value is valid CSS, and pass a wrapping selector // to ensure that `transformStyles` validates the CSS. Note that the // wrapping selector here is not used in the actual output of any styles. const [ transformed ] = transformStyles( - [ { css: event.target.value } ], + [ { css: newValue } ], '.for-validation-only' ); - setCSSError( transformed === null ? __( 'There is an error with your CSS structure.' ) @@ -68,15 +124,29 @@ export default function AdvancedPanel( { { cssError } ) } - handleOnChange( newValue ) } - onBlur={ handleOnBlur } - className="block-editor-global-styles-advanced-panel__custom-css-input" - spellCheck={ false } - /> + + ensureMaxHeight(instanceId), + content: ensureMinLines( inheritedValue?.css ), + onBlur: handleOnBlur, + onChange: handleOnChange, + mode: "css", + } + } + /> + ); } diff --git a/packages/block-editor/src/components/global-styles/editor-view.js b/packages/block-editor/src/components/global-styles/editor-view.js new file mode 100644 index 00000000000000..8d0775761c9185 --- /dev/null +++ b/packages/block-editor/src/components/global-styles/editor-view.js @@ -0,0 +1,144 @@ +/** + * WordPress dependencies + */ +import { VisuallyHidden } from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; +import { useEffect, useRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Dynamically import the language support for the editor. + * + * @param {string} mode - Language mode for the editor. Currently supports 'css' and 'html'. + */ +function importLanguageSupport( mode ) { + switch ( mode ) { + case 'css': + return import( '@codemirror/lang-css' ); + case 'html': + return import( '@codemirror/lang-html' ); + default: + return import( '@codemirror/lang-css' ); + } +} + +/** + * @typedef {Object} Config + * @property {Function} [callback] - Callback after the editor is initialized. + * @property {string} content - Text content of the editor. + * @property {Function} onChange - Callback for when the content changes. + * @property {Function} [onBlur] - Callback for when the editor loses focus. + * @property {string} mode - Language mode for the editor. Currently supports 'css' and 'html'. + */ + +/** + * EditorView provided by CodeMirror + * + * @param {Object} props + * @param {string} props.editorId + * @param {string} [props.editorInstructionsText] - Instructions text for accessibility. + * @param {Config} props.initialConfig - Initial configuration for the editor. This can only be used for the initial setup of the editor. + */ +const EditorView = ({ + editorId, + editorInstructionsText, + initialConfig: { + callback, + content, + onChange, + onBlur, + mode, + }, +}) => { + const editorRef = useRef(null); + const instanceId = useInstanceId( EditorView ); + useEffect( () => { + ( async () => { + /** + * Lazy load CodeMirror by using Webpack's dynamic import. + * This should be replaced with native dynamic import once it's supported. + * @see https://github.com/WordPress/gutenberg/pull/60155 + */ + const [{ EditorView: CmEditorView, basicSetup }, { indentWithTab }, { Compartment }, { keymap }, languageSupport] = + await Promise.all([ + import( 'codemirror' ), + import( '@codemirror/commands' ), + import( '@codemirror/state' ), + import( '@codemirror/view' ), + importLanguageSupport( mode ) + ]) + + if ( editorRef.current ) { + const isDarkTheme = editorRef.current.closest( '.is-dark-theme' ); + const theme = new Compartment(); + const view = new CmEditorView( { + doc: content, + extensions: [ + basicSetup, + languageSupport[mode](), + keymap.of( [ indentWithTab ] ), + CmEditorView.updateListener.of( ( editor ) => { + if ( editor.docChanged ) { + onChange( editor.state.doc.toString() ); + } + } ), + ...(onBlur ? + [CmEditorView.focusChangeEffect.of( + ( editorState, focusing ) => { + if ( ! focusing ) { + onBlur( editorState.doc.toString() ); + } + return null; + } + )] : []), + theme.of(CmEditorView.theme({}, { dark: isDarkTheme })), + ], + parent: editorRef.current, + } ); + + /** + * Observe `body` to detect dark theme changes. + */ + const observer = new window.MutationObserver( ( mutations ) => { + mutations.forEach( ( mutation ) => { + if ( mutation.attributeName === 'class' ) { + view.dispatch({ + effects: theme.reconfigure(CmEditorView.theme({}, { dark: editorRef.current.closest( '.is-dark-theme' ) })), + }); + } + }); + }); + observer.observe( + editorRef.current.closest( 'body' ), + { attributes: true } + ); + + if (typeof callback === 'function') { + callback(); + } + } + } )(); + // We only want to run this once when the editor is initialized, so we can ignore the dependency array. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [] ); + return ( + <> + {editorInstructionsText && ( + { editorInstructionsText } + { __( + `Press Escape then Tab to move focus out of the editor.` + ) } + )} +
+ + ); +}; + +export default EditorView; \ No newline at end of file diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 5263ca3332b250..6c12410722d7f3 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -174,3 +174,8 @@ export { useBlockCommands } from './use-block-commands'; * The following rename hint component can be removed in 6.4. */ export { default as ReusableBlocksRenameHint } from './inserter/reusable-block-rename-hint'; + +/** + * Code editor using CodeMirror. + */ +export { default as EditorView } from './global-styles/editor-view'; \ No newline at end of file diff --git a/packages/block-library/src/html/edit.js b/packages/block-library/src/html/edit.js index 3cf2ee08bb68b2..ba068849632e5d 100644 --- a/packages/block-library/src/html/edit.js +++ b/packages/block-library/src/html/edit.js @@ -3,11 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import { useContext, useState } from '@wordpress/element'; -import { - BlockControls, - PlainText, - useBlockProps, -} from '@wordpress/block-editor'; +import { BlockControls, useBlockProps, EditorView } from '@wordpress/block-editor'; import { ToolbarButton, Disabled, @@ -40,6 +36,10 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) { 'aria-describedby': isPreview ? instanceId : undefined, } ); + function onChange( newContent ) { + setAttributes( { content: newContent } ); + } + return (
@@ -73,11 +73,16 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) { ) : ( - setAttributes( { content } ) } - placeholder={ __( 'Write HTML…' ) } - aria-label={ __( 'HTML' ) } + <EditorView + editorId={'block-library-html__editor'} + editorInstructionsText={__( + `This editor allows you to input your custom HTML.` + )} + initialConfig={{ + content: attributes.content, + onChange, + mode: 'html', + }} /> ) } </div> diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss index 5cd5c355fe0dbe..22be157b309ff1 100644 --- a/packages/edit-site/src/components/global-styles/style.scss +++ b/packages/edit-site/src/components/global-styles/style.scss @@ -119,24 +119,21 @@ margin: $grid-unit-20; .components-v-stack { - flex: 1 1 auto; - - .block-editor-global-styles-advanced-panel__custom-css-input { - flex: 1 1 auto; - display: flex; - flex-direction: column; - - .components-base-control__field { - flex: 1 1 auto; - display: flex; - flex-direction: column; - - .components-textarea-control__input { - flex: 1 1 auto; - // CSS input is always LTR regardless of language. - /*rtl:ignore*/ - direction: ltr; - } + .block-editor-global-styles-advanced-panel__custom-css-editor { + overflow-y: scroll; + @include input-style__neutral(); + &:focus-within { + @include input-style__focus(); + } + } + .cm-editor { + // Override the default CodeMirror font-family. + .cm-line { + font-family: $editor-html-font; + } + // Hide the line numbers, as the space is limited. + .cm-gutters { + display: none; } } } diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index 0a4b8cef574464..9723078a11f4aa 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -160,6 +160,7 @@ module.exports = { } return `webpack://${ info.namespace }/${ info.resourcePath }`; }, + chunkFilename: './build/[name]/[name]-[contenthash].min.js', }, performance: { hints: false, // disable warnings about package sizes