From 9120171b0569581949f6b66c6f82120288dd5cc5 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Sun, 22 Dec 2024 01:03:52 +1100 Subject: [PATCH 001/268] Update edit menu interface. --- package-lock.json | 146 ++++++++------ package.json | 3 + src/css/common/_switch.scss | 75 ++++++++ src/css/common/_theme.scss | 45 +++-- src/css/common/_type-badges.scss | 28 ++- src/css/edit.scss | 148 ++------------ src/css/edit/{_types.scss => _editors.scss} | 67 +++---- src/css/edit/_form.scss | 76 ++++++++ src/css/edit/_sidebar.scss | 62 ++++++ src/css/edit/_tags.scss | 100 ---------- src/css/manage.scss | 117 ++--------- src/css/settings.scss | 1 - .../EditorSidebar/EditorSidebar.tsx | 51 +++++ .../actions}/DeleteButton.tsx | 7 +- .../actions}/ExportButtons.tsx | 4 +- .../EditorSidebar/actions/ShortcodeInfo.tsx | 109 +++++++++++ .../actions}/SubmitButtons.tsx | 91 +++------ .../controls/ActivationSwitch.tsx | 35 ++++ .../controls/MultisiteSharingSettings.tsx | 34 ++++ .../controls}/PriorityInput.tsx | 21 +- .../EditorSidebar/controls/RTLControl.tsx | 24 +++ .../EditorSidebar/controls/TagsInput.tsx | 31 +++ src/js/components/EditorSidebar/index.ts | 1 + .../SnippetEditor/SnippetEditor.tsx | 157 --------------- .../SnippetEditor/SnippetEditorToolbar.tsx | 41 ---- src/js/components/SnippetForm/SnippetForm.tsx | 55 +++--- .../SnippetForm/buttons/ActionButtons.tsx | 24 --- .../{SnippetEditor => fields}/CodeEditor.tsx | 42 ++-- .../CodeEditorShortcuts.tsx | 4 +- .../fields/MultisiteSharingSettings.tsx | 25 --- .../SnippetForm/fields/NameInput.tsx | 2 +- .../SnippetForm/fields/ScopeInput.tsx | 172 ----------------- .../fields/SnippetLocationInput.tsx | 67 +++++++ .../SnippetForm/fields/SnippetTypeInput.tsx | 85 ++++++++ .../SnippetForm/fields/TagsInput.tsx | 31 --- .../components/SnippetForm/page/Notices.tsx | 35 ++-- .../SnippetForm/page/PageHeading.tsx | 4 +- .../components/TagEditor/SuggestionList.tsx | 20 -- src/js/components/TagEditor/TagEditor.tsx | 181 ------------------ src/js/components/TagEditor/TagList.tsx | 21 -- src/js/components/TagEditor/index.ts | 1 - src/js/components/common/Button.tsx | 2 +- src/js/hooks/useSnippetSubmit.ts | 2 +- src/js/types/Snippet.ts | 2 + src/js/types/Window.ts | 2 - src/php/admin-menus/class-edit-menu.php | 4 +- 46 files changed, 974 insertions(+), 1281 deletions(-) create mode 100644 src/css/common/_switch.scss rename src/css/edit/{_types.scss => _editors.scss} (52%) create mode 100644 src/css/edit/_form.scss create mode 100644 src/css/edit/_sidebar.scss delete mode 100644 src/css/edit/_tags.scss create mode 100644 src/js/components/EditorSidebar/EditorSidebar.tsx rename src/js/components/{SnippetForm/buttons => EditorSidebar/actions}/DeleteButton.tsx (93%) rename src/js/components/{SnippetForm/buttons => EditorSidebar/actions}/ExportButtons.tsx (97%) create mode 100644 src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx rename src/js/components/{SnippetForm/buttons => EditorSidebar/actions}/SubmitButtons.tsx (61%) create mode 100644 src/js/components/EditorSidebar/controls/ActivationSwitch.tsx create mode 100644 src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx rename src/js/components/{SnippetForm/fields => EditorSidebar/controls}/PriorityInput.tsx (60%) create mode 100644 src/js/components/EditorSidebar/controls/RTLControl.tsx create mode 100644 src/js/components/EditorSidebar/controls/TagsInput.tsx create mode 100644 src/js/components/EditorSidebar/index.ts delete mode 100644 src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx delete mode 100644 src/js/components/SnippetForm/SnippetEditor/SnippetEditorToolbar.tsx delete mode 100644 src/js/components/SnippetForm/buttons/ActionButtons.tsx rename src/js/components/SnippetForm/{SnippetEditor => fields}/CodeEditor.tsx (60%) rename src/js/components/SnippetForm/{SnippetEditor => fields}/CodeEditorShortcuts.tsx (96%) delete mode 100644 src/js/components/SnippetForm/fields/MultisiteSharingSettings.tsx delete mode 100644 src/js/components/SnippetForm/fields/ScopeInput.tsx create mode 100644 src/js/components/SnippetForm/fields/SnippetLocationInput.tsx create mode 100644 src/js/components/SnippetForm/fields/SnippetTypeInput.tsx delete mode 100644 src/js/components/SnippetForm/fields/TagsInput.tsx delete mode 100644 src/js/components/TagEditor/SuggestionList.tsx delete mode 100644 src/js/components/TagEditor/TagEditor.tsx delete mode 100644 src/js/components/TagEditor/TagList.tsx delete mode 100644 src/js/components/TagEditor/index.ts diff --git a/package-lock.json b/package-lock.json index 3f7549f7..6c699962 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", "@wordpress/api-fetch": "^7.14.0", "@wordpress/block-editor": "^14.9.0", "@wordpress/blocks": "^14.3.0", @@ -27,6 +29,7 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-is": "^19.0.0", "react-select": "^5.9.0" }, "devDependencies": { @@ -2087,15 +2090,16 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", @@ -2110,14 +2114,15 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, @@ -2134,35 +2139,39 @@ } }, "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", "dependencies": { - "@emotion/memoize": "^0.8.1" + "@emotion/memoize": "^0.9.0" } }, "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, "node_modules/@emotion/react": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", - "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { @@ -2175,33 +2184,36 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", @@ -2214,27 +2226,31 @@ } }, "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", @@ -11357,6 +11373,12 @@ "react-dom": ">=16.4.0" } }, + "node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" + }, "node_modules/react-remove-scroll": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", diff --git a/package.json b/package.json index 91a20417..0a46b3f8 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ }, "dependencies": { "@codemirror/fold": "^0.19.4", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", "@wordpress/api-fetch": "^7.14.0", "@wordpress/block-editor": "^14.9.0", "@wordpress/blocks": "^14.3.0", @@ -51,6 +53,7 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-is": "^19.0.0", "react-select": "^5.9.0" }, "devDependencies": { diff --git a/src/css/common/_switch.scss b/src/css/common/_switch.scss new file mode 100644 index 00000000..59e14aac --- /dev/null +++ b/src/css/common/_switch.scss @@ -0,0 +1,75 @@ +$active-color: #2196f3; +$inactive-color: #ccc; + +.snippet-execution-button, +.snippet-activation-switch, +input[type=checkbox].switch { + display: block; + position: relative; +} + +.snippet-activation-switch, +input[type=checkbox].switch { + appearance: none; + border: 0; + outline: 0; + cursor: pointer; + margin-top: 5px; + width: 30px; + height: 17px; + border-radius: 34px; + background-color: #ccc; + text-align: left; + + &::before { + transition: all .4s; + content: ''; + height: 13px; + width: 13px; + display: inline-block; + margin: 2px; + background-color: white; + border-radius: 50%; + } + + &:checked { + background-color: $active-color; + + &::before { + transform: translateX(100%); + } + } +} + +.erroneous-snippet .snippet-activation-switch::before { + content: '!'; + transform: translateX(50%); + text-align: center; + font-weight: bold; + line-height: 1; + color: #bbb; +} + +.snippet-execution-button { + margin-left: 10px; + margin-top: 9px; + width: 0; + height: 0; + border-top: 9px solid transparent; + border-bottom: 9px solid transparent; + border-left: 10px solid $inactive-color; + transition: all 0.3s; + + &::before { + content: ''; + position: absolute; + top: -14px; + left: -21px; + bottom: -14px; + right: -8px; + border-radius: 50%; + border: 1.8px solid $inactive-color; + z-index: 2; + transition: all .3s; + } +} diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index a5003a97..76ac7cd1 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -7,27 +7,36 @@ $outline: #9e9e9e; $core: #0073aa; $pro: #ce0000; -$php-active: #0073aa; -$php-inactive: #579; -$php-background: #78c8e6; - -$css-inactive: #b452cd; -$css-active: #7d26cd; -$css-background: #551a8b; -$css-highlight: #8000ff; - -$html-active: #548b54; -$html-background: #548b54; -$html-highlight: $html-active; - -$js-inactive: #cd6600; -$js-active: #d44500; -$js-background: #cd6600; -$js-highlight: #cd6600; - $brand-discord: #5865f2; $brand-facebook: #3b5998; +$types: ( + php: ( + active: #0073aa, + inactive: #579, + background: #78c8e6, + highlight: #0073aa + ), + css: ( + active: #7d26cd, + inactive: #b452cd, + background: #551a8b, + highlight: #8000ff + ), + html: ( + active: #548b54, + inactive: #548b54, + background: #548b54, + highlight: #548b54 + ), + js: ( + active: #d44500, + inactive: #cd6600, + background: #cd6600, + highlight: #cd6600 + ) +); + @mixin link-colors($color, $lightness: 15%) { a { color: $color; diff --git a/src/css/common/_type-badges.scss b/src/css/common/_type-badges.scss index e63f2a94..f8c98927 100644 --- a/src/css/common/_type-badges.scss +++ b/src/css/common/_type-badges.scss @@ -1,3 +1,4 @@ +@use 'sass:map'; @use 'theme'; .nav-tab .badge, .snippet-type-badge, .go-pro-button .badge { @@ -8,8 +9,13 @@ border-radius: 5px; } -.nav-tab .badge, .button .snippet-type-badge, .go-pro-button .badge, -h1 .snippet-type-badge, h2 .snippet-type-badge, h3 .snippet-type-badge { +.nav-tab .badge, +.button .snippet-type-badge, +.go-pro-button .badge, +h1 .snippet-type-badge, +h2 .snippet-type-badge, +h3 .snippet-type-badge, +.snippet-type-option .badge { /* rtl:ignore */ margin-left: 3px; } @@ -19,14 +25,26 @@ h1 .snippet-type-badge, h2 .snippet-type-badge, h3 .snippet-type-badge { vertical-align: middle; } -$badges: (php: theme.$php-active, css: theme.$css-highlight, js: theme.$js-highlight, html: theme.$html-active); +.snippet-type-badge-inverted { + font-size: inherit; + padding: 3px 6px; +} + +@each $type, $colors in theme.$types { + $color: map.get($colors, 'highlight'); -@each $type, $color in $badges { - .nav-tab[data-snippet-type=#{$type}] .badge, .snippet-type-badge[data-snippet-type=#{$type}] { + .nav-tab[data-snippet-type=#{$type}] .badge, + .snippet-type-badge[data-snippet-type=#{$type}] { color: $color; border-color: currentColor; } + .snippet-type-badge-inverted[data-snippet-type=#{$type}] { + color: white; + background-color: $color; + border-color: $color; + } + .nav-tab-inactive[data-snippet-type=#{$type}]:hover .badge { color: $color; } diff --git a/src/css/edit.scss b/src/css/edit.scss index 546c2660..4c6a539c 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -1,78 +1,25 @@ +/** + * Styles for the edit snippet admin page. + */ + @use 'common/editor'; @use 'common/type-badges'; +@use 'common/switch'; + +@use 'edit/form'; +@use 'edit/sidebar'; +@use 'edit/editors'; + @use 'edit/tooltips'; -@use 'edit/types'; -@use 'edit/tags'; @use 'edit/upgrade-dialog'; @use 'edit/gpt'; -/** - * Custom styling for the single snippet admin page - */ - -.form-table th { - width: auto; -} - .notice.error blockquote { margin-bottom: 0; } -h2 { - /* Provide some decent space between the fields and titles. */ - margin: 25px 0 15px; - - label { - cursor: auto; - } -} - -h2:first-of-type, .submit-inline { - margin: 20px 0 10px; -} - -.saved-snippet { - &.inactive-snippet, &.active-snippet { - #title { - border-left-width: 4px; - } - } - - &.active-snippet #title { - border-left-color: #46b450; - } - - &.inactive-snippet #title { - border-left-color: #bbb; - } - - &.erroneous-snippet #title { - border-left-color: #dc3232; - } -} - -#snippet-form { - margin-top: 10px; - - #snippet-tags, textarea { - width: 100%; - font-family: monospace; - } -} - -/* Position the description heading on the same level as the editor buttons */ -label[for='snippet_description'] h3 div { - position: absolute; -} - -/* Add spacing in between the action buttons */ -.button + .button, -.generate-button + .button { - margin-left: .5em; -} - -h2 .button { - font-weight: normal; +.code-snippets-copy-text { + color: inherit; } .button svg { @@ -81,74 +28,3 @@ h2 .button { line-height: 1; margin: -2px 3px 0 0; } - -.submit, .submit-inline { - display: flex; -} - -.submit-inline { - float: right; - margin-bottom: 0; -} - -p.snippet-scope, .snippet-scope p { - margin-top: 15px; -} - -.snippet-description-container { - margin-top: 25px; - - .wp-editor-tools { - padding-top: 5px; - } - - .wp-editor-tabs { - float: left; - } -} - -.snippet-scope label, -.html-shortcode-options strong { - display: inline-block; - margin-right: 1.5em; -} - -.below-snippet-editor { - display: flex; - flex-flow: row wrap; - justify-content: space-between; - padding-top: 1px; -} - -.snippet-priority { - label { - font-weight: bold; - cursor: help; - font-size: 1.1em; - padding-right: 0.5em; - } - - input { - width: 4em; - } -} - -.snippet-editor { - position: relative; -} - -.CodeMirror { - width: 100%; -} - -.wrap h2.nav-tab-wrapper { - border-bottom: none; -} - -.code-snippets-copy-text { - color: inherit; -} - -.wrap .notice { - scroll-margin: 0.75em; -} diff --git a/src/css/edit/_types.scss b/src/css/edit/_editors.scss similarity index 52% rename from src/css/edit/_types.scss rename to src/css/edit/_editors.scss index b76c13c0..19b699dc 100644 --- a/src/css/edit/_types.scss +++ b/src/css/edit/_editors.scss @@ -1,4 +1,14 @@ +.snippet-editor { + position: relative; +} + +.CodeMirror, +.snippet-form textarea { + width: 100%; + font-family: monospace; +} + .CodeMirror-sizer { min-height: 300px !important; box-sizing: border-box; @@ -8,36 +18,16 @@ position: absolute; bottom: 0; } -} - -.snippet-scope { - display: none; - - .description { - display: block; - } -} - -.snippet-form.php-snippet { - .php-scopes-list { - display: block; - } - .CodeMirror-sizer { + .snippet-form.php-snippet & { padding-bottom: 0 !important; &::before { content: ''; } @@ -46,14 +36,8 @@ content: ''; } } -} - -.snippet-form.js-snippet { - .js-scopes-list { - display: block; - } - .CodeMirror-sizer { + .snippet-form.js-snippet & { &::before { content: ''; } } -} -.snippet-form.html-snippet { - .html-scopes-list { - display: block; - } - - .CodeMirror-sizer { + .snippet-form.html-snippet & { &::before { content: ''; } @@ -79,3 +57,20 @@ } } } + +.snippet-description-container { + margin-top: 25px; + + .wp-editor-tools { + padding-top: 5px; + } + + .wp-editor-tabs { + float: left; + } +} + +/* Position the description heading on the same level as the editor buttons */ +label[for='snippet_description'] h3 div { + position: absolute; +} diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss new file mode 100644 index 00000000..44a2212a --- /dev/null +++ b/src/css/edit/_form.scss @@ -0,0 +1,76 @@ + +.snippet-form { + margin-top: 10px; + max-width: 1440px; + display: grid; + grid-template-columns: auto 280px; + gap: 30px; + + @media (max-width: 1024px) { + grid-template-columns: 1fr; + } + + #titlediv #title { + height: 45px + } + + h2, h3 { + margin: 25px 0 15px; + font-size: 1.16em; + + label { + cursor: auto; + } + + .button { + font-weight: normal; + } + } +} + +.above-editor-container { + display: flex; + flex-flow: row wrap; + gap: 1em; + + > * { + flex: 1; + display: flex; + flex-flow: column; + min-width: 180px; + } +} + +.snippet-type-option { + display: flex; + align-items: center; + justify-content: space-between; + flex-flow: row; + + div.snippet-type-badge { + float: right; + } +} + +.conditions-editor-open .button { + font-weight: bold; + height: 100%; + display: flex; + align-items: center; + border-color: #ccc; + background-color: #fff;; + + .dashicons { + margin-right: 5px; + } + + .badge { + margin: 3px 0 3px auto; + text-transform: uppercase; + border: 1px solid currentColor; + border-radius: 4px; + line-height: 1; + padding: 5px; + font-size: smaller; + } +} diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss new file mode 100644 index 00000000..5aa6e580 --- /dev/null +++ b/src/css/edit/_sidebar.scss @@ -0,0 +1,62 @@ + +.snippet-editor-sidebar { + + .button-large { + height: 48px; + } + + .row-actions { + display: flex; + + .button { + background: none; + border: none; + } + } + + .delete-button { + color: #cc1818; + + &:hover { + color: #9e1313; + } + + &:focus { + color: #710d0d; + border-color: #710d0d; + } + } + + .box { + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + padding: 1.5em; + + display: flex; + flex-flow: column; + gap: 1em; + + h4 { + margin: 0.5em 0; + } + + > div { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + align-items: center; + } + } +} + +.snippet-priority input { + width: 4em; +} + +p.submit { + display: flex; + flex-flow: column; + gap: 1em; + margin-top: 1em; +} diff --git a/src/css/edit/_tags.scss b/src/css/edit/_tags.scss deleted file mode 100644 index 9b1dc91e..00000000 --- a/src/css/edit/_tags.scss +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Code based on Tagger, copyright (c) 2018-2022 Jakub T. Jankiewicz . - * Released under the MIT license. - */ - -.tagger { - border: 0; -} - -.tagger > ul { - display: flex; - width: 100%; - align-items: center; - padding: 4px 5px 0; - justify-content: space-between; - box-sizing: border-box; - height: auto; - flex: 0 0 auto; - overflow-y: auto; - margin: 0; - list-style: none; - border: 1px solid #dfdfdf; - border-radius: 3px; - background-color: #fff; -} - -.tagger > ul > li { - padding-bottom: 0.4rem; - margin: 0.4rem 5px 4px; - - &:not(.tagger-new) { - a, a:visited { - text-decoration: none; - color: black; - } - - > :first-child { - padding: 4px 4px 4px 8px; - background: #B1C3D7; - border: 1px solid #4181ed; - border-radius: 3px; - } - - > span, - > a > span { - white-space: nowrap; - } - } -} - -.tagger li a.close { - padding: 4px; - margin-left: 4px; - - &:hover { - color: white; - } -} - -.tagger .tagger-new { - flex-grow: 1; - position: relative; - min-width: 40px; - - input { - border: none; - outline: none; - box-shadow: none; - width: 100%; - padding-left: 0; - box-sizing: border-box; - background: transparent; - } -} - -.tagger.wrap > ul { - flex-wrap: wrap; - justify-content: start; -} - -.tagger-new ul, -.tagger > ul > li:not(.tagger-new) > a, -.tagger li:not(.tagger-new) > span { - border-radius: 6px; - border: 1px solid #cad8f3; - background-color: #dee7f8; - - &:hover { - background-color: #bbcef1; - border-color: #6d95e0; - } -} - -.tagger-new ul, -.tagger > ul > li:not(.tagger-new) { - a, a:visited { - color: #555; - font-size: 1.1em; - } -} diff --git a/src/css/manage.scss b/src/css/manage.scss index 818b176b..dceecb89 100644 --- a/src/css/manage.scss +++ b/src/css/manage.scss @@ -2,11 +2,11 @@ * Custom styling for the snippets table */ -$active-color: #2196f3; -$inactive-color: #ccc; - +@use 'sass:map'; +@use 'sass:color'; @use 'common/theme'; @use 'common/type-badges'; +@use 'common/switch'; @use 'manage/cloud'; .column-name, @@ -48,89 +48,6 @@ $inactive-color: #ccc; } } -.snippet-execution-button, -.snippet-activation-switch { - display: block; - position: relative; -} - -.snippet-activation-switch { - margin-top: 5px; - width: 30px; - height: 17px; - border-radius: 34px; - background-color: #ccc; - - &::before { - transition: all .4s; - content: ''; - height: 13px; - width: 13px; - display: inline-block; - margin: 2px; - background-color: white; - border-radius: 50%; - } - - &:hover::before { - transform: translateX(40%); - } - - .snippets .active-snippet & { - background-color: $active-color; - - &::before { - transform: translateX(100%); - } - - &:hover::before { - transform: translateX(60%); - } - } - - .snippets .erroneous-snippet &::before { - content: '!'; - transform: translateX(50%); - text-align: center; - font-weight: bold; - line-height: 1; - color: #bbb; - } -} - -.snippet-execution-button { - margin-left: 10px; - margin-top: 9px; - width: 0; - height: 0; - border-top: 9px solid transparent; - border-bottom: 9px solid transparent; - border-left: 10px solid $inactive-color; - transition: all 0.3s; - - &::before { - content: ''; - position: absolute; - top: -14px; - left: -21px; - bottom: -14px; - right: -8px; - border-radius: 50%; - border: 1.8px solid $inactive-color; - z-index: 2; - transition: all .3s; - } - - &:hover, &:focus { - border-left-color: #579; - - &::before { - transform: scale(1.1); - border-color: #579; - } - } -} - .clear-filters { vertical-align: baseline !important; } @@ -226,50 +143,42 @@ $inactive-color: #ccc; } .inactive-snippet { - @include theme.link-colors(theme.$php-inactive); + @include theme.link-colors(map.get(theme.$types, 'php', 'inactive')); } .active-snippet { td, th { - background-color: rgba(theme.$php-background, 0.06); + background-color: rgba(map.get(theme.$types, 'php', 'background'), 0.06); } th.check-column { border-left: 2px solid #2ea2cc; } - - .snippet-activation-switch { - background-color: $active-color; - } } -@mixin snippet-type-colors($type, $active, $inactive, $background, $highlight) { +@each $type, $colors in theme.$types { .#{$type}-snippet { - @include theme.link-colors($inactive); + @include theme.link-colors(map.get($colors, 'inactive')); } .#{$type}-snippet.active-snippet { - @include theme.link-colors($active); + @include theme.link-colors(map.get($colors, 'active')); td, th { - background-color: rgba($background, 0.06); + background-color: rgba(map.get($colors, 'background'), 0.06); } .snippet-activation-switch { - background-color: $highlight; + background-color: map.get($colors, 'highlight'); } th.check-column { - border-left-color: $highlight; + border-left-color: map.get($colors, 'highlight'); } } } -@include snippet-type-colors(css, theme.$css-active, theme.$css-inactive, theme.$css-background, theme.$css-highlight); -@include snippet-type-colors(html, theme.$html-active, theme.$html-active, theme.$html-background, theme.$html-highlight); -@include snippet-type-colors(js, theme.$js-active, theme.$js-inactive, theme.$js-background, theme.$js-highlight); - @media screen and (max-width: 782px) { p.search-box { float: left; @@ -297,3 +206,7 @@ $inactive-color: #ccc; .code-snippets-notice a.notice-dismiss { text-decoration: none; } + +.active-snippet .snippet-activation-switch::before { + transform: translateX(100%); +} diff --git a/src/css/settings.scss b/src/css/settings.scss index bf92fdb2..a751285e 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -41,7 +41,6 @@ body.no-js { } body.js { - .settings-section-title { border: 0; clip: rect(1px, 1px, 1px, 1px); diff --git a/src/js/components/EditorSidebar/EditorSidebar.tsx b/src/js/components/EditorSidebar/EditorSidebar.tsx new file mode 100644 index 00000000..0f8ad415 --- /dev/null +++ b/src/js/components/EditorSidebar/EditorSidebar.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { Spinner } from '@wordpress/components' +import { isRTL } from '@wordpress/i18n' +import { useSnippetForm } from '../../hooks/useSnippetForm' +import { isNetworkAdmin } from '../../utils/general' +import { getSnippetType } from '../../utils/snippets' +import { Notices } from '../SnippetForm/page/Notices' +import { ShortcodeInfo } from './actions/ShortcodeInfo' +import { MultisiteSharingSettings } from './controls/MultisiteSharingSettings' +import { ExportButtons } from './actions/ExportButtons' +import { SubmitButton } from './actions/SubmitButtons' +import { ActivationSwitch } from './controls/ActivationSwitch' +import { DeleteButton } from './actions/DeleteButton' +import { PriorityInput } from './controls/PriorityInput' +import { RTLControl } from './controls/RTLControl' +import { TagsInput } from './controls/TagsInput' + +export const EditorSidebar = () => { + const { snippet, isWorking } = useSnippetForm() + + return ( +
+
+ {snippet.id ? : null} + + {isNetworkAdmin() ? : null} + + {isRTL() ? : null} + + + + {window.CODE_SNIPPETS_EDIT?.tagOptions.enabled ? : null} + + {snippet.id ? +
+ + +
: null} +
+ +

+ + {isWorking ? : ''} +

+ + + + {'html' === getSnippetType(snippet) ? : null} +
+ ) +} diff --git a/src/js/components/SnippetForm/buttons/DeleteButton.tsx b/src/js/components/EditorSidebar/actions/DeleteButton.tsx similarity index 93% rename from src/js/components/SnippetForm/buttons/DeleteButton.tsx rename to src/js/components/EditorSidebar/actions/DeleteButton.tsx index e6571fe5..d896bdb5 100644 --- a/src/js/components/SnippetForm/buttons/DeleteButton.tsx +++ b/src/js/components/EditorSidebar/actions/DeleteButton.tsx @@ -14,9 +14,12 @@ export const DeleteButton: React.FC = () => { return ( <> diff --git a/src/js/components/SnippetForm/buttons/ExportButtons.tsx b/src/js/components/EditorSidebar/actions/ExportButtons.tsx similarity index 97% rename from src/js/components/SnippetForm/buttons/ExportButtons.tsx rename to src/js/components/EditorSidebar/actions/ExportButtons.tsx index 2438e3cc..cf5886b2 100644 --- a/src/js/components/SnippetForm/buttons/ExportButtons.tsx +++ b/src/js/components/EditorSidebar/actions/ExportButtons.tsx @@ -25,7 +25,7 @@ export const ExportButtons: React.FC = () => { } return ( - <> +
: ''} - +
) } diff --git a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx new file mode 100644 index 00000000..db2b474b --- /dev/null +++ b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx @@ -0,0 +1,109 @@ +import React, { useState } from 'react' +import { ExternalLink } from '@wordpress/components' +import { __ } from '@wordpress/i18n' +import { useSnippetForm } from '../../../hooks/useSnippetForm' +import { isNetworkAdmin } from '../../../utils/general' +import { buildShortcodeTag } from '../../../utils/shortcodes' +import { CopyToClipboardButton } from '../../common/CopyToClipboardButton' +import type { ShortcodeAtts } from '../../../utils/shortcodes' +import type { Dispatch, SetStateAction} from 'react' + +const SHORTCODE_TAG = 'code_snippet' + +interface ShortcodeOptions { + php: boolean + format: boolean + shortcodes: boolean +} + +const ShortcodeTag: React.FC<{ atts: ShortcodeAtts }> = ({ atts }) => +

+

{buildShortcodeTag(SHORTCODE_TAG, atts)}
+ + +

+ +interface ShortcodeOptionsProps { + optionLabels: [keyof ShortcodeOptions, string][] + options: ShortcodeOptions + setOptions: Dispatch> + isReadOnly: boolean +} + +const ShortcodeOptions: React.FC = ({ + optionLabels, + options, + setOptions, + isReadOnly +}) => +

+

{__('Shortcode Options', 'code-snippets')}

+
    + {optionLabels.map(([option, label]) => +
  • + +
  • + )} +
+

+ +export const ShortcodeInfo: React.FC = () => { + const { snippet, isReadOnly } = useSnippetForm() + const [options, setOptions] = useState(() => ({ + php: snippet.code.includes(' +

+ {__('There are multiple options for inserting this snippet into a post, page or other content.', 'code-snippets')} + {' '} + {snippet.id ? + // eslint-disable-next-line @stylistic/max-len + __('You can copy the below shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets') : + // eslint-disable-next-line @stylistic/max-len + __('After saving, you can copy a shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets')} + {' '} + + {__('Learn more', 'code-snippets')} + +

+ + {snippet.id ? + <> + + + + : null} + : null +} diff --git a/src/js/components/SnippetForm/buttons/SubmitButtons.tsx b/src/js/components/EditorSidebar/actions/SubmitButtons.tsx similarity index 61% rename from src/js/components/SnippetForm/buttons/SubmitButtons.tsx rename to src/js/components/EditorSidebar/actions/SubmitButtons.tsx index f1f62e15..64f6868d 100644 --- a/src/js/components/SnippetForm/buttons/SubmitButtons.tsx +++ b/src/js/components/EditorSidebar/actions/SubmitButtons.tsx @@ -8,52 +8,14 @@ import { useSnippetForm } from '../../../hooks/useSnippetForm' import type { Snippet } from '../../../types/Snippet' import type { ButtonProps } from '../../common/Button' -interface SubmitButtonProps extends ButtonProps { - inlineButtons?: boolean -} - -const SaveChangesButton: React.FC = ({ inlineButtons, ...props }) => +const SaveChangesButton: React.FC = ({ ...props }) => - -const SaveAndExecuteButton: React.FC = ({ inlineButtons, ...props }) => - - -const DeactivateButton: React.FC = ({ inlineButtons, ...props }) => - - -const ActivateButton: React.FC = ({ inlineButtons, ...props }) => - interface ActivateOrDeactivateButtonProps { @@ -70,24 +32,33 @@ const ActivateOrDeactivateButton: React.FC = ({ disabled, onActivate, onDeactivate, - inlineButtons, primaryActivate }) => { - const commonProps: SubmitButtonProps = { small: inlineButtons, type: 'submit', disabled, inlineButtons } - switch (true) { case snippet.shared_network && isNetworkAdmin(): return null case 'single-use' === snippet.scope: - return + return ( + + ) case snippet.active: - return + return ( + + ) default: case !snippet.active: - return + return ( + + ) } } @@ -110,8 +81,8 @@ const validateSnippet = (snippet: Snippet): undefined | string => { } } -const shouldActivateByDefault = (snippet: Snippet, inlineButtons?: boolean): boolean => - !inlineButtons && !!window.CODE_SNIPPETS_EDIT?.activateByDefault && +const shouldActivateByDefault = (snippet: Snippet): boolean => + !!window.CODE_SNIPPETS_EDIT?.activateByDefault && !snippet.active && 'single-use' !== snippet.scope && (!snippet.shared_network || !isNetworkAdmin()) @@ -136,16 +107,12 @@ const SubmitConfirmDialog: React.FC = ({ isOpen, onClo

{`${validationWarning} ${__('Continue?', 'code-snippets')}`}

-export interface SubmitButtonsProps { - inlineButtons?: boolean -} - -export const SubmitButton: React.FC = ({ inlineButtons }) => { +export const SubmitButton: React.FC = () => { const { snippet, isWorking, submitSnippet, submitAndActivateSnippet, submitAndDeactivateSnippet } = useSnippetForm() const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false) const [submitAction, setSubmitAction] = useState() const validationWarning = validateSnippet(snippet) - const activateByDefault = shouldActivateByDefault(snippet, inlineButtons) + const activateByDefault = shouldActivateByDefault(snippet) const handleSubmit = (submitAction: () => Promise): void => { if (validationWarning) { @@ -159,29 +126,27 @@ export const SubmitButton: React.FC = ({ inlineButtons }) => } return <> - {activateByDefault ? null : + {activateByDefault ? handleSubmit(submitSnippet)} disabled={isWorking} - inlineButtons={inlineButtons} - />} + /> : null} handleSubmit(submitAndActivateSnippet)} onDeactivate={() => handleSubmit(submitAndDeactivateSnippet)} /> - {activateByDefault ? + {activateByDefault ? null : handleSubmit(submitSnippet)} disabled={isWorking} - inlineButtons={inlineButtons} - /> : null} + />} { + const { snippet, isWorking, submitAndActivateSnippet, submitAndDeactivateSnippet } = useSnippetForm() + + return ( +
+

+ +

+ + { + (snippet.active ? + submitAndDeactivateSnippet() : + submitAndActivateSnippet()) + .then(() => undefined) + .catch(handleUnknownError) + }} + /> +
+ ) +} diff --git a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx new file mode 100644 index 00000000..9196c088 --- /dev/null +++ b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { __ } from '@wordpress/i18n' +import { useSnippetForm } from '../../../hooks/useSnippetForm' + +export const MultisiteSharingSettings: React.FC = () => { + const { snippet, setSnippet, isReadOnly } = useSnippetForm() + + return ( +
+

+ +

+ + {JSON.stringify(snippet.shared_network)} + + + setSnippet(previous => ({ + ...previous, + active: false, + shared_network: event.target.checked + }))} + /> +
+ ) +} diff --git a/src/js/components/SnippetForm/fields/PriorityInput.tsx b/src/js/components/EditorSidebar/controls/PriorityInput.tsx similarity index 60% rename from src/js/components/SnippetForm/fields/PriorityInput.tsx rename to src/js/components/EditorSidebar/controls/PriorityInput.tsx index 1dcefb73..95800c0e 100644 --- a/src/js/components/SnippetForm/fields/PriorityInput.tsx +++ b/src/js/components/EditorSidebar/controls/PriorityInput.tsx @@ -1,20 +1,24 @@ import React from 'react' import { __ } from '@wordpress/i18n' -import { getSnippetType } from '../../../utils/snippets' import { useSnippetForm } from '../../../hooks/useSnippetForm' -export const PriorityInput: React.FC = () => { - const { snippet, setSnippet, isReadOnly } = useSnippetForm() +export const PriorityInput = () => { + const { snippet, isReadOnly, setSnippet } = useSnippetForm() - return 'html' === getSnippetType(snippet) ? null : -

- +

+ +

+ { priority: parseInt(event.target.value, 10) }))} /> -

+ + ) } diff --git a/src/js/components/EditorSidebar/controls/RTLControl.tsx b/src/js/components/EditorSidebar/controls/RTLControl.tsx new file mode 100644 index 00000000..64496508 --- /dev/null +++ b/src/js/components/EditorSidebar/controls/RTLControl.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { __ } from '@wordpress/i18n' +import { useSnippetForm } from '../../../hooks/useSnippetForm' + +export const RTLControl: React.FC = () => { + const { codeEditorInstance } = useSnippetForm() + + return ( +
+

+ +

+ + +
+ ) +} diff --git a/src/js/components/EditorSidebar/controls/TagsInput.tsx b/src/js/components/EditorSidebar/controls/TagsInput.tsx new file mode 100644 index 00000000..86afbadf --- /dev/null +++ b/src/js/components/EditorSidebar/controls/TagsInput.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { __ } from '@wordpress/i18n' +import { FormTokenField } from '@wordpress/components' +import { useSnippetForm } from '../../../hooks/useSnippetForm' + +const options = window.CODE_SNIPPETS_EDIT?.tagOptions + +export const TagsInput: React.FC = () => { + const { snippet, setSnippet, isReadOnly } = useSnippetForm() + + return options?.enabled ? +
+

+ + { + setSnippet(previous => ({ + ...previous, + tags: tokens.map(token => 'string' === typeof token ? token : token.value) + })) + }} + /> +
: + null +} diff --git a/src/js/components/EditorSidebar/index.ts b/src/js/components/EditorSidebar/index.ts new file mode 100644 index 00000000..09684c9f --- /dev/null +++ b/src/js/components/EditorSidebar/index.ts @@ -0,0 +1 @@ +export * from './EditorSidebar' diff --git a/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx b/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx deleted file mode 100644 index 6cc99a4d..00000000 --- a/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useEffect } from 'react' -import { __, _x } from '@wordpress/i18n' -import { addQueryArgs } from '@wordpress/url' -import classnames from 'classnames' -import { SNIPPET_TYPES, SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' -import '../../../editor' -import { isLicensed } from '../../../utils/general' -import { getSnippetType, isProType } from '../../../utils/snippets' -import { useSnippetForm } from '../../../hooks/useSnippetForm' -import { CodeEditor } from './CodeEditor' -import type { SnippetScope, SnippetType } from '../../../types/Snippet' -import type { Editor, EditorConfiguration } from 'codemirror' - -interface SnippetTypeTabProps { - tabType: SnippetType - label: string - currentType: SnippetType - updateScope: (scope: SnippetScope) => void - openUpgradeDialog: VoidFunction -} - -const SnippetTypeTab: React.FC = ({ - tabType, - label, - currentType, - updateScope, - openUpgradeDialog -}) => - { - event.preventDefault() - openUpgradeDialog() - } - } : - { - href: addQueryArgs(window.location.href, { type: tabType }), - onClick: event => { - event.preventDefault() - const scope = SNIPPET_TYPE_SCOPES[tabType][0] - updateScope(scope) - } - } - }> - {`${label} `} - - {tabType} - - -export const TYPE_LABELS: Record = { - php: __('Functions', 'code-snippets'), - html: __('Content', 'code-snippets'), - css: __('Styles', 'code-snippets'), - js: __('Scripts', 'code-snippets') -} - -const EDITOR_MODES: Partial> = { - css: 'text/css', - js: 'javascript', - php: 'text/x-php', - html: 'application/x-httpd-php' -} - -interface SnippetTypeTabsProps { - codeEditor: Editor - snippetType: SnippetType - updateScope: (scope: SnippetScope) => void - openUpgradeDialog: VoidFunction -} - -const SnippetTypeTabs: React.FC = ({ - codeEditor, - updateScope, - snippetType, - openUpgradeDialog -}) => { - useEffect(() => { - codeEditor.setOption('lint' as keyof EditorConfiguration, 'php' === snippetType || 'css' === snippetType) - - if (snippetType in EDITOR_MODES) { - codeEditor.setOption('mode', EDITOR_MODES[snippetType]) - codeEditor.refresh() - } - }, [codeEditor, snippetType]) - - return ( -

- {SNIPPET_TYPES.map(type => - )} - - {!isLicensed() ? - { - event.preventDefault() - openUpgradeDialog() - }} - > - {_x('Upgrade to ', 'Upgrade to Pro', 'code-snippets')} - {_x('Pro', 'Upgrade to Pro', 'code-snippets')} - : - null} -

- ) -} - -export interface SnippetEditorProps { - openUpgradeDialog: VoidFunction -} - -export const SnippetEditor: React.FC = ({ openUpgradeDialog }) => { - const { snippet, setSnippet, codeEditorInstance } = useSnippetForm() - const snippetType = getSnippetType(snippet) - - return ( -
-

- -

- - {snippet.id || window.CODE_SNIPPETS_EDIT?.isPreview || !codeEditorInstance ? '' : - { - setSnippet(previous => ({ ...previous, scope })) - }} - />} - - -
- ) -} diff --git a/src/js/components/SnippetForm/SnippetEditor/SnippetEditorToolbar.tsx b/src/js/components/SnippetForm/SnippetEditor/SnippetEditorToolbar.tsx deleted file mode 100644 index 2ea1e9d9..00000000 --- a/src/js/components/SnippetForm/SnippetEditor/SnippetEditorToolbar.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import { Spinner } from '@wordpress/components' -import { __, isRTL } from '@wordpress/i18n' -import { SubmitButton } from '../buttons/SubmitButtons' -import { useSnippetForm } from '../../../hooks/useSnippetForm' - -const InlineActionButtons: React.FC = () => { - const { isWorking } = useSnippetForm() - - return ( - <> - {isWorking ? : ''} - - - ) -} - -const RTLControl: React.FC = () => { - const { codeEditorInstance } = useSnippetForm() - - return ( - <> - - - - - ) -} - -export const SnippetEditorToolbar: React.FC = () => -
- {window.CODE_SNIPPETS_EDIT?.extraSaveButtons ? : null} - {isRTL() ? : null} -
diff --git a/src/js/components/SnippetForm/SnippetForm.tsx b/src/js/components/SnippetForm/SnippetForm.tsx index 03b2d679..309b6f37 100644 --- a/src/js/components/SnippetForm/SnippetForm.tsx +++ b/src/js/components/SnippetForm/SnippetForm.tsx @@ -1,22 +1,17 @@ import React, { useState } from 'react' import classnames from 'classnames' -import { isNetworkAdmin } from '../../utils/general' +import { __ } from '@wordpress/i18n' import { createEmptySnippet, getSnippetType } from '../../utils/snippets' import { WithSnippetFormContext, useSnippetForm } from '../../hooks/useSnippetForm' -import { ActionButtons } from './buttons/ActionButtons' +import { Button } from '../common/Button' +import { EditorSidebar } from '../EditorSidebar' +import { SnippetLocationInput } from './fields/SnippetLocationInput' +import { SnippetTypeInput } from './fields/SnippetTypeInput' import { UpgradeDialog } from './page/UpgradeDialog' import { DescriptionEditor } from './fields/DescriptionEditor' -import { MultisiteSharingSettings } from './fields/MultisiteSharingSettings' import { NameInput } from './fields/NameInput' -import { PriorityInput } from './fields/PriorityInput' -import { ScopeInput } from './fields/ScopeInput' -import { TagsInput } from './fields/TagsInput' -import { Notices } from './page/Notices' import { PageHeading } from './page/PageHeading' -import { SnippetEditor } from './SnippetEditor/SnippetEditor' -import { SnippetEditorToolbar } from './SnippetEditor/SnippetEditorToolbar' - -const OPTIONS = window.CODE_SNIPPETS_EDIT +import { CodeEditor } from './fields/CodeEditor' const EditForm: React.FC = () => { const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false) @@ -24,8 +19,11 @@ const EditForm: React.FC = () => { return (
+

+ {__('Back to all snippets', 'code-snippets')} +

+ -
{ 'read-only-snippet': isReadOnly } )}> - +
+ + +
+ setIsUpgradeDialogOpen(true)} /> + + - - setIsUpgradeDialogOpen(true)} /> +
+

{__('Conditions', 'code-snippets')}

+ +
+
-
- - -
+ - {isNetworkAdmin() ? : null} - {OPTIONS?.enableDescription ? : null} - {OPTIONS?.tagOptions.enabled ? : null} + {window.CODE_SNIPPETS_EDIT?.enableDescription ? : null} +
- +
@@ -61,6 +68,6 @@ const EditForm: React.FC = () => { } export const SnippetForm: React.FC = () => - OPTIONS?.snippet ?? createEmptySnippet()}> + window.CODE_SNIPPETS_EDIT?.snippet ?? createEmptySnippet()}> diff --git a/src/js/components/SnippetForm/buttons/ActionButtons.tsx b/src/js/components/SnippetForm/buttons/ActionButtons.tsx deleted file mode 100644 index 48e132fa..00000000 --- a/src/js/components/SnippetForm/buttons/ActionButtons.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import { Spinner } from '@wordpress/components' -import { useSnippetForm } from '../../../hooks/useSnippetForm' -import { DeleteButton } from './DeleteButton' -import { ExportButtons } from './ExportButtons' -import { SubmitButton } from './SubmitButtons' - -export const ActionButtons: React.FC = () => { - const { snippet, isWorking } = useSnippetForm() - - return ( -

- - - {snippet.id ? - <> - - - : ''} - - {isWorking ? : ''} -

- ) -} diff --git a/src/js/components/SnippetForm/SnippetEditor/CodeEditor.tsx b/src/js/components/SnippetForm/fields/CodeEditor.tsx similarity index 60% rename from src/js/components/SnippetForm/SnippetEditor/CodeEditor.tsx rename to src/js/components/SnippetForm/fields/CodeEditor.tsx index 99ebca7a..619ca2f0 100644 --- a/src/js/components/SnippetForm/SnippetEditor/CodeEditor.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditor.tsx @@ -1,4 +1,6 @@ import React, { useEffect, useRef } from 'react' +import { __ } from '@wordpress/i18n' +import { handleUnknownError } from '../../../utils/errors' import { isMacOS } from '../../../utils/general' import { useSnippetForm } from '../../../hooks/useSnippetForm' import { CodeEditorShortcuts } from './CodeEditorShortcuts' @@ -26,28 +28,38 @@ export const CodeEditor: React.FC = () => { const extraKeys = codeEditorInstance.codemirror.getOption('extraKeys') const controlKey = isMacOS() ? 'Cmd' : 'Ctrl' + const onSave = () => { + submitSnippet() + .then(() => undefined) + .catch(handleUnknownError) + } + codeEditorInstance.codemirror.setOption('extraKeys', { ...'object' === typeof extraKeys ? extraKeys : undefined, - [`${controlKey}-S`]: submitSnippet, - [`${controlKey}-Enter`]: submitSnippet + [`${controlKey}-S`]: onSave, + [`${controlKey}-Enter`]: onSave }) } }, [submitSnippet, codeEditorInstance, snippet]) return ( -
- - - +
+

+ +
+ + + +
) } diff --git a/src/js/components/SnippetForm/SnippetEditor/CodeEditorShortcuts.tsx b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx similarity index 96% rename from src/js/components/SnippetForm/SnippetEditor/CodeEditorShortcuts.tsx rename to src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx index 4edca38e..7ed3b6d7 100644 --- a/src/js/components/SnippetForm/SnippetEditor/CodeEditorShortcuts.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx @@ -96,8 +96,8 @@ export const CodeEditorShortcuts: React.FC = ({ editor : 'Option' === mod ? - {KEYBOARD_KEYS.Option}{SEP} - : + {KEYBOARD_KEYS.Option}{SEP} + : <>{KEYBOARD_KEYS[modifier]}{SEP}} )} diff --git a/src/js/components/SnippetForm/fields/MultisiteSharingSettings.tsx b/src/js/components/SnippetForm/fields/MultisiteSharingSettings.tsx deleted file mode 100644 index 4754dc0c..00000000 --- a/src/js/components/SnippetForm/fields/MultisiteSharingSettings.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' -import { __ } from '@wordpress/i18n' -import { useSnippetForm } from '../../../hooks/useSnippetForm' - -export const MultisiteSharingSettings: React.FC = () => { - const { snippet, setSnippet, isReadOnly } = useSnippetForm() - - return <> -

{__('Sharing Settings', 'code-snippets')}

-

- -

- -} diff --git a/src/js/components/SnippetForm/fields/NameInput.tsx b/src/js/components/SnippetForm/fields/NameInput.tsx index 29da5faf..eb3ae0cf 100644 --- a/src/js/components/SnippetForm/fields/NameInput.tsx +++ b/src/js/components/SnippetForm/fields/NameInput.tsx @@ -18,7 +18,7 @@ export const NameInput: React.FC = () => { autoComplete="off" value={snippet.name} disabled={isReadOnly} - placeholder={__('Enter title here', 'code-snippets')} + placeholder={__('Enter snippet title', 'code-snippets')} onChange={event => setSnippet(previous => ({ ...previous, name: event.target.value }))} /> diff --git a/src/js/components/SnippetForm/fields/ScopeInput.tsx b/src/js/components/SnippetForm/fields/ScopeInput.tsx deleted file mode 100644 index bc1c5659..00000000 --- a/src/js/components/SnippetForm/fields/ScopeInput.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { ExternalLink } from '@wordpress/components' -import { __ } from '@wordpress/i18n' -import React, { useState } from 'react' -import { SNIPPET_TYPES, SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' -import { isNetworkAdmin } from '../../../utils/general' -import { buildShortcodeTag } from '../../../utils/shortcodes' -import { getSnippetType } from '../../../utils/snippets' -import { CopyToClipboardButton } from '../../common/CopyToClipboardButton' -import { useSnippetForm } from '../../../hooks/useSnippetForm' -import type { ShortcodeAtts } from '../../../utils/shortcodes' -import type { SnippetScope } from '../../../types/Snippet' -import type { Dispatch, SetStateAction} from 'react' - -const SHORTCODE_TAG = 'code_snippet' - -const SCOPE_ICONS: Record = { - 'global': 'admin-site', - 'admin': 'admin-tools', - 'front-end': 'admin-appearance', - 'single-use': 'clock', - 'content': 'shortcode', - 'head-content': 'editor-code', - 'footer-content': 'editor-code', - 'admin-css': 'dashboard', - 'site-css': 'admin-customizer', - 'site-head-js': 'media-code', - 'site-footer-js': 'media-code' -} - -const SCOPE_DESCRIPTIONS: Record = { - 'global': __('Run snippet everywhere', 'code-snippets'), - 'admin': __('Only run in administration area', 'code-snippets'), - 'front-end': __('Only run on site front-end', 'code-snippets'), - 'single-use': __('Only run once', 'code-snippets'), - 'content': __('Only display when inserted into a post or page.', 'code-snippets'), - 'head-content': __('Display in site section.', 'code-snippets'), - 'footer-content': __('Display at the end of the section, in the footer.', 'code-snippets'), - 'site-css': __('Site front-end styles', 'code-snippets'), - 'admin-css': __('Administration area styles', 'code-snippets'), - 'site-footer-js': __('Load JS at the end of the section', 'code-snippets'), - 'site-head-js': __('Load JS in the section', 'code-snippets') -} - -interface ShortcodeOptions { - php: boolean - format: boolean - shortcodes: boolean -} - -const ShortcodeTag: React.FC<{ atts: ShortcodeAtts }> = ({ atts }) => -

- {buildShortcodeTag(SHORTCODE_TAG, atts)} - - -

- -interface ShortcodeOptionsProps { - optionLabels: [keyof ShortcodeOptions, string][] - options: ShortcodeOptions - setOptions: Dispatch> - isReadOnly: boolean -} - -const ShortcodeOptions: React.FC = ({ - optionLabels, - options, - setOptions, - isReadOnly -}) => -

- {__('Shortcode Options: ', 'code-snippets')} - {optionLabels.map(([option, label]) => - - )} -

- -const ShortcodeInfo: React.FC = () => { - const { snippet, isReadOnly } = useSnippetForm() - const [options, setOptions] = useState(() => ({ - php: snippet.code.includes(' -

- {__('There are multiple options for inserting this snippet into a post, page or other content.', 'code-snippets')} - {' '} - {snippet.id ? - // eslint-disable-next-line @stylistic/max-len - __('You can copy the below shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets') : - // eslint-disable-next-line @stylistic/max-len - __('After saving, you can copy a shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets')} - {' '} - - {__('Learn more', 'code-snippets')} - -

- - {snippet.id ? - <> - - - - : null} - : null -} - -export const ScopeInput: React.FC = () => { - const { snippet, setSnippet, isReadOnly } = useSnippetForm() - - return <> -

{__('Scope', 'code-snippets')}

- - {SNIPPET_TYPES - .filter(type => !snippet.id || type === getSnippetType(snippet)) - .map(type => -

- {SNIPPET_TYPE_SCOPES[type].map(scope => - )} - - {'html' === type ? : null} -

- )} - -} diff --git a/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx b/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx new file mode 100644 index 00000000..4fc316d9 --- /dev/null +++ b/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx @@ -0,0 +1,67 @@ +import { __ } from '@wordpress/i18n' +import React from 'react' +import Select from 'react-select' +import { useSnippetForm } from '../../../hooks/useSnippetForm' +import { SNIPPET_TYPE_SCOPES, type SnippetScope } from '../../../types/Snippet' +import { getSnippetType } from '../../../utils/snippets' +import type { SelectOption } from '../../../types/SelectOption' + +const SCOPE_ICONS: Record = { + 'global': 'admin-site', + 'admin': 'admin-tools', + 'front-end': 'admin-appearance', + 'single-use': 'clock', + 'content': 'shortcode', + 'head-content': 'editor-code', + 'footer-content': 'editor-code', + 'admin-css': 'dashboard', + 'site-css': 'admin-customizer', + 'site-head-js': 'media-code', + 'site-footer-js': 'media-code' +} + +const SCOPE_DESCRIPTIONS: Record = { + 'global': __('Run everywhere', 'code-snippets'), + 'admin': __('Only run in administration area', 'code-snippets'), + 'front-end': __('Only run on site front-end', 'code-snippets'), + 'single-use': __('Only run once', 'code-snippets'), + 'content': __('Where inserted into a post or page', 'code-snippets'), + 'head-content': __('In site section', 'code-snippets'), + 'footer-content': __('In site footer (end of )', 'code-snippets'), + 'site-css': __('Site front-end', 'code-snippets'), + 'admin-css': __('Administration area', 'code-snippets'), + 'site-footer-js': __('In site footer (end of )', 'code-snippets'), + 'site-head-js': __('In site section', 'code-snippets') +} + +export const SnippetLocationInput: React.FC = () => { + const { snippet, setSnippet } = useSnippetForm() + + const options: SelectOption[] = SNIPPET_TYPE_SCOPES[getSnippetType(snippet)] + .map(scope => ({ + value: scope, + label: SCOPE_DESCRIPTIONS[scope] + })) + + return ( +
+

+ option.value === snippetType)} + styles={{ + menu: provided => ({ ...provided, zIndex: 9999 }), + input: provided => ({ ...provided, boxShadow: 'none' }) + }} + formatOptionLabel={SnippetTypeOption} + onChange={option => { + if (option && isProType(option.value) && !isLicensed()) { + openUpgradeDialog() + } else if (option) { + setSnippet(previous => ({ + ...previous, + scope: SNIPPET_TYPE_SCOPES[option.value][0] + })) + } + }} + /> +
+ ) +} diff --git a/src/js/components/SnippetForm/fields/TagsInput.tsx b/src/js/components/SnippetForm/fields/TagsInput.tsx deleted file mode 100644 index 92cf0a8e..00000000 --- a/src/js/components/SnippetForm/fields/TagsInput.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' -import { __ } from '@wordpress/i18n' -import { TagEditor } from '../../TagEditor' -import { useSnippetForm } from '../../../hooks/useSnippetForm' - -const options = window.CODE_SNIPPETS_EDIT?.tagOptions - -export const TagsInput: React.FC = () => { - const { snippet, setSnippet, isReadOnly } = useSnippetForm() - - return options?.enabled ? -
-

- -

- - setSnippet(previous => ({ ...previous, tags }))} - completions={options.availableTags} - allowSpaces={options.allowSpaces} - placeholder={__('Enter a list of tags; separated by commas.', 'code-snippets')} - /> -
: - null -} diff --git a/src/js/components/SnippetForm/page/Notices.tsx b/src/js/components/SnippetForm/page/Notices.tsx index 43d5eaae..0edde1f6 100644 --- a/src/js/components/SnippetForm/page/Notices.tsx +++ b/src/js/components/SnippetForm/page/Notices.tsx @@ -1,8 +1,8 @@ import classnames from 'classnames' -import React, { useEffect } from 'react' +import React from 'react' import { __, sprintf } from '@wordpress/i18n' import { useSnippetForm } from '../../../hooks/useSnippetForm' -import type { MouseEventHandler, ReactNode} from 'react' +import type { MouseEventHandler, ReactNode } from 'react' interface DismissibleNoticeProps { classNames?: classnames.Argument @@ -10,26 +10,17 @@ interface DismissibleNoticeProps { children?: ReactNode } -const DismissibleNotice: React.FC = ({ classNames, onRemove, children }) => { - useEffect(() => { - if (window.CODE_SNIPPETS_EDIT?.scrollToNotices) { - window.scrollTo({ top: 0, behavior: 'smooth' }) - } - }, []) - - return ( -
- <>{children} - - -
- ) -} +const DismissibleNotice: React.FC = ({ classNames, onRemove, children }) => +
+ <>{children} + + +
export const Notices: React.FC = () => { const { currentNotice, setCurrentNotice, snippet, setSnippet } = useSnippetForm() diff --git a/src/js/components/SnippetForm/page/PageHeading.tsx b/src/js/components/SnippetForm/page/PageHeading.tsx index b65d42e4..59fa580e 100644 --- a/src/js/components/SnippetForm/page/PageHeading.tsx +++ b/src/js/components/SnippetForm/page/PageHeading.tsx @@ -12,7 +12,7 @@ export const PageHeading: React.FC = () => {

{snippet.id ? __('Edit Snippet', 'code-snippets') : - __('Add New Snippet', 'code-snippets')} + __('Create New Snippet', 'code-snippets')} {snippet.id ? <>{' '} { @@ -22,7 +22,7 @@ export const PageHeading: React.FC = () => { window.document.title = window.document.title.replace( __('Edit Snippet', 'code-snippets'), - __('Add New Snippet', 'code-snippets') + __('Create New Snippet', 'code-snippets') ) window.history.replaceState({}, '', window.CODE_SNIPPETS?.urls.addNew) diff --git a/src/js/components/TagEditor/SuggestionList.tsx b/src/js/components/TagEditor/SuggestionList.tsx deleted file mode 100644 index 048deeb0..00000000 --- a/src/js/components/TagEditor/SuggestionList.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react' - -export interface SuggestionListProps { - id: string - suggestions: string[] - onSelect: (item: string) => void -} - -export const SuggestionList: React.FC = ({ id, suggestions, onSelect }) => -
- - {suggestions.map(suggestion => - -
diff --git a/src/js/components/TagEditor/TagEditor.tsx b/src/js/components/TagEditor/TagEditor.tsx deleted file mode 100644 index 488efb36..00000000 --- a/src/js/components/TagEditor/TagEditor.tsx +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Code based on Tagger, copyright (c) 2018-2022 Jakub T. Jankiewicz - * Released under the MIT license. - */ -import React, { useRef, useState } from 'react' -import { handleUnknownError } from '../../utils/errors' -import { SuggestionList } from './SuggestionList' -import { TagList } from './TagList' -import type { InputHTMLAttributes, KeyboardEventHandler } from 'react' - -const COMPLETION_MIN_LENGTH = 2 -const SPECIAL_CHARS_RE = /(?[-\\^$[\]()+{}?*.|])/g - -const escapeRegex = (str: string) => - str.replace(SPECIAL_CHARS_RE, '\\$1') - -const isNotEmpty = (value: string | null | undefined): value is string => - !['', '""', "''", '``', undefined, null].includes(value) - -const isTagSelected = (selected: string[], tag: string) => { - if (!selected.includes(tag)) { - return false - } - const re = new RegExp(`^${escapeRegex(tag)}`) - return 1 === selected.filter(test_tag => re.test(test_tag)).length -} - -export type TagEditorCompletions = string[] | ((value?: string) => Promise) | undefined - -const buildCompletions = (completions: TagEditorCompletions, value: string | undefined): Promise => { - if (!completions) { - return Promise.resolve(undefined) - } else if ('function' === typeof completions) { - return completions(value) - } else { - return Promise.resolve(completions) - } -} - -export interface TagEditorProps extends Omit, 'onChange'> { - id: string - tags: string[] - onChange: (tags: string[]) => void - tagLimit?: number - completions?: TagEditorCompletions - addOnBlur?: boolean - allowSpaces?: boolean - allowDuplicates?: boolean - completionMinLength?: number -} - -// eslint-disable-next-line max-lines-per-function -export const TagEditor: React.FC = ({ - id, - tags, - onChange, - tagLimit, - completions, - addOnBlur = false, - allowSpaces = true, - allowDuplicates = false, - completionMinLength = COMPLETION_MIN_LENGTH, - ...inputProps -}) => { - const inputRef = useRef(null) - const [inputValue, setInputValue] = useState('') - const [completionOpen, setCompletionOpen] = useState(false) - const [completionList, setCompletionList] = useState([]) - const [lastCompletionList, setLastCompletionList] = useState() - - const isTagLimit = () => Boolean(tagLimit && 0 < tagLimit && tags.length >= tagLimit) - - const addTag = (tag: string = inputValue.trim()) => { - const isTagValid = isNotEmpty(tag) && !isTagLimit() && (allowDuplicates || !tags.includes(tag)) - - if (isTagValid) { - onChange([...tags, tag]) - } - - setInputValue('') - return isTagValid - } - - const removeTag = (tag?: string) => - onChange(tag ? tags.filter(item => item !== tag) : tags.slice(0, -1)) - - const triggerCompletion = (openList = inputValue.length >= completionMinLength) => { - if (openList) { - buildCompletions(completions, inputValue) - .then(list => { - setLastCompletionList(completionList) - - if (list?.length) { - setCompletionList(allowDuplicates ? list : list.filter(item => !tags.includes(item))) - } - }) - .catch(handleUnknownError) - } - - setCompletionOpen(openList) - } - - const keyboardHandler: KeyboardEventHandler = event => { - const { key, ctrlKey, metaKey } = event - - console.log('keyboardHandler', key, ctrlKey, metaKey) - - switch (key) { - case 'Enter': - case ',': - event.preventDefault() - addTag() - break - - case 'Backspace': - if (!inputValue) { - event.preventDefault() - removeTag() - } - break - - case ' ': - if (ctrlKey || metaKey) { - event.preventDefault() - triggerCompletion(true) - - } else if (!allowSpaces) { - event.preventDefault() - addTag() - } - break - - case 'Tab': - if (!isTagLimit()) { - event.preventDefault() - } - break - } - } - - const inputHandler = () => { - if (completionOpen && lastCompletionList && isTagSelected(lastCompletionList, inputValue.trim())) { - if (addTag()) { - setCompletionOpen(false) - } - } else { - triggerCompletion() - } - } - - return ( -
-
    inputRef.current?.focus()}> - -
  • - addOnBlur ? addTag() : undefined} - onChange={event => setInputValue(event.target.value)} - onKeyDown={keyboardHandler} - onInput={inputHandler} - /> - !tags.includes(suggestion))} - onSelect={suggestion => { - addTag(suggestion) - setCompletionList([]) - setCompletionOpen(false) - }} - /> -
  • -
-
- ) -} diff --git a/src/js/components/TagEditor/TagList.tsx b/src/js/components/TagEditor/TagList.tsx deleted file mode 100644 index 50b8c5a5..00000000 --- a/src/js/components/TagEditor/TagList.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' - -export interface TagListProps { - tags: string[] - onRemove: (tag: string) => void -} - -export const TagList: React.FC = ({ tags, onRemove }) => - <> - {tags.map(tag => -
  • - - {tag} - { - onRemove(tag) - event.preventDefault() - }}>× - -
  • - )} - diff --git a/src/js/components/TagEditor/index.ts b/src/js/components/TagEditor/index.ts deleted file mode 100644 index 4645b215..00000000 --- a/src/js/components/TagEditor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TagEditor' diff --git a/src/js/components/common/Button.tsx b/src/js/components/common/Button.tsx index 02757769..6222ca00 100644 --- a/src/js/components/common/Button.tsx +++ b/src/js/components/common/Button.tsx @@ -14,7 +14,7 @@ export const Button: React.FC = ({ id, children, className, - name = 'submit', + name, primary = false, small = false, large = false, diff --git a/src/js/hooks/useSnippetSubmit.ts b/src/js/hooks/useSnippetSubmit.ts index 1f121474..75c409e3 100644 --- a/src/js/hooks/useSnippetSubmit.ts +++ b/src/js/hooks/useSnippetSubmit.ts @@ -66,7 +66,7 @@ export const useSnippetSubmit = ( if (snippet.id && result.id) { window.document.title = window.document.title.replace( - __('Add New Snippet', 'code-snippets'), + __('Create New Snippet', 'code-snippets'), __('Edit Snippet', 'code-snippets') ) diff --git a/src/js/types/Snippet.ts b/src/js/types/Snippet.ts index c03c8c4a..17d21b90 100644 --- a/src/js/types/Snippet.ts +++ b/src/js/types/Snippet.ts @@ -13,6 +13,8 @@ export interface Snippet { code_error?: [string, number] | null } +export type SnippetLocation = 'site-head' | 'site-footer' + export type SnippetType = typeof SNIPPET_TYPES[number] export type SnippetScope = typeof SNIPPET_SCOPES[number] diff --git a/src/js/types/Window.ts b/src/js/types/Window.ts index d8bb6f4a..6439b457 100644 --- a/src/js/types/Window.ts +++ b/src/js/types/Window.ts @@ -36,8 +36,6 @@ declare global { isPreview: boolean enableTags: boolean enableDownloads: boolean - scrollToNotices: boolean - extraSaveButtons: boolean activateByDefault: boolean enableDescription: boolean editorTheme: string diff --git a/src/php/admin-menus/class-edit-menu.php b/src/php/admin-menus/class-edit-menu.php index 48846563..350b258f 100644 --- a/src/php/admin-menus/class-edit-menu.php +++ b/src/php/admin-menus/class-edit-menu.php @@ -63,7 +63,7 @@ public function register() { $this->add_menu( code_snippets()->get_menu_slug( 'add' ), _x( 'Add New', 'menu label', 'code-snippets' ), - __( 'Add New Snippet', 'code-snippets' ) + __( 'Create New Snippet', 'code-snippets' ) ); } @@ -194,8 +194,6 @@ public function enqueue_assets() { 'isPreview' => isset( $_REQUEST['preview'] ), 'activateByDefault' => get_setting( 'general', 'activate_by_default' ), 'editorTheme' => get_setting( 'editor', 'theme' ), - 'scrollToNotices' => apply_filters( 'code_snippets/scroll_to_notices', true ), - 'extraSaveButtons' => apply_filters( 'code_snippets/extra_save_buttons', true ), 'enableDownloads' => apply_filters( 'code_snippets/enable_downloads', true ), 'enableDescription' => $desc_enabled, 'tagOptions' => apply_filters( From 4bebe071657fcaf3f4ef0d3ed314c85bc7cda0d8 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Sun, 22 Dec 2024 01:54:16 +1100 Subject: [PATCH 002/268] Add tooltips to multisite sharing and priority controls on edit page. --- .../common/{_editor.scss => _codemirror.scss} | 0 src/css/{edit => common}/_tooltips.scss | 42 ++++++------------- src/css/edit.scss | 10 +++-- src/css/edit/{_editors.scss => _editor.scss} | 35 +++++++++------- src/css/edit/_sidebar.scss | 4 ++ src/css/settings.scss | 2 +- .../controls/MultisiteSharingSettings.tsx | 9 +++- .../EditorSidebar/controls/PriorityInput.tsx | 14 ++++--- .../fields/CodeEditorShortcuts.tsx | 8 ++-- 9 files changed, 65 insertions(+), 59 deletions(-) rename src/css/common/{_editor.scss => _codemirror.scss} (100%) rename src/css/{edit => common}/_tooltips.scss (65%) rename src/css/edit/{_editors.scss => _editor.scss} (76%) diff --git a/src/css/common/_editor.scss b/src/css/common/_codemirror.scss similarity index 100% rename from src/css/common/_editor.scss rename to src/css/common/_codemirror.scss diff --git a/src/css/edit/_tooltips.scss b/src/css/common/_tooltips.scss similarity index 65% rename from src/css/edit/_tooltips.scss rename to src/css/common/_tooltips.scss index 5653726b..32a9fec3 100644 --- a/src/css/edit/_tooltips.scss +++ b/src/css/common/_tooltips.scss @@ -1,5 +1,9 @@ -.editor-help-tooltip { +.help-tooltip { + position: relative; +} + +.help-tooltip-anchor { cursor: help; padding: 0.3em 0.3em 0; display: inline-block; @@ -7,18 +11,11 @@ background: transparent !important; } -.snippet-editor-help { - position: absolute; - right: 5px; - top: 5px; - - &:hover .editor-help-text { - visibility: visible; - opacity: 1; - } +.help-tooltip .dashicons { + color: lightslategrey; } -.editor-help-text { +.help-tooltip-text { visibility: hidden; background-color: #555; color: #fff; @@ -34,6 +31,11 @@ white-space: nowrap; font-size: small; + .help-tooltip:hover > & { + visibility: visible; + opacity: 1; + } + &::after { content: ""; position: absolute; @@ -43,22 +45,4 @@ border: 5px solid transparent; border-bottom-color: #555; } - - td:first-child { - padding-right: 0.5em; - } - - .mac-key { - display: none; - } - - &.platform-mac { - .mac-key { - display: inline; - } - - .pc-key { - display: none; - } - } } diff --git a/src/css/edit.scss b/src/css/edit.scss index 4c6a539c..9e4a7f3b 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -2,15 +2,15 @@ * Styles for the edit snippet admin page. */ -@use 'common/editor'; +@use 'common/codemirror'; @use 'common/type-badges'; @use 'common/switch'; @use 'edit/form'; @use 'edit/sidebar'; -@use 'edit/editors'; +@use 'edit/editor'; -@use 'edit/tooltips'; +@use 'common/tooltips'; @use 'edit/upgrade-dialog'; @use 'edit/gpt'; @@ -28,3 +28,7 @@ line-height: 1; margin: -2px 3px 0 0; } + +.snippet-description-container { + margin-top: 25px; +} diff --git a/src/css/edit/_editors.scss b/src/css/edit/_editor.scss similarity index 76% rename from src/css/edit/_editors.scss rename to src/css/edit/_editor.scss index 19b699dc..5350ea23 100644 --- a/src/css/edit/_editors.scss +++ b/src/css/edit/_editor.scss @@ -1,8 +1,4 @@ -.snippet-editor { - position: relative; -} - .CodeMirror, .snippet-form textarea { width: 100%; @@ -58,19 +54,30 @@ } } -.snippet-description-container { - margin-top: 25px; +.snippet-editor { + position: relative; +} - .wp-editor-tools { - padding-top: 5px; +.snippet-editor-help { + position: absolute; + right: 5px; + top: 5px; + + td:first-child { + padding-right: 0.5em; } - .wp-editor-tabs { - float: left; + .mac-key { + display: none; } -} -/* Position the description heading on the same level as the editor buttons */ -label[for='snippet_description'] h3 div { - position: absolute; + .help-tooltip-text.platform-mac { + .mac-key { + display: inline; + } + + .pc-key { + display: none; + } + } } diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss index 5aa6e580..40be0869 100644 --- a/src/css/edit/_sidebar.scss +++ b/src/css/edit/_sidebar.scss @@ -27,6 +27,10 @@ } } + .help-tooltip { + margin: 0 auto 0 5px; + } + .box { background-color: #fff; border: 1px solid #ccc; diff --git a/src/css/settings.scss b/src/css/settings.scss index a751285e..4ae4cddd 100644 --- a/src/css/settings.scss +++ b/src/css/settings.scss @@ -1,4 +1,4 @@ -@use 'common/editor'; +@use 'common/codemirror'; $sections: general, editor, debug; diff --git a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx index 9196c088..2d00e810 100644 --- a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx +++ b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx @@ -6,14 +6,19 @@ export const MultisiteSharingSettings: React.FC = () => { const { snippet, setSnippet, isReadOnly } = useSnippetForm() return ( -
    +

    - {JSON.stringify(snippet.shared_network)} +
    + + + {__('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')} + +
    { const { snippet, isReadOnly, setSnippet } = useSnippetForm() return ( -
    +

    +
    + +
    + {__('Snippets with a lower priority number will run before those with a higher number.', 'code-snippets')} +
    +
    + = ({ editorTheme }) => -
    -
    - {_x('?', 'help tooltip', 'code-snippets')} -
    +
    + -
    +
    {Object.entries(shortcuts).map(([name, { label, mod, key }]) => From 7b12e59781e3afdeff6bd47ada379408cbc65f23 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Sun, 22 Dec 2024 01:55:35 +1100 Subject: [PATCH 003/268] Update version number to 3.7.0-dev.1. --- package-lock.json | 4 ++-- package.json | 2 +- src/code-snippets.php | 8 ++++---- src/readme.txt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c699962..6a391737 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-snippets", - "version": "3.6.6", + "version": "3.7.0-dev.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-snippets", - "version": "3.6.6", + "version": "3.7.0-dev.1", "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", diff --git a/package.json b/package.json index 0a46b3f8..5046218e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-snippets", "description": "Manage code snippets running on a WordPress-powered site through a graphical interface.", "homepage": "https://wordpress.org/plugins/code-snippets", - "version": "3.6.6", + "version": "3.7.0-dev.1", "main": "src/dist/edit.js", "directories": { "test": "tests" diff --git a/src/code-snippets.php b/src/code-snippets.php index c2b9752c..c259fcb2 100644 --- a/src/code-snippets.php +++ b/src/code-snippets.php @@ -8,14 +8,14 @@ * License: GPL-2.0-or-later * License URI: license.txt * Text Domain: code-snippets - * Version: 3.6.6.1 + * Version: 3.7.0-dev.1 * Requires PHP: 7.4 * Requires at least: 5.0 * - * @version 3.6.6.1 + * @version 3.7.0-dev.1 * @package Code_Snippets * @author Shea Bunge - * @copyright 2012-2023 Code Snippets Pro + * @copyright 2012-2024 Code Snippets Pro * @license GPL-2.0-or-later https://spdx.org/licenses/GPL-2.0-or-later.html * @link https://github.com/codesnippetspro/code-snippets * @@ -37,7 +37,7 @@ * * @const string */ - define( 'CODE_SNIPPETS_VERSION', '3.6.6.1' ); + define( 'CODE_SNIPPETS_VERSION', '3.7.0-dev.1' ); /** * The full path to the main file of this plugin. diff --git a/src/readme.txt b/src/readme.txt index 2d42d37a..f965c26d 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -4,7 +4,7 @@ Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later License URI: license.txt -Stable tag: 3.6.6.1 +Stable tag: 3.7.0-dev.1 Tested up to: 6.7.1 An easy, clean and simple way to enhance your site with code snippets. From 0c995dcdb79298378d5b0e379254f9a418b1bbe2 Mon Sep 17 00:00:00 2001 From: brandonjp Date: Sun, 2 Feb 2025 14:25:52 -0600 Subject: [PATCH 004/268] correcting laquo to lsaquo --- src/php/cloud/list-table-shared-ops.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php/cloud/list-table-shared-ops.php b/src/php/cloud/list-table-shared-ops.php index dd787409..dbc0b1b9 100644 --- a/src/php/cloud/list-table-shared-ops.php +++ b/src/php/cloud/list-table-shared-ops.php @@ -182,7 +182,7 @@ function cloud_lts_pagination( string $which, string $source, int $total_items, $page_links[] = ''; } else { $page_links[] = sprintf( - '%s', + '%s', esc_url( remove_query_arg( $source . '_page', $current_url ) ), esc_html__( 'First page', 'code-snippets' ) ); From b6941d64e82160f656dcbf51210663d00d216f7e Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Tue, 11 Feb 2025 15:54:09 +1100 Subject: [PATCH 005/268] Revert functionality allowing shortcodes to be embedded within shortcodes. --- src/php/front-end/class-front-end.php | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 3143fe6d..21d823fc 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -327,31 +327,9 @@ public function render_content_shortcode( array $atts ): string { } if ( $atts['shortcodes'] ) { - // Remove this shortcode from the list to prevent recursion. + // Temporarily remove this shortcode from the list to prevent recursion while executing do_shortcode. remove_shortcode( self::CONTENT_SHORTCODE ); - - // Recursion depth is limited to prevent infinite loops. - static $depth = 0; - - // Find the shortcode in the content and replace it with the evaluated content. - $content = preg_replace_callback( - '/\[' . self::CONTENT_SHORTCODE . '([^]]*)]/', - function ( $matches ) use ( &$depth ) { - if ( $depth >= self::MAX_SHORTCODE_DEPTH ) { - return ''; - } - - $depth++; - $atts = shortcode_parse_atts( $matches[1] ); - $result = $this->render_content_shortcode( $atts ); - $depth--; - - return $result; - }, - $content - ); - - // Add this shortcode back to the list. + $content = do_shortcode( $atts['format'] ? shortcode_unautop( $content ) : $content ); add_shortcode( self::CONTENT_SHORTCODE, [ $this, 'render_content_shortcode' ] ); } From 04a81633fc40905c550d855d50d70737689bf3c3 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Wed, 12 Feb 2025 20:41:31 +1100 Subject: [PATCH 006/268] Update dependencies. --- package-lock.json | 796 ++++++++++++++++++++++++---------------------- package.json | 44 +-- src/composer.lock | 14 +- 3 files changed, 439 insertions(+), 415 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a0490ad..ed9f538f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,15 +10,15 @@ "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", - "@wordpress/api-fetch": "^7.16.0", - "@wordpress/block-editor": "^14.11.0", - "@wordpress/blocks": "^14.5.0", - "@wordpress/components": "^29.2.0", - "@wordpress/data": "^10.16.0", - "@wordpress/dom-ready": "^4.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/server-side-render": "^5.16.0", + "@wordpress/api-fetch": "^7.17.0", + "@wordpress/block-editor": "^14.12.0", + "@wordpress/blocks": "^14.6.0", + "@wordpress/components": "^29.3.0", + "@wordpress/data": "^10.17.0", + "@wordpress/dom-ready": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/server-side-render": "^5.17.0", "axios": "^1.7.9", "classnames": "^2.5.1", "codemirror": "^5.29", @@ -27,34 +27,34 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-select": "^5.9.0" + "react-select": "^5.10.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.18.0", - "@stylistic/eslint-plugin": "^2.13.0", + "@eslint/js": "^9.20.0", + "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node18": "^18.2.4", "@types/archiver": "^6.0.3", "@types/codemirror": "^5.60.15", "@types/jquery": "^3.5.32", - "@types/node": "^22.10.10", + "@types/node": "^22.13.1", "@types/prismjs": "^1.26.5", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "@types/rtlcss": "^3.5.4", "@types/tinymce": "^4.6.9", - "@types/web": "^0.0.198", + "@types/web": "^0.0.202", "@types/wordpress__editor": "^14.12.0", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", - "@wordpress/babel-preset-default": "^8.16.0", + "@typescript-eslint/eslint-plugin": "^8.24.0", + "@typescript-eslint/parser": "^8.24.0", + "@wordpress/babel-preset-default": "^8.17.0", "archiver": "^7.0.1", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "babel-plugin-prismjs": "^2.1.0", "css-loader": "^7.1.2", "cssnano": "^7.0.6", - "eslint": "^9.18.0", + "eslint": "^9.20.1", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-react": "^7.37.4", @@ -63,18 +63,18 @@ "glob": "^11.0.1", "globals": "^15.14.0", "mini-css-extract-plugin": "^2.9.2", - "postcss": "^8.5.1", + "postcss": "^8.5.2", "postcss-hexrgba": "^2.1.0", "postcss-load-config": "^6.0.1", "postcss-loader": "^8.1.1", "rtlcss": "^4.3.0", - "sass": "^1.83.4", + "sass": "^1.84.0", "sass-loader": "^16.0.4", "style-loader": "^4.0.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "typescript": "^5.7.3", - "typescript-eslint": "^8.21.0", + "typescript-eslint": "^8.24.0", "webpack": "^5.97.1", "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1", @@ -2262,13 +2262,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -2277,9 +2277,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2349,9 +2349,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -2359,9 +2359,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2382,6 +2382,19 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", @@ -3482,9 +3495,9 @@ "license": "MIT" }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz", - "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", + "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==", "dev": true, "license": "MIT", "dependencies": { @@ -3712,9 +3725,9 @@ "integrity": "sha512-aY69yje/bZllr99dbIcQwB365YDH/9myLodpxQ8cQZhGfavICi389aRvwa5LUoW+gTpcZKHjVI/sc0dDjUqVuw==" }, "node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "devOptional": true, "license": "MIT", "dependencies": { @@ -3818,9 +3831,9 @@ } }, "node_modules/@types/web": { - "version": "0.0.198", - "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.198.tgz", - "integrity": "sha512-J27wGYTzLv+IkTCmwFThzVDbH0Zm6u81atgNeRJsjhPgMgQwvL4jTRqnQpFHET6VamDX1ggbyZHC+tKwDjOLtA==", + "version": "0.0.202", + "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.202.tgz", + "integrity": "sha512-2iO+wBir5OBnMlB9Z7aD/0SUZjR2mhiCLtPvGPboTqwBC4O3Yv6Vjwn5eMxGMXtRAm01OV9yUBi9C8pJa02TIA==", "dev": true, "license": "Apache-2.0" }, @@ -3853,21 +3866,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz", + "integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/type-utils": "8.24.0", + "@typescript-eslint/utils": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3883,16 +3896,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz", + "integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4" }, "engines": { @@ -3908,14 +3921,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz", + "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3926,16 +3939,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz", + "integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.24.0", + "@typescript-eslint/utils": "8.24.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3950,9 +3963,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz", + "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==", "dev": true, "license": "MIT", "engines": { @@ -3964,20 +3977,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz", + "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4017,9 +4030,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -4030,16 +4043,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz", + "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4054,13 +4067,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz", + "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.24.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4309,14 +4322,14 @@ } }, "node_modules/@wordpress/a11y": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.16.0.tgz", - "integrity": "sha512-i3zrNFx+N+dNivQxUeQXWKGT1ccWePXcqPkVTqjdO+lACv+MtJoyE9PXZCmaxHWq00g1RTvIpLtrzV5L4gzZkA==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.17.0.tgz", + "integrity": "sha512-TCQ/PGC0Me3yzPUrmY2FpECl7GUcUcx6kVGUugmlMxNwxeZRYUOEMxsHGm07iKV5l7zbi3y5c/i5bbYwJfXA4g==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/dom-ready": "^4.16.0", - "@wordpress/i18n": "^5.16.0" + "@wordpress/dom-ready": "^4.17.0", + "@wordpress/i18n": "^5.17.0" }, "engines": { "node": ">=18.12.0", @@ -4324,14 +4337,14 @@ } }, "node_modules/@wordpress/api-fetch": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-7.16.0.tgz", - "integrity": "sha512-JMHUUWQQnuFDYQfWtOBPxbB8YEefew3fnGwwDrdOAN7drkZ7ob7fJ2H1tY6iV5i+wIEl/5em3f0jXD3zcwkBDw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-7.17.0.tgz", + "integrity": "sha512-L3iT/K41R6KResTy/7EOsTD+KKO20U3B4lPz/jQMRNgFdq4MOxtalEMjrRoj1mG+qiYGYdvGmpSgOzSx9o3eRg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/i18n": "^5.16.0", - "@wordpress/url": "^4.16.0" + "@wordpress/i18n": "^5.17.0", + "@wordpress/url": "^4.17.0" }, "engines": { "node": ">=18.12.0", @@ -4339,9 +4352,9 @@ } }, "node_modules/@wordpress/autop": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-4.16.0.tgz", - "integrity": "sha512-5/XBRZ7Y731moR/hzZ+/k9tavHMHvshi+IdsJAecgUcqYC45YMLmqOmA4DOzfzCjBkuVvoy+6itMHQ+Q87Gb9g==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-4.17.0.tgz", + "integrity": "sha512-6O9Eo/S02OHIa4GflfcWHANHpuy5/SifaWiprWYTrhIt6L6DyVxr1AErSWfDXIrkNNVXuhhykYDHAtApKqpqsQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4352,9 +4365,9 @@ } }, "node_modules/@wordpress/babel-preset-default": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.16.0.tgz", - "integrity": "sha512-CPAMxQ5eMmsRNJN0edUZvsJDnmALQFGbWyCxsJiJJ/0LFi1lYFC7r5YGcJXVVD+oX9L908NZpxwsQBHb6gkxig==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.17.0.tgz", + "integrity": "sha512-+ivwvBI92u6abFf0DlwHem8fH5HujKy5e8a0cwDBOJivEzIJLPKYSYLlnLZL9I0QIstB+KdcJBARuWuR0l58Sw==", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -4364,8 +4377,8 @@ "@babel/preset-env": "7.25.7", "@babel/preset-typescript": "7.25.7", "@babel/runtime": "7.25.7", - "@wordpress/browserslist-config": "^6.16.0", - "@wordpress/warning": "^3.16.0", + "@wordpress/browserslist-config": "^6.17.0", + "@wordpress/warning": "^3.17.0", "browserslist": "^4.21.10", "core-js": "^3.31.0", "react": "^18.3.0" @@ -4376,9 +4389,9 @@ } }, "node_modules/@wordpress/blob": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-4.16.0.tgz", - "integrity": "sha512-vhaEhh0jqSZG+LPrLL6wco4kmw4lYC6OLTOspeOWuAEPMKOs4YyfF8x2iA8p6CZiWjVcahwZlcP7DnZDIwowsQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-4.17.0.tgz", + "integrity": "sha512-qH0Q48clM+UTdTMWUsCyyAuy4J+koNGLz4oXyJZCrUvUQ31Hpj6VwQulM2lSXYQyzOWJEKf3deHM47Uz1JYhhg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4389,47 +4402,47 @@ } }, "node_modules/@wordpress/block-editor": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-14.11.0.tgz", - "integrity": "sha512-Io2m3pvsU6ksZcIb/Juqr60sB6OtW6vJMVAxMedw+KlvRfVVqiLPeEwo7zt4ovD2NCU7FtrOYF15pYz6gLQqkw==", + "version": "14.12.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-14.12.0.tgz", + "integrity": "sha512-i8tUlPiRgLqUFVnAHDjS7MNHZMFDYMkm5gR2xsNryzhsvoAndUYJiktftbXNaQVki/EMoDf1zHicHZ2g2AQy5Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@react-spring/web": "^9.4.5", - "@wordpress/a11y": "^4.16.0", - "@wordpress/api-fetch": "^7.16.0", - "@wordpress/blob": "^4.16.0", - "@wordpress/block-serialization-default-parser": "^5.16.0", - "@wordpress/blocks": "^14.5.0", - "@wordpress/commands": "^1.16.0", - "@wordpress/components": "^29.2.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/date": "^5.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/dom": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/escape-html": "^3.16.0", - "@wordpress/hooks": "^4.16.0", - "@wordpress/html-entities": "^4.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/is-shallow-equal": "^5.16.0", - "@wordpress/keyboard-shortcuts": "^5.16.0", - "@wordpress/keycodes": "^4.16.0", - "@wordpress/notices": "^5.16.0", - "@wordpress/preferences": "^4.16.0", - "@wordpress/priority-queue": "^3.16.0", - "@wordpress/private-apis": "^1.16.0", - "@wordpress/rich-text": "^7.16.0", - "@wordpress/style-engine": "^2.16.0", - "@wordpress/token-list": "^3.16.0", - "@wordpress/upload-media": "^0.1.0", - "@wordpress/url": "^4.16.0", - "@wordpress/warning": "^3.16.0", - "@wordpress/wordcount": "^4.16.0", + "@wordpress/a11y": "^4.17.0", + "@wordpress/api-fetch": "^7.17.0", + "@wordpress/blob": "^4.17.0", + "@wordpress/block-serialization-default-parser": "^5.17.0", + "@wordpress/blocks": "^14.6.0", + "@wordpress/commands": "^1.17.0", + "@wordpress/components": "^29.3.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/date": "^5.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/dom": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/escape-html": "^3.17.0", + "@wordpress/hooks": "^4.17.0", + "@wordpress/html-entities": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/is-shallow-equal": "^5.17.0", + "@wordpress/keyboard-shortcuts": "^5.17.0", + "@wordpress/keycodes": "^4.17.0", + "@wordpress/notices": "^5.17.0", + "@wordpress/preferences": "^4.17.0", + "@wordpress/priority-queue": "^3.17.0", + "@wordpress/private-apis": "^1.17.0", + "@wordpress/rich-text": "^7.17.0", + "@wordpress/style-engine": "^2.17.0", + "@wordpress/token-list": "^3.17.0", + "@wordpress/upload-media": "^0.2.0", + "@wordpress/url": "^4.17.0", + "@wordpress/warning": "^3.17.0", + "@wordpress/wordcount": "^4.17.0", "change-case": "^4.1.2", "clsx": "^2.1.1", "colord": "^2.7.0", @@ -4455,9 +4468,9 @@ } }, "node_modules/@wordpress/block-serialization-default-parser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-5.16.0.tgz", - "integrity": "sha512-F8n8GMeVAepWl9nuA9Ly/WWaqvZ9Lg0KI/OUd0Bm0Y3Ssz65UNRf6DT+ScN35OCAOklf1bQ1PGaq9JdVmC43mg==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-5.17.0.tgz", + "integrity": "sha512-4oVgm6f/kRqersuTH1SS85x89P4foPAo2xwjoXvHdjy1Rp0UQ86uxyKn0j0A6k7uQEXc5BJeUevk/Z1AT1Z9bQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4468,27 +4481,27 @@ } }, "node_modules/@wordpress/blocks": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-14.5.0.tgz", - "integrity": "sha512-RsX8hWsTegbkUaYcqIfYE2k1OEkF3hOV3PZFsm9Zn1ZN0/ESUQGRXxUCQvr/7yZIjMRxVNnakYlln2OMrXt1rQ==", + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-14.6.0.tgz", + "integrity": "sha512-9FkjXHRTXIaOU7BJfoeRUe1snh+5H8rypOTJoDpiMCoXMfGKyBVpacRMzbltQiK7SrzmHbzst4EuxHoK7a/TVw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/autop": "^4.16.0", - "@wordpress/blob": "^4.16.0", - "@wordpress/block-serialization-default-parser": "^5.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/dom": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/hooks": "^4.16.0", - "@wordpress/html-entities": "^4.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/is-shallow-equal": "^5.16.0", - "@wordpress/private-apis": "^1.16.0", - "@wordpress/rich-text": "^7.16.0", - "@wordpress/shortcode": "^4.16.0", - "@wordpress/warning": "^3.16.0", + "@wordpress/autop": "^4.17.0", + "@wordpress/blob": "^4.17.0", + "@wordpress/block-serialization-default-parser": "^5.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/dom": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/hooks": "^4.17.0", + "@wordpress/html-entities": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/is-shallow-equal": "^5.17.0", + "@wordpress/private-apis": "^1.17.0", + "@wordpress/rich-text": "^7.17.0", + "@wordpress/shortcode": "^4.17.0", + "@wordpress/warning": "^3.17.0", "change-case": "^4.1.2", "colord": "^2.7.0", "fast-deep-equal": "^3.1.3", @@ -4515,9 +4528,9 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/@wordpress/browserslist-config": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.16.0.tgz", - "integrity": "sha512-ppjgUOjCFwLjH5XCDAfavRkIAj9DKEVGj12YMGL+dzSikbDU9L8VnMT1p08G1bAcBkTRLtBX8KkJJdyUI1JGsQ==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.17.0.tgz", + "integrity": "sha512-cjMclWLwfam5O03gOHWjD8veeLVnfmC93V9LX1aPt/ZT9aE0cmEZUxBa3VzkDM7NvuZFj7SjSvJr+vuar9Np1A==", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -4526,19 +4539,19 @@ } }, "node_modules/@wordpress/commands": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-1.16.0.tgz", - "integrity": "sha512-LxDPzF32QVcZJ42VrI07tSxvTvxKmzI9YZ5JAam/pR0UsjHAFxsnBT3t45oVItRlco/AVL+eLxwuCC/OuSfgLQ==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-1.17.0.tgz", + "integrity": "sha512-oZLv9pi0iiIO7DXRijK9gze5+iktoUyfDVipAmbmxAVEqptfWuPP3BRSkZxf+ccoIWpz0EhNKShsbQM86FwVbg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/components": "^29.2.0", - "@wordpress/data": "^10.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/keyboard-shortcuts": "^5.16.0", - "@wordpress/private-apis": "^1.16.0", + "@wordpress/components": "^29.3.0", + "@wordpress/data": "^10.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/keyboard-shortcuts": "^5.17.0", + "@wordpress/private-apis": "^1.17.0", "clsx": "^2.1.1", "cmdk": "^1.0.0" }, @@ -4552,9 +4565,9 @@ } }, "node_modules/@wordpress/components": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-29.2.0.tgz", - "integrity": "sha512-a7vUL4oUGu4Jicqf0cFSdQvGtZVw9h4Gvxr53o8yJYmXY8YRVrbeEsZjA/twaqRa8WxGzzWnWNQ/XwtMEXNG0w==", + "version": "29.3.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-29.3.0.tgz", + "integrity": "sha512-9lQIXsbgFeGY1QXEhNHQ6mq+6sS1TGGdZdaGSoQoP682WWgdjshnyq/0yhGULY9ReDKnZF2mHJ/J3FvleyYMcg==", "license": "GPL-2.0-or-later", "dependencies": { "@ariakit/react": "^0.4.15", @@ -4569,23 +4582,23 @@ "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.3.1", - "@wordpress/a11y": "^4.16.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/date": "^5.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/dom": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/escape-html": "^3.16.0", - "@wordpress/hooks": "^4.16.0", - "@wordpress/html-entities": "^4.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/is-shallow-equal": "^5.16.0", - "@wordpress/keycodes": "^4.16.0", - "@wordpress/primitives": "^4.16.0", - "@wordpress/private-apis": "^1.16.0", - "@wordpress/rich-text": "^7.16.0", - "@wordpress/warning": "^3.16.0", + "@wordpress/a11y": "^4.17.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/date": "^5.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/dom": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/escape-html": "^3.17.0", + "@wordpress/hooks": "^4.17.0", + "@wordpress/html-entities": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/is-shallow-equal": "^5.17.0", + "@wordpress/keycodes": "^4.17.0", + "@wordpress/primitives": "^4.17.0", + "@wordpress/private-apis": "^1.17.0", + "@wordpress/rich-text": "^7.17.0", + "@wordpress/warning": "^3.17.0", "change-case": "^4.1.2", "clsx": "^2.1.1", "colord": "^2.7.0", @@ -4613,20 +4626,20 @@ } }, "node_modules/@wordpress/compose": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.16.0.tgz", - "integrity": "sha512-FTpfEUeEyH3LnVRlNZxRwce3sEUPDAVI1P+AaF7ZrbzcV2ita4WamCoEHFDS4OMOnvISnSbVh2Rz3gF9oLvomQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.17.0.tgz", + "integrity": "sha512-jn5uCw08HHLfOpIDp0pKBDZh1oZiMwjiK3c3IZdZo6eoWZjpOr3ecsMa4RBl/4HbqnUoeFDD6Lj83IEKPuzHQg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", "@types/mousetrap": "^1.6.8", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/dom": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/is-shallow-equal": "^5.16.0", - "@wordpress/keycodes": "^4.16.0", - "@wordpress/priority-queue": "^3.16.0", - "@wordpress/undo-manager": "^1.16.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/dom": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/is-shallow-equal": "^5.17.0", + "@wordpress/keycodes": "^4.17.0", + "@wordpress/priority-queue": "^3.17.0", + "@wordpress/undo-manager": "^1.17.0", "change-case": "^4.1.2", "clipboard": "^2.0.11", "mousetrap": "^1.6.5", @@ -4680,19 +4693,19 @@ } }, "node_modules/@wordpress/data": { - "version": "10.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.16.0.tgz", - "integrity": "sha512-5Gx0Hb1VsnvACQJBJhgaFf0xn6cf1s0Wqv3q2DRnRShuSQTNxkUxA41+eZsPxC1JrnVXg0vPRCu5GpqwPO4O9g==", + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.17.0.tgz", + "integrity": "sha512-NezfpsRH3BIV2i10wFohsGfOQ+pp9TvSHFuVK/AlQmnAogoMpFOxAumXCI7rvDoH1X4rEPiX2ggRnxP2+Z6jwQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/compose": "^7.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/is-shallow-equal": "^5.16.0", - "@wordpress/priority-queue": "^3.16.0", - "@wordpress/private-apis": "^1.16.0", - "@wordpress/redux-routine": "^5.16.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/is-shallow-equal": "^5.17.0", + "@wordpress/priority-queue": "^3.17.0", + "@wordpress/private-apis": "^1.17.0", + "@wordpress/redux-routine": "^5.17.0", "deepmerge": "^4.3.0", "equivalent-key-map": "^0.2.2", "is-plain-object": "^5.0.0", @@ -4739,13 +4752,13 @@ } }, "node_modules/@wordpress/date": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.16.0.tgz", - "integrity": "sha512-Sb2eJ7S7bn7ODfe0WVgNEqLpilgwpKIoJjVxMxT8wtJpx4rEs25+BAyQ6Bpj6lxX9P99ZmPsdrq5YavxP9NKHg==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.17.0.tgz", + "integrity": "sha512-vFi+h+YpiicfDHtp1SKkFmgQR0PI9I76Dqoi7lBP95BPTGC/adQ3u2ee5wGd5uVUlR+ca+TfR6siC4Igau73oA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/deprecated": "^4.16.0", + "@wordpress/deprecated": "^4.17.0", "moment": "^2.29.4", "moment-timezone": "^0.5.40" }, @@ -4755,13 +4768,13 @@ } }, "node_modules/@wordpress/deprecated": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.16.0.tgz", - "integrity": "sha512-apv94cskjvWqkanqNn3vP7lugJODkSkPlLqjKPY5iBXI0RATaKuxcTNxuO9/Gn5QcuM89fjhsGTcZ4X/SZTGNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.17.0.tgz", + "integrity": "sha512-7IlFpQ6tNkUbOuuxm6kBCR2R6C9Etlzojgh0ykJ/OmwgRMrosH/m6/zAmaA15oRYpd6dvO7ozJN+ArPz7LSOiQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/hooks": "^4.16.0" + "@wordpress/hooks": "^4.17.0" }, "engines": { "node": ">=18.12.0", @@ -4769,13 +4782,13 @@ } }, "node_modules/@wordpress/dom": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.16.0.tgz", - "integrity": "sha512-iT9D8BnoSgD9w+viDCKtO7lfMmzku3tC7oLEakH6LNZRas0jQuTC46cfokMAz6HTchxiAnuXoPYHsCPhGzWy8Q==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.17.0.tgz", + "integrity": "sha512-raAeub1L/a2yHd9rwCGs67yDSUsafcpERi9rJCeHiaBE/+h7gZn7Li+Pya+DMk7tGxoIHNpPuGVTAyVhQbjWdQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/deprecated": "^4.16.0" + "@wordpress/deprecated": "^4.17.0" }, "engines": { "node": ">=18.12.0", @@ -4783,9 +4796,9 @@ } }, "node_modules/@wordpress/dom-ready": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.16.0.tgz", - "integrity": "sha512-rlp7gZRRPsob8z+//tS8bHHRTlkRiOfbKA1SJmyfakU/p4fcEXskLfdq/0wGPZtoHnia6kLKUyFMWhIBe8SuYQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.17.0.tgz", + "integrity": "sha512-u/ocyrPV4MJIKxM1OJg+Q6yOBD0pIYi1jcXE1HVYnc/9Mte0IFlfovYRJj6oGUc7u4dM6AVE2BUCQMJgmG406Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4860,15 +4873,15 @@ } }, "node_modules/@wordpress/element": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.16.0.tgz", - "integrity": "sha512-1Db9jeu7dxil/fJqAiLN5dA6gwoHWcgMSqZJ4dmZ0kMDMs40rtm6o60GFmAQGlrj+mmUvhOHTTwrBdpyfuv4bA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.17.0.tgz", + "integrity": "sha512-mRLFDPmZiI3+POi/iUGoof/9fQi4YTJ/RAuIUipr7yG7l4SwOoQy4eSJy6QTyqtJxZ+/7qA+b/+Ek15UzFst5Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", "@types/react": "^18.2.79", "@types/react-dom": "^18.2.25", - "@wordpress/escape-html": "^3.16.0", + "@wordpress/escape-html": "^3.17.0", "change-case": "^4.1.2", "is-plain-object": "^5.0.0", "react": "^18.3.0", @@ -4880,9 +4893,9 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.16.0.tgz", - "integrity": "sha512-Rb3nUsqK2tzLpKhSRO5IID5O+gvNlyHRkKVmTszTB+0vjK+yh0Mc4UPzdHksPo8K7KnlAFt3SgjcfWYo3LYyUA==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.17.0.tgz", + "integrity": "sha512-yOfJwgmrtIXQDwX6zTC0L7ymYBXz3K3hlW0nDdtYy+bCw5z0gbrEOnBotOD6YdXlejAgnaAH+K1VSf0xxG5uGA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4936,9 +4949,9 @@ } }, "node_modules/@wordpress/hooks": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.16.0.tgz", - "integrity": "sha512-W82L1PdIhJPNpEb2F+0NWzrDoUqZo6NnYID7qHCexBiagq4+QS4uydM6anyFvUNrpL51CmkCNu31Xi8HjpSTGg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.17.0.tgz", + "integrity": "sha512-LGOHGuwCXCevuzaFpM2sgyPZxf3H7tWaSKzlvDzx2kmwiWIrFug/yebywv4Cxsl82I5DfZkDpxXRpqTxXrC0Nw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4949,9 +4962,9 @@ } }, "node_modules/@wordpress/html-entities": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.16.0.tgz", - "integrity": "sha512-wnCtif4GsQ3gZgINN2GK6+yLH+vIsW3ASvUfdUlxYMcvMagNhJsqaE6dqsnKkezD8q/WNL7zv82BDyGSLKeHNQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.17.0.tgz", + "integrity": "sha512-8cVD8KTxsKLHA9r6Lt3fkQoNBUQ6zMWdgaK1VNRYRJgTfx8C6FlNBjvHrIIgS0nJ43k9iAmAObGQiL3GkGVI1g==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4962,13 +4975,13 @@ } }, "node_modules/@wordpress/i18n": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-5.16.0.tgz", - "integrity": "sha512-O4ZUvjS8AlYzTxvw7fmp3xk51rpKv1h2/dGFc/L+IB97UrCBAiC9HBv6FIHRF1gci4Vdu/QnCDw3qpC+N/2gCw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-5.17.0.tgz", + "integrity": "sha512-aAsYls8sTTSEimsvjxBl9mCYbZYD3BddHVpuHgbBxzC+2SZE+JYJ+IpcwEghC712qo0jEkG8Vdzhqae1PL6vCQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/hooks": "^4.16.0", + "@wordpress/hooks": "^4.17.0", "gettext-parser": "^1.3.1", "memize": "^2.1.0", "sprintf-js": "^1.1.1", @@ -4983,14 +4996,14 @@ } }, "node_modules/@wordpress/icons": { - "version": "10.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.16.0.tgz", - "integrity": "sha512-fHZujKpOkYD3JnPGCYqB1VafUiqsUOnpdVGdBd7En5ELwRg189a0NcI4EmM8OkeItNDml4LU/4nCCkypSy29eA==", + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.17.0.tgz", + "integrity": "sha512-qzWFrMfa5HZdGxGq7I+s9bmUJqZrFfx6ow/slY1USKJqp1uRHRekAbq6UrOrJscs8rSUQiV/aNNPDgSfqBEM6A==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/element": "^6.16.0", - "@wordpress/primitives": "^4.16.0" + "@wordpress/element": "^6.17.0", + "@wordpress/primitives": "^4.17.0" }, "engines": { "node": ">=18.12.0", @@ -5028,9 +5041,9 @@ } }, "node_modules/@wordpress/is-shallow-equal": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.16.0.tgz", - "integrity": "sha512-9JI0bz7bQ9PdXPtXSnZXtbkyh0h7ZtojeG0lFtf9xtFkA56JUuMALa623v1YeuHKYbYmCc03/pqtpDKc/8QfVQ==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.17.0.tgz", + "integrity": "sha512-PRykD6MgDkptKsKwETjNHiQUVtaegXkREX6UetN1iL6u+2la4XC/naDHByq7TL+Cg4snyR+PlNdw45Y4dgMf5w==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -5041,15 +5054,15 @@ } }, "node_modules/@wordpress/keyboard-shortcuts": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-5.16.0.tgz", - "integrity": "sha512-JI/e7NGWjgbWY7Q546NExuwfwdx7OokHrwKzj2yTd3DluM3zhQax0d4ZZvJiqyHMaMQvKcT0ILJ4KYMgB9EkGg==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-5.17.0.tgz", + "integrity": "sha512-XQbtiTSq6rsP/5KYMMDCmZegABlqcq7IpLtymrbeQNSPjyAP4aflU0rCcNWaXhBbdWWDRmaU9u/X1/fI5wGxUQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/data": "^10.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/keycodes": "^4.16.0" + "@wordpress/data": "^10.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/keycodes": "^4.17.0" }, "engines": { "node": ">=18.12.0", @@ -5060,13 +5073,13 @@ } }, "node_modules/@wordpress/keycodes": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.16.0.tgz", - "integrity": "sha512-T4kaFkw6R1VkcBk+F7B4gmzEhSPRJwpMdkP7roNvENzKGtXs49K4xO0koOZhWUlGpZvhPJ1WWERyoub8S7rX2A==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.17.0.tgz", + "integrity": "sha512-6aZ28uoCmzjXONpRVtDPjevkw834fhIRBnn2KQdzENMnPiQCNbiG71mPNxkTw1yRHRRT5ptHvOe49ztWm9KMcA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/i18n": "^5.16.0" + "@wordpress/i18n": "^5.17.0" }, "engines": { "node": ">=18.12.0", @@ -5093,14 +5106,14 @@ } }, "node_modules/@wordpress/notices": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-5.16.0.tgz", - "integrity": "sha512-RoXfFLuvgmANRmuHLmbfPyi2PEw3dYMw4GwS8t6DVvC16i6zd75S8JFCoxjA1tRJNeLw8R/NQ57Pm35w5zCzMg==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-5.17.0.tgz", + "integrity": "sha512-1qsRcxE2dnvIJO9IQHnK9D/U/RgRmccDhbNrBxcgOqEVHTFwDambuxte4JXOmJZVr+uqh8Z3ggr+4H6zCjs/9Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.16.0", - "@wordpress/data": "^10.16.0" + "@wordpress/a11y": "^4.17.0", + "@wordpress/data": "^10.17.0" }, "engines": { "node": ">=18.12.0", @@ -5169,21 +5182,21 @@ } }, "node_modules/@wordpress/preferences": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-4.16.0.tgz", - "integrity": "sha512-FLHwwC1z+1jef8oN8QykMqK/D6beEkBwI3EZSMj3gPKxlA5Q5CqUF32blUBMDRVE4h1xx6jQaM/B2DBroMoJew==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-4.17.0.tgz", + "integrity": "sha512-jNyHhuar2RflBJ9JqGs0ZQXnU86URCQXlR4syXzZdVU75Sm1fPByqKDtR9/F/bWnPxLlU1uP89SKv54kGpSM4Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.16.0", - "@wordpress/components": "^29.2.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/private-apis": "^1.16.0", + "@wordpress/a11y": "^4.17.0", + "@wordpress/components": "^29.3.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/private-apis": "^1.17.0", "clsx": "^2.1.1" }, "engines": { @@ -5196,13 +5209,13 @@ } }, "node_modules/@wordpress/primitives": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.16.0.tgz", - "integrity": "sha512-mf5LPcA500KOo/UPiwNanNlH6Satwf4xBB1DPzw4InE67eACvAlde3oSYsoE6Uce6+7URRIefg9j47yXP2jkxw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.17.0.tgz", + "integrity": "sha512-O1dysI/Y9xv5uUMllH2VIxuBDCOVUX8WmouE9KKr11Yv4gkHzxzaU2M5rFtu7RbUCv6jtkvjidy2cuZuNpEIHQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/element": "^6.16.0", + "@wordpress/element": "^6.17.0", "clsx": "^2.1.1" }, "engines": { @@ -5214,9 +5227,9 @@ } }, "node_modules/@wordpress/priority-queue": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.16.0.tgz", - "integrity": "sha512-YmVPE/kHmAIEYiSnnfxQI7JBAvXxlCyVoGlfWxCJ/IH8W7gbZtl1R+iuRvT8L4Cdr+sUybT68Ry+o4o39OqURg==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.17.0.tgz", + "integrity": "sha512-WzQHNx6wjgbxhuaKErjIRLSL9E9La8slsAXRTQPmkgvKqa11Rh4RYl2FLUh8tABK3xo5HzaHCplkZSm2q5wlbg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -5228,9 +5241,9 @@ } }, "node_modules/@wordpress/private-apis": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.16.0.tgz", - "integrity": "sha512-k/AQ11+vlbpSWhyOU5t8huoGdN+PursA8bUxVfhEnqcKRWTK+LKSmSEpdyL1sv6XJqznWYjgaNSdIxTkadsPpg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.17.0.tgz", + "integrity": "sha512-9NGPyuUvtJD0OjWJ/Cn+6Qhjb8hXhiJH4i80W7MFVHRgUZLc/Tu5BOg2+OnXMRSePbgYivo1NLEukqdXqse5IA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -5241,9 +5254,9 @@ } }, "node_modules/@wordpress/redux-routine": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.16.0.tgz", - "integrity": "sha512-piLnJnV+tzWOEPJPdp431TcaKRoCpgGJ309W+UxM7fRHrYH/XZcXD8a+0pq56Dh/1QFO1elXXhPzhZQknwtyYw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.17.0.tgz", + "integrity": "sha512-RBUNOp+wSweymRB0+fThv1HKUf1c8GVMUT/Xv0kqtrRsGFD70ciwnnfVXnPY0V6po9Uzj5Bb4+2qO/l/e2IwXw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -5289,20 +5302,20 @@ } }, "node_modules/@wordpress/rich-text": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.16.0.tgz", - "integrity": "sha512-p+9WPzVo5pXLr1Xt04gQ1kdYQYmw05r2Kp42tgIfFNjgCBH1plpSrzjCyV/dyHrZ7APpJFg8sNjlOJmyLQiCFg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.17.0.tgz", + "integrity": "sha512-HEmApVDjConxYe3cP8P+Zs0xLJZPMhfWal38MQmFelQtCNk+kT0IBg5SkFAcWYY+c4gzhK+dMKawc72uWDfm8w==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.16.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/escape-html": "^3.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/keycodes": "^4.16.0", + "@wordpress/a11y": "^4.17.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/escape-html": "^3.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/keycodes": "^4.17.0", "memize": "^2.1.0" }, "engines": { @@ -5335,21 +5348,21 @@ } }, "node_modules/@wordpress/server-side-render": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-5.16.0.tgz", - "integrity": "sha512-OB6Omav6X4yciYGNQHHJpw/psKyAO2Z7ab9UGvlaST/YnmBD+9IZYtp9boo3D3zfOjB5aeD+2shRpt9zOBYEbw==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-5.17.0.tgz", + "integrity": "sha512-xJWABbtCZmkO6+Xa1DS3Mq+f2ZKH540aj5xeN7M1W1meAFdcZlEAbQI+Kn1PuXI9VpHIh5K+JOybHD06TI4hZQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/api-fetch": "^7.16.0", - "@wordpress/blocks": "^14.5.0", - "@wordpress/components": "^29.2.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/deprecated": "^4.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/url": "^4.16.0", + "@wordpress/api-fetch": "^7.17.0", + "@wordpress/blocks": "^14.6.0", + "@wordpress/components": "^29.3.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/deprecated": "^4.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/url": "^4.17.0", "fast-deep-equal": "^3.1.3" }, "engines": { @@ -5362,9 +5375,9 @@ } }, "node_modules/@wordpress/shortcode": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-4.16.0.tgz", - "integrity": "sha512-T3KLZq5SYGigltEQumCKExKBJqnI2IF2elnJpd5+CFxQQAb9/gsrZ4TBG7ommOlXHZKn5hwD2YIEEM/ZAkGQ4A==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-4.17.0.tgz", + "integrity": "sha512-sNPUmeeK/dxK5z8BWSsk5OqRSf2UzfczpKu3upRn9eIdgG31SCXPgzvps73upIrxZNDCTQVVFhq47KADX8TiUA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -5376,9 +5389,9 @@ } }, "node_modules/@wordpress/style-engine": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-2.16.0.tgz", - "integrity": "sha512-eOaa14iAqXDS8y9ndQxM1f+Ibc9suuA6naEZ+8d74eEvBIZclQgi0GZpA1n8HEuDsRi9o2VhnXzJe6X6lsQduA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-2.17.0.tgz", + "integrity": "sha512-6eIdeQH0t7va1AjZIGo8sEW8NE+dcz//KXp+HsW/2XhATAIPjUjFJ2/SVRNCj3JHFKSjKpxnZi26xalfET0PqA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -5413,9 +5426,9 @@ } }, "node_modules/@wordpress/token-list": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-3.16.0.tgz", - "integrity": "sha512-bhBZTKcR8NZXgW9tsHDJm3YTIsCnWb/i50cN59w9FqYICOUFGC4RKyEDzJN6rpgW9dRu6J7xfsC8PjlMukNc6Q==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-3.17.0.tgz", + "integrity": "sha512-TO224Seolfy/eapbOg15poz1Ws44xW3KHrqeo7Jp+6hmqQh/5OJE5wDFTzgsbdnAXFzy3DAGJxxxrCv0qpf+YA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -5426,13 +5439,13 @@ } }, "node_modules/@wordpress/undo-manager": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.16.0.tgz", - "integrity": "sha512-IE3u5Yk8QzUhiLAiGmYostsygxQExs9mVWlZ1BAXniEGCAcVdvDv7IB16dIgQxCYG3/idvmFdNbN8aQGX+nEIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.17.0.tgz", + "integrity": "sha512-inSOCUneGMmFq3jRTB9uIws/+6VWpz0zvY2IPW/vjWbz7Gg1YbJ+lmbbgtJCoiJ7Ei00b4sagvzI00TNUXe9mg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/is-shallow-equal": "^5.16.0" + "@wordpress/is-shallow-equal": "^5.17.0" }, "engines": { "node": ">=18.12.0", @@ -5440,32 +5453,37 @@ } }, "node_modules/@wordpress/upload-media": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.1.0.tgz", - "integrity": "sha512-K0B9XlHrqOJ6YwwVchP6v6ohduD/mfJKNQxDauZiCqcWim8r8rcTrFp+nBUw3TujDCKas23aVMl09aQpA8zcMw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.2.0.tgz", + "integrity": "sha512-xPPru9rSDTKWpFMMM5dOaPQIkf38L3gNinjSHkU7arFyK14G60HklvZJ/MTk7RjjgQ7h1sYe8tvdiTvI8CQZyQ==", "license": "GPL-2.0-or-later", "dependencies": { + "@babel/runtime": "7.25.7", "@shopify/web-worker": "^6.4.0", - "@wordpress/api-fetch": "^7.16.0", - "@wordpress/blob": "^4.16.0", - "@wordpress/compose": "^7.16.0", - "@wordpress/data": "^10.16.0", - "@wordpress/element": "^6.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/preferences": "^4.16.0", - "@wordpress/private-apis": "^1.16.0", - "@wordpress/url": "^4.16.0", + "@wordpress/api-fetch": "^7.17.0", + "@wordpress/blob": "^4.17.0", + "@wordpress/compose": "^7.17.0", + "@wordpress/data": "^10.17.0", + "@wordpress/element": "^6.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/preferences": "^4.17.0", + "@wordpress/private-apis": "^1.17.0", + "@wordpress/url": "^4.17.0", "uuid": "^9.0.1" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@wordpress/url": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.16.0.tgz", - "integrity": "sha512-A9kkw/ye2qL9ZHvm1Eew8bvGVnNMq4fW0t5dakdDuVXyXtSOvZVT268JhP9QaD0FYzOFrmxL5Ks8Z6ufP1yLwg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.17.0.tgz", + "integrity": "sha512-aFU1w2Wcz2/YdapPYozeXbb7C7LzfYZmAg4Bu28zTSxxrpKYocr/oYH7D8V13uHzfBoqTzL8XYM7wj17Dlcdag==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -5497,9 +5515,9 @@ } }, "node_modules/@wordpress/warning": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.16.0.tgz", - "integrity": "sha512-XsgqRPvB+VSecXnD3VfvJJxhcdTTX4EkgdzvWspmQnw0rNCV636KByZVgolzYhvr3La9EgqO+MqXzwvPHg/xfQ==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.17.0.tgz", + "integrity": "sha512-dmEjDbYtfPD8rMRtSrLxoW3g8CLKl+vK5pdXvDvG0lBoRjqwtRPP4cgNBOC8cq8gXRCwh5NDDtM2C8MTjGjVsQ==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -5507,9 +5525,9 @@ } }, "node_modules/@wordpress/wordcount": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-4.16.0.tgz", - "integrity": "sha512-6SDQRa1rKSZlTlpWh0wfxOUGFfeM2fgLRm7MPFK4/ORX1oOOYAWCN3nMd4zLXzoq2fSqcVem7rcLBm9kHqLhQg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-4.17.0.tgz", + "integrity": "sha512-lT4NmbK0fMX+mqm/1XSoTsW7VqmxApZcZFPtWvT5UH6js1XcDrQa9liIUv6RyMlrrLHTTDrq+e4mNVeND68o5A==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -7757,18 +7775,18 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", + "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -10054,12 +10072,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -10562,9 +10581,10 @@ } }, "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -10678,9 +10698,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "funding": [ { "type": "opencollective", @@ -11575,9 +11595,9 @@ } }, "node_modules/react-select": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz", - "integrity": "sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.0.tgz", + "integrity": "sha512-k96gw+i6N3ExgDwPIg0lUPmexl1ygPe6u5BdQFNBhkpbwroIgCNXdubtIzHfThYXYYTubwOBafoMnn7ruEP1xA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.0", @@ -12065,9 +12085,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.83.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.4.tgz", - "integrity": "sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==", + "version": "1.84.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.84.0.tgz", + "integrity": "sha512-XDAbhEPJRxi7H0SxrnOpiXFQoUJHwkR2u3Zc4el+fK/Tt5Hpzw5kkQ59qVDfvdaUq6gCrEZIbySFBM2T9DNKHg==", "dev": true, "license": "MIT", "dependencies": { @@ -13092,9 +13112,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { @@ -13273,15 +13293,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.21.0.tgz", - "integrity": "sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.0.tgz", + "integrity": "sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.21.0", - "@typescript-eslint/parser": "8.21.0", - "@typescript-eslint/utils": "8.21.0" + "@typescript-eslint/eslint-plugin": "8.24.0", + "@typescript-eslint/parser": "8.24.0", + "@typescript-eslint/utils": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 72559477..63b947ee 100644 --- a/package.json +++ b/package.json @@ -34,15 +34,15 @@ }, "dependencies": { "@codemirror/fold": "^0.19.4", - "@wordpress/api-fetch": "^7.16.0", - "@wordpress/block-editor": "^14.11.0", - "@wordpress/blocks": "^14.5.0", - "@wordpress/components": "^29.2.0", - "@wordpress/data": "^10.16.0", - "@wordpress/dom-ready": "^4.16.0", - "@wordpress/i18n": "^5.16.0", - "@wordpress/icons": "^10.16.0", - "@wordpress/server-side-render": "^5.16.0", + "@wordpress/api-fetch": "^7.17.0", + "@wordpress/block-editor": "^14.12.0", + "@wordpress/blocks": "^14.6.0", + "@wordpress/components": "^29.3.0", + "@wordpress/data": "^10.17.0", + "@wordpress/dom-ready": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/icons": "^10.17.0", + "@wordpress/server-side-render": "^5.17.0", "axios": "^1.7.9", "classnames": "^2.5.1", "codemirror": "^5.29", @@ -51,34 +51,34 @@ "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-select": "^5.9.0" + "react-select": "^5.10.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.18.0", - "@stylistic/eslint-plugin": "^2.13.0", + "@eslint/js": "^9.20.0", + "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node18": "^18.2.4", "@types/archiver": "^6.0.3", "@types/codemirror": "^5.60.15", "@types/jquery": "^3.5.32", - "@types/node": "^22.10.10", + "@types/node": "^22.13.1", "@types/prismjs": "^1.26.5", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "@types/rtlcss": "^3.5.4", "@types/tinymce": "^4.6.9", - "@types/web": "^0.0.198", + "@types/web": "^0.0.202", "@types/wordpress__editor": "^14.12.0", - "@typescript-eslint/eslint-plugin": "^8.21.0", - "@typescript-eslint/parser": "^8.21.0", - "@wordpress/babel-preset-default": "^8.16.0", + "@typescript-eslint/eslint-plugin": "^8.24.0", + "@typescript-eslint/parser": "^8.24.0", + "@wordpress/babel-preset-default": "^8.17.0", "archiver": "^7.0.1", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "babel-plugin-prismjs": "^2.1.0", "css-loader": "^7.1.2", "cssnano": "^7.0.6", - "eslint": "^9.18.0", + "eslint": "^9.20.1", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-react": "^7.37.4", @@ -87,25 +87,25 @@ "glob": "^11.0.1", "globals": "^15.14.0", "mini-css-extract-plugin": "^2.9.2", - "postcss": "^8.5.1", + "postcss": "^8.5.2", "postcss-hexrgba": "^2.1.0", "postcss-load-config": "^6.0.1", "postcss-loader": "^8.1.1", "rtlcss": "^4.3.0", - "sass": "^1.83.4", + "sass": "^1.84.0", "sass-loader": "^16.0.4", "style-loader": "^4.0.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "typescript": "^5.7.3", - "typescript-eslint": "^8.21.0", + "typescript-eslint": "^8.24.0", "webpack": "^5.97.1", "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1", "webpack-remove-empty-scripts": "^1.0.4" }, "overrides": { - "eslint": "^9.18.0", + "eslint": "^9.20.1", "react": "^18.3.1", "react-dom": "^18.3.1" } diff --git a/src/composer.lock b/src/composer.lock index 60b0885d..0654d2c5 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -609,16 +609,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.1", + "version": "3.11.3", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" + "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", "shasum": "" }, "require": { @@ -683,9 +683,13 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-11-16T12:02:36+00:00" + "time": "2025-01-23T17:04:15+00:00" }, { "name": "wp-coding-standards/wpcs", From 48ce4bb04fdf172d979c6f7c7357c6e130e95700 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Wed, 12 Feb 2025 20:49:06 +1100 Subject: [PATCH 007/268] Update composer dependencies. --- src/composer.json | 12 +++++------ src/composer.lock | 55 +++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/composer.json b/src/composer.json index 9a34f94c..53cb85d7 100644 --- a/src/composer.json +++ b/src/composer.json @@ -26,15 +26,15 @@ ] }, "require": { - "composer/installers": "~1.12", - "php": ">=7.4", + "php": ">=7.4", "ext-dom": "*", - "ext-json": "*" + "ext-json": "*", + "composer/installers": "^2.3" }, "require-dev": { - "wp-coding-standards/wpcs": "^3.1.0", - "phpcompatibility/phpcompatibility-wp": "2.1.5", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0" + "wp-coding-standards/wpcs": "^3.1", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" }, "config": { "allow-plugins": { diff --git a/src/composer.lock b/src/composer.lock index 0654d2c5..890dda8f 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,43 +4,41 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "25629a3fd0cc345a14bc2723a9213b55", + "content-hash": "4d247b821abb0ab8945681a4e0110753", "packages": [ { "name": "composer/installers", - "version": "v1.12.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19" + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "replace": { - "roundcube/plugin-installer": "*", - "shama/baton": "*" + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" }, "require-dev": { - "composer/composer": "1.6.* || ^2.0", - "composer/semver": "^1 || ^3", - "phpstan/phpstan": "^0.12.55", - "phpstan/phpstan-phpunit": "^0.12.16", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.3" + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" }, "type": "composer-plugin", "extra": { "class": "Composer\\Installers\\Plugin", "branch-alias": { - "dev-main": "1.x-dev" - } + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true }, "autoload": { "psr-4": { @@ -61,7 +59,6 @@ "description": "A multi-framework Composer library installer", "homepage": "https://composer.github.io/installers/", "keywords": [ - "Craft", "Dolibarr", "Eliasis", "Hurad", @@ -82,7 +79,6 @@ "Whmcs", "WolfCMS", "agl", - "aimeos", "annotatecms", "attogram", "bitrix", @@ -91,6 +87,7 @@ "cockpit", "codeigniter", "concrete5", + "concreteCMS", "croogo", "dokuwiki", "drupal", @@ -101,7 +98,6 @@ "grav", "installer", "itop", - "joomla", "known", "kohana", "laravel", @@ -110,6 +106,7 @@ "magento", "majima", "mako", + "matomo", "mediawiki", "miaoxing", "modulework", @@ -129,9 +126,7 @@ "silverstripe", "sydes", "sylius", - "symfony", "tastyigniter", - "typo3", "wordpress", "yawik", "zend", @@ -139,7 +134,7 @@ ], "support": { "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.12.0" + "source": "https://github.com/composer/installers/tree/v2.3.0" }, "funding": [ { @@ -155,7 +150,7 @@ "type": "tidelift" } ], - "time": "2021-09-13T08:19:44+00:00" + "time": "2024-06-24T20:46:46+00:00" } ], "packages-dev": [ @@ -373,16 +368,16 @@ }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.5", + "version": "2.1.6", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082" + "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/01c1ff2704a58e46f0cb1ca9d06aee07b3589082", - "reference": "01c1ff2704a58e46f0cb1ca9d06aee07b3589082", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/80ccb1a7640995edf1b87a4409fa584cd5869469", + "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469", "shasum": "" }, "require": { @@ -439,7 +434,7 @@ "type": "open_collective" } ], - "time": "2024-04-24T21:37:59+00:00" + "time": "2025-01-16T22:34:19+00:00" }, { "name": "phpcsstandards/phpcsextra", From 5a833fbd7cdd25d7e0b89e1b1985f5b101cc0601 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Wed, 12 Feb 2025 21:20:11 +1100 Subject: [PATCH 008/268] Ensure Composer packages are compatible with minimum PHP version. --- package.json | 2 +- src/code-snippets.php | 6 +++--- src/composer.json | 5 ++++- src/composer.lock | 6 ++++-- src/readme.txt | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 63b947ee..405a9ef3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-snippets", "description": "Manage code snippets running on a WordPress-powered site through a graphical interface.", "homepage": "https://codesnippets.pro", - "version": "3.6.7", + "version": "3.6.8-dev.1", "main": "src/dist/edit.js", "directories": { "test": "tests" diff --git a/src/code-snippets.php b/src/code-snippets.php index bec9edca..8683efa8 100644 --- a/src/code-snippets.php +++ b/src/code-snippets.php @@ -8,11 +8,11 @@ * License: GPL-2.0-or-later * License URI: license.txt * Text Domain: code-snippets - * Version: 3.6.7 + * Version: 3.6.8-dev.1 * Requires PHP: 7.4 * Requires at least: 5.0 * - * @version 3.6.7 + * @version 3.6.8-dev.1 * @package Code_Snippets * @author Shea Bunge * @copyright 2012-2023 Code Snippets Pro @@ -37,7 +37,7 @@ * * @const string */ - define( 'CODE_SNIPPETS_VERSION', '3.6.7' ); + define( 'CODE_SNIPPETS_VERSION', '3.6.8-dev.1' ); /** * The full path to the main file of this plugin. diff --git a/src/composer.json b/src/composer.json index 53cb85d7..f08c96b3 100644 --- a/src/composer.json +++ b/src/composer.json @@ -26,7 +26,7 @@ ] }, "require": { - "php": ">=7.4", + "php": ">=7.4", "ext-dom": "*", "ext-json": "*", "composer/installers": "^2.3" @@ -37,6 +37,9 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.0" }, "config": { + "platform": { + "php": "7.4" + }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "composer/installers": true diff --git a/src/composer.lock b/src/composer.lock index 890dda8f..7ab674d3 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4d247b821abb0ab8945681a4e0110753", + "content-hash": "483a8311b70d7fb9078831174276ee2e", "packages": [ { "name": "composer/installers", @@ -759,10 +759,12 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4", "ext-dom": "*", "ext-json": "*" }, "platform-dev": [], + "platform-overrides": { + "php": "7.4" + }, "plugin-api-version": "2.6.0" } diff --git a/src/readme.txt b/src/readme.txt index 31928c89..d15be28b 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -4,8 +4,8 @@ Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later License URI: license.txt -Stable tag: 3.6.7 -Tested up to: 6.7.1 +Stable tag: 3.6.8-dev.1 +Tested up to: 6.7.2 An easy, clean and simple way to enhance your site with code snippets. From a123b52a79762068c27f183d9b4b7d2cef0dc697 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Wed, 12 Feb 2025 22:53:19 +1100 Subject: [PATCH 009/268] Add filter hook for hiding promo notices and remove redundant 'Go Pro' notice. --- src/php/class-admin.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/php/class-admin.php b/src/php/class-admin.php index 89c33cf1..2d65797c 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -262,6 +262,10 @@ public function debug_information( array $info ): array { public function print_notices() { global $current_user; + if ( apply_filters( 'code_snippets/hide_welcome_banner', false ) ) { + return; + } + $meta_key = 'ignore_code_snippets_survey_message'; $dismissed = get_user_meta( $current_user->ID, $meta_key ); @@ -287,12 +291,6 @@ public function print_notices() { $action_url = $welcome['action_url_free']; $action_label = $welcome['action_label_free']; - } elseif ( ! in_array( 'pro', $dismissed, true ) ) { - $notice = 'pro'; - $action_url = 'https://snipco.de/Mlll'; - $action_label = __( 'Upgrade now', 'code-snippets' ); - $text = __( 'Lifetime plans return! Enjoy Code Snippets Pro with new pricing choices, including lifetime, monthly and yearly subscriptions.', 'code-snippets' ); - } elseif ( ! in_array( 'survey', $dismissed, true ) && ! in_array( 'true', $dismissed, true ) ) { $notice = 'survey'; $action_url = 'https://codesnippets.pro/survey/'; From 7b4f7313b33e5bc8e2ceb9970302d85c8cc120a5 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Wed, 12 Feb 2025 23:19:03 +1100 Subject: [PATCH 010/268] Build list of setting defaults without calling translation functions early. --- src/php/settings/settings-fields.php | 164 ++++++++++++--------------- 1 file changed, 74 insertions(+), 90 deletions(-) diff --git a/src/php/settings/settings-fields.php b/src/php/settings/settings-fields.php index 49948b82..6d49fe87 100644 --- a/src/php/settings/settings-fields.php +++ b/src/php/settings/settings-fields.php @@ -8,6 +8,8 @@ namespace Code_Snippets\Settings; +use function Code_Snippets\code_snippets; + /** * Retrieve the default setting values * @@ -20,17 +22,33 @@ function get_default_settings(): array { return $defaults; } - $defaults = array(); - - foreach ( get_settings_fields() as $section_id => $fields ) { - $defaults[ $section_id ] = array(); + $defaults = [ + 'general' => [ + 'activate_by_default' => true, + 'enable_tags' => true, + 'enable_description' => true, + 'visual_editor_rows' => 5, + 'list_order' => 'priority-asc', + 'disable_prism' => false, + 'hide_upgrade_menu' => false, + 'complete_uninstall' => false, + ], + 'editor' => [ + 'indent_with_tabs' => true, + 'tab_size' => 4, + 'indent_unit' => 4, + 'wrap_lines' => true, + 'code_folding' => true, + 'line_numbers' => true, + 'auto_close_brackets' => true, + 'highlight_selection_matches' => true, + 'highlight_active_line' => true, + 'keymap' => 'default', + 'theme' => 'default', + ], + ]; - foreach ( $fields as $field_id => $field_atts ) { - if ( isset( $field_atts['default'] ) ) { - $defaults[ $section_id ][ $field_id ] = $field_atts['default']; - } - } - } + $defaults = apply_filters( 'code_snippets_settings_defaults', $defaults ); return $defaults; } @@ -65,35 +83,27 @@ function get_settings_fields(): array { $fields['general'] = [ 'activate_by_default' => [ - 'name' => __( 'Activate by Default', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( "Make the 'Save and Activate' button the default action when saving a snippet.", 'code-snippets' ), - 'default' => true, - ], - - 'enable_tags' => [ - 'name' => __( 'Enable Snippet Tags', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( 'Show snippet tags on admin pages.', 'code-snippets' ), - 'default' => true, - ], - - 'enable_description' => [ - 'name' => __( 'Enable Snippet Descriptions', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( 'Show snippet descriptions on admin pages.', 'code-snippets' ), - 'default' => true, - ], - - 'visual_editor_rows' => [ - 'name' => __( 'Description Editor Height', 'code-snippets' ), - 'type' => 'number', - 'label' => _x( 'rows', 'unit', 'code-snippets' ), - 'default' => 5, - 'min' => 0, - ], - - 'list_order' => [ + 'name' => __( 'Activate by Default', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( "Make the 'Save and Activate' button the default action when saving a snippet.", 'code-snippets' ), + ], + 'enable_tags' => [ + 'name' => __( 'Enable Snippet Tags', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'Show snippet tags on admin pages.', 'code-snippets' ), + ], + 'enable_description' => [ + 'name' => __( 'Enable Snippet Descriptions', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'Show snippet descriptions on admin pages.', 'code-snippets' ), + ], + 'visual_editor_rows' => [ + 'name' => __( 'Description Editor Height', 'code-snippets' ), + 'type' => 'number', + 'label' => _x( 'rows', 'unit', 'code-snippets' ), + 'min' => 0, + ], + 'list_order' => [ 'name' => __( 'Snippets List Order', 'code-snippets' ), 'type' => 'select', 'desc' => __( 'Default way to order snippets on the All Snippets admin menu.', 'code-snippets' ), @@ -104,117 +114,93 @@ function get_settings_fields(): array { 'modified-desc' => __( 'Modified (latest first)', 'code-snippets' ), 'modified-asc' => __( 'Modified (oldest first)', 'code-snippets' ), ], - 'default' => 'priority-asc', - ], - - 'disable_prism' => [ - 'name' => __( 'Disable Syntax Highlighter', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( 'Disable syntax highlighting when displaying snippet code on the front-end.', 'code-snippets' ), - 'default' => false, ], - - 'hide_upgrade_menu' => [ - 'name' => __( 'Hide Upgrade Menu', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( 'Hide the Upgrade button from the admin menu.', 'code-snippets' ), - 'default' => false, + 'disable_prism' => [ + 'name' => __( 'Disable Syntax Highlighter', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'Disable syntax highlighting when displaying snippet code on the front-end.', 'code-snippets' ), ], ]; + if ( ! code_snippets()->licensing->is_licensed() ) { + $fields['general']['hide_upgrade_menu'] = [ + 'name' => __( 'Hide Upgrade Menu', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'Hide the Upgrade button from the admin menu.', 'code-snippets' ), + ]; + } + if ( ! is_multisite() || is_main_site() ) { $fields['general']['complete_uninstall'] = [ - 'name' => __( 'Complete Uninstall', 'code-snippets' ), - 'type' => 'checkbox', - 'label' => __( 'When the plugin is deleted from the Plugins menu, also delete all snippets and plugin settings.', 'code-snippets' ), - 'default' => false, + 'name' => __( 'Complete Uninstall', 'code-snippets' ), + 'type' => 'checkbox', + 'label' => __( 'When the plugin is deleted from the Plugins menu, also delete all snippets and plugin settings.', 'code-snippets' ), ]; } - // Code Editor settings section. - $fields['editor'] = [ - 'indent_with_tabs' => [ + 'indent_with_tabs' => [ 'name' => __( 'Indent With Tabs', 'code-snippets' ), 'type' => 'checkbox', 'label' => __( 'Use hard tabs instead of spaces for indentation.', 'code-snippets' ), - 'default' => true, 'codemirror' => 'indentWithTabs', ], - - 'tab_size' => [ + 'tab_size' => [ 'name' => __( 'Tab Size', 'code-snippets' ), 'type' => 'number', 'desc' => __( 'The width of a tab character.', 'code-snippets' ), - 'default' => 4, 'label' => _x( 'spaces', 'unit', 'code-snippets' ), 'codemirror' => 'tabSize', 'min' => 0, ], - - 'indent_unit' => [ + 'indent_unit' => [ 'name' => __( 'Indent Unit', 'code-snippets' ), 'type' => 'number', 'desc' => __( 'The number of spaces to indent a block.', 'code-snippets' ), - 'default' => 4, 'label' => _x( 'spaces', 'unit', 'code-snippets' ), 'codemirror' => 'indentUnit', 'min' => 0, ], - - 'wrap_lines' => [ + 'wrap_lines' => [ 'name' => __( 'Wrap Lines', 'code-snippets' ), 'type' => 'checkbox', 'label' => __( 'Soft-wrap long lines of code instead of horizontally scrolling.', 'code-snippets' ), - 'default' => true, 'codemirror' => 'lineWrapping', ], - - 'code_folding' => [ + 'code_folding' => [ 'name' => __( 'Code Folding', 'code-snippets' ), 'type' => 'checkbox', 'label' => __( 'Allow folding functions or other blocks into a single line.', 'code-snippets' ), - 'default' => true, 'codemirror' => 'foldGutter', ], - - 'line_numbers' => [ + 'line_numbers' => [ 'name' => __( 'Line Numbers', 'code-snippets' ), 'type' => 'checkbox', 'label' => __( 'Show line numbers to the left of the editor.', 'code-snippets' ), - 'default' => true, 'codemirror' => 'lineNumbers', ], - - 'auto_close_brackets' => [ + 'auto_close_brackets' => [ 'name' => __( 'Auto Close Brackets', 'code-snippets' ), 'type' => 'checkbox', 'label' => __( 'Auto-close brackets and quotes when typed.', 'code-snippets' ), - 'default' => true, 'codemirror' => 'autoCloseBrackets', ], - 'highlight_selection_matches' => [ 'name' => __( 'Highlight Selection Matches', 'code-snippets' ), 'label' => __( 'Highlight all instances of a currently selected word.', 'code-snippets' ), 'type' => 'checkbox', - 'default' => true, 'codemirror' => 'highlightSelectionMatches', ], - - 'highlight_active_line' => [ + 'highlight_active_line' => [ 'name' => __( 'Highlight Active Line', 'code-snippets' ), 'label' => __( 'Highlight the line that is currently being edited.', 'code-snippets' ), 'type' => 'checkbox', - 'default' => true, 'codemirror' => 'styleActiveLine', ], - - 'keymap' => [ + 'keymap' => [ 'name' => __( 'Keymap', 'code-snippets' ), 'type' => 'select', 'desc' => __( 'The set of keyboard shortcuts to use in the code editor.', 'code-snippets' ), - 'default' => 'default', 'options' => [ 'default' => __( 'Default', 'code-snippets' ), 'vim' => __( 'Vim', 'code-snippets' ), @@ -223,11 +209,9 @@ function get_settings_fields(): array { ], 'codemirror' => 'keyMap', ], - - 'theme' => [ + 'theme' => [ 'name' => __( 'Theme', 'code-snippets' ), 'type' => 'select', - 'default' => 'default', 'options' => get_editor_theme_list(), 'codemirror' => 'theme', ], From a8799f820421dd1ee5e63b332df58ef30f256050 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Thu, 13 Feb 2025 21:01:46 +1100 Subject: [PATCH 011/268] Update version number to 3.6.8. --- CHANGELOG.md | 14 ++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/code-snippets.php | 6 +++--- src/php/settings/settings-fields.php | 2 +- src/readme.txt | 20 +++++++++++++++++--- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6055345..ad388cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [3.6.8] (2025-02-13) + +### Added +* `code_snippets/hide_welcome_banner` filter hook for hiding welcome banner in dashboard. + +### Removed +* Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future version. + +### Fixed +* Shortcodes embedded within `[code_snippet]` shortcodes not evaluating correctly. +* Translation functions being called too early in some instances when loading plugin settings. +* 'Generate' button not appearing on some sites. (PRO) +* Incorrect arrow entity used in cloud list table (props to [brandonjp]). + ## [3.6.7] (2025-01-24) ### Added diff --git a/package-lock.json b/package-lock.json index ed9f538f..ba0cbd27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-snippets", - "version": "3.6.7", + "version": "3.6.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-snippets", - "version": "3.6.7", + "version": "3.6.8", "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", diff --git a/package.json b/package.json index 405a9ef3..9fbfb63c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-snippets", "description": "Manage code snippets running on a WordPress-powered site through a graphical interface.", "homepage": "https://codesnippets.pro", - "version": "3.6.8-dev.1", + "version": "3.6.8", "main": "src/dist/edit.js", "directories": { "test": "tests" diff --git a/src/code-snippets.php b/src/code-snippets.php index 8683efa8..b2b9cd2f 100644 --- a/src/code-snippets.php +++ b/src/code-snippets.php @@ -8,11 +8,11 @@ * License: GPL-2.0-or-later * License URI: license.txt * Text Domain: code-snippets - * Version: 3.6.8-dev.1 + * Version: 3.6.8 * Requires PHP: 7.4 * Requires at least: 5.0 * - * @version 3.6.8-dev.1 + * @version 3.6.8 * @package Code_Snippets * @author Shea Bunge * @copyright 2012-2023 Code Snippets Pro @@ -37,7 +37,7 @@ * * @const string */ - define( 'CODE_SNIPPETS_VERSION', '3.6.8-dev.1' ); + define( 'CODE_SNIPPETS_VERSION', '3.6.8' ); /** * The full path to the main file of this plugin. diff --git a/src/php/settings/settings-fields.php b/src/php/settings/settings-fields.php index 6d49fe87..593635b3 100644 --- a/src/php/settings/settings-fields.php +++ b/src/php/settings/settings-fields.php @@ -13,7 +13,7 @@ /** * Retrieve the default setting values * - * @return array> + * @return array> */ function get_default_settings(): array { static $defaults; diff --git a/src/readme.txt b/src/readme.txt index d15be28b..bf316c95 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -1,10 +1,10 @@ === Code Snippets === -Contributors: bungeshea, ver3, nate33, lightbulbman, 0aksmith, pauserratgutierrez, johnpixle, codesnippetspro +Contributors: bungeshea, ver3, nate33, lightbulbman, 0aksmith, johnpixle, codesnippetspro Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later License URI: license.txt -Stable tag: 3.6.8-dev.1 +Stable tag: 3.6.8 Tested up to: 6.7.2 An easy, clean and simple way to enhance your site with code snippets. @@ -103,6 +103,20 @@ You can report security bugs found in the source code of this plugin through the == Changelog == += 3.6.8 (2025-02-13) = + +__Added__ +* `code_snippets/hide_welcome_banner` filter hook for hiding welcome banner in dashboard. + +__Removed__ +* Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future version. + +__Fixed__ +* Shortcodes embedded within `[code_snippet]` shortcodes not evaluating correctly. +* Translation functions being called too early in some instances when loading plugin settings. +* 'Generate' button not appearing on some sites. (PRO) +* Incorrect arrow entity used in cloud list table (props to brandonjp). + = 3.6.7 (2025-01-24) = __Added__ @@ -111,7 +125,7 @@ __Added__ __Changed__ * Updated CSS to use latest Sass features. -* Moved theme selector to just above editor preview on settings page (thanks to [brandonjp]). ([#206](https://github.com/codesnippetspro/code-snippets/issues/206)) +* Moved theme selector to just above editor preview on settings page (thanks to brandonjp). ([#206](https://github.com/codesnippetspro/code-snippets/issues/206)) * `[code_snippet]` shortcodes can now be nested within each other. ([#198](https://github.com/codesnippetspro/code-snippets/issues/198)) __Fixed__ From 5f40b9d54352cef4c8a442fa7235b115c94a70ea Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 14 Feb 2025 17:17:59 +1100 Subject: [PATCH 012/268] Remove reference to plugins.css. --- CHANGELOG.md | 6 +++++- src/php/class-admin.php | 19 ------------------- src/readme.txt | 8 ++++++-- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad388cdf..8895d528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # Changelog -## [3.6.8] (2025-02-13) +## [3.6.8] (2025-02-14) ### Added * `code_snippets/hide_welcome_banner` filter hook for hiding welcome banner in dashboard. +### Changed +* Updated Freemius SDK to the latest version. (PRO) + ### Removed * Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future version. @@ -13,6 +16,7 @@ * Translation functions being called too early in some instances when loading plugin settings. * 'Generate' button not appearing on some sites. (PRO) * Incorrect arrow entity used in cloud list table (props to [brandonjp]). +* Removed reference to missing plugins.css file in core plugin version. ## [3.6.7] (2025-01-24) diff --git a/src/php/class-admin.php b/src/php/class-admin.php index 2d65797c..503819f6 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -67,25 +67,6 @@ public function run() { add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 ); add_filter( 'debug_information', array( $this, 'debug_information' ) ); add_action( 'code_snippets/admin/manage', array( $this, 'print_notices' ) ); - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); - } - - /** - * Enqueue general admin assets. - * - * @param string|null $hook_name Current plugin page hook name. - * - * @return void - */ - public function enqueue_admin_assets( ?string $hook_name ) { - if ( 'plugins.php' === $hook_name ) { - wp_enqueue_style( - 'code-snippets-plugins-css', - plugins_url( 'dist/plugins.css', PLUGIN_FILE ), - [], - PLUGIN_VERSION - ); - } } /** diff --git a/src/readme.txt b/src/readme.txt index bf316c95..cfe5b55c 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -103,11 +103,14 @@ You can report security bugs found in the source code of this plugin through the == Changelog == -= 3.6.8 (2025-02-13) = += 3.6.8 (2025-02-14) = __Added__ * `code_snippets/hide_welcome_banner` filter hook for hiding welcome banner in dashboard. +__Changed__ +* Updated Freemius SDK to the latest version. (PRO) + __Removed__ * Functionality allowing `[code_snippet]` shortcodes to be embedded recursively – it will be re-added in a future version. @@ -115,7 +118,8 @@ __Fixed__ * Shortcodes embedded within `[code_snippet]` shortcodes not evaluating correctly. * Translation functions being called too early in some instances when loading plugin settings. * 'Generate' button not appearing on some sites. (PRO) -* Incorrect arrow entity used in cloud list table (props to brandonjp). +* Incorrect arrow entity used in cloud list table (props to [brandonjp]). +* Removed reference to missing plugins.css file in core plugin version. = 3.6.7 (2025-01-24) = From 6f7bba0431bbdd189c12f3f0f885d3d3ed74ff71 Mon Sep 17 00:00:00 2001 From: brandonjp Date: Wed, 19 Feb 2025 00:09:58 -0600 Subject: [PATCH 013/268] re-correcting laquo to lsaquo in cloud table nav Well, good golly. I flipped 'em! In https://github.com/codesnippetspro/code-snippets/pull/215/commits/0c995dcdb79298378d5b0e379254f9a418b1bbe2 I did nothing of value for issue #215 . This one adds value by placing the correct left single/double angle bracket in the cloud table nav. --- src/php/cloud/list-table-shared-ops.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/php/cloud/list-table-shared-ops.php b/src/php/cloud/list-table-shared-ops.php index dbc0b1b9..b8e3f95e 100644 --- a/src/php/cloud/list-table-shared-ops.php +++ b/src/php/cloud/list-table-shared-ops.php @@ -182,7 +182,7 @@ function cloud_lts_pagination( string $which, string $source, int $total_items, $page_links[] = ''; } else { $page_links[] = sprintf( - '%s', + '%s', esc_url( remove_query_arg( $source . '_page', $current_url ) ), esc_html__( 'First page', 'code-snippets' ) ); @@ -192,7 +192,7 @@ function cloud_lts_pagination( string $which, string $source, int $total_items, $page_links[] = ''; } else { $page_links[] = sprintf( - '%s', + '%s', esc_url( add_query_arg( $source . '_page', max( 1, $current - 1 ), $current_url ) ), esc_html__( 'Previous page', 'code-snippets' ) ); From 10351061200bf2cd28a382b914ce33b9c3fe1317 Mon Sep 17 00:00:00 2001 From: lightbulbman Date: Fri, 14 Mar 2025 00:33:19 +0000 Subject: [PATCH 014/268] #228 html validation bug fix --- src/php/snippet-ops.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index 2e3a9023..6e189d32 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -295,10 +295,12 @@ function activate_snippet( int $id, ?bool $network = null ) { // translators: %d: snippet identifier. return sprintf( __( 'Could not locate snippet with ID %d.', 'code-snippets' ), $id ); } - - $validator = new Validator( $snippet->code ); - if ( $validator->validate() ) { - return __( 'Could not activate snippet: code did not pass validation.', 'code-snippets' ); + + if('php' == $snippet->type ){ + $validator = new Validator( $snippet->code ); + if ( $validator->validate() ) { + return __( 'Could not activate snippet: code did not pass validation.', 'code-snippets' ); + } } $result = $wpdb->update( From 6d64c3b790c5868b113960f9ffcf8af32ea52a8c Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 21 Mar 2025 21:35:49 +1100 Subject: [PATCH 015/268] Apply changes from pro branch. --- package-lock.json | 3437 +++++------------ package.json | 13 +- src/css/common/_codemirror.scss | 2 - .../EditorSidebar/EditorSidebar.tsx | 6 +- .../EditorSidebar/actions/DeleteButton.tsx | 2 +- .../EditorSidebar/actions/ExportButtons.tsx | 2 +- .../EditorSidebar/actions/ShortcodeInfo.tsx | 16 +- .../EditorSidebar/actions/SubmitButtons.tsx | 18 +- .../controls/ActivationSwitch.tsx | 12 +- .../controls/MultisiteSharingSettings.tsx | 5 +- .../EditorSidebar/controls/TagsInput.tsx | 8 +- src/js/components/SnippetForm/SnippetForm.tsx | 2 +- .../SnippetForm/fields/CodeEditor.tsx | 6 +- .../fields/CodeEditorShortcuts.tsx | 2 +- .../SnippetForm/fields/SnippetTypeInput.tsx | 27 +- .../SnippetForm/page/PageHeading.tsx | 54 +- src/js/components/TagEditor/TagEditor.tsx | 178 - src/js/hooks/useSnippetForm.tsx | 2 +- src/js/hooks/useSnippetSubmit.ts | 40 +- .../{useSnippets.ts => useSnippetsAPI.ts} | 2 +- src/js/services/manage/requests.ts | 2 +- src/js/types/SelectOption.ts | 6 +- src/js/types/Snippet.ts | 16 +- src/js/types/wp/User.ts | 2 - src/js/utils/restAPI.ts | 20 - src/js/utils/{general.ts => screen.ts} | 0 src/js/utils/snippets.ts | 2 +- src/php/admin-menus/class-edit-menu.php | 4 +- 28 files changed, 1013 insertions(+), 2873 deletions(-) delete mode 100644 src/js/components/TagEditor/TagEditor.tsx rename src/js/hooks/{useSnippets.ts => useSnippetsAPI.ts} (98%) delete mode 100644 src/js/utils/restAPI.ts rename src/js/utils/{general.ts => screen.ts} (100%) diff --git a/package-lock.json b/package-lock.json index 9a1c2839..5f63cd61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,83 +10,74 @@ "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/fold": "^0.19.4", - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@wordpress/api-fetch": "^7.14.0", - "@wordpress/block-editor": "^14.9.0", - "@wordpress/blocks": "^14.3.0", - "@wordpress/components": "^29.0.0", - "@wordpress/data": "^10.14.0", - "@wordpress/dom-ready": "^4.14.0", - "@wordpress/i18n": "^5.14.0", - "@wordpress/icons": "^10.14.0", - "@wordpress/server-side-render": "^5.14.0", + "@wordpress/components": "^29.3.0", + "@wordpress/dom-ready": "^4.17.0", + "@wordpress/i18n": "^5.17.0", + "@wordpress/url": "^4.20.0", "axios": "^1.7.9", "classnames": "^2.5.1", "codemirror": "^5.29", - "codemirror-colorpicker": "^1.9.80", - "php-parser": "^3.2.1", + "php-parser": "^3.2.2", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-is": "^19.0.0", - "react-select": "^5.9.0" + "react-select": "^5.10.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.17.0", - "@stylistic/eslint-plugin": "^2.12.1", + "@eslint/js": "^9.20.0", + "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node18": "^18.2.4", "@types/archiver": "^6.0.3", "@types/codemirror": "^5.60.15", "@types/jquery": "^3.5.32", - "@types/node": "^22.10.2", + "@types/node": "^22.13.1", "@types/prismjs": "^1.26.5", - "@types/react": "^18.3.12", + "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "@types/rtlcss": "^3.5.4", "@types/tinymce": "^4.6.9", - "@types/web": "^0.0.187", - "@types/wordpress__editor": "^14.12.0", - "@typescript-eslint/eslint-plugin": "^8.18.1", - "@typescript-eslint/parser": "^8.18.1", - "@wordpress/babel-preset-default": "^8.14.0", + "@types/web": "^0.0.202", + "@typescript-eslint/eslint-plugin": "^8.24.0", + "@typescript-eslint/parser": "^8.24.0", + "@wordpress/babel-preset-default": "^8.17.0", "archiver": "^7.0.1", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "babel-plugin-prismjs": "^2.1.0", "css-loader": "^7.1.2", "cssnano": "^7.0.6", - "eslint": "^9.17.0", + "eslint": "^9.20.1", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.1.0", "eslint-webpack-plugin": "^4.2.0", - "glob": "^11.0.0", + "glob": "^11.0.1", "globals": "^15.14.0", "mini-css-extract-plugin": "^2.9.2", - "postcss": "^8.4.49", + "postcss": "^8.5.2", "postcss-hexrgba": "^2.1.0", "postcss-load-config": "^6.0.1", "postcss-loader": "^8.1.1", + "react-is": "^19.0.0", "rtlcss": "^4.3.0", - "sass": "^1.83.0", + "sass": "^1.84.0", "sass-loader": "^16.0.4", "style-loader": "^4.0.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.1", + "typescript": "^5.7.3", + "typescript-eslint": "^8.24.0", "webpack": "^5.97.1", - "webpack-cli": "^6.0.0", + "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1", "webpack-remove-empty-scripts": "^1.0.4" } }, "node_modules/@ampproject/remapping": { "version": "2.2.1", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -142,7 +133,7 @@ }, "node_modules/@babel/compat-data": { "version": "7.26.2", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -150,7 +141,7 @@ }, "node_modules/@babel/core": { "version": "7.25.7", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -216,7 +207,7 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.25.9", @@ -305,7 +296,7 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.26.0", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", @@ -410,7 +401,7 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.25.9", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -430,22 +421,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "devOptional": true, + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.10" }, "bin": { "parser": "bin/babel-parser.js" @@ -1709,12 +1704,14 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" @@ -1744,7 +1741,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2353,7 +2352,7 @@ }, "node_modules/@jridgewell/source-map": { "version": "0.3.6", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2486,1603 +2485,797 @@ "node": ">=14" } }, - "node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - } + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "dev": true, + "license": "MIT" }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", + "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@typescript-eslint/utils": "^8.13.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=12" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", + "node_modules/@tannin/compile": { + "version": "1.1.0", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@tannin/evaluate": "^1.2.0", + "@tannin/postfix": "^1.1.0" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", + "node_modules/@tannin/evaluate": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/@tannin/plural-forms": { + "version": "1.1.0", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@tannin/compile": "^1.1.0" + } + }, + "node_modules/@tannin/postfix": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10.13.0" } }, - "node_modules/@radix-ui/react-focus-scope": { + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node18": { + "version": "18.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/archiver": { + "version": "6.0.3", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/readdir-glob": "*" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.0.1", + "node_modules/@types/codemirror": { + "version": "5.60.15", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/tern": "*" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.0.4", + "node_modules/@types/eslint": { + "version": "8.56.11", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.0.1", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", + "node_modules/@types/estree": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gradient-parser": { + "version": "0.1.3", + "license": "MIT" + }, + "node_modules/@types/highlight-words-core": { + "version": "1.2.1", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/istanbul-lib-report": "*" } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", + "node_modules/@types/jquery": { + "version": "3.5.32", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/sizzle": "*" } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mousetrap": { + "version": "1.6.14", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.1", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "undici-types": "~6.20.0" } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", + "node_modules/@types/parse-json": { + "version": "4.0.2", + "license": "MIT" + }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.10", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.12", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/prop-types": "*", + "csstype": "^3.0.2" } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", + "node_modules/@types/react-dom": { + "version": "18.3.1", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/react": "*" } }, - "node_modules/@react-spring/animated": { - "version": "9.7.3", + "node_modules/@types/react-transition-group": { + "version": "4.4.9", "license": "MIT", "dependencies": { - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*" } }, - "node_modules/@react-spring/core": { - "version": "9.7.3", + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "dev": true, "license": "MIT", "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/node": "*" } }, - "node_modules/@react-spring/shared": { - "version": "9.7.3", + "node_modules/@types/rtlcss": { + "version": "3.5.4", + "dev": true, "license": "MIT", "dependencies": { - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "postcss": "^8.2.x" } }, - "node_modules/@react-spring/types": { - "version": "9.7.3", + "node_modules/@types/sizzle": { + "version": "2.3.6", + "dev": true, "license": "MIT" }, - "node_modules/@react-spring/web": { - "version": "9.7.3", + "node_modules/@types/tern": { + "version": "0.23.7", + "dev": true, "license": "MIT", "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/core": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/estree": "*" } }, - "node_modules/@remote-ui/rpc": { - "version": "1.4.5", - "license": "MIT" + "node_modules/@types/tinymce": { + "version": "4.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jquery": "*" + } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", + "node_modules/@types/web": { + "version": "0.0.202", + "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.202.tgz", + "integrity": "sha512-2iO+wBir5OBnMlB9Z7aD/0SUZjR2mhiCLtPvGPboTqwBC4O3Yv6Vjwn5eMxGMXtRAm01OV9yUBi9C8pJa02TIA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", "dev": true, "license": "MIT" }, - "node_modules/@shopify/web-worker": { - "version": "6.4.0", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.24.0", + "dev": true, "license": "MIT", "dependencies": { - "@remote-ui/rpc": "^1.2.5" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/type-utils": "8.24.0", + "@typescript-eslint/utils": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" }, "engines": { - "node": ">=18.12.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": "^5.38.0", - "webpack-virtual-modules": "^0.4.3 || ^0.5.0 || ^0.6.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "webpack": { - "optional": true - }, - "webpack-virtual-modules": { - "optional": true - } + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", + "node_modules/@typescript-eslint/parser": { + "version": "8.24.0", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } }, - "node_modules/@stylistic/eslint-plugin": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz", - "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.24.0", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.13.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "eslint": ">=8.40.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.0", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.24.0", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.24.0", + "@typescript-eslint/utils": "8.24.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.2", + "node_modules/@typescript-eslint/types": { + "version": "8.24.0", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@tannin/compile": { - "version": "1.1.0", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.24.0", + "dev": true, "license": "MIT", "dependencies": { - "@tannin/evaluate": "^1.2.0", - "@tannin/postfix": "^1.1.0" + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@tannin/evaluate": { - "version": "1.2.0", - "license": "MIT" - }, - "node_modules/@tannin/plural-forms": { - "version": "1.1.0", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { - "@tannin/compile": "^1.1.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@tannin/postfix": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", "dev": true, "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=10.13.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node18": { - "version": "18.2.4", + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", "dev": true, - "license": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@types/archiver": { - "version": "6.0.3", + "node_modules/@typescript-eslint/utils": { + "version": "8.24.0", "dev": true, "license": "MIT", "dependencies": { - "@types/readdir-glob": "*" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@types/codemirror": { - "version": "5.60.15", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.24.0", "dev": true, "license": "MIT", "dependencies": { - "@types/tern": "*" + "@typescript-eslint/types": "8.24.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@types/eslint": { - "version": "8.56.11", - "devOptional": true, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", "license": "MIT", "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "devOptional": true, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "dev": true, "license": "MIT", "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, - "node_modules/@types/estree": { - "version": "1.0.6", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/gradient-parser": { - "version": "0.1.3", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "dev": true, "license": "MIT" }, - "node_modules/@types/highlight-words-core": { - "version": "1.2.1", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "dev": true, "license": "MIT" }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", "dev": true, "license": "MIT" }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", "dev": true, "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", "dev": true, "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, - "node_modules/@types/jquery": { - "version": "3.5.32", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", "dev": true, "license": "MIT", "dependencies": { - "@types/sizzle": "*" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", "dev": true, - "license": "MIT" - }, - "node_modules/@types/mousetrap": { - "version": "1.6.14", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.13.1", - "devOptional": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~6.20.0" + "@xtuc/long": "4.2.2" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "license": "MIT" - }, - "node_modules/@types/prismjs": { - "version": "1.26.5", + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", "dev": true, "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.10", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.12", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.1", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "dev": true, "license": "MIT", "dependencies": { - "@types/react": "*" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.9", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "dev": true, "license": "MIT", "dependencies": { - "@types/react": "*" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/@types/readdir-glob": { - "version": "1.1.5", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, - "node_modules/@types/rtlcss": { - "version": "3.5.4", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^8.2.x" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/@types/simple-peer": { - "version": "9.11.8", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/@types/sizzle": { - "version": "2.3.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tern": { - "version": "0.23.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/tinymce": { - "version": "4.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/jquery": "*" - } - }, - "node_modules/@types/web": { - "version": "0.0.187", - "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.187.tgz", - "integrity": "sha512-PNvOlIo9AkUA8dvdqJHrvvIZG40Qi37QHNyl1qOD1m/4Lj+2thAtwJM2N9UmtGBtkhb3x9mP/6JlXYtQcZ3uFg==", - "dev": true - }, - "node_modules/@types/wordpress__editor": { - "version": "14.12.0", - "deprecated": "This is a stub types definition. @wordpress/editor provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "@wordpress/editor": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/type-utils": "8.24.0", - "@typescript-eslint/utils": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/typescript-estree": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.24.0", - "@typescript-eslint/utils": "8.24.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/visitor-keys": "8.24.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.24.0", - "@typescript-eslint/types": "8.24.0", - "@typescript-eslint/typescript-estree": "8.24.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.24.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "license": "MIT" - }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "license": "MIT", - "dependencies": { - "@use-gesture/core": "10.3.1" - }, - "peerDependencies": { - "react": ">= 16.8.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", "dev": true, "license": "MIT", "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@wordpress/a11y": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/dom-ready": "^4.17.0", - "@wordpress/i18n": "^5.17.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/api-fetch": { - "version": "7.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/i18n": "^5.17.0", - "@wordpress/url": "^4.17.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/autop": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/babel-preset-default": { - "version": "8.17.0", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/core": "7.25.7", - "@babel/plugin-transform-react-jsx": "7.25.7", - "@babel/plugin-transform-runtime": "7.25.7", - "@babel/preset-env": "7.25.7", - "@babel/preset-typescript": "7.25.7", - "@babel/runtime": "7.25.7", - "@wordpress/browserslist-config": "^6.17.0", - "@wordpress/warning": "^3.17.0", - "browserslist": "^4.21.10", - "core-js": "^3.31.0", - "react": "^18.3.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/blob": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/block-editor": { - "version": "14.12.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@emotion/react": "^11.7.1", - "@emotion/styled": "^11.6.0", - "@react-spring/web": "^9.4.5", - "@wordpress/a11y": "^4.17.0", - "@wordpress/api-fetch": "^7.17.0", - "@wordpress/blob": "^4.17.0", - "@wordpress/block-serialization-default-parser": "^5.17.0", - "@wordpress/blocks": "^14.6.0", - "@wordpress/commands": "^1.17.0", - "@wordpress/components": "^29.3.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/date": "^5.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/dom": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/escape-html": "^3.17.0", - "@wordpress/hooks": "^4.17.0", - "@wordpress/html-entities": "^4.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/icons": "^10.17.0", - "@wordpress/is-shallow-equal": "^5.17.0", - "@wordpress/keyboard-shortcuts": "^5.17.0", - "@wordpress/keycodes": "^4.17.0", - "@wordpress/notices": "^5.17.0", - "@wordpress/preferences": "^4.17.0", - "@wordpress/priority-queue": "^3.17.0", - "@wordpress/private-apis": "^1.17.0", - "@wordpress/rich-text": "^7.17.0", - "@wordpress/style-engine": "^2.17.0", - "@wordpress/token-list": "^3.17.0", - "@wordpress/upload-media": "^0.2.0", - "@wordpress/url": "^4.17.0", - "@wordpress/warning": "^3.17.0", - "@wordpress/wordcount": "^4.17.0", - "change-case": "^4.1.2", - "clsx": "^2.1.1", - "colord": "^2.7.0", - "deepmerge": "^4.3.0", - "diff": "^4.0.2", - "fast-deep-equal": "^3.1.3", - "memize": "^2.1.0", - "parsel-js": "^1.1.2", - "postcss": "^8.4.21", - "postcss-prefix-selector": "^1.16.0", - "postcss-urlrebase": "^1.4.0", - "react-autosize-textarea": "^7.1.0", - "react-easy-crop": "^5.0.6", - "remove-accents": "^0.5.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@wordpress/block-serialization-default-parser": { - "version": "5.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/blocks": { - "version": "14.6.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/autop": "^4.17.0", - "@wordpress/blob": "^4.17.0", - "@wordpress/block-serialization-default-parser": "^5.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/dom": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/hooks": "^4.17.0", - "@wordpress/html-entities": "^4.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/is-shallow-equal": "^5.17.0", - "@wordpress/private-apis": "^1.17.0", - "@wordpress/rich-text": "^7.17.0", - "@wordpress/shortcode": "^4.17.0", - "@wordpress/warning": "^3.17.0", - "change-case": "^4.1.2", - "colord": "^2.7.0", - "fast-deep-equal": "^3.1.3", - "hpq": "^1.3.0", - "is-plain-object": "^5.0.0", - "memize": "^2.1.0", - "react-is": "^18.3.0", - "remove-accents": "^0.5.0", - "showdown": "^1.9.1", - "simple-html-tokenizer": "^0.5.7", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" - } - }, - "node_modules/@wordpress/blocks/node_modules/react-is": { - "version": "18.3.1", - "license": "MIT" - }, - "node_modules/@wordpress/browserslist-config": { - "version": "6.17.0", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/commands": { - "version": "1.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/components": "^29.3.0", - "@wordpress/data": "^10.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/icons": "^10.17.0", - "@wordpress/keyboard-shortcuts": "^5.17.0", - "@wordpress/private-apis": "^1.17.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@wordpress/components": { - "version": "29.3.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@ariakit/react": "^0.4.15", - "@babel/runtime": "7.25.7", - "@emotion/cache": "^11.7.1", - "@emotion/css": "^11.7.1", - "@emotion/react": "^11.7.1", - "@emotion/serialize": "^1.0.2", - "@emotion/styled": "^11.6.0", - "@emotion/utils": "^1.0.0", - "@floating-ui/react-dom": "^2.0.8", - "@types/gradient-parser": "0.1.3", - "@types/highlight-words-core": "1.2.1", - "@use-gesture/react": "^10.3.1", - "@wordpress/a11y": "^4.17.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/date": "^5.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/dom": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/escape-html": "^3.17.0", - "@wordpress/hooks": "^4.17.0", - "@wordpress/html-entities": "^4.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/icons": "^10.17.0", - "@wordpress/is-shallow-equal": "^5.17.0", - "@wordpress/keycodes": "^4.17.0", - "@wordpress/primitives": "^4.17.0", - "@wordpress/private-apis": "^1.17.0", - "@wordpress/rich-text": "^7.17.0", - "@wordpress/warning": "^3.17.0", - "change-case": "^4.1.2", - "clsx": "^2.1.1", - "colord": "^2.7.0", - "date-fns": "^3.6.0", - "deepmerge": "^4.3.0", - "fast-deep-equal": "^3.1.3", - "framer-motion": "^11.1.9", - "gradient-parser": "^0.1.5", - "highlight-words-core": "^1.2.2", - "is-plain-object": "^5.0.0", - "memize": "^2.1.0", - "path-to-regexp": "^6.2.1", - "re-resizable": "^6.4.0", - "react-colorful": "^5.3.1", - "remove-accents": "^0.5.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@wordpress/compose": { - "version": "7.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@types/mousetrap": "^1.6.8", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/dom": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/is-shallow-equal": "^5.17.0", - "@wordpress/keycodes": "^4.17.0", - "@wordpress/priority-queue": "^3.17.0", - "@wordpress/undo-manager": "^1.17.0", - "change-case": "^4.1.2", - "clipboard": "^2.0.11", - "mousetrap": "^1.6.5", - "use-memo-one": "^1.1.1" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "node": ">=18.12.0" }, "peerDependencies": { - "react": "^18.0.0" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, - "node_modules/@wordpress/core-data": { - "version": "7.13.0", + "node_modules/@webpack-cli/info": { + "version": "3.0.1", "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/api-fetch": "*", - "@wordpress/block-editor": "*", - "@wordpress/blocks": "*", - "@wordpress/compose": "*", - "@wordpress/data": "*", - "@wordpress/deprecated": "*", - "@wordpress/element": "*", - "@wordpress/html-entities": "*", - "@wordpress/i18n": "*", - "@wordpress/is-shallow-equal": "*", - "@wordpress/private-apis": "*", - "@wordpress/rich-text": "*", - "@wordpress/sync": "*", - "@wordpress/undo-manager": "*", - "@wordpress/url": "*", - "@wordpress/warning": "*", - "change-case": "^4.1.2", - "equivalent-key-map": "^0.2.2", - "fast-deep-equal": "^3.1.3", - "memize": "^2.1.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@wordpress/data": { - "version": "10.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/compose": "^7.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/is-shallow-equal": "^5.17.0", - "@wordpress/priority-queue": "^3.17.0", - "@wordpress/private-apis": "^1.17.0", - "@wordpress/redux-routine": "^5.17.0", - "deepmerge": "^4.3.0", - "equivalent-key-map": "^0.2.2", - "is-plain-object": "^5.0.0", - "is-promise": "^4.0.0", - "redux": "^5.0.1", - "rememo": "^4.0.2", - "use-memo-one": "^1.1.1" - }, + "license": "MIT", "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "node": ">=18.12.0" }, "peerDependencies": { - "react": "^18.0.0" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, - "node_modules/@wordpress/dataviews": { - "version": "4.9.0", + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@ariakit/react": "^0.4.10", - "@babel/runtime": "7.25.7", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/data": "*", - "@wordpress/element": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/primitives": "*", - "@wordpress/private-apis": "*", - "@wordpress/warning": "*", - "clsx": "^2.1.1", - "remove-accents": "^0.5.0" - }, + "license": "MIT", "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "node": ">=18.12.0" }, "peerDependencies": { - "react": "^18.0.0" - } - }, - "node_modules/@wordpress/date": { - "version": "5.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/deprecated": "^4.17.0", - "moment": "^2.29.4", - "moment-timezone": "^0.5.40" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/@wordpress/deprecated": { - "version": "4.17.0", + "node_modules/@wordpress/a11y": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.20.0.tgz", + "integrity": "sha512-hyFKC3D1o0Cvy1HeFgujsuW9gTrwVL4DVIfnQytG2+gMFaDyux4Qmzyg2e3k71BKlHn7J28Q3i0xNqC2k7ZoFw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/hooks": "^4.17.0" + "@wordpress/dom-ready": "^4.20.0", + "@wordpress/i18n": "^5.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/dom": { - "version": "4.17.0", + "node_modules/@wordpress/babel-preset-default": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.20.0.tgz", + "integrity": "sha512-UGfPuNFjN8RG1BsFc04jOHoJFi3ZINYo4nsmrrUx1PFSFD2qpttmV03dWFWfqSvLvrMlYPQPMkYyK5KS6THxVQ==", + "dev": true, "license": "GPL-2.0-or-later", "dependencies": { + "@babel/core": "7.25.7", + "@babel/plugin-transform-react-jsx": "7.25.7", + "@babel/plugin-transform-runtime": "7.25.7", + "@babel/preset-env": "7.25.7", + "@babel/preset-typescript": "7.25.7", "@babel/runtime": "7.25.7", - "@wordpress/deprecated": "^4.17.0" + "@wordpress/browserslist-config": "^6.20.0", + "@wordpress/warning": "^3.20.0", + "browserslist": "^4.21.10", + "core-js": "^3.31.0", + "react": "^18.3.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/dom-ready": { - "version": "4.17.0", + "node_modules/@wordpress/browserslist-config": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.20.0.tgz", + "integrity": "sha512-n9Q1UN3QL4DuZLySZpbJoZbQvBTjMjRV5yaxnmQaEpOyqablX4GnYq39fwTY72hBN/c1b0oyOFcsbhsrx0wqzg==", + "dev": true, "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/editor": { - "version": "14.13.0", - "dev": true, + "node_modules/@wordpress/components": { + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-29.6.0.tgz", + "integrity": "sha512-kk9GxGnoGBqHz0S4gT2UJHQBwudE1AgTPOc3v3k72kZkDaT88ZayBd/4/gHsa659zImgrwXZ6SjQ6Nczt80Bgg==", "license": "GPL-2.0-or-later", "dependencies": { + "@ariakit/react": "^0.4.15", "@babel/runtime": "7.25.7", - "@wordpress/a11y": "*", - "@wordpress/api-fetch": "*", - "@wordpress/blob": "*", - "@wordpress/block-editor": "*", - "@wordpress/blocks": "*", - "@wordpress/commands": "*", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/core-data": "*", - "@wordpress/data": "*", - "@wordpress/dataviews": "*", - "@wordpress/date": "*", - "@wordpress/deprecated": "*", - "@wordpress/dom": "*", - "@wordpress/element": "*", - "@wordpress/fields": "*", - "@wordpress/hooks": "*", - "@wordpress/html-entities": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/interface": "*", - "@wordpress/keyboard-shortcuts": "*", - "@wordpress/keycodes": "*", - "@wordpress/media-utils": "*", - "@wordpress/notices": "*", - "@wordpress/patterns": "*", - "@wordpress/plugins": "*", - "@wordpress/preferences": "*", - "@wordpress/private-apis": "*", - "@wordpress/reusable-blocks": "*", - "@wordpress/rich-text": "*", - "@wordpress/server-side-render": "*", - "@wordpress/url": "*", - "@wordpress/warning": "*", - "@wordpress/wordcount": "*", + "@emotion/cache": "^11.7.1", + "@emotion/css": "^11.7.1", + "@emotion/react": "^11.7.1", + "@emotion/serialize": "^1.0.2", + "@emotion/styled": "^11.6.0", + "@emotion/utils": "^1.0.0", + "@floating-ui/react-dom": "^2.0.8", + "@types/gradient-parser": "0.1.3", + "@types/highlight-words-core": "1.2.1", + "@use-gesture/react": "^10.3.1", + "@wordpress/a11y": "^4.20.0", + "@wordpress/compose": "^7.20.0", + "@wordpress/date": "^5.20.0", + "@wordpress/deprecated": "^4.20.0", + "@wordpress/dom": "^4.20.0", + "@wordpress/element": "^6.20.0", + "@wordpress/escape-html": "^3.20.0", + "@wordpress/hooks": "^4.20.0", + "@wordpress/html-entities": "^4.20.0", + "@wordpress/i18n": "^5.20.0", + "@wordpress/icons": "^10.20.0", + "@wordpress/is-shallow-equal": "^5.20.0", + "@wordpress/keycodes": "^4.20.0", + "@wordpress/primitives": "^4.20.0", + "@wordpress/private-apis": "^1.20.0", + "@wordpress/rich-text": "^7.20.0", + "@wordpress/warning": "^3.20.0", "change-case": "^4.1.2", - "client-zip": "^2.4.5", "clsx": "^2.1.1", + "colord": "^2.7.0", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.1.9", + "gradient-parser": "^0.1.5", + "highlight-words-core": "^1.2.2", "is-plain-object": "^5.0.0", "memize": "^2.1.0", - "react-autosize-textarea": "^7.1.0", + "path-to-regexp": "^6.2.1", + "re-resizable": "^6.4.0", + "react-colorful": "^5.3.1", "remove-accents": "^0.5.0", "uuid": "^9.0.1" }, @@ -4095,222 +3288,55 @@ "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/element": { - "version": "6.17.0", + "node_modules/@wordpress/compose": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.20.0.tgz", + "integrity": "sha512-L84QUGXbXPdCAgNDNmmH+4tJuAl1MwH5an6CaQ+NaSXk4kM4xAc42znHo0n5LfsRmWxOPrtlGikxMXCaejvoyw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@types/react": "^18.2.79", - "@types/react-dom": "^18.2.25", - "@wordpress/escape-html": "^3.17.0", + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^4.20.0", + "@wordpress/dom": "^4.20.0", + "@wordpress/element": "^6.20.0", + "@wordpress/is-shallow-equal": "^5.20.0", + "@wordpress/keycodes": "^4.20.0", + "@wordpress/priority-queue": "^3.20.0", + "@wordpress/undo-manager": "^1.20.0", "change-case": "^4.1.2", - "is-plain-object": "^5.0.0", - "react": "^18.3.0", - "react-dom": "^18.3.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/escape-html": { - "version": "3.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/fields": { - "version": "0.5.0", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/api-fetch": "*", - "@wordpress/blob": "*", - "@wordpress/blocks": "*", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/core-data": "*", - "@wordpress/data": "*", - "@wordpress/dataviews": "*", - "@wordpress/date": "*", - "@wordpress/element": "*", - "@wordpress/hooks": "*", - "@wordpress/html-entities": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/media-utils": "*", - "@wordpress/notices": "*", - "@wordpress/patterns": "*", - "@wordpress/primitives": "*", - "@wordpress/private-apis": "*", - "@wordpress/router": "*", - "@wordpress/url": "*", - "@wordpress/warning": "*", - "change-case": "4.1.2", - "client-zip": "^2.4.5", - "clsx": "2.1.1", - "remove-accents": "^0.5.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" - } - }, - "node_modules/@wordpress/hooks": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/html-entities": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/i18n": { - "version": "5.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/hooks": "^4.17.0", - "gettext-parser": "^1.3.1", - "memize": "^2.1.0", - "sprintf-js": "^1.1.1", - "tannin": "^1.2.0" - }, - "bin": { - "pot-to-php": "tools/pot-to-php.js" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/icons": { - "version": "10.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/element": "^6.17.0", - "@wordpress/primitives": "^4.17.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/interface": { - "version": "8.2.0", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/a11y": "*", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/data": "*", - "@wordpress/deprecated": "*", - "@wordpress/element": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/plugins": "*", - "@wordpress/preferences": "*", - "@wordpress/viewport": "*", - "clsx": "^2.1.1" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@wordpress/is-shallow-equal": { - "version": "5.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/keyboard-shortcuts": { - "version": "5.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/data": "^10.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/keycodes": "^4.17.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" - } - }, - "node_modules/@wordpress/keycodes": { - "version": "4.17.0", - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/i18n": "^5.17.0" - }, - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/media-utils": { - "version": "5.13.0", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/api-fetch": "*", - "@wordpress/blob": "*", - "@wordpress/element": "*", - "@wordpress/i18n": "*", - "@wordpress/private-apis": "*" + "clipboard": "^2.0.11", + "mousetrap": "^1.6.5", + "use-memo-one": "^1.1.1" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" } }, - "node_modules/@wordpress/notices": { - "version": "5.17.0", + "node_modules/@wordpress/data": { + "version": "10.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.20.0.tgz", + "integrity": "sha512-oj1Ci7mPZ2kbmI2cdqk7apfvd4nlWziPstlIZIKCb02rCEMqP8dC0lc/CDt8GVOXJ23iMhZgkfkvnFNaMXmBNQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.17.0", - "@wordpress/data": "^10.17.0" + "@wordpress/compose": "^7.20.0", + "@wordpress/deprecated": "^4.20.0", + "@wordpress/element": "^6.20.0", + "@wordpress/is-shallow-equal": "^5.20.0", + "@wordpress/priority-queue": "^3.20.0", + "@wordpress/private-apis": "^1.20.0", + "@wordpress/redux-routine": "^5.20.0", + "deepmerge": "^4.3.0", + "equivalent-key-map": "^0.2.2", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "redux": "^5.0.1", + "rememo": "^4.0.2", + "use-memo-one": "^1.1.1" }, "engines": { "node": ">=18.12.0", @@ -4320,115 +3346,87 @@ "react": "^18.0.0" } }, - "node_modules/@wordpress/patterns": { - "version": "2.13.0", - "dev": true, + "node_modules/@wordpress/date": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.20.0.tgz", + "integrity": "sha512-V34zSLveuXTe8wvnIpUXroP7dP9FK1HzMmGNB5JtoPhrqJeNvP4fzju8RJwBGpU1sFaqO3w+EZoNdTV9k0hqxA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "*", - "@wordpress/block-editor": "*", - "@wordpress/blocks": "*", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/core-data": "*", - "@wordpress/data": "*", - "@wordpress/element": "*", - "@wordpress/html-entities": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/notices": "*", - "@wordpress/private-apis": "*", - "@wordpress/url": "*" + "@wordpress/deprecated": "^4.20.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.40" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/plugins": { - "version": "7.13.0", - "dev": true, + "node_modules/@wordpress/deprecated": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.20.0.tgz", + "integrity": "sha512-36JbtGUSQ49SM33fvfSAvN8ZGDqCxCPAj2PByAney4WhoVbznxGWnao8qKwWrNNG5xec1reQvXFxOsD7qab4rg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/components": "*", - "@wordpress/compose": "*", - "@wordpress/deprecated": "*", - "@wordpress/element": "*", - "@wordpress/hooks": "*", - "@wordpress/icons": "*", - "@wordpress/is-shallow-equal": "*", - "memize": "^2.0.1" + "@wordpress/hooks": "^4.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/preferences": { - "version": "4.17.0", + "node_modules/@wordpress/dom": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.20.0.tgz", + "integrity": "sha512-uLYH7hKfJDUHkooAy0uoFJXMCkraTP3gdybblAJT9a/dqAOVcsMODH9gTGI99IoFhsvJwWo5Vk94/kgqeOdarA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.17.0", - "@wordpress/components": "^29.3.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/icons": "^10.17.0", - "@wordpress/private-apis": "^1.17.0", - "clsx": "^2.1.1" + "@wordpress/deprecated": "^4.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/primitives": { - "version": "4.17.0", + "node_modules/@wordpress/dom-ready": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.20.0.tgz", + "integrity": "sha512-FkdfoITfj1yBSUMn+IKIqpm7zwA4AbHPkYdCXNgP9w5BRBpoTqXMGgDbe8rt4aSWkSEiRChZ9rGmtG84LByRTA==", "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/element": "^6.17.0", - "clsx": "^2.1.1" + "@babel/runtime": "7.25.7" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" } }, - "node_modules/@wordpress/priority-queue": { - "version": "3.17.0", + "node_modules/@wordpress/element": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.20.0.tgz", + "integrity": "sha512-JsM1Cy283BusHOb1uyD3tG9GAb5hp/ycgPnBS/ScKT/8VD8yGzsX6Pz910GWo5udXP03d2+UI/BQ68KPqPQKqQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "requestidlecallback": "^0.3.0" + "@types/react": "^18.2.79", + "@types/react-dom": "^18.2.25", + "@wordpress/escape-html": "^3.20.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/private-apis": { - "version": "1.17.0", + "node_modules/@wordpress/escape-html": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.20.0.tgz", + "integrity": "sha512-Jrm+RdTZo8cj1JUo4Vqx92/yw7B+XS6aClEyQ/xlHoQU0WIZ+xByWZHOPgDFBcKczuO34UkFTWmDFFHMSy1uyw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4438,164 +3436,131 @@ "npm": ">=8.19.2" } }, - "node_modules/@wordpress/redux-routine": { - "version": "5.17.0", + "node_modules/@wordpress/hooks": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.20.0.tgz", + "integrity": "sha512-nn6RbAER5EitMJVr+jpOg5HDIUEEOEv6jC/P1s5C0HvsOaldBeJ80A73Gsd/NFGlUqCc7o51uoZO36wGoPjIpg==", "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "7.25.7", - "is-plain-object": "^5.0.0", - "is-promise": "^4.0.0", - "rungen": "^0.3.2" + "@babel/runtime": "7.25.7" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "redux": ">=4" } }, - "node_modules/@wordpress/reusable-blocks": { - "version": "5.13.0", - "dev": true, + "node_modules/@wordpress/html-entities": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.20.0.tgz", + "integrity": "sha512-ZOQ9zsfs5p32K+uAEy2vbY7rnAG5KjMdXwOn4v2FPeXF6A6jWQudK/smV7nRB3ZMaSZnzQ54tiUXbuSpCmmGYA==", "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/block-editor": "*", - "@wordpress/blocks": "*", - "@wordpress/components": "*", - "@wordpress/core-data": "*", - "@wordpress/data": "*", - "@wordpress/element": "*", - "@wordpress/i18n": "*", - "@wordpress/icons": "*", - "@wordpress/notices": "*", - "@wordpress/private-apis": "*", - "@wordpress/url": "*" + "@babel/runtime": "7.25.7" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/rich-text": { - "version": "7.17.0", + "node_modules/@wordpress/i18n": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-5.20.0.tgz", + "integrity": "sha512-JrgVe5QT+nDHFbujeD0lJifDpdgmOt1SSnEK631jIISjfGjriYwphoOEAzBGRh9S9ThqOOfW4mLOOeXPYmJR7w==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/a11y": "^4.17.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/escape-html": "^3.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/keycodes": "^4.17.0", - "memize": "^2.1.0" + "@wordpress/hooks": "^4.20.0", + "gettext-parser": "^1.3.1", + "memize": "^2.1.0", + "sprintf-js": "^1.1.1", + "tannin": "^1.2.0" + }, + "bin": { + "pot-to-php": "tools/pot-to-php.js" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" } }, - "node_modules/@wordpress/router": { - "version": "1.13.0", - "dev": true, + "node_modules/@wordpress/icons": { + "version": "10.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.20.0.tgz", + "integrity": "sha512-wGmmGDQoDKjmuGdC2I8C3JA9GlqVM9DK5FJZuUukHTh+Nz72W8CA30PzGKavxWOYd7cZ0B97VioE85aVwOAe3g==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/element": "*", - "@wordpress/private-apis": "*", - "@wordpress/url": "*", - "history": "^5.3.0" + "@wordpress/element": "^6.20.0", + "@wordpress/primitives": "^4.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" } }, - "node_modules/@wordpress/server-side-render": { - "version": "5.17.0", + "node_modules/@wordpress/is-shallow-equal": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.20.0.tgz", + "integrity": "sha512-/m8P/6AQgZchMbeDhne5z8Wzde07mv8+l7qsYK6VhChEWonrYN7Sfig9uGPtWijkWwOkxYjWE6ggcJ5xn8KVlg==", "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/api-fetch": "^7.17.0", - "@wordpress/blocks": "^14.6.0", - "@wordpress/components": "^29.3.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/deprecated": "^4.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/url": "^4.17.0", - "fast-deep-equal": "^3.1.3" + "@babel/runtime": "7.25.7" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/shortcode": { - "version": "4.17.0", + "node_modules/@wordpress/keycodes": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.20.0.tgz", + "integrity": "sha512-GLzp9uTSNOPvX378FInwvLj4riqq1N/By1kd40iAr1hXfRAjy0H//vktJ70r+AkwK0R07txtCPiLnDcW53hLmg==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "memize": "^2.0.1" + "@wordpress/i18n": "^5.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/style-engine": { - "version": "2.17.0", + "node_modules/@wordpress/primitives": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.20.0.tgz", + "integrity": "sha512-fVs9EnuI2UV1xfAYY//OOfO+O3n4VvPVGcI/zHMAfIdJGWEbCQVDatAnteX/2hkjBe85jqErkU+0bAKsddhpcA==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "change-case": "^4.1.2" + "@wordpress/element": "^6.20.0", + "clsx": "^2.1.1" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" } }, - "node_modules/@wordpress/sync": { - "version": "1.13.0", - "dev": true, + "node_modules/@wordpress/priority-queue": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.20.0.tgz", + "integrity": "sha512-2gOa8LQaTLPgk1GDkkXWALA9yH47yhDZKHKBHy8YH61c+m8ai8RctWegzXA6pSInPW77nbBUNHSOzxWTsDN1Sw==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@types/simple-peer": "^9.11.5", - "@wordpress/url": "*", - "import-locals": "^2.0.0", - "lib0": "^0.2.42", - "simple-peer": "^9.11.0", - "y-indexeddb": "~9.0.11", - "y-protocols": "^1.0.5", - "y-webrtc": "~10.2.5", - "yjs": "~13.6.6" + "requestidlecallback": "^0.3.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/token-list": { - "version": "3.17.0", + "node_modules/@wordpress/private-apis": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.20.0.tgz", + "integrity": "sha512-DngnywYj6zDt9D0HgnX7k0il5SsdDYUxEg82GqNu3Jd879LlG9MtIxcoV+ErCsH7ryTydXw4sC17W09m2LEMBQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7" @@ -4605,88 +3570,83 @@ "npm": ">=8.19.2" } }, - "node_modules/@wordpress/undo-manager": { - "version": "1.17.0", + "node_modules/@wordpress/redux-routine": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.20.0.tgz", + "integrity": "sha512-6JZI75oMAWGBgo+x2rmfIGzqVuxiZ3wQBqNCdVDDOGYH9qcRzYgBWRSPVfh4rvGLTtpVnFHnnBQ+jr5iPGHOxQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/is-shallow-equal": "^5.17.0" + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "rungen": "^0.3.2" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "redux": ">=4" } }, - "node_modules/@wordpress/upload-media": { - "version": "0.2.0", + "node_modules/@wordpress/rich-text": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.20.0.tgz", + "integrity": "sha512-irx6cvmoxSSajzGGt5iVxek3vNfG5LslORQ1g7HXcNawfFBxhptU3vzPF2+ywvs6o3BCbTZVfa98rOfX3C2J/Q==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@shopify/web-worker": "^6.4.0", - "@wordpress/api-fetch": "^7.17.0", - "@wordpress/blob": "^4.17.0", - "@wordpress/compose": "^7.17.0", - "@wordpress/data": "^10.17.0", - "@wordpress/element": "^6.17.0", - "@wordpress/i18n": "^5.17.0", - "@wordpress/preferences": "^4.17.0", - "@wordpress/private-apis": "^1.17.0", - "@wordpress/url": "^4.17.0", - "uuid": "^9.0.1" + "@wordpress/a11y": "^4.20.0", + "@wordpress/compose": "^7.20.0", + "@wordpress/data": "^10.20.0", + "@wordpress/deprecated": "^4.20.0", + "@wordpress/element": "^6.20.0", + "@wordpress/escape-html": "^3.20.0", + "@wordpress/i18n": "^5.20.0", + "@wordpress/keycodes": "^4.20.0", + "memize": "^2.1.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0" } }, - "node_modules/@wordpress/url": { - "version": "4.17.0", + "node_modules/@wordpress/undo-manager": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.20.0.tgz", + "integrity": "sha512-IG3/u0uR0nfZ/kXRfC6DVFK52hbbNx4aMB/c5DAMQgKtJElE7Mz1Mf5zgU1XNlpBOdguQp6oo/nMpyJUIasipQ==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "remove-accents": "^0.5.0" + "@wordpress/is-shallow-equal": "^5.20.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/viewport": { - "version": "6.13.0", - "dev": true, + "node_modules/@wordpress/url": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.20.0.tgz", + "integrity": "sha512-IUkph25ewBDTxuSC9wXvMbec6IB2A3pNz0Xkm1Ffzm2ngk/f+0+Ko2WSKdXqqR8U67Eyb+ZUZFtBPmEsKvEZ4A==", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", - "@wordpress/compose": "*", - "@wordpress/data": "*", - "@wordpress/element": "*" + "remove-accents": "^0.5.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" - }, - "peerDependencies": { - "react": "^18.0.0" } }, "node_modules/@wordpress/warning": { - "version": "3.17.0", - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - } - }, - "node_modules/@wordpress/wordcount": { - "version": "4.17.0", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.20.0.tgz", + "integrity": "sha512-IQRvlWwNWO6kncZ/qQEX/KCvsrm/0FIcuCXrTXlGP4OslRG7XtU9xs2lOP34Y6G3onMwhpD8mXFUK7udq305EQ==", "license": "GPL-2.0-or-later", - "dependencies": { - "@babel/runtime": "7.25.7" - }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -4694,12 +3654,12 @@ }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/abort-controller": { @@ -4715,7 +3675,7 @@ }, "node_modules/acorn": { "version": "8.12.1", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4793,16 +3753,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ansis": { "version": "1.5.2", "dev": true, @@ -4962,16 +3912,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/aria-hidden": { - "version": "1.2.4", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "dev": true, @@ -5166,10 +4106,6 @@ "postcss": "^8.1.0" } }, - "node_modules/autosize": { - "version": "4.0.4", - "license": "MIT" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "dev": true, @@ -5185,7 +4121,9 @@ } }, "node_modules/axios": { - "version": "1.7.9", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -5427,7 +4365,7 @@ }, "node_modules/browserslist": { "version": "4.24.2", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -5481,7 +4419,7 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/call-bind": { @@ -5556,7 +4494,7 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001685", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -5661,7 +4599,7 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.3", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.0" @@ -5685,11 +4623,6 @@ "version": "2.5.1", "license": "MIT" }, - "node_modules/client-zip": { - "version": "2.4.6", - "dev": true, - "license": "MIT" - }, "node_modules/clipboard": { "version": "2.0.11", "license": "MIT", @@ -5730,40 +4663,10 @@ "node": ">=6" } }, - "node_modules/cmdk": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/codemirror": { "version": "5.65.15", "license": "MIT" }, - "node_modules/codemirror-colorpicker": { - "version": "1.9.80", - "license": "MIT", - "peerDependencies": { - "codemirror": "^5.48.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, "node_modules/colord": { "version": "2.9.3", "license": "MIT" @@ -5834,9 +4737,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/computed-style": { - "version": "0.1.4" - }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, @@ -5853,7 +4753,7 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/core-js": { @@ -6251,13 +5151,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -6325,12 +5218,9 @@ "node": ">=0.10" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "license": "MIT" - }, "node_modules/diff": { "version": "4.0.2", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -6434,7 +5324,7 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.68", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { @@ -6451,7 +5341,7 @@ }, "node_modules/enhanced-resolve": { "version": "5.17.1", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6495,11 +5385,6 @@ "version": "0.2.2", "license": "MIT" }, - "node_modules/err-code": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/error-ex": { "version": "1.3.2", "license": "MIT", @@ -6615,7 +5500,7 @@ }, "node_modules/es-module-lexer": { "version": "1.4.0", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -6669,7 +5554,7 @@ }, "node_modules/escalade": { "version": "3.2.0", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6962,7 +5847,7 @@ }, "node_modules/eslint-scope": { "version": "5.1.1", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -6974,7 +5859,7 @@ }, "node_modules/eslint-scope/node_modules/estraverse": { "version": "4.3.0", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -7158,7 +6043,7 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -7169,7 +6054,7 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -7193,7 +6078,7 @@ }, "node_modules/events": { "version": "3.3.0", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.x" @@ -7225,7 +6110,7 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -7450,17 +6335,12 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/get-browser-rtc": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/get-intrinsic": { "version": "1.2.7", "dev": true, @@ -7484,13 +6364,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/get-proto": { "version": "1.0.1", "dev": true, @@ -7573,7 +6446,7 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause" }, "node_modules/glob/node_modules/brace-expansion": { @@ -7692,7 +6565,7 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/gradient-parser": { @@ -7719,7 +6592,7 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7797,14 +6670,6 @@ "version": "1.2.2", "license": "MIT" }, - "node_modules/history": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.7.6" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "license": "BSD-3-Clause", @@ -7816,10 +6681,6 @@ "version": "16.13.1", "license": "MIT" }, - "node_modules/hpq": { - "version": "1.4.0", - "license": "MIT" - }, "node_modules/iconv-lite": { "version": "0.6.3", "license": "MIT", @@ -7905,11 +6766,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-locals": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/imurmurhash": { "version": "0.1.4", "dev": true, @@ -7944,13 +6800,6 @@ "node": ">=10.13.0" } }, - "node_modules/invariant": { - "version": "2.2.4", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "dev": true, @@ -8185,6 +7034,8 @@ }, "node_modules/is-promise": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/is-regex": { @@ -8343,15 +7194,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic.js": { - "version": "0.2.5", - "dev": true, - "license": "MIT", - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } - }, "node_modules/iterator.prototype": { "version": "1.1.5", "dev": true, @@ -8400,7 +7242,7 @@ }, "node_modules/jest-worker": { "version": "27.5.1", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -8413,7 +7255,7 @@ }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -8479,7 +7321,7 @@ }, "node_modules/json5": { "version": "2.2.3", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -8541,26 +7383,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lib0": { - "version": "0.2.98", - "dev": true, - "license": "MIT", - "dependencies": { - "isomorphic.js": "^0.2.4" - }, - "bin": { - "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", - "0gentesthtml": "bin/gentesthtml.js", - "0serve": "bin/0serve.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } - }, "node_modules/lilconfig": { "version": "3.1.2", "dev": true, @@ -8572,23 +7394,13 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/line-height": { - "version": "0.3.1", - "license": "MIT", - "dependencies": { - "computed-style": "~0.1.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "license": "MIT" }, "node_modules/loader-runner": { "version": "4.3.0", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.11.5" @@ -8652,7 +7464,7 @@ }, "node_modules/lru-cache": { "version": "5.1.1", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -8686,7 +7498,7 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -8791,6 +7603,7 @@ }, "node_modules/nanoid": { "version": "3.3.8", + "dev": true, "funding": [ { "type": "github", @@ -8812,7 +7625,7 @@ }, "node_modules/neo-async": { "version": "2.6.2", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/no-case": { @@ -8831,7 +7644,7 @@ }, "node_modules/node-releases": { "version": "2.0.18", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/normalize-path": { @@ -8850,10 +7663,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-wheel": { - "version": "1.0.1", - "license": "BSD-3-Clause" - }, "node_modules/nth-check": { "version": "2.1.1", "dev": true, @@ -9032,6 +7841,7 @@ }, "node_modules/p-try": { "version": "2.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9076,10 +7886,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parsel-js": { - "version": "1.2.1", - "license": "MIT" - }, "node_modules/pascal-case": { "version": "3.1.2", "license": "MIT", @@ -9243,6 +8049,7 @@ }, "node_modules/postcss": { "version": "8.5.2", + "dev": true, "funding": [ { "type": "opencollective", @@ -9766,13 +8573,6 @@ "postcss": "^8.4.31" } }, - "node_modules/postcss-prefix-selector": { - "version": "1.16.1", - "license": "MIT", - "peerDependencies": { - "postcss": ">4 <9" - } - }, "node_modules/postcss-reduce-initial": { "version": "7.0.2", "dev": true, @@ -9843,18 +8643,9 @@ "postcss": "^8.4.31" } }, - "node_modules/postcss-urlrebase": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.3.0" - } - }, "node_modules/postcss-value-parser": { "version": "4.2.0", + "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -9866,7 +8657,9 @@ } }, "node_modules/prismjs": { - "version": "1.29.0", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "license": "MIT", "engines": { "node": ">=6" @@ -9904,7 +8697,7 @@ }, "node_modules/punycode": { "version": "2.3.1", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9936,7 +8729,7 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -9960,19 +8753,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-autosize-textarea": { - "version": "7.1.0", - "license": "MIT", - "dependencies": { - "autosize": "^4.0.2", - "line-height": "^0.3.1", - "prop-types": "^15.5.6" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/react-colorful": { "version": "5.6.1", "license": "MIT", @@ -9992,65 +8772,13 @@ "react": "^18.3.1" } }, - "node_modules/react-easy-crop": { - "version": "5.0.8", - "license": "MIT", - "dependencies": { - "normalize-wheel": "^1.0.1", - "tslib": "^2.0.1" - }, - "peerDependencies": { - "react": ">=16.4.0", - "react-dom": ">=16.4.0" - } - }, "node_modules/react-is": { "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "dev": true, "license": "MIT" }, - "node_modules/react-remove-scroll": { - "version": "2.5.5", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-select": { "version": "5.10.0", "license": "MIT", @@ -10070,27 +8798,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/react-style-singleton": { - "version": "2.2.1", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "license": "BSD-3-Clause", @@ -10181,6 +8888,8 @@ }, "node_modules/redux": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", "license": "MIT" }, "node_modules/reflect.getprototypeof": { @@ -10291,15 +9000,10 @@ }, "node_modules/requestidlecallback": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", + "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==", "license": "MIT" }, - "node_modules/require-directory": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "dev": true, @@ -10407,6 +9111,8 @@ }, "node_modules/rungen": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", + "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==", "license": "MIT" }, "node_modules/safe-array-concat": { @@ -10557,289 +9263,112 @@ }, "engines": { "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select": { - "version": "1.1.2", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "devOptional": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/showdown": { - "version": "1.9.1", - "license": "BSD-3-Clause", - "dependencies": { - "yargs": "^14.2" - }, - "bin": { - "showdown": "bin/showdown.js" - } - }, - "node_modules/showdown/node_modules/ansi-regex": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/showdown/node_modules/camelcase": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/showdown/node_modules/cliui": { - "version": "5.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/showdown/node_modules/emoji-regex": { - "version": "7.0.3", - "license": "MIT" - }, - "node_modules/showdown/node_modules/find-up": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/showdown/node_modules/get-caller-file": { - "version": "2.0.5", + "node_modules/select": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/showdown/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", + "node_modules/sentence-case": { + "version": "3.0.4", "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" } }, - "node_modules/showdown/node_modules/locate-path": { - "version": "3.0.0", - "license": "MIT", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" + "randombytes": "^2.1.0" } }, - "node_modules/showdown/node_modules/p-limit": { - "version": "2.3.0", + "node_modules/set-function-length": { + "version": "1.2.2", + "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/showdown/node_modules/p-locate": { - "version": "3.0.0", + "node_modules/set-function-name": { + "version": "2.0.2", + "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=6" - } - }, - "node_modules/showdown/node_modules/path-exists": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/showdown/node_modules/require-main-filename": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/showdown/node_modules/string-width": { - "version": "3.1.0", + "node_modules/set-proto": { + "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/showdown/node_modules/strip-ansi": { - "version": "5.2.0", + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^4.1.0" + "kind-of": "^6.0.2" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/showdown/node_modules/which-module": { - "version": "2.0.1", - "license": "ISC" - }, - "node_modules/showdown/node_modules/wrap-ansi": { - "version": "5.1.0", + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/showdown/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, - "node_modules/showdown/node_modules/yargs": { - "version": "14.2.3", + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - } - }, - "node_modules/showdown/node_modules/yargs-parser": { - "version": "15.0.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=8" } }, "node_modules/side-channel": { @@ -10910,51 +9439,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/simple-html-tokenizer": { - "version": "0.5.11", - "license": "MIT" - }, - "node_modules/simple-peer": { - "version": "9.11.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3", - "debug": "^4.3.2", - "err-code": "^3.0.1", - "get-browser-rtc": "^1.1.0", - "queue-microtask": "^1.2.3", - "randombytes": "^2.1.0", - "readable-stream": "^3.6.0" - } - }, - "node_modules/simple-peer/node_modules/readable-stream": { - "version": "3.6.2", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/snake-case": { "version": "3.0.4", "license": "MIT", @@ -10972,6 +9456,7 @@ }, "node_modules/source-map-js": { "version": "1.2.1", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10979,7 +9464,7 @@ }, "node_modules/source-map-support": { "version": "0.5.21", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -10988,7 +9473,7 @@ }, "node_modules/source-map-support/node_modules/source-map": { "version": "0.6.1", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -11274,7 +9759,7 @@ }, "node_modules/tapable": { "version": "2.2.1", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11292,7 +9777,7 @@ }, "node_modules/terser": { "version": "5.31.6", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -11309,7 +9794,7 @@ }, "node_modules/terser-webpack-plugin": { "version": "5.3.10", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", @@ -11342,7 +9827,7 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv": { "version": "6.12.6", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11357,7 +9842,7 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { "version": "3.5.2", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" @@ -11365,12 +9850,12 @@ }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.3.0", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", @@ -11387,7 +9872,7 @@ }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/tiny-emitter": { @@ -11608,7 +10093,7 @@ }, "node_modules/undici-types": { "version": "6.20.0", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -11649,7 +10134,7 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.1", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -11692,31 +10177,12 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-isomorphic-layout-effect": { "version": "1.2.0", "license": "MIT", @@ -11736,26 +10202,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-sync-external-store": { "version": "1.4.0", "license": "MIT", @@ -11790,7 +10236,7 @@ }, "node_modules/watchpack": { "version": "2.4.2", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -11802,7 +10248,7 @@ }, "node_modules/webpack": { "version": "5.97.1", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -11927,7 +10373,7 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" @@ -11935,7 +10381,7 @@ }, "node_modules/webpack/node_modules/acorn": { "version": "8.14.0", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -11946,7 +10392,7 @@ }, "node_modules/webpack/node_modules/ajv": { "version": "6.12.6", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11961,7 +10407,7 @@ }, "node_modules/webpack/node_modules/ajv-keywords": { "version": "3.5.2", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" @@ -11969,12 +10415,12 @@ }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", @@ -12169,111 +10615,10 @@ "node": ">=8" } }, - "node_modules/ws": { - "version": "8.18.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y-indexeddb": { - "version": "9.0.12", - "dev": true, - "license": "MIT", - "dependencies": { - "lib0": "^0.2.74" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "peerDependencies": { - "yjs": "^13.0.0" - } - }, - "node_modules/y-protocols": { - "version": "1.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "lib0": "^0.2.85" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "peerDependencies": { - "yjs": "^13.0.0" - } - }, - "node_modules/y-webrtc": { - "version": "10.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "lib0": "^0.2.42", - "simple-peer": "^9.11.0", - "y-protocols": "^1.0.6" - }, - "bin": { - "y-webrtc-signaling": "bin/server.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "optionalDependencies": { - "ws": "^8.14.2" - }, - "peerDependencies": { - "yjs": "^13.6.8" - } - }, "node_modules/yallist": { "version": "3.1.1", - "devOptional": true, - "license": "ISC" - }, - "node_modules/yjs": { - "version": "13.6.20", "dev": true, - "license": "MIT", - "dependencies": { - "lib0": "^0.2.98" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } + "license": "ISC" }, "node_modules/yn": { "version": "3.1.1", diff --git a/package.json b/package.json index a09d4c73..3914c09d 100644 --- a/package.json +++ b/package.json @@ -34,26 +34,17 @@ }, "dependencies": { "@codemirror/fold": "^0.19.4", - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@wordpress/api-fetch": "^7.17.0", - "@wordpress/block-editor": "^14.12.0", - "@wordpress/blocks": "^14.6.0", "@wordpress/components": "^29.3.0", - "@wordpress/data": "^10.17.0", "@wordpress/dom-ready": "^4.17.0", "@wordpress/i18n": "^5.17.0", - "@wordpress/icons": "^10.17.0", - "@wordpress/server-side-render": "^5.17.0", + "@wordpress/url": "^4.20.0", "axios": "^1.7.9", "classnames": "^2.5.1", "codemirror": "^5.29", - "codemirror-colorpicker": "^1.9.80", "php-parser": "^3.2.2", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-is": "^19.0.0", "react-select": "^5.10.0" }, "devDependencies": { @@ -71,7 +62,6 @@ "@types/rtlcss": "^3.5.4", "@types/tinymce": "^4.6.9", "@types/web": "^0.0.202", - "@types/wordpress__editor": "^14.12.0", "@typescript-eslint/eslint-plugin": "^8.24.0", "@typescript-eslint/parser": "^8.24.0", "@wordpress/babel-preset-default": "^8.17.0", @@ -94,6 +84,7 @@ "postcss-hexrgba": "^2.1.0", "postcss-load-config": "^6.0.1", "postcss-loader": "^8.1.1", + "react-is": "^19.0.0", "rtlcss": "^4.3.0", "sass": "^1.84.0", "sass-loader": "^16.0.4", diff --git a/src/css/common/_codemirror.scss b/src/css/common/_codemirror.scss index 7157e002..b08aecd4 100644 --- a/src/css/common/_codemirror.scss +++ b/src/css/common/_codemirror.scss @@ -1,5 +1,3 @@ -@import '~codemirror-colorpicker/dist/codemirror-colorpicker.css'; - .CodeMirror { border: 1px solid #dfdfdf; border-radius: 3px; diff --git a/src/js/components/EditorSidebar/EditorSidebar.tsx b/src/js/components/EditorSidebar/EditorSidebar.tsx index 0f8ad415..fae0939b 100644 --- a/src/js/components/EditorSidebar/EditorSidebar.tsx +++ b/src/js/components/EditorSidebar/EditorSidebar.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Spinner } from '@wordpress/components' import { isRTL } from '@wordpress/i18n' import { useSnippetForm } from '../../hooks/useSnippetForm' -import { isNetworkAdmin } from '../../utils/general' +import { isNetworkAdmin } from '../../utils/screen' import { getSnippetType } from '../../utils/snippets' import { Notices } from '../SnippetForm/page/Notices' import { ShortcodeInfo } from './actions/ShortcodeInfo' @@ -31,8 +31,8 @@ export const EditorSidebar = () => { {window.CODE_SNIPPETS_EDIT?.tagOptions.enabled ? : null} - {snippet.id ? -
    + {snippet.id + ?
    : null} diff --git a/src/js/components/EditorSidebar/actions/DeleteButton.tsx b/src/js/components/EditorSidebar/actions/DeleteButton.tsx index d896bdb5..eecd6f21 100644 --- a/src/js/components/EditorSidebar/actions/DeleteButton.tsx +++ b/src/js/components/EditorSidebar/actions/DeleteButton.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react' import { __ } from '@wordpress/i18n' import { Button } from '../../common/Button' import { ConfirmDialog } from '../../common/ConfirmDialog' -import { useSnippetsAPI } from '../../../hooks/useSnippets' +import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI' import { useSnippetForm } from '../../../hooks/useSnippetForm' export const DeleteButton: React.FC = () => { diff --git a/src/js/components/EditorSidebar/actions/ExportButtons.tsx b/src/js/components/EditorSidebar/actions/ExportButtons.tsx index df4e816d..d074da26 100644 --- a/src/js/components/EditorSidebar/actions/ExportButtons.tsx +++ b/src/js/components/EditorSidebar/actions/ExportButtons.tsx @@ -1,7 +1,7 @@ import React from 'react' import { __ } from '@wordpress/i18n' import { Button } from '../../common/Button' -import { useSnippetsAPI } from '../../../hooks/useSnippets' +import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI' import { downloadSnippetExportFile } from '../../../utils/files' import { useSnippetForm } from '../../../hooks/useSnippetForm' import type { SnippetsExport } from '../../../types/SnippetsExport' diff --git a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx index db2b474b..578da241 100644 --- a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx +++ b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { ExternalLink } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { useSnippetForm } from '../../../hooks/useSnippetForm' -import { isNetworkAdmin } from '../../../utils/general' +import { isNetworkAdmin } from '../../../utils/screen' import { buildShortcodeTag } from '../../../utils/shortcodes' import { CopyToClipboardButton } from '../../common/CopyToClipboardButton' import type { ShortcodeAtts } from '../../../utils/shortcodes' @@ -68,16 +68,16 @@ export const ShortcodeInfo: React.FC = () => { shortcodes: false })) - return 'content' === snippet.scope ? - <> + return 'content' === snippet.scope + ? <>

    {__('There are multiple options for inserting this snippet into a post, page or other content.', 'code-snippets')} {' '} - {snippet.id ? + {snippet.id // eslint-disable-next-line @stylistic/max-len - __('You can copy the below shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets') : + ? __('You can copy the below shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets') // eslint-disable-next-line @stylistic/max-len - __('After saving, you can copy a shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets')} + : __('After saving, you can copy a shortcode, or use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets')} {' '} {

    - {snippet.id ? - <> + {snippet.id + ? <> = ({ } const validateSnippet = (snippet: Snippet): undefined | string => { - const missingCode = '' === snippet.code.trim() const missingTitle = '' === snippet.name.trim() + const missingCode = '' === snippet.code.trim() switch (true) { case missingCode && missingTitle: @@ -81,9 +81,9 @@ const validateSnippet = (snippet: Snippet): undefined | string => { } } const shouldActivateByDefault = (snippet: Snippet): boolean => - !!window.CODE_SNIPPETS_EDIT?.activateByDefault && - !snippet.active && 'single-use' !== snippet.scope && - (!snippet.shared_network || !isNetworkAdmin()) + !!window.CODE_SNIPPETS_EDIT?.activateByDefault + && !snippet.active && 'single-use' !== snippet.scope + && (!snippet.shared_network || !isNetworkAdmin()) interface SubmitConfirmDialogProps { isOpen: boolean @@ -125,8 +125,8 @@ export const SubmitButton: React.FC = () => { } return <> - {activateByDefault ? - handleSubmit(submitSnippet)} disabled={isWorking} @@ -140,8 +140,8 @@ export const SubmitButton: React.FC = () => { onDeactivate={() => handleSubmit(submitAndDeactivateSnippet)} /> - {activateByDefault ? null : - handleSubmit(submitSnippet)} disabled={isWorking} diff --git a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx index 822f94d0..5a6e04fd 100644 --- a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx +++ b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx @@ -19,13 +19,13 @@ export const ActivationSwitch = () => { type="checkbox" disabled={isWorking || !!snippet.shared_network} className="switch snippet-activation-switch" - title={snippet.active ? - __('Deactivate', 'code-snippets') : - __('Activate', 'code-snippets')} + title={snippet.active + ? __('Deactivate', 'code-snippets') + : __('Activate', 'code-snippets')} onChange={() => { - (snippet.active ? - submitAndDeactivateSnippet() : - submitAndActivateSnippet()) + (snippet.active + ? submitAndDeactivateSnippet() + : submitAndActivateSnippet()) .then(() => undefined) .catch(handleUnknownError) }} diff --git a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx index 2d00e810..9d268cf1 100644 --- a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx +++ b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx @@ -16,7 +16,10 @@ export const MultisiteSharingSettings: React.FC = () => {
    - {__('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')} + { + // eslint-disable-next-line @stylistic/max-len + __('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets') + }
    diff --git a/src/js/components/EditorSidebar/controls/TagsInput.tsx b/src/js/components/EditorSidebar/controls/TagsInput.tsx index 86afbadf..1218dbca 100644 --- a/src/js/components/EditorSidebar/controls/TagsInput.tsx +++ b/src/js/components/EditorSidebar/controls/TagsInput.tsx @@ -8,8 +8,8 @@ const options = window.CODE_SNIPPETS_EDIT?.tagOptions export const TagsInput: React.FC = () => { const { snippet, setSnippet, isReadOnly } = useSnippetForm() - return options?.enabled ? -
    + return options?.enabled + ?

    { })) }} /> -
    : - null +
    + : null } diff --git a/src/js/components/SnippetForm/SnippetForm.tsx b/src/js/components/SnippetForm/SnippetForm.tsx index 309b6f37..6bdf01df 100644 --- a/src/js/components/SnippetForm/SnippetForm.tsx +++ b/src/js/components/SnippetForm/SnippetForm.tsx @@ -5,13 +5,13 @@ import { createEmptySnippet, getSnippetType } from '../../utils/snippets' import { WithSnippetFormContext, useSnippetForm } from '../../hooks/useSnippetForm' import { Button } from '../common/Button' import { EditorSidebar } from '../EditorSidebar' +import { CodeEditor } from './fields/CodeEditor' import { SnippetLocationInput } from './fields/SnippetLocationInput' import { SnippetTypeInput } from './fields/SnippetTypeInput' import { UpgradeDialog } from './page/UpgradeDialog' import { DescriptionEditor } from './fields/DescriptionEditor' import { NameInput } from './fields/NameInput' import { PageHeading } from './page/PageHeading' -import { CodeEditor } from './fields/CodeEditor' const EditForm: React.FC = () => { const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false) diff --git a/src/js/components/SnippetForm/fields/CodeEditor.tsx b/src/js/components/SnippetForm/fields/CodeEditor.tsx index 619ca2f0..074f72eb 100644 --- a/src/js/components/SnippetForm/fields/CodeEditor.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditor.tsx @@ -1,10 +1,9 @@ import React, { useEffect, useRef } from 'react' import { __ } from '@wordpress/i18n' import { handleUnknownError } from '../../../utils/errors' -import { isMacOS } from '../../../utils/general' +import { isMacOS } from '../../../utils/screen' import { useSnippetForm } from '../../../hooks/useSnippetForm' import { CodeEditorShortcuts } from './CodeEditorShortcuts' - export const CodeEditor: React.FC = () => { const { snippet, setSnippet, codeEditorInstance, setCodeEditorInstance, submitSnippet } = useSnippetForm() const textareaRef = useRef(null) @@ -25,9 +24,8 @@ export const CodeEditor: React.FC = () => { useEffect(() => { if (codeEditorInstance) { - const extraKeys = codeEditorInstance.codemirror.getOption('extraKeys') + const extraKeys = codeEditorInstance.codemirror.getOption('extraKeys') ?? {} const controlKey = isMacOS() ? 'Cmd' : 'Ctrl' - const onSave = () => { submitSnippet() .then(() => undefined) diff --git a/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx index 1a85358e..648133d9 100644 --- a/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx @@ -2,7 +2,7 @@ import { __, _x } from '@wordpress/i18n' import classnames from 'classnames' import React from 'react' import { KEYBOARD_KEYS } from '../../../types/KeyboardShortcut' -import { isMacOS } from '../../../utils/general' +import { isMacOS } from '../../../utils/screen' import type { KeyboardKey, KeyboardShortcut } from '../../../types/KeyboardShortcut' const shortcuts: Record = { diff --git a/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx b/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx index 58026573..71b5bb42 100644 --- a/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx +++ b/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx @@ -2,8 +2,8 @@ import React, { useEffect } from 'react' import { __, _x } from '@wordpress/i18n' import Select from 'react-select' import { useSnippetForm } from '../../../hooks/useSnippetForm' -import { SNIPPET_TYPES, SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' -import { isLicensed } from '../../../utils/general' +import { SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' +import { isLicensed } from '../../../utils/screen' import { getSnippetType, isProType } from '../../../utils/snippets' import type { SnippetType } from '../../../types/Snippet' import type { SelectOption } from '../../../types/SelectOption' @@ -13,29 +13,26 @@ export interface SnippetTypeInputProps { openUpgradeDialog: VoidFunction } -const TYPE_LABELS: Record = { - php: __('Functions', 'code-snippets'), - html: __('Content', 'code-snippets'), - css: __('Styles', 'code-snippets'), - js: __('Scripts', 'code-snippets') -} - -const EDITOR_MODES: Partial> = { +const EDITOR_MODES: Record = { css: 'text/css', js: 'javascript', php: 'text/x-php', html: 'application/x-httpd-php' } -const OPTIONS: SelectOption[] = SNIPPET_TYPES.map(snippetType => - ({ label: TYPE_LABELS[snippetType], value: snippetType })) +const OPTIONS: SelectOption[] = [ + { value: 'php', label: __('Functions', 'code-snippets') }, + { value: 'html', label: __('Content', 'code-snippets') }, + { value: 'css', label: __('Styles', 'code-snippets') }, + { value: 'js', label: __('Scripts', 'code-snippets') } +] const SnippetTypeOption: React.FC> = ({ label, value }) =>
    {label} - {isProType(value) && !isLicensed() && - {_x('Pro', 'Upgrade to Pro', 'code-snippets')}} + {isProType(value) && !isLicensed() + && {_x('Pro', 'Upgrade to Pro', 'code-snippets')}}
    {value.toUpperCase()}
    @@ -51,7 +48,7 @@ export const SnippetTypeInput: React.FC = ({ openUpgradeD codeEditor.setOption('lint' as keyof EditorConfiguration, 'php' === snippetType || 'css' === snippetType) - if (snippetType in EDITOR_MODES) { + if (EDITOR_MODES[snippetType]) { codeEditor.setOption('mode', EDITOR_MODES[snippetType]) codeEditor.refresh() } diff --git a/src/js/components/SnippetForm/page/PageHeading.tsx b/src/js/components/SnippetForm/page/PageHeading.tsx index e008505b..31d2cd4b 100644 --- a/src/js/components/SnippetForm/page/PageHeading.tsx +++ b/src/js/components/SnippetForm/page/PageHeading.tsx @@ -1,40 +1,50 @@ -import React from 'react' import { __, _x } from '@wordpress/i18n' +import React from 'react' import { createEmptySnippet } from '../../../utils/snippets' import { useSnippetForm } from '../../../hooks/useSnippetForm' +import type { Snippet } from '../../../types/Snippet' const OPTIONS = window.CODE_SNIPPETS_EDIT +const getEditHeading = (snippet: Snippet): string => + 'condition' === snippet.scope + ? __('Edit Conditions', 'code-snippets') + : __('Edit Snippet', 'code-snippets') + +const getAddNewHeading = (snippet: Snippet): string => + 'condition' === snippet.scope + ? __('Add New Conditions', 'code-snippets') + : __('Add New Snippet', 'code-snippets') + export const PageHeading: React.FC = () => { const { snippet, updateSnippet, setCurrentNotice } = useSnippetForm() return (

    {snippet.id - ? __('Edit Snippet', 'code-snippets') - : __('Add New Snippet', 'code-snippets')} - - {snippet.id ? <>{' '} - { - event.preventDefault() - updateSnippet(() => createEmptySnippet()) - setCurrentNotice(undefined) - - window.document.title = window.document.title.replace( - __('Edit Snippet', 'code-snippets'), - __('Create New Snippet', 'code-snippets') - ) - - window.history.replaceState({}, '', window.CODE_SNIPPETS?.urls.addNew) - }}> - {_x('Add New', 'snippet', 'code-snippets')} - - : null} + ? <> + {`${getEditHeading(snippet)} `} + + { + event.preventDefault() + updateSnippet(() => createEmptySnippet()) + setCurrentNotice(undefined) + + window.document.title = window.document.title.replace(getEditHeading(snippet), getAddNewHeading(snippet)) + window.history.replaceState({}, '', window.CODE_SNIPPETS?.urls.addNew) + }} + > + {_x('Add New', 'snippet', 'code-snippets')} + + + : getAddNewHeading(snippet)} {OPTIONS?.pageTitleActions && Object.entries(OPTIONS.pageTitleActions).map(([label, url]) => <> - {label} - {' '} + {label}{' '} )}

    diff --git a/src/js/components/TagEditor/TagEditor.tsx b/src/js/components/TagEditor/TagEditor.tsx deleted file mode 100644 index 03064e75..00000000 --- a/src/js/components/TagEditor/TagEditor.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Code based on Tagger, copyright (c) 2018-2022 Jakub T. Jankiewicz - * Released under the MIT license. - */ -import React, { useRef, useState } from 'react' -import { handleUnknownError } from '../../utils/errors' -import { SuggestionList } from './SuggestionList' -import { TagList } from './TagList' -import type { InputHTMLAttributes, KeyboardEventHandler } from 'react' - -const COMPLETION_MIN_LENGTH = 2 -const SPECIAL_CHARS_RE = /(?[-\\^$[\]()+{}?*.|])/g - -const escapeRegex = (str: string) => - str.replace(SPECIAL_CHARS_RE, '\\$1') - -const isNotEmpty = (value: string | null | undefined): value is string => - !['', '""', "''", '``', undefined, null].includes(value) - -const isTagSelected = (selected: string[], tag: string) => { - if (!selected.includes(tag)) { - return false - } - const re = new RegExp(`^${escapeRegex(tag)}`) - return 1 === selected.filter(test_tag => re.test(test_tag)).length -} - -export type TagEditorCompletions = string[] | ((value?: string) => Promise) | undefined - -const buildCompletions = (completions: TagEditorCompletions, value: string | undefined): Promise => { - if (!completions) { - return Promise.resolve(undefined) - } else if ('function' === typeof completions) { - return completions(value) - } else { - return Promise.resolve(completions) - } -} - -export interface TagEditorProps extends Omit, 'onChange'> { - id: string - tags: string[] - onChange: (tags: string[]) => void - tagLimit?: number - completions?: TagEditorCompletions - addOnBlur?: boolean - allowSpaces?: boolean - allowDuplicates?: boolean - completionMinLength?: number -} - -// eslint-disable-next-line max-lines-per-function -export const TagEditor: React.FC = ({ - id, - tags, - onChange, - tagLimit, - completions, - addOnBlur = false, - allowSpaces = true, - allowDuplicates = false, - completionMinLength = COMPLETION_MIN_LENGTH, - ...inputProps -}) => { - const inputRef = useRef(null) - const [inputValue, setInputValue] = useState('') - const [completionOpen, setCompletionOpen] = useState(false) - const [completionList, setCompletionList] = useState([]) - const [lastCompletionList, setLastCompletionList] = useState() - - const isTagLimit = () => Boolean(tagLimit && 0 < tagLimit && tags.length >= tagLimit) - - const addTag = (tag: string = inputValue.trim()) => { - const isTagValid = isNotEmpty(tag) && !isTagLimit() && (allowDuplicates || !tags.includes(tag)) - - if (isTagValid) { - onChange([...tags, tag]) - } - - setInputValue('') - return isTagValid - } - - const removeTag = (tag?: string) => - onChange(tag ? tags.filter(item => item !== tag) : tags.slice(0, -1)) - - const triggerCompletion = (openList = inputValue.length >= completionMinLength) => { - if (openList) { - buildCompletions(completions, inputValue) - .then(list => { - setLastCompletionList(completionList) - - if (list?.length) { - setCompletionList(allowDuplicates ? list : list.filter(item => !tags.includes(item))) - } - }) - .catch(handleUnknownError) - } - - setCompletionOpen(openList) - } - - const keyboardHandler: KeyboardEventHandler = event => { - const { key, ctrlKey, metaKey } = event - - switch (key) { - case 'Enter': - case ',': - event.preventDefault() - addTag() - break - - case 'Backspace': - if (!inputValue) { - event.preventDefault() - removeTag() - } - break - - case ' ': - if (ctrlKey || metaKey) { - event.preventDefault() - triggerCompletion(true) - } else if (!allowSpaces) { - event.preventDefault() - addTag() - } - break - - case 'Tab': - if (!isTagLimit()) { - event.preventDefault() - } - break - } - } - - const inputHandler = () => { - if (completionOpen && lastCompletionList && isTagSelected(lastCompletionList, inputValue.trim())) { - if (addTag()) { - setCompletionOpen(false) - } - } else { - triggerCompletion() - } - } - - return ( -
    -
      inputRef.current?.focus()}> - -
    • - addOnBlur ? addTag() : undefined} - onChange={event => setInputValue(event.target.value)} - onKeyDown={keyboardHandler} - onInput={inputHandler} - /> - !tags.includes(suggestion))} - onSelect={suggestion => { - addTag(suggestion) - setCompletionList([]) - setCompletionOpen(false) - }} - /> -
    • -
    -
    - ) -} diff --git a/src/js/hooks/useSnippetForm.tsx b/src/js/hooks/useSnippetForm.tsx index c5a36f77..d67bad5e 100644 --- a/src/js/hooks/useSnippetForm.tsx +++ b/src/js/hooks/useSnippetForm.tsx @@ -1,6 +1,6 @@ import { isAxiosError } from 'axios' import React, { createContext, useCallback, useContext, useMemo, useState } from 'react' -import { isLicensed } from '../utils/general' +import { isLicensed } from '../utils/screen' import { isProSnippet } from '../utils/snippets' import { useSnippetSubmit } from './useSnippetSubmit' import type { Dispatch, PropsWithChildren, SetStateAction} from 'react' diff --git a/src/js/hooks/useSnippetSubmit.ts b/src/js/hooks/useSnippetSubmit.ts index 75d06cc0..764e24d9 100644 --- a/src/js/hooks/useSnippetSubmit.ts +++ b/src/js/hooks/useSnippetSubmit.ts @@ -2,28 +2,40 @@ import { __ } from '@wordpress/i18n' import { addQueryArgs } from '@wordpress/url' import { isAxiosError } from 'axios' import { useCallback } from 'react' -import { useSnippetsAPI } from './useSnippets' +import { useSnippetsAPI } from './useSnippetsAPI' import type { Dispatch, SetStateAction } from 'react' import type { ScreenNotice } from '../types/ScreenNotice' import type { Snippet } from '../types/Snippet' -const getSuccessNotice = (request: Snippet, response: Snippet, active: boolean | undefined) => { +const messages = { + addNew: __('Add New Snippet', 'code-snippets'), + edit: __('Edit Snippet', 'code-snippets'), + created: __('Snippet created.', 'code-snippets'), + updated: __('Snippet updated.', 'code-snippets'), + createdActivated: __('Snippet created and activated.', 'code-snippets'), + updatedActivated: __('Snippet updated and activated.', 'code-snippets'), + updatedExecuted: __('Snippet updated and executed.', 'code-snippets'), + updatedDeactivated: __('Snippet updated and deactivated'), + failedCreate: __('Could not create snippet.', 'code-snippets'), + failedUpdate: __('Could not update snippet.', 'code-snippets') +} + +const getSuccessNotice = (request: Snippet, response: Snippet, active: boolean | undefined): string => { + if (active === undefined) { - return 0 === request.id - ? __('Snippet created.', 'code-snippets') - : __('Snippet updated.', 'code-snippets') + return 0 === request.id ? messages.created : messages.updated } if (0 === request.id && active) { - return __('Snippet created and activated.', 'code-snippets') + return messages.createdActivated } if (active) { return 'single-use' === response.scope - ? __('Snippet updated and executed.', 'code-snippets') - : __('Snippet updated and activated.', 'code-snippets') + ? messages.updatedExecuted + : messages.updatedActivated } else { - return __('Snippet updated and deactivated') + return messages.updatedDeactivated } } @@ -52,9 +64,7 @@ export const useSnippetSubmit = ( if (undefined === result || 'string' === typeof result) { const message = [ - snippet.id - ? __('Could not create snippet.', 'code-snippets') - : __('Could not update snippet.', 'code-snippets'), + snippet.id ? messages.failedCreate : messages.failedUpdate, result ?? __('The server did not send a valid response.', 'code-snippets') ] @@ -65,11 +75,7 @@ export const useSnippetSubmit = ( setCurrentNotice(['updated', getSuccessNotice(snippet, result, active)]) if (snippet.id && result.id) { - window.document.title = window.document.title.replace( - __('Create New Snippet', 'code-snippets'), - __('Edit Snippet', 'code-snippets') - ) - + window.document.title = window.document.title.replace(messages.addNew, messages.edit) window.history.replaceState({}, '', addQueryArgs(window.CODE_SNIPPETS?.urls.edit, { id: result.id })) } diff --git a/src/js/hooks/useSnippets.ts b/src/js/hooks/useSnippetsAPI.ts similarity index 98% rename from src/js/hooks/useSnippets.ts rename to src/js/hooks/useSnippetsAPI.ts index 4a59752d..e4570cc6 100644 --- a/src/js/hooks/useSnippets.ts +++ b/src/js/hooks/useSnippetsAPI.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react' import { addQueryArgs } from '@wordpress/url' import { handleUnknownError } from '../utils/errors' -import { isNetworkAdmin } from '../utils/general' +import { isNetworkAdmin } from '../utils/screen' import { useAxios } from './useAxios' import type { Snippet } from '../types/Snippet' import type { SnippetsExport } from '../types/SnippetsExport' diff --git a/src/js/services/manage/requests.ts b/src/js/services/manage/requests.ts index a80fbca6..fe23393c 100644 --- a/src/js/services/manage/requests.ts +++ b/src/js/services/manage/requests.ts @@ -1,4 +1,4 @@ -import { isNetworkAdmin } from '../../utils/general' +import { isNetworkAdmin } from '../../utils/screen' import type { Snippet, SnippetScope } from '../../types/Snippet' export interface ResponseData { diff --git a/src/js/types/SelectOption.ts b/src/js/types/SelectOption.ts index 918fd1a5..f7674758 100644 --- a/src/js/types/SelectOption.ts +++ b/src/js/types/SelectOption.ts @@ -1,4 +1,4 @@ -import type { GroupBase } from 'react-select' +import type { GroupBase, Options, OptionsOrGroups } from 'react-select' export interface SelectOption { readonly value: T @@ -6,3 +6,7 @@ export interface SelectOption { } export type SelectGroup = GroupBase> + +export type SelectOptions = Options> + +export type SelectGroups = OptionsOrGroups, SelectGroup> diff --git a/src/js/types/Snippet.ts b/src/js/types/Snippet.ts index 17d21b90..78ba27cd 100644 --- a/src/js/types/Snippet.ts +++ b/src/js/types/Snippet.ts @@ -13,21 +13,11 @@ export interface Snippet { code_error?: [string, number] | null } -export type SnippetLocation = 'site-head' | 'site-footer' +export type SnippetType = 'php' | 'html' | 'css' | 'js' -export type SnippetType = typeof SNIPPET_TYPES[number] -export type SnippetScope = typeof SNIPPET_SCOPES[number] +export type SnippetScope = typeof SNIPPET_TYPE_SCOPES[SnippetType][number] -export const SNIPPET_SCOPES = [ - 'global', 'admin', 'front-end', 'single-use', - 'content', 'head-content', 'footer-content', - 'admin-css', 'site-css', - 'site-head-js', 'site-footer-js' -] - -export const SNIPPET_TYPES = ['php', 'html', 'css', 'js'] - -export const SNIPPET_TYPE_SCOPES: Record = { +export const SNIPPET_TYPE_SCOPES = { php: ['global', 'admin', 'front-end', 'single-use'], html: ['content', 'head-content', 'footer-content'], css: ['admin-css', 'site-css'], diff --git a/src/js/types/wp/User.ts b/src/js/types/wp/User.ts index fb79d77b..265bf0aa 100644 --- a/src/js/types/wp/User.ts +++ b/src/js/types/wp/User.ts @@ -1,5 +1,3 @@ -export const USERS_ENDPOINT = '/wp/v2/users' - export interface User { readonly id: number username?: string diff --git a/src/js/utils/restAPI.ts b/src/js/utils/restAPI.ts deleted file mode 100644 index 774c6b8b..00000000 --- a/src/js/utils/restAPI.ts +++ /dev/null @@ -1,20 +0,0 @@ -import axios from 'axios' -import { trimLeadingChar, trimTrailingChar } from './text' -import type { AxiosRequestConfig, AxiosResponse } from 'axios' - -const REST_BASE = window.CODE_SNIPPETS?.restAPI.base ?? '' - -const getRestUrl = (endpoint: string): string => - `${trimTrailingChar(REST_BASE, '/')}/${trimLeadingChar(endpoint, '/')}` - -const GET_CACHE: Record | undefined> = {} - -export const getCached = (endpoint: string, refresh = false, config?: AxiosRequestConfig): Promise> => - !refresh && GET_CACHE[endpoint] - ? Promise.resolve(> GET_CACHE[endpoint]) - : axios - .get, D>(getRestUrl(endpoint), config) - .then(response => { - GET_CACHE[endpoint] = response - return response - }) diff --git a/src/js/utils/general.ts b/src/js/utils/screen.ts similarity index 100% rename from src/js/utils/general.ts rename to src/js/utils/screen.ts diff --git a/src/js/utils/snippets.ts b/src/js/utils/snippets.ts index 1f199476..eca78d62 100644 --- a/src/js/utils/snippets.ts +++ b/src/js/utils/snippets.ts @@ -1,4 +1,4 @@ -import { isNetworkAdmin } from './general' +import { isNetworkAdmin } from './screen' import type { Snippet, SnippetScope, SnippetType } from '../types/Snippet' const PRO_TYPES: SnippetType[] = ['css', 'js'] diff --git a/src/php/admin-menus/class-edit-menu.php b/src/php/admin-menus/class-edit-menu.php index 3960bd84..9b89b5fc 100644 --- a/src/php/admin-menus/class-edit-menu.php +++ b/src/php/admin-menus/class-edit-menu.php @@ -176,9 +176,7 @@ public function enqueue_assets() { 'react-dom', 'wp-url', 'wp-i18n', - 'wp-api-fetch', - 'wp-components', - 'wp-block-editor', + 'wp-components' ], $plugin->version, true From 2a2081101c63008ac0d9e9fdffe6fc9814fd8ee2 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 28 Mar 2025 14:05:26 +1100 Subject: [PATCH 016/268] Improving parsing of unknown data into snippet objects. --- src/js/components/SnippetForm/SnippetForm.tsx | 7 ++- .../SnippetForm/page/PageHeading.tsx | 22 +++---- src/js/hooks/useSnippetSubmit.ts | 3 +- src/js/hooks/useSnippetsAPI.ts | 4 +- src/js/utils/snippets.ts | 59 +++++++++++++++---- 5 files changed, 63 insertions(+), 32 deletions(-) diff --git a/src/js/components/SnippetForm/SnippetForm.tsx b/src/js/components/SnippetForm/SnippetForm.tsx index 6bdf01df..4ecad677 100644 --- a/src/js/components/SnippetForm/SnippetForm.tsx +++ b/src/js/components/SnippetForm/SnippetForm.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import classnames from 'classnames' import { __ } from '@wordpress/i18n' -import { createEmptySnippet, getSnippetType } from '../../utils/snippets' +import { createSnippetObject, getSnippetType } from '../../utils/snippets' import { WithSnippetFormContext, useSnippetForm } from '../../hooks/useSnippetForm' import { Button } from '../common/Button' import { EditorSidebar } from '../EditorSidebar' @@ -68,6 +68,9 @@ const EditForm: React.FC = () => { } export const SnippetForm: React.FC = () => - window.CODE_SNIPPETS_EDIT?.snippet ?? createEmptySnippet()}> + + typeof window.CODE_SNIPPETS_EDIT?.snippet === 'object' && null !== window.CODE_SNIPPETS_EDIT?.snippet + ? createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet) + : createSnippetObject()}> diff --git a/src/js/components/SnippetForm/page/PageHeading.tsx b/src/js/components/SnippetForm/page/PageHeading.tsx index 31d2cd4b..518b6085 100644 --- a/src/js/components/SnippetForm/page/PageHeading.tsx +++ b/src/js/components/SnippetForm/page/PageHeading.tsx @@ -1,20 +1,12 @@ import { __, _x } from '@wordpress/i18n' import React from 'react' -import { createEmptySnippet } from '../../../utils/snippets' import { useSnippetForm } from '../../../hooks/useSnippetForm' -import type { Snippet } from '../../../types/Snippet' +import { createSnippetObject } from '../../../utils/snippets' const OPTIONS = window.CODE_SNIPPETS_EDIT -const getEditHeading = (snippet: Snippet): string => - 'condition' === snippet.scope - ? __('Edit Conditions', 'code-snippets') - : __('Edit Snippet', 'code-snippets') - -const getAddNewHeading = (snippet: Snippet): string => - 'condition' === snippet.scope - ? __('Add New Conditions', 'code-snippets') - : __('Add New Snippet', 'code-snippets') +const editHeading = __('Edit Snippet', 'code-snippets') +const addNewHeading = __('Add New Snippet', 'code-snippets') export const PageHeading: React.FC = () => { const { snippet, updateSnippet, setCurrentNotice } = useSnippetForm() @@ -23,24 +15,24 @@ export const PageHeading: React.FC = () => {

    {snippet.id ? <> - {`${getEditHeading(snippet)} `} + {`{${editHeading} `} { event.preventDefault() - updateSnippet(() => createEmptySnippet()) + updateSnippet(() => createSnippetObject()) setCurrentNotice(undefined) - window.document.title = window.document.title.replace(getEditHeading(snippet), getAddNewHeading(snippet)) + window.document.title = window.document.title.replace(editHeading, addNewHeading) window.history.replaceState({}, '', window.CODE_SNIPPETS?.urls.addNew) }} > {_x('Add New', 'snippet', 'code-snippets')} - : getAddNewHeading(snippet)} + : addNewHeading} {OPTIONS?.pageTitleActions && Object.entries(OPTIONS.pageTitleActions).map(([label, url]) => <> diff --git a/src/js/hooks/useSnippetSubmit.ts b/src/js/hooks/useSnippetSubmit.ts index 764e24d9..eb85f299 100644 --- a/src/js/hooks/useSnippetSubmit.ts +++ b/src/js/hooks/useSnippetSubmit.ts @@ -2,6 +2,7 @@ import { __ } from '@wordpress/i18n' import { addQueryArgs } from '@wordpress/url' import { isAxiosError } from 'axios' import { useCallback } from 'react' +import { createSnippetObject } from '../utils/snippets' import { useSnippetsAPI } from './useSnippetsAPI' import type { Dispatch, SetStateAction } from 'react' import type { ScreenNotice } from '../types/ScreenNotice' @@ -71,7 +72,7 @@ export const useSnippetSubmit = ( setCurrentNotice(['error', message.filter(Boolean).join(' ')]) return undefined } else { - setSnippet({ ...result }) + setSnippet(createSnippetObject(result)) setCurrentNotice(['updated', getSuccessNotice(snippet, result, active)]) if (snippet.id && result.id) { diff --git a/src/js/hooks/useSnippetsAPI.ts b/src/js/hooks/useSnippetsAPI.ts index e4570cc6..7c675945 100644 --- a/src/js/hooks/useSnippetsAPI.ts +++ b/src/js/hooks/useSnippetsAPI.ts @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react' import { addQueryArgs } from '@wordpress/url' import { handleUnknownError } from '../utils/errors' import { isNetworkAdmin } from '../utils/screen' +import { createSnippetObject } from '../utils/snippets' import { useAxios } from './useAxios' import type { Snippet } from '../types/Snippet' import type { SnippetsExport } from '../types/SnippetsExport' @@ -71,7 +72,8 @@ export const useSnippets = (): Snippet[] | undefined => { useEffect(() => { if (!snippets) { api.fetchAll(isNetworkAdmin()) - .then(response => setSnippets(response.data)) + .then(response => + setSnippets(response.data.map(snippet => createSnippetObject(snippet)))) .catch(handleUnknownError) } }, [api, snippets]) diff --git a/src/js/utils/snippets.ts b/src/js/utils/snippets.ts index eca78d62..2baff98a 100644 --- a/src/js/utils/snippets.ts +++ b/src/js/utils/snippets.ts @@ -1,20 +1,53 @@ import { isNetworkAdmin } from './screen' -import type { Snippet, SnippetScope, SnippetType } from '../types/Snippet' +import { Snippet, SNIPPET_TYPE_SCOPES, SnippetScope, SnippetType } from '../types/Snippet' const PRO_TYPES: SnippetType[] = ['css', 'js'] -export const createEmptySnippet = (): Snippet => ({ - id: 0, - name: '', - desc: '', - code: '', - tags: [], - scope: 'global', - modified: '', - active: false, - network: isNetworkAdmin(), - shared_network: null, - priority: 10 +const isAbsInt = (value: unknown): value is number => + typeof value === 'number' && value > 0 + +const isBooleanOrUndefined = (value: unknown): value is boolean | undefined => + 'boolean' == typeof value || value === undefined + +export const isValidScope = (scope: unknown): scope is SnippetScope => + 'string' === typeof scope && + Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => + typeScopes.some(typeScope => typeScope === scope)) + +export const createSnippetObject = (fields: {} = {}): Snippet => ({ + id: 'id' in fields && isAbsInt(fields.id) + ? fields.id + : 0, + name: 'name' in fields && 'string' === typeof fields.name + ? fields.name + : '', + desc: 'desc' in fields && 'string' === typeof fields.desc + ? fields.desc + : '', + code: 'code' in fields && 'string' === typeof fields.code + ? fields.code + : '', + tags: 'tags' in fields && Array.isArray(fields.tags) ? + fields.tags.filter(value => typeof value === 'string') + : [], + scope: 'scope' in fields && isValidScope(fields.scope) + ? fields.scope + : 'global', + modified: 'modified' in fields && 'string' === typeof fields.modified + ? fields.modified + : '', + active: 'active' in fields && 'boolean' === typeof fields.active + ? fields.active + : false, + network: 'network' in fields && isBooleanOrUndefined(fields.network) + ? fields.network + : isNetworkAdmin(), + shared_network: 'shared_network' in fields && isBooleanOrUndefined(fields.shared_network) + ? fields.shared_network + : null, + priority: 'priority' in fields && typeof fields.priority === 'number' + ? fields.priority + : 10 }) export const getSnippetType = (snippetOrScope: Snippet | SnippetScope): SnippetType => { From e22e89a7268f064fb43e11eb478ffa30af05f6ee Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 28 Mar 2025 14:26:34 +1100 Subject: [PATCH 017/268] Update snippet parsing function to accept an unknown value. --- src/js/components/SnippetForm/SnippetForm.tsx | 5 +- src/js/utils/snippets.ts | 72 ++++++++++--------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/js/components/SnippetForm/SnippetForm.tsx b/src/js/components/SnippetForm/SnippetForm.tsx index 4ecad677..0c61f51f 100644 --- a/src/js/components/SnippetForm/SnippetForm.tsx +++ b/src/js/components/SnippetForm/SnippetForm.tsx @@ -68,9 +68,6 @@ const EditForm: React.FC = () => { } export const SnippetForm: React.FC = () => - - typeof window.CODE_SNIPPETS_EDIT?.snippet === 'object' && null !== window.CODE_SNIPPETS_EDIT?.snippet - ? createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet) - : createSnippetObject()}> + createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}> diff --git a/src/js/utils/snippets.ts b/src/js/utils/snippets.ts index 2baff98a..c4b57954 100644 --- a/src/js/utils/snippets.ts +++ b/src/js/utils/snippets.ts @@ -9,46 +9,48 @@ const isAbsInt = (value: unknown): value is number => const isBooleanOrUndefined = (value: unknown): value is boolean | undefined => 'boolean' == typeof value || value === undefined +const parseStringArray = (value: unknown): string[] | undefined => + Array.isArray(value) ? value.filter(entry => typeof entry === 'string') : undefined + export const isValidScope = (scope: unknown): scope is SnippetScope => 'string' === typeof scope && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => typeScopes.some(typeScope => typeScope === scope)) -export const createSnippetObject = (fields: {} = {}): Snippet => ({ - id: 'id' in fields && isAbsInt(fields.id) - ? fields.id - : 0, - name: 'name' in fields && 'string' === typeof fields.name - ? fields.name - : '', - desc: 'desc' in fields && 'string' === typeof fields.desc - ? fields.desc - : '', - code: 'code' in fields && 'string' === typeof fields.code - ? fields.code - : '', - tags: 'tags' in fields && Array.isArray(fields.tags) ? - fields.tags.filter(value => typeof value === 'string') - : [], - scope: 'scope' in fields && isValidScope(fields.scope) - ? fields.scope - : 'global', - modified: 'modified' in fields && 'string' === typeof fields.modified - ? fields.modified - : '', - active: 'active' in fields && 'boolean' === typeof fields.active - ? fields.active - : false, - network: 'network' in fields && isBooleanOrUndefined(fields.network) - ? fields.network - : isNetworkAdmin(), - shared_network: 'shared_network' in fields && isBooleanOrUndefined(fields.shared_network) - ? fields.shared_network - : null, - priority: 'priority' in fields && typeof fields.priority === 'number' - ? fields.priority - : 10 -}) +export const createSnippetObject = (fields: unknown = undefined): Snippet => { + const defaults: Snippet = { + id: 0, + name: '', + code: '', + desc: '', + tags: [], + scope: 'global', + modified: '', + active: false, + network: isNetworkAdmin(), + shared_network: null, + priority: 10 + } + + if (typeof fields !== 'object' || null === fields) { + return defaults + } + + return ({ + id: 'id' in fields && isAbsInt(fields.id) ? fields.id : defaults.id, + name: 'name' in fields && 'string' === typeof fields.name ? fields.name : defaults.name, + desc: 'desc' in fields && 'string' === typeof fields.desc ? fields.desc : defaults.desc, + code: 'code' in fields && 'string' === typeof fields.code ? fields.code : defaults.code, + tags: 'tags' in fields ? parseStringArray(fields.tags) ?? defaults.tags : defaults.tags, + scope: 'scope' in fields && isValidScope(fields.scope) ? fields.scope : defaults.scope, + modified: 'modified' in fields && 'string' === typeof fields.modified ? fields.modified : defaults.modified, + active: 'active' in fields && 'boolean' === typeof fields.active ? fields.active : defaults.active, + network: 'network' in fields && isBooleanOrUndefined(fields.network) ? fields.network : defaults.network, + shared_network: 'shared_network' in fields && isBooleanOrUndefined(fields.shared_network) + ? fields.shared_network : defaults.shared_network, + priority: 'priority' in fields && typeof fields.priority === 'number' ? fields.priority : defaults.priority + }) +} export const getSnippetType = (snippetOrScope: Snippet | SnippetScope): SnippetType => { const scope = 'string' === typeof snippetOrScope ? snippetOrScope : snippetOrScope.scope From dd48e30642ca44a42db3f03c5c1e97e62f17a4d5 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 28 Mar 2025 17:58:02 +1100 Subject: [PATCH 018/268] Introduce updated type badge styles. --- src/css/common/_switch.scss | 36 +++++---- src/css/common/_theme.scss | 43 +++++----- src/css/common/_type-badges.scss | 81 ++++++++----------- src/css/manage.scss | 30 ++----- .../SnippetForm/page/PageHeading.tsx | 2 +- src/php/class-list-table.php | 8 -- 6 files changed, 78 insertions(+), 122 deletions(-) diff --git a/src/css/common/_switch.scss b/src/css/common/_switch.scss index 59e14aac..fc6ead0d 100644 --- a/src/css/common/_switch.scss +++ b/src/css/common/_switch.scss @@ -1,5 +1,4 @@ -$active-color: #2196f3; -$inactive-color: #ccc; +@use 'theme'; .snippet-execution-button, .snippet-activation-switch, @@ -11,15 +10,14 @@ input[type=checkbox].switch { .snippet-activation-switch, input[type=checkbox].switch { appearance: none; - border: 0; outline: 0; cursor: pointer; margin-top: 5px; width: 30px; height: 17px; border-radius: 34px; - background-color: #ccc; text-align: left; + border: 1px solid theme.$accent; &::before { transition: all .4s; @@ -28,16 +26,16 @@ input[type=checkbox].switch { width: 13px; display: inline-block; margin: 2px; - background-color: white; + background-color: theme.$accent; border-radius: 50%; } +} - &:checked { - background-color: $active-color; - - &::before { - transform: translateX(100%); - } +.active-snippet .snippet-activation-switch, +input[type=checkbox].switch:checked { + &::before { + background-color: white; + transform: translateX(100%); } } @@ -57,8 +55,7 @@ input[type=checkbox].switch { height: 0; border-top: 9px solid transparent; border-bottom: 9px solid transparent; - border-left: 10px solid $inactive-color; - transition: all 0.3s; + border-left: 10px solid #ccc; &::before { content: ''; @@ -68,8 +65,17 @@ input[type=checkbox].switch { bottom: -14px; right: -8px; border-radius: 50%; - border: 1.8px solid $inactive-color; + border: 1.8px solid #ccc; z-index: 2; - transition: all .3s; + } + + &:hover { + border-left-color: theme.$accent; + transition: border-left-color 0.6s; + + &::before { + border-color: theme.$accent; + transition: border-color 0.6s; + } } } diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index 76ac7cd1..c76b71be 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -1,5 +1,7 @@ @use 'sass:color'; +$accent: #2271B1; + $primary: #03c7d2; $secondary: #d46f4d; $outline: #9e9e9e; @@ -11,32 +13,25 @@ $brand-discord: #5865f2; $brand-facebook: #3b5998; $types: ( - php: ( - active: #0073aa, - inactive: #579, - background: #78c8e6, - highlight: #0073aa - ), - css: ( - active: #7d26cd, - inactive: #b452cd, - background: #551a8b, - highlight: #8000ff - ), - html: ( - active: #548b54, - inactive: #548b54, - background: #548b54, - highlight: #548b54 - ), - js: ( - active: #d44500, - inactive: #cd6600, - background: #cd6600, - highlight: #cd6600 - ) + php: #1D97C6, + html: #EF6A36, + css: #9B59B6, + js: #FFEB3B, + cond: #2EAE95 ); +@function contrasting-text-color($bg-color) { + $red: color.channel($bg-color, 'red', $space: rgb); + $green: color.channel($bg-color, 'green', $space: rgb); + $blue: color.channel($bg-color, 'blue', $space: rgb); + + @if ($red * 0.299 + $green * 0.587 + $blue * 0.114) > 186 { + @return #1C1F20; + } @else { + @return #fff; + } +} + @mixin link-colors($color, $lightness: 15%) { a { color: $color; diff --git a/src/css/common/_type-badges.scss b/src/css/common/_type-badges.scss index 0d9f445c..471156e0 100644 --- a/src/css/common/_type-badges.scss +++ b/src/css/common/_type-badges.scss @@ -1,14 +1,31 @@ @use 'sass:map'; @use 'theme'; -.nav-tab .badge, .snippet-type-badge, .go-pro-button .badge, .button .badge { - font-size: 10px; +%badge { + display: inline-block; + font-size: 12px; + font-weight: 700; text-transform: uppercase; - border: 1px solid; padding: 1px 2px; - border-radius: 5px; + border-radius: 3px; + text-align: center; + width: 3.5em; } +.nav-tab .badge, +.snippet-type-badge, +.go-pro-button .badge, +.button .badge { + @extend %badge; +} + +.go-pro-badge, +.pro-badge, +.core-badge { + @extend %badge; +} + + .nav-tab .badge, .button .snippet-type-badge, .go-pro-button .badge, @@ -17,12 +34,7 @@ h2 .snippet-type-badge, h3 .snippet-type-badge, .snippet-type-option .badge { /* rtl:ignore */ - margin-left: 3px; -} - -.button .badge { - font-size: 8px; - text-transform: lowercase; + margin-left: 8px; } .nav-tab span, @@ -30,28 +42,12 @@ h3 .snippet-type-badge, vertical-align: middle; } -.snippet-type-badge-inverted { - font-size: inherit; - padding: 3px 6px; -} - -@each $type, $colors in theme.$types { - $color: map.get($colors, 'highlight'); - +@each $type, $color in theme.$types { .nav-tab[data-snippet-type=#{$type}] .badge, - .snippet-type-badge[data-snippet-type=#{$type}] { - color: $color; - border-color: currentColor; - } - - .snippet-type-badge-inverted[data-snippet-type=#{$type}] { - color: white; - background-color: $color; - border-color: $color; - } - + .snippet-type-badge[data-snippet-type=#{$type}], .nav-tab-inactive[data-snippet-type=#{$type}]:hover .badge { - color: $color; + color: theme.contrasting-text-color($color); + background-color: $color; } } @@ -63,27 +59,13 @@ h3 .snippet-type-badge, .nav-tab.nav-tab-inactive { background: transparent; - text-shadow: 0 1px 0 #fff; - - &, .badge { - color: #a7aaad; - } - &:hover { - color: #50575e; + .badge { + color: #fff; + background-color: #a7aaad; } } -.go-pro-badge, .pro-badge, .core-badge { - margin-left: 3px; - border: 1px solid currentColor; - border-radius: 5px; - font-size: 10px; - padding: 1px 2px; - text-transform: uppercase; - -} - .go-pro-badge, .pro-badge { color: theme.$pro; } @@ -93,9 +75,10 @@ h3 .snippet-type-badge, } .go-pro-button .badge, .go-pro-badge { - color: theme.$pro; - border-color: theme.$pro; + color: #fff; + background-color: theme.$pro; margin-left: 1px; + font-size: 10px; } .wp-core-ui .button.nav-tab-button { diff --git a/src/css/manage.scss b/src/css/manage.scss index 783129f5..6161582d 100644 --- a/src/css/manage.scss +++ b/src/css/manage.scss @@ -53,7 +53,6 @@ } .snippets { - tr { background: #fff; } @@ -107,7 +106,7 @@ } } - .badge { + .small-badge { margin-left: 4px; padding: 3px 6px; text-decoration: none; @@ -143,39 +142,20 @@ } .inactive-snippet { - @include theme.link-colors(map.get(theme.$types, 'php', 'inactive')); + @include theme.link-colors(#579); } .active-snippet { - td, th { - background-color: rgba(map.get(theme.$types, 'php', 'background'), 0.06); + background-color: rgba(#78c8e6, 0.06); } th.check-column { border-left: 2px solid #2ea2cc; } -} - -@each $type, $colors in theme.$types { - .#{$type}-snippet { - @include theme.link-colors(map.get($colors, 'inactive')); - } - - .#{$type}-snippet.active-snippet { - @include theme.link-colors(map.get($colors, 'active')); - td, th { - background-color: rgba(map.get($colors, 'background'), 0.06); - } - - .snippet-activation-switch { - background-color: map.get($colors, 'highlight'); - } - - th.check-column { - border-left-color: map.get($colors, 'highlight'); - } + .snippet-activation-switch { + background-color: #0073aa; } } diff --git a/src/js/components/SnippetForm/page/PageHeading.tsx b/src/js/components/SnippetForm/page/PageHeading.tsx index 518b6085..99491797 100644 --- a/src/js/components/SnippetForm/page/PageHeading.tsx +++ b/src/js/components/SnippetForm/page/PageHeading.tsx @@ -15,7 +15,7 @@ export const PageHeading: React.FC = () => {

    {snippet.id ? <> - {`{${editHeading} `} + {`${editHeading} `} display_name ); - if ( 'global' !== $snippet->scope ) { - $out .= sprintf( ' ', $snippet->scope_icon ); - } - // Add a link to the snippet if it isn't an unreadable network-only snippet. if ( $this->is_network || ! $snippet->network || current_user_can( code_snippets()->get_network_cap_name() ) ) { $out = sprintf( @@ -412,10 +408,6 @@ public function get_columns(): array { 'id' => __( 'ID', 'code-snippets' ), ); - if ( isset( $_GET['type'] ) && 'all' !== $_GET['type'] ) { - unset( $columns['type'] ); - } - if ( ! get_setting( 'general', 'enable_description' ) ) { unset( $columns['description'] ); } From bb037efd736b763b07bbac882af4fb4fb461ff65 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 28 Mar 2025 18:15:39 +1100 Subject: [PATCH 019/268] Improve layout of navigation buttons on manage menu. --- src/css/common/_type-badges.scss | 25 +++++-------------------- src/css/manage.scss | 24 +++++++++++++++++++++--- src/php/class-admin.php | 3 +++ src/php/class-list-table.php | 2 +- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/css/common/_type-badges.scss b/src/css/common/_type-badges.scss index 471156e0..7d0a0d80 100644 --- a/src/css/common/_type-badges.scss +++ b/src/css/common/_type-badges.scss @@ -2,7 +2,6 @@ @use 'theme'; %badge { - display: inline-block; font-size: 12px; font-weight: 700; text-transform: uppercase; @@ -10,6 +9,9 @@ border-radius: 3px; text-align: center; width: 3.5em; + display: flex; + justify-content: center; + align-items: center; } .nav-tab .badge, @@ -25,23 +27,6 @@ @extend %badge; } - -.nav-tab .badge, -.button .snippet-type-badge, -.go-pro-button .badge, -h1 .snippet-type-badge, -h2 .snippet-type-badge, -h3 .snippet-type-badge, -.snippet-type-option .badge { - /* rtl:ignore */ - margin-left: 8px; -} - -.nav-tab span, -.nav-tab .badge { - vertical-align: middle; -} - @each $type, $color in theme.$types { .nav-tab[data-snippet-type=#{$type}] .badge, .snippet-type-badge[data-snippet-type=#{$type}], @@ -54,7 +39,6 @@ h3 .snippet-type-badge, .nav-tab-button .dashicons-external { font-size: 15px; color: #666; - vertical-align: middle; } .nav-tab.nav-tab-inactive { @@ -74,7 +58,8 @@ h3 .snippet-type-badge, color: theme.$core; } -.go-pro-button .badge, .go-pro-badge { +.go-pro-button .badge, +.go-pro-badge { color: #fff; background-color: theme.$pro; margin-left: 1px; diff --git a/src/css/manage.scss b/src/css/manage.scss index 6161582d..7f3988cb 100644 --- a/src/css/manage.scss +++ b/src/css/manage.scss @@ -203,13 +203,31 @@ line-height: 1.4; } +.cloud-badge { + border: 1px solid; + border-radius: 5px; + font-size: 15px; + line-height: 19px; +} + +.wrap h2.nav-tab-wrapper { + display: flex; + flex-flow: row wrap-reverse; + gap: 0.5em; + + .nav-tab { + display: flex; + flex-flow: row wrap; + align-items: center; + gap: 8px; + margin: 0; + } +} + @media screen and (max-width: 1190px) { .nav-tab { .snippet-label { display: none; } - .cloud-badge{ - margin-right: 10px; - } } } diff --git a/src/php/class-admin.php b/src/php/class-admin.php index 503819f6..06776477 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -364,6 +364,9 @@ public static function render_snippet_type_tab( string $type_name, string $label case 'ai': echo '', esc_html__( 'AI', 'code-snippets' ), ''; break; + case 'cond': + echo ''; + break; default: echo '' . esc_html( $type_name ) . ''; break; diff --git a/src/php/class-list-table.php b/src/php/class-list-table.php index 2d82ea72..50f3dbef 100644 --- a/src/php/class-list-table.php +++ b/src/php/class-list-table.php @@ -172,7 +172,7 @@ protected function column_default( $item, $column_name ): string { '%s', esc_url( $url ), esc_attr( $type ), - esc_html( $type ) + 'cond' === $type ? '' : esc_html( $type ) ); case 'date': From 45cff3d4069923c7119b513f145c077b65f5a739 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Sat, 29 Mar 2025 01:44:49 +1100 Subject: [PATCH 020/268] Apply cosmetic changes from pro branch. --- src/css/common/_select.scss | 5 ++ src/css/common/_switch.scss | 13 ++++- src/css/common/_theme.scss | 1 - src/css/common/_type-badges.scss | 9 +--- src/css/edit.scss | 1 + src/css/edit/_sidebar.scss | 4 ++ src/css/manage.scss | 5 +- .../controls/ActivationSwitch.tsx | 6 +-- .../fields/SnippetLocationInput.tsx | 4 +- .../SnippetForm/fields/SnippetTypeInput.tsx | 1 + src/js/hooks/useSnippetSubmit.ts | 1 - src/js/hooks/useSnippetsAPI.ts | 19 +++---- src/js/utils/restAPI.ts | 11 ++++ src/js/utils/snippets.ts | 52 +++++++++---------- src/php/class-active-snippets.php | 1 + src/php/class-snippet.php | 4 +- src/php/snippet-ops.php | 2 +- 17 files changed, 79 insertions(+), 60 deletions(-) create mode 100644 src/css/common/_select.scss create mode 100644 src/js/utils/restAPI.ts diff --git a/src/css/common/_select.scss b/src/css/common/_select.scss new file mode 100644 index 00000000..452145df --- /dev/null +++ b/src/css/common/_select.scss @@ -0,0 +1,5 @@ +.code-snippets-select { + input[type="text"]:focus { + box-shadow: none; + } +} diff --git a/src/css/common/_switch.scss b/src/css/common/_switch.scss index fc6ead0d..fd31ced3 100644 --- a/src/css/common/_switch.scss +++ b/src/css/common/_switch.scss @@ -12,7 +12,7 @@ input[type=checkbox].switch { appearance: none; outline: 0; cursor: pointer; - margin-top: 5px; + margin: 0; width: 30px; height: 17px; border-radius: 34px; @@ -25,14 +25,23 @@ input[type=checkbox].switch { height: 13px; width: 13px; display: inline-block; - margin: 2px; background-color: theme.$accent; border-radius: 50%; } } +.snippet-activation-switch::before { + margin: 2px; +} + +input[type=checkbox].switch::before { + margin: 1px; +} + .active-snippet .snippet-activation-switch, input[type=checkbox].switch:checked { + background-color: #0073aa; + &::before { background-color: white; transform: translateX(100%); diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index c76b71be..7462869c 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -7,7 +7,6 @@ $secondary: #d46f4d; $outline: #9e9e9e; $core: #0073aa; -$pro: #ce0000; $brand-discord: #5865f2; $brand-facebook: #3b5998; diff --git a/src/css/common/_type-badges.scss b/src/css/common/_type-badges.scss index 7d0a0d80..ee6e7267 100644 --- a/src/css/common/_type-badges.scss +++ b/src/css/common/_type-badges.scss @@ -49,19 +49,14 @@ background-color: #a7aaad; } } - -.go-pro-badge, .pro-badge { - color: theme.$pro; -} - .core-badge { color: theme.$core; } .go-pro-button .badge, .go-pro-badge { - color: #fff; - background-color: theme.$pro; + color: #DF9279; + background-color: #F7E8E3; margin-left: 1px; font-size: 10px; } diff --git a/src/css/edit.scss b/src/css/edit.scss index d2d0bb02..d3ee9a7a 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -11,6 +11,7 @@ @use 'edit/editor'; @use 'common/tooltips'; +@use 'common/select'; @use 'edit/upgrade-dialog'; @use 'edit/gpt'; diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss index 40be0869..61f68b4e 100644 --- a/src/css/edit/_sidebar.scss +++ b/src/css/edit/_sidebar.scss @@ -52,6 +52,10 @@ align-items: center; } } + + .components-form-token-field { + width: 100%; + } } .snippet-priority input { diff --git a/src/css/manage.scss b/src/css/manage.scss index 7f3988cb..6a1cf6f4 100644 --- a/src/css/manage.scss +++ b/src/css/manage.scss @@ -7,6 +7,7 @@ @use 'common/theme'; @use 'common/type-badges'; @use 'common/switch'; +@use 'common/select'; @use 'manage/cloud'; .column-name, @@ -153,10 +154,6 @@ th.check-column { border-left: 2px solid #2ea2cc; } - - .snippet-activation-switch { - background-color: #0073aa; - } } @media screen and (max-width: 782px) { diff --git a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx index 5a6e04fd..73bcd494 100644 --- a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx +++ b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx @@ -9,16 +9,16 @@ export const ActivationSwitch = () => { return (

    -

    = { @@ -48,6 +49,7 @@ export const SnippetLocationInput: React.FC = () => {

    option.value === snippetType)} styles={{ diff --git a/src/js/hooks/useSnippetSubmit.ts b/src/js/hooks/useSnippetSubmit.ts index eb85f299..8eb4b25b 100644 --- a/src/js/hooks/useSnippetSubmit.ts +++ b/src/js/hooks/useSnippetSubmit.ts @@ -22,7 +22,6 @@ const messages = { } const getSuccessNotice = (request: Snippet, response: Snippet, active: boolean | undefined): string => { - if (active === undefined) { return 0 === request.id ? messages.created : messages.updated } diff --git a/src/js/hooks/useSnippetsAPI.ts b/src/js/hooks/useSnippetsAPI.ts index 7c675945..630e7e24 100644 --- a/src/js/hooks/useSnippetsAPI.ts +++ b/src/js/hooks/useSnippetsAPI.ts @@ -1,18 +1,13 @@ import { useEffect, useMemo, useState } from 'react' import { addQueryArgs } from '@wordpress/url' import { handleUnknownError } from '../utils/errors' +import { REST_API_AXIOS_CONFIG, REST_SNIPPETS_BASE } from '../utils/restAPI' import { isNetworkAdmin } from '../utils/screen' import { createSnippetObject } from '../utils/snippets' import { useAxios } from './useAxios' import type { Snippet } from '../types/Snippet' import type { SnippetsExport } from '../types/SnippetsExport' -import type { AxiosResponse, CreateAxiosDefaults } from 'axios' - -const ROUTE_BASE = window.CODE_SNIPPETS?.restAPI.snippets - -const AXIOS_CONFIG: CreateAxiosDefaults = { - headers: { 'X-WP-Nonce': window.CODE_SNIPPETS?.restAPI.nonce } -} +import type { AxiosResponse } from 'axios' export interface SnippetsAPI { fetchAll: (network?: boolean | null) => Promise> @@ -28,22 +23,22 @@ export interface SnippetsAPI { const buildURL = ({ id, network }: Snippet, action?: string) => addQueryArgs( - [ROUTE_BASE, id, action].filter(Boolean).join('/'), + [REST_SNIPPETS_BASE, id, action].filter(Boolean).join('/'), { network: network ? true : undefined } ) export const useSnippetsAPI = (): SnippetsAPI => { - const { get, post, del } = useAxios(AXIOS_CONFIG) + const { get, post, del } = useAxios(REST_API_AXIOS_CONFIG) return useMemo((): SnippetsAPI => ({ fetchAll: network => - get(addQueryArgs(ROUTE_BASE, { network })), + get(addQueryArgs(REST_SNIPPETS_BASE, { network })), fetch: (snippetId, network) => - get(addQueryArgs(`${ROUTE_BASE}/${snippetId}`, { network })), + get(addQueryArgs(`${REST_SNIPPETS_BASE}/${snippetId}`, { network })), create: snippet => - post(`${ROUTE_BASE}`, snippet), + post(REST_SNIPPETS_BASE, snippet), update: snippet => post(buildURL(snippet), snippet), diff --git a/src/js/utils/restAPI.ts b/src/js/utils/restAPI.ts new file mode 100644 index 00000000..17275c12 --- /dev/null +++ b/src/js/utils/restAPI.ts @@ -0,0 +1,11 @@ +import { trimTrailingChar } from './text' +import type { AxiosRequestConfig } from 'axios' + +export const REST_SNIPPETS_BASE = trimTrailingChar(window.CODE_SNIPPETS?.restAPI.snippets ?? '', '/') + +export const REST_API_AXIOS_CONFIG: AxiosRequestConfig = { + headers: { + 'X-WP-Nonce': window.CODE_SNIPPETS?.restAPI.nonce, + 'Access-Control': window.CODE_SNIPPETS?.restAPI.localToken + } +} diff --git a/src/js/utils/snippets.ts b/src/js/utils/snippets.ts index c4b57954..91e70742 100644 --- a/src/js/utils/snippets.ts +++ b/src/js/utils/snippets.ts @@ -1,55 +1,55 @@ +import { SNIPPET_TYPE_SCOPES } from '../types/Snippet' import { isNetworkAdmin } from './screen' -import { Snippet, SNIPPET_TYPE_SCOPES, SnippetScope, SnippetType } from '../types/Snippet' +import type { Snippet, SnippetScope, SnippetType } from '../types/Snippet' const PRO_TYPES: SnippetType[] = ['css', 'js'] +const defaults: Omit = { + id: 0, + name: '', + code: '', + desc: '', + scope: 'global', + modified: '', + active: false, + network: isNetworkAdmin(), + shared_network: null, + priority: 10 +} + const isAbsInt = (value: unknown): value is number => - typeof value === 'number' && value > 0 + 'number' === typeof value && 0 < value const isBooleanOrUndefined = (value: unknown): value is boolean | undefined => - 'boolean' == typeof value || value === undefined + 'boolean' === typeof value || value === undefined const parseStringArray = (value: unknown): string[] | undefined => - Array.isArray(value) ? value.filter(entry => typeof entry === 'string') : undefined + Array.isArray(value) ? value.filter(entry => 'string' === typeof entry) : undefined export const isValidScope = (scope: unknown): scope is SnippetScope => - 'string' === typeof scope && - Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => + 'string' === typeof scope + && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => typeScopes.some(typeScope => typeScope === scope)) export const createSnippetObject = (fields: unknown = undefined): Snippet => { - const defaults: Snippet = { - id: 0, - name: '', - code: '', - desc: '', - tags: [], - scope: 'global', - modified: '', - active: false, - network: isNetworkAdmin(), - shared_network: null, - priority: 10 + if ('object' !== typeof fields || null === fields) { + return { ...defaults, tags: [] } } - if (typeof fields !== 'object' || null === fields) { - return defaults - } - - return ({ + return { id: 'id' in fields && isAbsInt(fields.id) ? fields.id : defaults.id, name: 'name' in fields && 'string' === typeof fields.name ? fields.name : defaults.name, desc: 'desc' in fields && 'string' === typeof fields.desc ? fields.desc : defaults.desc, code: 'code' in fields && 'string' === typeof fields.code ? fields.code : defaults.code, - tags: 'tags' in fields ? parseStringArray(fields.tags) ?? defaults.tags : defaults.tags, + tags: 'tags' in fields ? parseStringArray(fields.tags) ?? [] : [], scope: 'scope' in fields && isValidScope(fields.scope) ? fields.scope : defaults.scope, modified: 'modified' in fields && 'string' === typeof fields.modified ? fields.modified : defaults.modified, active: 'active' in fields && 'boolean' === typeof fields.active ? fields.active : defaults.active, network: 'network' in fields && isBooleanOrUndefined(fields.network) ? fields.network : defaults.network, shared_network: 'shared_network' in fields && isBooleanOrUndefined(fields.shared_network) ? fields.shared_network : defaults.shared_network, - priority: 'priority' in fields && typeof fields.priority === 'number' ? fields.priority : defaults.priority - }) + priority: 'priority' in fields && 'number' === typeof fields.priority ? fields.priority : defaults.priority + } } export const getSnippetType = (snippetOrScope: Snippet | SnippetScope): SnippetType => { diff --git a/src/php/class-active-snippets.php b/src/php/class-active-snippets.php index 7c1de751..e60929e7 100644 --- a/src/php/class-active-snippets.php +++ b/src/php/class-active-snippets.php @@ -1,4 +1,5 @@ db; - $scopes = array( 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ); + $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; $data = $db->fetch_active_snippets( $scopes ); // Detect if a snippet is currently being edited, and if so, spare it from execution. From 529375422a5d9edd6359796ee2678389ec9b10ec Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Sat, 29 Mar 2025 02:01:21 +1100 Subject: [PATCH 021/268] Simplify request API in JavaScript. --- src/css/edit.scss | 4 ++-- src/css/edit/_form.scss | 2 +- src/js/hooks/useAxios.ts | 18 +++++++++--------- src/js/hooks/useSnippetSubmit.ts | 2 +- src/js/hooks/useSnippetsAPI.ts | 21 ++++++++++----------- src/js/types/Snippet.ts | 24 ++++++++++++------------ src/js/utils/snippets.ts | 3 +-- 7 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/css/edit.scss b/src/css/edit.scss index d3ee9a7a..cefd3212 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -5,13 +5,13 @@ @use 'common/codemirror'; @use 'common/type-badges'; @use 'common/switch'; +@use 'common/select'; +@use 'common/tooltips'; @use 'edit/form'; @use 'edit/sidebar'; @use 'edit/editor'; -@use 'common/tooltips'; -@use 'common/select'; @use 'edit/upgrade-dialog'; @use 'edit/gpt'; diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index 44a2212a..96d6deec 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -65,7 +65,7 @@ } .badge { - margin: 3px 0 3px auto; + margin: 3px 0 3px 7px; text-transform: uppercase; border: 1px solid currentColor; border-radius: 4px; diff --git a/src/js/hooks/useAxios.ts b/src/js/hooks/useAxios.ts index a513e6ab..2bef7f27 100644 --- a/src/js/hooks/useAxios.ts +++ b/src/js/hooks/useAxios.ts @@ -3,9 +3,9 @@ import axios from 'axios' import type { AxiosInstance, AxiosResponse, CreateAxiosDefaults } from 'axios' export interface AxiosAPI { - get: (url: string) => Promise> - post: (url: string, data?: D) => Promise> - del: (url: string) => Promise> + get: (url: string) => Promise + post: (url: string, data?: object) => Promise + del: (url: string) => Promise axiosInstance: AxiosInstance } @@ -14,24 +14,24 @@ const debugRequest = async ( url: string, doRequest: Promise>, data?: D -): Promise> => { +): Promise => { console.debug(`${method} ${url}`, ...data ? [data] : []) const response = await doRequest console.debug('Response', response) - return response + return response.data } export const useAxios = (defaultConfig: CreateAxiosDefaults): AxiosAPI => { const axiosInstance = useMemo(() => axios.create(defaultConfig), [defaultConfig]) return useMemo((): AxiosAPI => ({ - get: (url: string): Promise> => + get: (url: string): Promise => debugRequest('GET', url, axiosInstance.get, never>(url)), - post: (url: string, data?: D) => - debugRequest('POST', url, axiosInstance.post, D>(url, data), data), + post: (url: string, data?: object): Promise => + debugRequest('POST', url, axiosInstance.post, typeof data>(url, data), data), - del: (url: string) => + del: (url: string): Promise => debugRequest('DELETE', url, axiosInstance.delete, never>(url)), axiosInstance diff --git a/src/js/hooks/useSnippetSubmit.ts b/src/js/hooks/useSnippetSubmit.ts index 8eb4b25b..bbc9e2ff 100644 --- a/src/js/hooks/useSnippetSubmit.ts +++ b/src/js/hooks/useSnippetSubmit.ts @@ -53,7 +53,7 @@ export const useSnippetSubmit = ( const result = await (async (): Promise => { try { const requestData: Snippet = { ...snippet, active: active ?? snippet.active } - const { data } = await (0 === snippet.id ? api.create(requestData) : api.update(requestData)) + const data = await (0 === snippet.id ? api.create(requestData) : api.update(requestData)) setIsWorking(false) return data.id ? data : undefined } catch (error) { diff --git a/src/js/hooks/useSnippetsAPI.ts b/src/js/hooks/useSnippetsAPI.ts index 630e7e24..714e1e4e 100644 --- a/src/js/hooks/useSnippetsAPI.ts +++ b/src/js/hooks/useSnippetsAPI.ts @@ -7,18 +7,17 @@ import { createSnippetObject } from '../utils/snippets' import { useAxios } from './useAxios' import type { Snippet } from '../types/Snippet' import type { SnippetsExport } from '../types/SnippetsExport' -import type { AxiosResponse } from 'axios' export interface SnippetsAPI { - fetchAll: (network?: boolean | null) => Promise> - fetch: (snippetId: number, network?: boolean | null) => Promise> - create: (snippet: Snippet) => Promise> - update: (snippet: Snippet) => Promise> - delete: (snippet: Snippet) => Promise> - activate: (snippet: Snippet) => Promise> - deactivate: (snippet: Snippet) => Promise> - export: (snippet: Snippet) => Promise> - exportCode: (snippet: Snippet) => Promise> + fetchAll: (network?: boolean | null) => Promise + fetch: (snippetId: number, network?: boolean | null) => Promise + create: (snippet: Snippet) => Promise + update: (snippet: Snippet) => Promise + delete: (snippet: Snippet) => Promise + activate: (snippet: Snippet) => Promise + deactivate: (snippet: Snippet) => Promise + export: (snippet: Snippet) => Promise + exportCode: (snippet: Snippet) => Promise } const buildURL = ({ id, network }: Snippet, action?: string) => @@ -68,7 +67,7 @@ export const useSnippets = (): Snippet[] | undefined => { if (!snippets) { api.fetchAll(isNetworkAdmin()) .then(response => - setSnippets(response.data.map(snippet => createSnippetObject(snippet)))) + setSnippets(response.map(snippet => createSnippetObject(snippet)))) .catch(handleUnknownError) } }, [api, snippets]) diff --git a/src/js/types/Snippet.ts b/src/js/types/Snippet.ts index 78ba27cd..f5518743 100644 --- a/src/js/types/Snippet.ts +++ b/src/js/types/Snippet.ts @@ -1,16 +1,16 @@ export interface Snippet { - id: number - name: string - desc: string - code: string - tags: string[] - scope: SnippetScope - priority: number - active: boolean - network?: boolean - shared_network?: boolean | null - modified?: string - code_error?: [string, number] | null + readonly id: number + readonly name: string + readonly desc: string + readonly code: string + readonly tags: string[] + readonly scope: SnippetScope + readonly priority: number + readonly active: boolean + readonly network?: boolean + readonly shared_network?: boolean | null + readonly modified?: string + readonly code_error?: [string, number] | null } export type SnippetType = 'php' | 'html' | 'css' | 'js' diff --git a/src/js/utils/snippets.ts b/src/js/utils/snippets.ts index 91e70742..123d826c 100644 --- a/src/js/utils/snippets.ts +++ b/src/js/utils/snippets.ts @@ -27,8 +27,7 @@ const parseStringArray = (value: unknown): string[] | undefined => Array.isArray(value) ? value.filter(entry => 'string' === typeof entry) : undefined export const isValidScope = (scope: unknown): scope is SnippetScope => - 'string' === typeof scope - && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => + 'string' === typeof scope && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => typeScopes.some(typeScope => typeScope === scope)) export const createSnippetObject = (fields: unknown = undefined): Snippet => { From db2f9eef835274631f20c73c35b9980548136207 Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Fri, 4 Apr 2025 20:43:21 +1100 Subject: [PATCH 022/268] Update list of contributors in readme. --- src/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readme.txt b/src/readme.txt index cfe5b55c..d03fee28 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -1,5 +1,5 @@ === Code Snippets === -Contributors: bungeshea, ver3, nate33, lightbulbman, 0aksmith, johnpixle, codesnippetspro +Contributors: bungeshea, ver3, lightbulbman, 0aksmith, johnpixle, codesnippetspro Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later From eee5906c351198871f1f5a9e882051e2ce5e6468 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Sat, 26 Apr 2025 21:33:11 +0300 Subject: [PATCH 023/268] Merge similar translations strings --- src/js/components/SnippetForm/page/UpgradeDialog.tsx | 4 ++-- src/php/admin-menus/class-manage-menu.php | 2 +- src/php/admin-menus/class-welcome-menu.php | 2 +- src/php/class-validator.php | 2 +- src/php/cloud/list-table-shared-ops.php | 5 ++--- src/php/strings.php | 2 +- src/php/views/welcome.php | 4 ++-- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/js/components/SnippetForm/page/UpgradeDialog.tsx b/src/js/components/SnippetForm/page/UpgradeDialog.tsx index e8fc8a97..02b7e95c 100644 --- a/src/js/components/SnippetForm/page/UpgradeDialog.tsx +++ b/src/js/components/SnippetForm/page/UpgradeDialog.tsx @@ -100,14 +100,14 @@ const UpgradeInfo: React.FC = ({ nextTab }) => className="button button-secondary" href="https://codesnippets.pro/pricing/" > - {__('Learn More', 'code-snippets')} + {__('Learn more', 'code-snippets')}

    diff --git a/src/php/admin-menus/class-manage-menu.php b/src/php/admin-menus/class-manage-menu.php index 846747c5..4e90b8b2 100644 --- a/src/php/admin-menus/class-manage-menu.php +++ b/src/php/admin-menus/class-manage-menu.php @@ -229,7 +229,7 @@ protected function get_current_type(): string { */ public function print_pro_message() { if ( ! code_snippets()->licensing->is_licensed() ) { - echo '', esc_html_x( 'Pro', 'go pro badge', 'code-snippets' ), ''; + echo '', esc_html__( 'Pro', 'code-snippets' ), ''; } } diff --git a/src/php/admin-menus/class-welcome-menu.php b/src/php/admin-menus/class-welcome-menu.php index fbebe04b..ceb816ac 100644 --- a/src/php/admin-menus/class-welcome-menu.php +++ b/src/php/admin-menus/class-welcome-menu.php @@ -79,7 +79,7 @@ protected function get_header_links(): array { $links['pro'] = [ 'url' => 'https://codesnippets.pro/pricing/', 'icon' => 'cart', - 'label' => __( 'Get Pro', 'code-snippets' ), + 'label' => __( 'Upgrade to Pro', 'code-snippets' ), ]; } diff --git a/src/php/class-validator.php b/src/php/class-validator.php index ac98131b..27effb43 100644 --- a/src/php/class-validator.php +++ b/src/php/class-validator.php @@ -255,7 +255,7 @@ public function validate() { // If we did not make it out of the class, then there's a problem. if ( $depth > 0 ) { return array( - 'message' => __( 'Parse error: syntax error, unexpected end of snippet', 'code-snippets' ), + 'message' => __( 'Parse error: syntax error, unexpected end of snippet.', 'code-snippets' ), 'line' => $token[2], ); } diff --git a/src/php/cloud/list-table-shared-ops.php b/src/php/cloud/list-table-shared-ops.php index b8e3f95e..2623f979 100644 --- a/src/php/cloud/list-table-shared-ops.php +++ b/src/php/cloud/list-table-shared-ops.php @@ -115,11 +115,10 @@ function cloud_lts_build_action_links( Cloud_Snippet $cloud_snippet, string $sou esc_html__( 'Download', 'code-snippets' ) ) : sprintf( - '%s%s', + '%s', $additional_classes, 'https://codesnippets.pro/pricing/', - esc_html_x( 'Pro', 'pro only', 'code-snippets' ), - esc_html_x( ' Only', 'pro only', 'code-snippets' ) + __( 'Pro Only', 'code-snippets' ), ); } diff --git a/src/php/strings.php b/src/php/strings.php index 4f94ea0a..6839784c 100644 --- a/src/php/strings.php +++ b/src/php/strings.php @@ -10,7 +10,7 @@ __( 'Success', 'code-snippets' ); __( 'Notice', 'code-snippets' ); __( 'Thanks', 'code-snippets' ); -__( 'Okay', 'code-snippets' ); +__( 'Ok', 'code-snippets' ); // settings-fields.php. __( 'Minify Snippet Output', 'code-snippets' ); diff --git a/src/php/views/welcome.php b/src/php/views/welcome.php index fc8e100c..c3dce427 100644 --- a/src/php/views/welcome.php +++ b/src/php/views/welcome.php @@ -40,8 +40,8 @@ ]; $plugin_types = [ - 'core' => _x( 'Core', 'badge label', 'code-snippets' ), - 'pro' => _x( 'Pro', 'badge label', 'code-snippets' ), + 'core' => __( 'Core', 'code-snippets' ), + 'pro' => __( 'Pro', 'code-snippets' ), ]; ?> From 844a79a3d63da1e68da84e680d32742a46190ad8 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Sun, 27 Apr 2025 23:18:04 +0300 Subject: [PATCH 024/268] Merge similar translations strings Fixes --- src/php/class-admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php/class-admin.php b/src/php/class-admin.php index 503819f6..cc4f77ec 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -128,7 +128,7 @@ public function plugin_action_links( array $actions, string $plugin_file ): arra '%3$s', 'https://snipco.de/JE2i', esc_attr__( 'Upgrade to Code Snippets Pro', 'code-snippets' ), - esc_html__( 'Go Pro', 'code-snippets' ) + esc_html__( 'Get Pro', 'code-snippets' ) ); } return $actions; From 28595997e9a948cfee345a9f46c604c2d2920ca6 Mon Sep 17 00:00:00 2001 From: Rami Yushuvaev Date: Mon, 28 Apr 2025 18:20:57 +0300 Subject: [PATCH 025/268] Improve i18n, a11y, and security --- .../SnippetEditor/SnippetEditor.tsx | 4 +-- src/php/class-admin.php | 16 +++++------ src/php/class-contextual-help.php | 2 +- src/php/class-list-table.php | 2 +- .../cloud/class-cloud-search-list-table.php | 28 +++++++++++-------- src/php/cloud/list-table-shared-ops.php | 6 ++-- src/php/views/manage.php | 11 +++++--- src/php/views/partials/cloud-search.php | 2 +- src/php/views/partials/list-table-notices.php | 13 ++++++++- src/php/views/welcome.php | 8 +++--- 10 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx b/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx index 5873d299..36b68a7c 100644 --- a/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx +++ b/src/js/components/SnippetForm/SnippetEditor/SnippetEditor.tsx @@ -109,14 +109,12 @@ const SnippetTypeTabs: React.FC = ({ ? { event.preventDefault() openUpgradeDialog() }} > - {_x('Upgrade to ', 'Upgrade to Pro', 'code-snippets')} - {_x('Pro', 'Upgrade to Pro', 'code-snippets')} + {__('Upgrade to Pro', 'code-snippets')} : null}

    diff --git a/src/php/class-admin.php b/src/php/class-admin.php index cc4f77ec..913afe5e 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -103,20 +103,20 @@ public function plugin_action_links( array $actions, string $plugin_file ): arra return $actions; } - $format = '%3$s'; + $format = '%3$s'; $actions = array_merge( [ sprintf( $format, esc_url( code_snippets()->get_menu_url( 'settings' ) ), - esc_html__( 'Change plugin settings', 'code-snippets' ), + esc_attr__( 'Change plugin settings', 'code-snippets' ), esc_html__( 'Settings', 'code-snippets' ) ), sprintf( $format, esc_url( code_snippets()->get_menu_url() ), - esc_html__( 'Manage your existing snippets', 'code-snippets' ), + esc_attr__( 'Manage your existing snippets', 'code-snippets' ), esc_html__( 'Snippets', 'code-snippets' ) ), ], @@ -125,10 +125,10 @@ public function plugin_action_links( array $actions, string $plugin_file ): arra if ( ! code_snippets()->licensing->is_licensed() ) { $actions[] = sprintf( - '%3$s', + '%3$s', 'https://snipco.de/JE2i', esc_attr__( 'Upgrade to Code Snippets Pro', 'code-snippets' ), - esc_html__( 'Get Pro', 'code-snippets' ) + esc_attr__( 'Get Pro', 'code-snippets' ) ); } return $actions; @@ -148,7 +148,7 @@ public function plugin_row_meta( array $plugin_meta, string $plugin_file ): arra return $plugin_meta; } - $format = '%3$s'; + $format = '%3$s'; return array_merge( $plugin_meta, @@ -297,7 +297,7 @@ public function print_notices() { printf( '%s', esc_url( wp_nonce_url( add_query_arg( $meta_key, $notice ), $meta_key ) ), - esc_attr__( 'Dismiss', 'code-snippets' ) + esc_html__( 'Dismiss', 'code-snippets' ) ); echo '

    '; @@ -321,7 +321,7 @@ public static function render_snippet_type_tab( string $type_name, string $label } elseif ( ! code_snippets()->licensing->is_licensed() && Plugin::is_pro_type( $type_name ) ) { printf( - '', + '', esc_attr( $type_name ), esc_attr__( 'Available in Code Snippets Pro (external link)', 'code-snippets' ) ); diff --git a/src/php/class-contextual-help.php b/src/php/class-contextual-help.php index 8dc7156c..a1ba4707 100644 --- a/src/php/class-contextual-help.php +++ b/src/php/class-contextual-help.php @@ -72,7 +72,7 @@ private function load_help_sidebar() { 'https://codesnippets.pro' => __( 'Plugin Website', 'code-snippets' ), ]; - $contents = '

    ' . __( 'For more information:', 'code-snippets' ) . "

    \n"; + $contents = '

    ' . esc_html__( 'For more information:', 'code-snippets' ) . "

    \n"; foreach ( $sidebar_links as $url => $label ) { $contents .= "\n" . sprintf( '

    %s

    ', esc_url( $url ), esc_html( $label ) ); diff --git a/src/php/class-list-table.php b/src/php/class-list-table.php index ecb02017..3eaa15b2 100644 --- a/src/php/class-list-table.php +++ b/src/php/class-list-table.php @@ -292,7 +292,7 @@ protected function column_activate( Snippet $snippet ): string { } return sprintf( - '  ', + '  ', esc_attr( $class ), esc_url( $this->get_action_link( $action, $snippet ) ), esc_attr( $label ) diff --git a/src/php/cloud/class-cloud-search-list-table.php b/src/php/cloud/class-cloud-search-list-table.php index 69068510..af0d3583 100644 --- a/src/php/cloud/class-cloud-search-list-table.php +++ b/src/php/cloud/class-cloud-search-list-table.php @@ -117,7 +117,7 @@ public function display_rows() { */ foreach ( $this->items as $item ) { ?> -
    +
    ', esc_url( code_snippets()->get_snippet_edit_url( $link->local_id ) ) ); } else { printf( - '', + '', '#TB_inline?&width=700&height=500&inlineId=show-code-preview', esc_attr__( 'Preview this snippet', 'code-snippets' ), esc_attr( $item->id ), @@ -163,14 +163,12 @@ public function display_rows() {

    %s', + '%s %s', + esc_html__( 'Codevault:', 'code-snippets' ); esc_url( sprintf( 'https://codesnippets.cloud/codevault/%s', $item->codevault ) ), esc_html( $item->codevault ) ); - ?>

    @@ -250,14 +248,22 @@ public function display_rows() {
    - + ', esc_html__( 'Not indicated by author', 'code-snippets' ), ''; + printf( + '%s', + esc_html__( 'Not indicated by author', 'code-snippets' ) + ); } else { - // translators: tested status. - $text = sprintf( __( 'Author states %s', 'code-snippets' ), $wp_tested ); - echo '', esc_html( $text ), ''; + printf( + '%s', + sprintf( + // translators: %s: tested status. + __( 'Author states %s', 'code-snippets' ), + $wp_tested + ) + ); } ?>
    diff --git a/src/php/cloud/list-table-shared-ops.php b/src/php/cloud/list-table-shared-ops.php index 2623f979..1a6ab2bc 100644 --- a/src/php/cloud/list-table-shared-ops.php +++ b/src/php/cloud/list-table-shared-ops.php @@ -125,7 +125,7 @@ function cloud_lts_build_action_links( Cloud_Snippet $cloud_snippet, string $sou $thickbox_url = '#TB_inline?&width=700&height=500&inlineId=show-code-preview'; $thickbox_link = sprintf( - '%s', + '%s', esc_url( $thickbox_url ), esc_attr( $cloud_snippet->name ), $additional_classes, @@ -224,7 +224,7 @@ function cloud_lts_pagination( string $which, string $source, int $total_items, $page_links[] = sprintf( '%s', esc_url( add_query_arg( $source . '_page', min( $total_pages, $current + 1 ), $current_url ) ), - __( 'Next page' ), + esc_html__( 'Next page', 'code-snippets' ), '›' ); } @@ -235,7 +235,7 @@ function cloud_lts_pagination( string $which, string $source, int $total_items, $page_links[] = sprintf( '%s', esc_url( add_query_arg( $source . '_page', $total_pages, $current_url ) ), - __( 'Last page', 'code-snippets' ), + esc_html__( 'Last page', 'code-snippets' ), '»' ); } diff --git a/src/php/views/manage.php b/src/php/views/manage.php index 8a76ac79..2e1572ec 100644 --- a/src/php/views/manage.php +++ b/src/php/views/manage.php @@ -28,8 +28,11 @@ [ 'span' => [ 'class' => [ 'highlight-yellow' ] ] ] ); - $feedback_url = __( 'mailto:team@codesnippets.pro?subject=Code Snippet Beta Test Feedback', 'code-snippets' ); - printf( ' %s', esc_url( $feedback_url ), esc_html__( 'Click here to submit your feedback', 'code-snippets' ) ); + printf( + ' %s', + esc_url( __( 'mailto:team@codesnippets.pro?subject=Code Snippet Beta Test Feedback', 'code-snippets' ) ), + esc_html__( 'Click here to submit your feedback', 'code-snippets' ) + ); echo '

    '; } @@ -58,7 +61,7 @@ ?> + aria-label=""> Pro', 'code-snippets' ), [ 'span' => [ 'class' => 'badge' ] ] ); ?> @@ -79,7 +82,7 @@ ], 'css' => [ __( 'Style snippets are written in CSS and loaded in the admin area or on the site front-end, just like the theme style.css.', 'code-snippets' ), - esc_html__( 'Learn more about style snippets →', 'code-snippets' ), + __( 'Learn more about style snippets →', 'code-snippets' ), 'https://codesnippets.pro/learn-css/', ], 'js' => [ diff --git a/src/php/views/partials/cloud-search.php b/src/php/views/partials/cloud-search.php index 29ee9b7a..078399ed 100644 --- a/src/php/views/partials/cloud-search.php +++ b/src/php/views/partials/cloud-search.php @@ -53,7 +53,7 @@ + placeholder=""> + diff --git a/src/js/components/EditorSidebar/EditorSidebar.tsx b/src/js/components/EditorSidebar/EditorSidebar.tsx index fae0939b..a29cee20 100644 --- a/src/js/components/EditorSidebar/EditorSidebar.tsx +++ b/src/js/components/EditorSidebar/EditorSidebar.tsx @@ -3,12 +3,12 @@ import { Spinner } from '@wordpress/components' import { isRTL } from '@wordpress/i18n' import { useSnippetForm } from '../../hooks/useSnippetForm' import { isNetworkAdmin } from '../../utils/screen' -import { getSnippetType } from '../../utils/snippets' +import { getSnippetType, isCondition } from '../../utils/snippets/snippets' import { Notices } from '../SnippetForm/page/Notices' import { ShortcodeInfo } from './actions/ShortcodeInfo' import { MultisiteSharingSettings } from './controls/MultisiteSharingSettings' import { ExportButtons } from './actions/ExportButtons' -import { SubmitButton } from './actions/SubmitButtons' +import { SubmitButtons } from './actions/SubmitButtons' import { ActivationSwitch } from './controls/ActivationSwitch' import { DeleteButton } from './actions/DeleteButton' import { PriorityInput } from './controls/PriorityInput' @@ -21,7 +21,7 @@ export const EditorSidebar = () => { return (
    - {snippet.id ? : null} + {snippet.id && !isCondition(snippet) ? : null} {isNetworkAdmin() ? : null} @@ -39,7 +39,7 @@ export const EditorSidebar = () => {

    - + {isWorking ? : ''}

    diff --git a/src/js/components/EditorSidebar/actions/DeleteButton.tsx b/src/js/components/EditorSidebar/actions/DeleteButton.tsx index eecd6f21..1dc7f1cb 100644 --- a/src/js/components/EditorSidebar/actions/DeleteButton.tsx +++ b/src/js/components/EditorSidebar/actions/DeleteButton.tsx @@ -1,13 +1,13 @@ import { addQueryArgs } from '@wordpress/url' import React, { useState } from 'react' import { __ } from '@wordpress/i18n' +import { useRestAPI } from '../../../hooks/useRestAPI' import { Button } from '../../common/Button' import { ConfirmDialog } from '../../common/ConfirmDialog' -import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI' import { useSnippetForm } from '../../../hooks/useSnippetForm' export const DeleteButton: React.FC = () => { - const api = useSnippetsAPI() + const { snippetsAPI } = useRestAPI() const { snippet, setIsWorking, isWorking, handleRequestError } = useSnippetForm() const [isDialogOpen, setIsDialogOpen] = useState(false) @@ -34,7 +34,7 @@ export const DeleteButton: React.FC = () => { setIsDialogOpen(false) setIsWorking(true) - api.delete(snippet) + snippetsAPI.delete(snippet) .then(() => { setIsWorking(false) window.location.replace(addQueryArgs(window.CODE_SNIPPETS?.urls.manage, { result: 'deleted' })) diff --git a/src/js/components/EditorSidebar/actions/ExportButtons.tsx b/src/js/components/EditorSidebar/actions/ExportButtons.tsx index d074da26..466256db 100644 --- a/src/js/components/EditorSidebar/actions/ExportButtons.tsx +++ b/src/js/components/EditorSidebar/actions/ExportButtons.tsx @@ -1,26 +1,24 @@ import React from 'react' import { __ } from '@wordpress/i18n' +import { useRestAPI } from '../../../hooks/useRestAPI' import { Button } from '../../common/Button' -import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI' import { downloadSnippetExportFile } from '../../../utils/files' import { useSnippetForm } from '../../../hooks/useSnippetForm' -import type { SnippetsExport } from '../../../types/SnippetsExport' -import type { AxiosResponse } from 'axios' +import type { SnippetsExport } from '../../../types/schema/SnippetsExport' export const ExportButtons: React.FC = () => { - const api = useSnippetsAPI() + const { snippetsAPI } = useRestAPI() const { snippet, isWorking, setIsWorking, handleRequestError } = useSnippetForm() - const handleFileResponse = (response: AxiosResponse) => { - const data = response.data + const handleFileResponse = (response: string | SnippetsExport) => { setIsWorking(false) console.info('file response', response) - if ('string' === typeof data) { - downloadSnippetExportFile(data, snippet) + if ('string' === typeof response) { + downloadSnippetExportFile(response, snippet) } else { const JSON_INDENT_SPACES = 2 - downloadSnippetExportFile(JSON.stringify(data, undefined, JSON_INDENT_SPACES), snippet, 'json') + downloadSnippetExportFile(JSON.stringify(response, undefined, JSON_INDENT_SPACES), snippet, 'json') } } @@ -31,7 +29,7 @@ export const ExportButtons: React.FC = () => { onClick={() => { setIsWorking(true) - api.export(snippet) + snippetsAPI.export(snippet) .then(handleFileResponse) // translators: %s: error message. .catch((error: unknown) => handleRequestError(error, __('Could not download export file.', 'code-snippets'))) @@ -45,7 +43,7 @@ export const ExportButtons: React.FC = () => { ? +import { SubmitButton } from '../../common/SubmitButton' +import type { SubmitButtonProps } from '../../common/SubmitButton' + +const SaveButton = (props: SubmitButtonProps) => { + const { snippet } = useSnippetForm() + + return ( + + ) +} interface ActivateOrDeactivateButtonProps { - snippet: Snippet - onActivate: VoidFunction - onDeactivate: VoidFunction primaryActivate: boolean - inlineButtons?: boolean - disabled: boolean } -const ActivateOrDeactivateButton: React.FC = ({ - snippet, - disabled, - onActivate, - onDeactivate, - primaryActivate -}) => { +const ActivateOrDeactivateButton: React.FC = ({ primaryActivate }) => { + const { snippet, isWorking } = useSnippetForm() + switch (true) { - case snippet.shared_network && isNetworkAdmin(): + case isCondition(snippet) || snippet.shared_network && isNetworkAdmin(): return null case 'single-use' === snippet.scope: return ( - + ) case snippet.active: return ( - + ) default: case !snippet.active: return ( - + ) } } -const validateSnippet = (snippet: Snippet): undefined | string => { - const missingTitle = '' === snippet.name.trim() - const missingCode = '' === snippet.code.trim() - - switch (true) { - case missingCode && missingTitle: - return __('This snippet has no code or title.', 'code-snippets') - - case missingCode: - return __('This snippet has no snippet code.', 'code-snippets') - - case missingTitle: - return __('This snippet has no title.', 'code-snippets') - - default: - return undefined - } -} -const shouldActivateByDefault = (snippet: Snippet): boolean => - !!window.CODE_SNIPPETS_EDIT?.activateByDefault - && !snippet.active && 'single-use' !== snippet.scope - && (!snippet.shared_network || !isNetworkAdmin()) - -interface SubmitConfirmDialogProps { - isOpen: boolean - onClose: VoidFunction - onSubmit?: VoidFunction - validationWarning?: string -} - -const SubmitConfirmDialog: React.FC = ({ isOpen, onClose, onSubmit, validationWarning }) => - { - onSubmit?.() - onClose() - }} - > -

    {`${validationWarning} ${__('Continue?', 'code-snippets')}`}

    -
    - -export const SubmitButton: React.FC = () => { - const { snippet, isWorking, submitSnippet, submitAndActivateSnippet, submitAndDeactivateSnippet } = useSnippetForm() - const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false) - const [submitAction, setSubmitAction] = useState() - const validationWarning = validateSnippet(snippet) - const activateByDefault = shouldActivateByDefault(snippet) +export const SubmitButtons: React.FC = () => { + const { snippet } = useSnippetForm() - const handleSubmit = (submitAction: () => Promise): void => { - if (validationWarning) { - setIsConfirmDialogOpen(true) - setSubmitAction(() => submitAction) - } else { - submitAction() - .then(() => undefined) - .catch(handleUnknownError) - } - } + const activateByDefault = + !!window.CODE_SNIPPETS_EDIT?.activateByDefault && + !snippet.active && 'single-use' !== snippet.scope && + (!snippet.shared_network || !isNetworkAdmin()) return <> - {activateByDefault - ? handleSubmit(submitSnippet)} - disabled={isWorking} - /> : null} - - handleSubmit(submitAndActivateSnippet)} - onDeactivate={() => handleSubmit(submitAndDeactivateSnippet)} - /> - - {activateByDefault ? null - : handleSubmit(submitSnippet)} - disabled={isWorking} - />} - - { - setIsConfirmDialogOpen(false) - setSubmitAction(undefined) - }} - /> + {activateByDefault && } + + {!activateByDefault && } } diff --git a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx index 73bcd494..00f2d301 100644 --- a/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx +++ b/src/js/components/EditorSidebar/controls/ActivationSwitch.tsx @@ -1,10 +1,12 @@ import React from 'react' import { __ } from '@wordpress/i18n' import { useSnippetForm } from '../../../hooks/useSnippetForm' +import { SubmitSnippetAction, useSubmitSnippet } from '../../../hooks/useSubmitSnippet' import { handleUnknownError } from '../../../utils/errors' export const ActivationSwitch = () => { - const { snippet, isWorking, submitAndActivateSnippet, submitAndDeactivateSnippet } = useSnippetForm() + const { snippet, isWorking } = useSnippetForm() + const { submitSnippet } = useSubmitSnippet() return (
    @@ -17,15 +19,16 @@ export const ActivationSwitch = () => { { - (snippet.active - ? submitAndDeactivateSnippet() - : submitAndActivateSnippet()) + submitSnippet(snippet.active + ? SubmitSnippetAction.SAVE_AND_DEACTIVATE + : SubmitSnippetAction.SAVE_AND_ACTIVATE) .then(() => undefined) .catch(handleUnknownError) }} diff --git a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx index 9d268cf1..ed33b74f 100644 --- a/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx +++ b/src/js/components/EditorSidebar/controls/MultisiteSharingSettings.tsx @@ -15,12 +15,9 @@ export const MultisiteSharingSettings: React.FC = () => {
    - - { - // eslint-disable-next-line @stylistic/max-len - __('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets') - } - + { + __('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets') + }
    { const { snippet, isReadOnly, setSnippet } = useSnippetForm() @@ -13,12 +14,9 @@ export const PriorityInput = () => { -
    - -
    - {__('Snippets with a lower priority number will run before those with a higher number.', 'code-snippets')} -
    -
    + + {__('Snippets with a lower priority number will run before those with a higher number.', 'code-snippets')} + { - const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false) +const editFormClassName = ({ snippet, isReadOnly }: { snippet: Snippet, isReadOnly: boolean }) => + classnames( + 'snippet-form', + `${snippet.scope}-snippet`, + `${getSnippetType(snippet)}-snippet`, + `${snippet.id ? 'saved' : 'new'}-snippet`, + `${snippet.active ? 'active' : 'inactive'}-snippet`, + { + 'erroneous-snippet': !!snippet.code_error, + 'read-only-snippet': isReadOnly + } + ) + +interface ConfirmSubmitDialogProps { + doSubmit: (action: SubmitSnippetAction | undefined) => void + submitAction: SubmitSnippetAction | undefined + setSubmitAction: (action: SubmitSnippetAction | undefined) => void + validationWarning: string | undefined + setValidationWarning: (warning: string | undefined) => void +} + +const ConfirmSubmitDialog: React.FC = ({ + doSubmit, + submitAction, + setSubmitAction, + validationWarning, + setValidationWarning +}) => + { + setSubmitAction(undefined) + setValidationWarning(undefined) + }} + onConfirm={() => { + doSubmit(submitAction) + setSubmitAction(undefined) + setValidationWarning(undefined) + }} + > +

    {`${validationWarning} ${__('Continue?', 'code-snippets')}`}

    +
    + +const EditForm: React.FC = ({ children }) => { + const { submitSnippet } = useSubmitSnippet() const { snippet, isReadOnly } = useSnippetForm() + const { refreshSnippetsList } = useSnippetsList() + + const [validationWarning, setValidationWarning] = useState() + const [submitAction, setSubmitAction] = useState() + + const doSubmit = (action?: SubmitSnippetAction) => { + submitSnippet(action) + .then(refreshSnippetsList) + .catch(handleUnknownError) + } + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault() + + const action = Object.values(SubmitSnippetAction).find(actionName => + actionName === document.activeElement?.getAttribute('name')) + + const validationWarning = validateSnippet(snippet) + + if (validationWarning) { + setValidationWarning(validationWarning) + setSubmitAction(action) + } else { + doSubmit(action) + } + } + + return ( + <> +
    + {children} + + + + + ) +} + +const SnippetConditionsEditor: React.FC = () => +
    + {/* TODO */} +

    {__('This snippet type is not supported in this version of Code Snippets.')}

    +
    + +const ConditionModalButton: React.FC = () => +
    TODO
    + +const EditFormWrap: React.FC = () => { + const { snippet } = useSnippetForm() + const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false) return (
    @@ -25,42 +131,25 @@ const EditForm: React.FC = () => { -
    +
    setIsUpgradeDialogOpen(true)} /> - - -
    -

    {__('Conditions', 'code-snippets')}

    - -
    +
    - + {isCondition(snippet) + ? + : } {window.CODE_SNIPPETS_EDIT?.enableDescription ? : null}
    -
    +
    @@ -68,6 +157,10 @@ const EditForm: React.FC = () => { } export const SnippetForm: React.FC = () => - createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}> - - + + + createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}> + + + + diff --git a/src/js/components/SnippetForm/fields/CodeEditor.tsx b/src/js/components/SnippetForm/fields/CodeEditor.tsx index 074f72eb..3e417334 100644 --- a/src/js/components/SnippetForm/fields/CodeEditor.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditor.tsx @@ -1,11 +1,40 @@ import React, { useEffect, useRef } from 'react' import { __ } from '@wordpress/i18n' +import { useSubmitSnippet } from '../../../hooks/useSubmitSnippet' import { handleUnknownError } from '../../../utils/errors' import { isMacOS } from '../../../utils/screen' import { useSnippetForm } from '../../../hooks/useSnippetForm' import { CodeEditorShortcuts } from './CodeEditorShortcuts' +import type { RefObject} from 'react' + +interface EditorTextareaProps { + ref: RefObject +} + +const EditorTextarea: React.FC = ({ ref }) => { + const { snippet, setSnippet } = useSnippetForm() + + return ( +
    + - - -
    +
    ) } diff --git a/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx index 648133d9..63cf0a8c 100644 --- a/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditorShortcuts.tsx @@ -104,19 +104,20 @@ export const CodeEditorShortcuts: React.FC = ({ editor
    - {Object.entries(shortcuts).map(([name, { label, mod, key }]) => - - - - - )} + + {Object.entries(shortcuts).map(([name, { label, mod, key }]) => + + + + )} +
    {label} - {(Array.isArray(mod) ? mod : [mod]).map(modifier => - - - - )} - {KEYBOARD_KEYS[key]} -
    {label} + {(Array.isArray(mod) ? mod : [mod]).map(modifier => + + + + )} + {KEYBOARD_KEYS[key]} +
    diff --git a/src/js/components/SnippetForm/fields/DescriptionEditor.tsx b/src/js/components/SnippetForm/fields/DescriptionEditor.tsx index 0efaf5c5..d9c6b331 100644 --- a/src/js/components/SnippetForm/fields/DescriptionEditor.tsx +++ b/src/js/components/SnippetForm/fields/DescriptionEditor.tsx @@ -76,7 +76,8 @@ export const DescriptionEditor: React.FC = () => { disabled={isReadOnly} rows={window.CODE_SNIPPETS_EDIT.descEditorOptions.rows} cols={40} - >{snippet.desc} + value={snippet.desc} + />
    : null } diff --git a/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx b/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx index 2ecbf9f4..5e19bb8f 100644 --- a/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx +++ b/src/js/components/SnippetForm/fields/SnippetLocationInput.tsx @@ -3,11 +3,11 @@ import React from 'react' import Select from 'react-select' import { useSnippetForm } from '../../../hooks/useSnippetForm' import { SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' -import { getSnippetType } from '../../../utils/snippets' -import type { SnippetScope } from '../../../types/Snippet' +import { getSnippetType, isCondition } from '../../../utils/snippets/snippets' +import type { SnippetCodeScope } from '../../../types/Snippet' import type { SelectOption } from '../../../types/SelectOption' -const SCOPE_ICONS: Record = { +const SCOPE_ICONS: Record = { 'global': 'admin-site', 'admin': 'admin-tools', 'front-end': 'admin-appearance', @@ -21,7 +21,7 @@ const SCOPE_ICONS: Record = { 'site-footer-js': 'media-code' } -const SCOPE_DESCRIPTIONS: Record = { +const SCOPE_DESCRIPTIONS: Record = { 'global': __('Run everywhere', 'code-snippets'), 'admin': __('Only run in administration area', 'code-snippets'), 'front-end': __('Only run on site front-end', 'code-snippets'), @@ -38,32 +38,37 @@ const SCOPE_DESCRIPTIONS: Record = { export const SnippetLocationInput: React.FC = () => { const { snippet, setSnippet } = useSnippetForm() - const options: SelectOption[] = SNIPPET_TYPE_SCOPES[getSnippetType(snippet)] + const options: SelectOption[] = SNIPPET_TYPE_SCOPES[getSnippetType(snippet)] + .filter(scope => 'condition' !== scope) .map(scope => ({ + key: scope, value: scope, label: SCOPE_DESCRIPTIONS[scope] })) return (
    -

    - ({ ...provided, zIndex: 9999 }), + input: provided => ({ ...provided, ':focus': { boxShadow: 'none' } }) + }} + value={options.find(option => option.value === snippet.scope)} + formatOptionLabel={({ label, value }) => + <> + {` ${label}`} + + } + onChange={option => + option?.value && setSnippet(previous => ({ ...previous, scope: option.value }))} + /> + }
    ) } diff --git a/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx b/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx index c1225bb9..af4be11d 100644 --- a/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx +++ b/src/js/components/SnippetForm/fields/SnippetTypeInput.tsx @@ -4,8 +4,9 @@ import Select from 'react-select' import { useSnippetForm } from '../../../hooks/useSnippetForm' import { SNIPPET_TYPE_SCOPES } from '../../../types/Snippet' import { isLicensed } from '../../../utils/screen' -import { getSnippetType, isProType } from '../../../utils/snippets' -import type { SnippetType } from '../../../types/Snippet' +import { getSnippetType, isCondition, isProType } from '../../../utils/snippets/snippets' +import { SnippetTypeBadge } from '../../common/SnippetTypeBadge' +import type { SnippetCodeType, SnippetType } from '../../../types/Snippet' import type { SelectOption } from '../../../types/SelectOption' import type { EditorConfiguration } from 'codemirror' @@ -13,7 +14,7 @@ export interface SnippetTypeInputProps { openUpgradeDialog: VoidFunction } -const EDITOR_MODES: Record = { +const EDITOR_MODES: Record = { css: 'text/css', js: 'javascript', php: 'text/x-php', @@ -24,23 +25,23 @@ const OPTIONS: SelectOption[] = [ { value: 'php', label: __('Functions', 'code-snippets') }, { value: 'html', label: __('Content', 'code-snippets') }, { value: 'css', label: __('Styles', 'code-snippets') }, - { value: 'js', label: __('Scripts', 'code-snippets') } + { value: 'js', label: __('Scripts', 'code-snippets') }, + { value: 'cond', label: __('Conditions', 'code-snippets') } ] const SnippetTypeOption: React.FC> = ({ label, value }) =>
    {label} - {isProType(value) && !isLicensed() - && {_x('Pro', 'Upgrade to Pro', 'code-snippets')}} + {isProType(value) && !isLicensed() && + {_x('Pro', 'Upgrade to Pro', 'code-snippets')}}
    -
    {value.toUpperCase()}
    +
    export const SnippetTypeInput: React.FC = ({ openUpgradeDialog }) => { const { snippet, setSnippet, codeEditorInstance } = useSnippetForm() - - const snippetType = getSnippetType(snippet.scope) + const snippetType = getSnippetType(snippet) useEffect(() => { if (codeEditorInstance) { @@ -48,7 +49,7 @@ export const SnippetTypeInput: React.FC = ({ openUpgradeD codeEditor.setOption('lint' as keyof EditorConfiguration, 'php' === snippetType || 'css' === snippetType) - if (EDITOR_MODES[snippetType]) { + if ('cond' !== snippetType && EDITOR_MODES[snippetType]) { codeEditor.setOption('mode', EDITOR_MODES[snippetType]) codeEditor.refresh() } @@ -60,12 +61,14 @@ export const SnippetTypeInput: React.FC = ({ openUpgradeD + + return wrap ?

    {button}

    : button +} diff --git a/src/js/components/common/Tooltip.tsx b/src/js/components/common/Tooltip.tsx new file mode 100644 index 00000000..38b9c726 --- /dev/null +++ b/src/js/components/common/Tooltip.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import classnames from 'classnames' +import type { ReactNode } from 'react' + +export interface TooltipProps { + children: ReactNode + invertBlock?: boolean +} + +export const Tooltip: React.FC = ({ children, invertBlock }) => +
    + +
    + {children} +
    +
    diff --git a/src/js/hooks/useRestAPI.tsx b/src/js/hooks/useRestAPI.tsx new file mode 100644 index 00000000..95503183 --- /dev/null +++ b/src/js/hooks/useRestAPI.tsx @@ -0,0 +1,60 @@ +import React, { useMemo } from 'react' +import axios from 'axios' +import { createContextHook } from '../utils/hooks' +import { REST_API_AXIOS_CONFIG } from '../utils/restAPI' +import { buildSnippetsAPI } from '../utils/snippets/api' +import type { SnippetsAPI } from '../utils/snippets/api' +import type { PropsWithChildren } from 'react' +import type { AxiosInstance, AxiosResponse } from 'axios' + +export interface RestAPIContext { + api: RestAPI + snippetsAPI: SnippetsAPI + axiosInstance: AxiosInstance +} + +export interface RestAPI { + get: (url: string) => Promise + post: (url: string, data?: object) => Promise + put: (url: string, data?: object) => Promise + del: (url: string) => Promise +} + +const debugRequest = async ( + method: 'GET' | 'POST' | 'PUT' | 'DELETE', + url: string, + doRequest: Promise>, + data?: D +): Promise => { + console.debug(`${method} ${url}`, ...data ? [data] : []) + const response = await doRequest + console.debug('Response', response) + return response.data +} + +const buildRestAPI = (axiosInstance: AxiosInstance): RestAPI => ({ + get: (url: string): Promise => + debugRequest('GET', url, axiosInstance.get, never>(url)), + + post: (url: string, data?: object): Promise => + debugRequest('POST', url, axiosInstance.post, typeof data>(url, data), data), + + del: (url: string): Promise => + debugRequest('DELETE', url, axiosInstance.delete, never>(url)), + + put: (url: string, data?: object): Promise => + debugRequest('PUT', url, axiosInstance.put, typeof data>(url, data), data) +}) + +export const [RestAPIContext, useRestAPI] = createContextHook('RestAPI') + +export const WithRestAPIContext: React.FC = ({ children }) => { + const axiosInstance = useMemo(() => axios.create(REST_API_AXIOS_CONFIG), []) + + const api = useMemo(() => buildRestAPI(axiosInstance), [axiosInstance]) + const snippetsAPI = useMemo(() => buildSnippetsAPI(api), [api]) + + const value: RestAPIContext = { api, snippetsAPI, axiosInstance } + + return {children} +} diff --git a/src/js/hooks/useSnippetForm.tsx b/src/js/hooks/useSnippetForm.tsx index d67bad5e..458b9f39 100644 --- a/src/js/hooks/useSnippetForm.tsx +++ b/src/js/hooks/useSnippetForm.tsx @@ -1,41 +1,28 @@ import { isAxiosError } from 'axios' -import React, { createContext, useCallback, useContext, useMemo, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' +import { createContextHook } from '../utils/hooks' import { isLicensed } from '../utils/screen' -import { isProSnippet } from '../utils/snippets' -import { useSnippetSubmit } from './useSnippetSubmit' -import type { Dispatch, PropsWithChildren, SetStateAction} from 'react' +import { isProSnippet } from '../utils/snippets/snippets' +import type { Dispatch, PropsWithChildren, SetStateAction } from 'react' import type { ScreenNotice } from '../types/ScreenNotice' import type { Snippet } from '../types/Snippet' import type { CodeEditorInstance } from '../types/WordPressCodeEditor' export interface SnippetFormContext { snippet: Snippet + isWorking: boolean + isReadOnly: boolean setSnippet: Dispatch> updateSnippet: Dispatch> - isReadOnly: boolean - isWorking: boolean setIsWorking: Dispatch> currentNotice: ScreenNotice | undefined setCurrentNotice: Dispatch> codeEditorInstance: CodeEditorInstance | undefined - setCodeEditorInstance: Dispatch> handleRequestError: (error: unknown, message?: string) => void - submitSnippet: () => Promise - submitAndActivateSnippet: () => Promise - submitAndDeactivateSnippet: () => Promise + setCodeEditorInstance: Dispatch> } -const SnippetFormContext = createContext(undefined) - -export const useSnippetForm = () => { - const value = useContext(SnippetFormContext) - - if (value === undefined) { - throw Error('useSnippetForm can only be used within a SnippetForm context provider') - } - - return value -} +export const [SnippetFormContext, useSnippetForm] = createContextHook('SnippetForm') export interface WithSnippetFormContextProps extends PropsWithChildren { initialSnippet: () => Snippet @@ -46,8 +33,8 @@ export const WithSnippetFormContext: React.FC = ({ const [isWorking, setIsWorking] = useState(false) const [currentNotice, setCurrentNotice] = useState() const [codeEditorInstance, setCodeEditorInstance] = useState() - const submitSnippet = useSnippetSubmit(setSnippet, setIsWorking, setCurrentNotice) - const isReadOnly = useMemo(() => !isLicensed() && isProSnippet(snippet.scope), [snippet.scope]) + + const isReadOnly = useMemo(() => !isLicensed() && isProSnippet({ scope: snippet.scope }), [snippet.scope]) const handleRequestError = useCallback((error: unknown, message?: string) => { console.error('Request failed', error) @@ -66,19 +53,16 @@ export const WithSnippetFormContext: React.FC = ({ const value: SnippetFormContext = { snippet, - setSnippet, - updateSnippet, - isReadOnly, isWorking, + isReadOnly, + setSnippet, setIsWorking, + updateSnippet, currentNotice, setCurrentNotice, codeEditorInstance, - setCodeEditorInstance, handleRequestError, - submitSnippet: () => submitSnippet(snippet), - submitAndActivateSnippet: () => submitSnippet(snippet, true), - submitAndDeactivateSnippet: () => submitSnippet(snippet, false) + setCodeEditorInstance } return {children} diff --git a/src/js/hooks/useSnippetsList.tsx b/src/js/hooks/useSnippetsList.tsx new file mode 100644 index 00000000..06325cc4 --- /dev/null +++ b/src/js/hooks/useSnippetsList.tsx @@ -0,0 +1,42 @@ +import React, { useCallback, useEffect, useState } from 'react' +import { createContextHook } from '../utils/hooks' +import { isNetworkAdmin } from '../utils/screen' +import { useRestAPI } from './useRestAPI' +import type { PropsWithChildren } from 'react' +import type { Snippet } from '../types/Snippet' + +export interface SnippetsListContext { + snippetsList: readonly Snippet[] | undefined + refreshSnippetsList: () => Promise +} + +const [SnippetsListContext, useSnippetsList] = createContextHook('SnippetsList') + +export const WithSnippetsListContext: React.FC = ({ children }) => { + const { snippetsAPI: { fetchAll } } = useRestAPI() + const [snippetsList, setSnippetsList] = useState() + + const refreshSnippetsList = useCallback(async (): Promise => { + try { + console.info('Fetching snippets list') + const response = await fetchAll(isNetworkAdmin()) + setSnippetsList(response) + } catch (error: unknown) { + console.error('Error fetching snippets list', error) + } + }, [fetchAll]) + + useEffect(() => { + refreshSnippetsList() + .catch(() => undefined) + }, [refreshSnippetsList]) + + const value: SnippetsListContext = { + snippetsList, + refreshSnippetsList + } + + return {children} +} + +export { useSnippetsList } diff --git a/src/js/hooks/useSubmitSnippet.ts b/src/js/hooks/useSubmitSnippet.ts new file mode 100644 index 00000000..48e4fe7d --- /dev/null +++ b/src/js/hooks/useSubmitSnippet.ts @@ -0,0 +1,122 @@ +import { __ } from '@wordpress/i18n' +import { addQueryArgs } from '@wordpress/url' +import { isAxiosError } from 'axios' +import { useCallback } from 'react' +import { createSnippetObject, isCondition } from '../utils/snippets/snippets' +import { useRestAPI } from './useRestAPI' +import { useSnippetForm } from './useSnippetForm' +import type { Snippet } from '../types/Snippet' + +const snippetMessages = { + addNew: __('Add New Snippet', 'code-snippets'), + edit: __('Edit Snippet', 'code-snippets'), + created: __('Snippet created.', 'code-snippets'), + updated: __('Snippet updated.', 'code-snippets'), + createdActivated: __('Snippet created and activated.', 'code-snippets'), + updatedActivated: __('Snippet updated and activated.', 'code-snippets'), + updatedDeactivated: __('Snippet updated and deactivated'), + updatedExecuted: __('Snippet updated and executed.', 'code-snippets'), + failedCreate: __('Could not create snippet.', 'code-snippets'), + failedUpdate: __('Could not update snippet.', 'code-snippets') +} + +const conditionMessages: typeof snippetMessages = { + addNew: __('Add New Condition', 'code-snippets'), + edit: __('Edit Condition', 'code-snippets'), + created: __('Condition created.', 'code-snippets'), + updated: __('Condition updated.', 'code-snippets'), + createdActivated: __('Condition created and activated', 'code-snippets'), + updatedActivated: __('Condition updated and activated.', 'code-snippets'), + updatedDeactivated: __('Condition updated and deactivated'), + updatedExecuted: snippetMessages.updatedExecuted, + failedCreate: __('Could not create condition.', 'code-snippets'), + failedUpdate: __('Could not update condition.', 'code-snippets') +} + +export enum SubmitSnippetAction { + SAVE = 'save_snippet', + SAVE_AND_ACTIVATE = 'save_snippet_activate', + SAVE_AND_EXECUTE = 'save_snippet_execute', + SAVE_AND_DEACTIVATE = 'save_snippet_deactivate' +} + +const getSuccessNotice = (request: Snippet, response: Snippet, action: SubmitSnippetAction): string => { + const messages = 'condition' === request.scope ? conditionMessages : snippetMessages + const wasCreated = 0 === request.id + + switch (action) { + case SubmitSnippetAction.SAVE: + return wasCreated ? messages.created : messages.updated + + case SubmitSnippetAction.SAVE_AND_EXECUTE: + return messages.updatedExecuted + + case SubmitSnippetAction.SAVE_AND_ACTIVATE: + if ('single-use' === response.scope) { + return messages.updatedExecuted + } else { + return wasCreated + ? messages.createdActivated + : messages.updatedActivated + } + + case SubmitSnippetAction.SAVE_AND_DEACTIVATE: + return messages.updatedDeactivated + } +} + +const SUBMIT_ACTION_DELTA: Record> = { + [SubmitSnippetAction.SAVE]: {}, + [SubmitSnippetAction.SAVE_AND_ACTIVATE]: { active: true }, + [SubmitSnippetAction.SAVE_AND_DEACTIVATE]: { active: false }, + [SubmitSnippetAction.SAVE_AND_EXECUTE]: { active: true } +} + +export interface UseSubmitSnippet { + submitSnippet: (action?: SubmitSnippetAction) => Promise +} + +export const useSubmitSnippet = (): UseSubmitSnippet => { + const { snippetsAPI } = useRestAPI() + const { setIsWorking, setCurrentNotice, snippet, setSnippet } = useSnippetForm() + + const submitSnippet = useCallback(async (action: SubmitSnippetAction = SubmitSnippetAction.SAVE) => { + setCurrentNotice(undefined) + + const result = await (async (): Promise => { + try { + const request: Snippet = { ...snippet, ...SUBMIT_ACTION_DELTA[action] } + const response = await (0 === request.id ? snippetsAPI.create(request) : snippetsAPI.update(request)) + setIsWorking(false) + return response.id ? response : undefined + } catch (error) { + setIsWorking(false) + return isAxiosError(error) ? error.message : undefined + } + })() + + const messages = isCondition(snippet) ? conditionMessages : snippetMessages + + if (undefined === result || 'string' === typeof result) { + const message = [ + snippet.id ? messages.failedCreate : messages.failedUpdate, + result ?? __('The server did not send a valid response.', 'code-snippets') + ] + + setCurrentNotice(['error', message.filter(Boolean).join(' ')]) + return undefined + } else { + setSnippet(createSnippetObject(result)) + setCurrentNotice(['updated', getSuccessNotice(snippet, result, action)]) + + if (snippet.id && result.id) { + window.document.title = window.document.title.replace(snippetMessages.addNew, messages.edit) + window.history.replaceState({}, '', addQueryArgs(window.CODE_SNIPPETS?.urls.edit, { id: result.id })) + } + + return result + } + }, [snippetsAPI, setIsWorking, setCurrentNotice, snippet, setSnippet]) + + return { submitSnippet } +} diff --git a/src/js/types/ConditionGroups.ts b/src/js/types/ConditionGroups.ts new file mode 100644 index 00000000..0020aa7f --- /dev/null +++ b/src/js/types/ConditionGroups.ts @@ -0,0 +1,9 @@ + +export type ConditionGroups = Record +export type ConditionGroup = Record + +export interface ConditionRule { + readonly subject?: string + readonly operator?: string + readonly object?: string +} diff --git a/src/js/types/SelectOption.ts b/src/js/types/SelectOption.ts index f7674758..7fb5f5e8 100644 --- a/src/js/types/SelectOption.ts +++ b/src/js/types/SelectOption.ts @@ -1,6 +1,7 @@ import type { GroupBase, Options, OptionsOrGroups } from 'react-select' export interface SelectOption { + readonly key?: string | number readonly value: T readonly label: string } diff --git a/src/js/types/Snippet.ts b/src/js/types/Snippet.ts index f5518743..20761992 100644 --- a/src/js/types/Snippet.ts +++ b/src/js/types/Snippet.ts @@ -1,3 +1,5 @@ +import type { ConditionGroups } from './ConditionGroups' + export interface Snippet { readonly id: number readonly name: string @@ -7,19 +9,24 @@ export interface Snippet { readonly scope: SnippetScope readonly priority: number readonly active: boolean - readonly network?: boolean + readonly network: boolean readonly shared_network?: boolean | null readonly modified?: string - readonly code_error?: [string, number] | null + readonly conditionId: number + readonly code_error?: readonly [string, number] | null + readonly conditions: ConditionGroups } -export type SnippetType = 'php' | 'html' | 'css' | 'js' +export type SnippetCodeType = 'php' | 'html' | 'css' | 'js' +export type SnippetType = SnippetCodeType | 'cond' +export type SnippetCodeScope = typeof SNIPPET_TYPE_SCOPES[SnippetCodeType][number] export type SnippetScope = typeof SNIPPET_TYPE_SCOPES[SnippetType][number] export const SNIPPET_TYPE_SCOPES = { php: ['global', 'admin', 'front-end', 'single-use'], html: ['content', 'head-content', 'footer-content'], css: ['admin-css', 'site-css'], - js: ['site-head-js', 'site-footer-js'] + js: ['site-head-js', 'site-footer-js'], + cond: ['condition'] } diff --git a/src/js/types/Window.ts b/src/js/types/Window.ts index 39e68443..5f589f0b 100644 --- a/src/js/types/Window.ts +++ b/src/js/types/Window.ts @@ -22,6 +22,7 @@ declare global { restAPI: { base: string snippets: string + conditions: string cloud: string nonce: string localToken: string diff --git a/src/js/types/schema/SnippetSchema.ts b/src/js/types/schema/SnippetSchema.ts new file mode 100644 index 00000000..25402000 --- /dev/null +++ b/src/js/types/schema/SnippetSchema.ts @@ -0,0 +1,17 @@ +import type { SnippetScope } from '../Snippet' + +export interface SnippetSchema { + readonly id: number + name: string + desc: string + code: string + tags: string[] + scope: SnippetScope + priority: number + active: boolean + network: boolean + condition_id?: number + shared_network?: boolean | null + readonly modified?: string + readonly code_error?: readonly [string, number] | null +} diff --git a/src/js/types/schema/SnippetsExport.ts b/src/js/types/schema/SnippetsExport.ts new file mode 100644 index 00000000..8cb8239f --- /dev/null +++ b/src/js/types/schema/SnippetsExport.ts @@ -0,0 +1,7 @@ +import type { Snippet } from '../Snippet' + +export interface SnippetsExport { + generator: string + date_created: string + snippets: Snippet[] +} diff --git a/src/js/types/wp/Page.ts b/src/js/types/wp/Page.ts deleted file mode 100644 index 437aa76e..00000000 --- a/src/js/types/wp/Page.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Post } from './Post' - -export const PAGES_ENDPOINT = '/wp/v2/pages' - -export interface Page extends Omit { - parent: number - menu_order: number -} - -export type Pages = Page[] diff --git a/src/js/types/wp/Post.ts b/src/js/types/wp/Post.ts deleted file mode 100644 index 52b2e836..00000000 --- a/src/js/types/wp/Post.ts +++ /dev/null @@ -1,43 +0,0 @@ -export const POSTS_ENDPOINT = '/wp/v2/posts' - -export interface Post { - id: number - date: string | null - date_gmt?: string | null - readonly guid?: { rendered: string } - link: string - modified?: string - modified_gmt?: string - slug: string - status?: PostStatus - readonly type: string - password?: string - readonly permalink_template?: string - readonly generated_slug?: string - title: { rendered: string } - content?: { - rendered: string - protected: boolean - } - excerpt: { - rendered: string - protected: false - } - author: number - featured_media: number - comment_status?: 'open' | 'closed' - ping_status?: 'open' | 'closed' - format?: PostFormat - meta?: Record - sticky?: boolean - template?: string - categories?: number[] - tags?: number[] -} - -export type PostStatus = 'publish' | 'future' | 'draft' | 'pending' | 'private' - -export type PostFormat = - 'standard' | 'aside' | 'chat' | 'gallery' | 'link' | 'image' | 'quote' | 'status' | 'video' | 'audio' - -export type Posts = Post[] diff --git a/src/js/types/wp/PostType.ts b/src/js/types/wp/PostType.ts deleted file mode 100644 index 2c44d3d8..00000000 --- a/src/js/types/wp/PostType.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const POST_TYPES_ENDPOINT = '/wp/v2/types' - -export interface PostType { - description: string - hierarchical: boolean - has_archive: boolean - name: string - slug: string - icon: string - taxonomies: string[] - rest_base: string - rest_namespace: string -} - -export type PostTypes = Record diff --git a/src/js/types/wp/Term.ts b/src/js/types/wp/Term.ts deleted file mode 100644 index 23c94dfd..00000000 --- a/src/js/types/wp/Term.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const TAGS_ENDPOINT = '/wp/v2/tags' -export const CATEGORIES_ENDPOINT = '/wp/v2/categories' - -export interface Term { - id: number - count: number - description: string - link: string - name: string - slug: string - taxonomy: Taxonomy - meta: Record -} - -export type PostTag = Term - -export interface Category extends Term { - parent: number -} - -export type Taxonomy = 'category' | 'post_tag' | 'nav_menu' | 'link_category' | 'post_format' - -export type Terms = Term[] -export type PostTags = PostTag[] -export type Categories = Category[] diff --git a/src/js/types/wp/User.ts b/src/js/types/wp/User.ts deleted file mode 100644 index 265bf0aa..00000000 --- a/src/js/types/wp/User.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface User { - readonly id: number - username?: string - name: string - first_name?: string - last_name?: string - email?: string - url: string - description: string - readonly link: string - locale?: string - nickname?: string - slug: string - registered_date?: string - roles?: string[] - readonly capabilities?: Record - readonly extra_capabilities?: Record - readonly avatar_urls: Record - meta: Record -} - -export type Users = User[] diff --git a/src/js/utils/files.ts b/src/js/utils/files.ts index 30617a1b..f0a54880 100644 --- a/src/js/utils/files.ts +++ b/src/js/utils/files.ts @@ -1,4 +1,4 @@ -import { getSnippetType } from './snippets' +import { getSnippetType } from './snippets/snippets' import type { Snippet } from '../types/Snippet' const SECOND_IN_MS = 1000 @@ -9,6 +9,7 @@ const MIME_INFO = { html: ['php', 'text/php'], css: ['css', 'text/css'], js: ['js', 'text/javascript'], + cond: ['json', 'application/json'], json: ['json', 'application/json'] } @@ -26,7 +27,7 @@ export const downloadSnippetExportFile = ( { id, name, scope }: Snippet, type?: keyof typeof MIME_INFO ) => { - const [ext, mimeType] = MIME_INFO[type ?? getSnippetType(scope)] + const [ext, mimeType] = MIME_INFO[type ?? getSnippetType({ scope })] const sanitizedName = name.toLowerCase().replace(/[^\w-]+/g, '-').trim() diff --git a/src/js/utils/hooks.ts b/src/js/utils/hooks.ts new file mode 100644 index 00000000..3a52d8c2 --- /dev/null +++ b/src/js/utils/hooks.ts @@ -0,0 +1,21 @@ +import { createContext, useContext } from 'react' +import type { Context } from 'react' + +export const createContextHook = (name: string): [ + Context, + () => T +] => { + const contextValue = createContext(undefined) + + const useContextHook = (): T => { + const value = useContext(contextValue) + + if (value === undefined) { + throw Error(`use${name} can only be used within a ${name} context provider.`) + } + + return value + } + + return [contextValue, useContextHook] +} diff --git a/src/js/utils/options.ts b/src/js/utils/options.ts new file mode 100644 index 00000000..e73c8a43 --- /dev/null +++ b/src/js/utils/options.ts @@ -0,0 +1,28 @@ +import type { SelectGroup, SelectOption } from '../types/SelectOption' + +export const buildOptionGroups = ({ groups, items, getGroup, buildOption }: { + items: T[], + groups: Record, + getGroup: (item: T) => G, + buildOption: (item: T) => SelectOption | undefined +}): SelectGroup[] => { + const optionGroups = new Map[]> + + for (const item of items) { + const option = buildOption(item) + + if (option) { + const group = getGroup(item) + const optionGroup = optionGroups.get(group) + + if (optionGroup) { + optionGroup.push(option) + } else { + optionGroups.set(group, [option]) + } + } + } + + return [...optionGroups].map(([group, options]) => + ({ label: groups[group], options })) +} diff --git a/src/js/utils/restAPI.ts b/src/js/utils/restAPI.ts index 17275c12..1a6b14e8 100644 --- a/src/js/utils/restAPI.ts +++ b/src/js/utils/restAPI.ts @@ -1,6 +1,7 @@ import { trimTrailingChar } from './text' import type { AxiosRequestConfig } from 'axios' +export const REST_BASE = trimTrailingChar(window.CODE_SNIPPETS?.restAPI.base ?? '', '/') export const REST_SNIPPETS_BASE = trimTrailingChar(window.CODE_SNIPPETS?.restAPI.snippets ?? '', '/') export const REST_API_AXIOS_CONFIG: AxiosRequestConfig = { diff --git a/src/js/utils/snippets/api.ts b/src/js/utils/snippets/api.ts new file mode 100644 index 00000000..ca606705 --- /dev/null +++ b/src/js/utils/snippets/api.ts @@ -0,0 +1,73 @@ +import { addQueryArgs } from '@wordpress/url' +import { REST_SNIPPETS_BASE } from '../restAPI' +import { createSnippetObject } from './snippets' +import type { RestAPI } from '../../hooks/useRestAPI' +import type { SnippetSchema } from '../../types/schema/SnippetSchema' +import type { Snippet } from '../../types/Snippet' +import type { SnippetsExport } from '../../types/schema/SnippetsExport' + +export interface SnippetsAPI { + fetchAll: (network?: boolean | null) => Promise + fetch: (snippetId: number, network?: boolean | null) => Promise + create: (snippet: Snippet) => Promise + update: (snippet: Snippet) => Promise + delete: (snippet: Pick) => Promise + activate: (snippet: Pick) => Promise + deactivate: (snippet: Pick) => Promise + export: (snippet: Pick) => Promise + exportCode: (snippet: Pick) => Promise + attach: (snippet: Pick) => Promise + detach: (snippet: Pick) => Promise +} + +const buildURL = ({ id, network }: Pick, action?: string) => + addQueryArgs( + [REST_SNIPPETS_BASE, id, action].filter(Boolean).join('/'), + { network: network ? true : undefined } + ) + +const mapToSchema = ({ conditionId, ...fields }: Snippet): SnippetSchema => ({ + condition_id: conditionId, + ...fields +}) + +export const buildSnippetsAPI = ({ get, post, del, put }: RestAPI): SnippetsAPI => ({ + fetchAll: network => + get(addQueryArgs(REST_SNIPPETS_BASE, { network })) + .then(response => response.map(createSnippetObject)), + + fetch: (snippetId, network) => + get(addQueryArgs(`${REST_SNIPPETS_BASE}/${snippetId}`, { network })) + .then(createSnippetObject), + + create: snippet => + post(REST_SNIPPETS_BASE, mapToSchema(snippet)) + .then(createSnippetObject), + + update: snippet => + post(snippet.id ? buildURL(snippet) : REST_SNIPPETS_BASE, mapToSchema(snippet)) + .then(createSnippetObject), + + delete: snippet => + del(buildURL(snippet)), + + activate: snippet => + post(buildURL(snippet, 'activate')) + .then(createSnippetObject), + + deactivate: snippet => + post(buildURL(snippet, 'deactivate')) + .then(createSnippetObject), + + export: snippet => + get(buildURL(snippet, 'export')), + + exportCode: snippet => + get(buildURL(snippet, 'export-code')), + + attach: snippet => + put(buildURL(snippet, 'attach'), { condition_id: snippet.conditionId }), + + detach: snippet => + put(buildURL(snippet, 'detach')) +}) diff --git a/src/js/utils/snippets/objects.ts b/src/js/utils/snippets/objects.ts new file mode 100644 index 00000000..37e0e50a --- /dev/null +++ b/src/js/utils/snippets/objects.ts @@ -0,0 +1,73 @@ +import { SNIPPET_TYPE_SCOPES } from '../../types/Snippet' +import type { Snippet, SnippetScope } from '../../types/Snippet' +import type { ConditionGroups } from '../../types/ConditionGroups' + +const isAbsInt = (value: unknown): value is number => + 'number' === typeof value && 0 < value + +const parseStringArray = (value: unknown): string[] | undefined => + Array.isArray(value) ? value.filter(entry => 'string' === typeof entry) : undefined + +export const isValidScope = (scope: unknown): scope is SnippetScope => + 'string' === typeof scope && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes => + typeScopes.some(typeScope => typeScope === scope)) + +const isValidCondition = (condition: unknown): condition is ConditionGroups => + 'object' === typeof condition && null !== condition && Object.values(condition) + .every((group: unknown) => 'object' === typeof group && null !== group && + Object.values(group).every((rule: unknown) => 'object' === typeof rule && null !== rule)) + +const generateObjectKeys = (items: Record, transformItem?: (item: T) => T): Record => + Object.fromEntries( + Object.values(items).map((item, index) => + [`_${index}`, transformItem ? transformItem(item) : item] + )) + +const parseConditionGroups = (condition: ConditionGroups) => + generateObjectKeys(condition, group => group && generateObjectKeys(group)) + +const parseConditions = (parsed: Snippet, fields: object): Partial => { + if ('conditions' in fields && isValidCondition(fields.conditions)) { + return { conditions: parseConditionGroups(fields.conditions) } + } + + if ('condition' === parsed.scope && '' !== parsed.code.trim()) { + try { + const parsedRules: unknown = JSON.parse(parsed.code) + + if (isValidCondition(parsedRules)) { + return { conditions: parseConditionGroups(parsedRules), code: '' } + } + } catch (error) { + console.error('Failed to parse condition rules JSON.', parsed.code, error) + } + } + + return {} +} + +export const parseSnippetObject = (fields: unknown, defaults: Snippet): Snippet => { + if ('object' !== typeof fields || null === fields) { + return defaults + } + + const parsed: Snippet = { + id: 'id' in fields && isAbsInt(fields.id) ? fields.id : defaults.id, + name: 'name' in fields && 'string' === typeof fields.name ? fields.name : defaults.name, + desc: 'desc' in fields && 'string' === typeof fields.desc ? fields.desc : defaults.desc, + code: 'code' in fields && 'string' === typeof fields.code ? fields.code : defaults.code, + tags: 'tags' in fields ? parseStringArray(fields.tags) ?? defaults.tags : defaults.tags, + scope: 'scope' in fields && isValidScope(fields.scope) ? fields.scope : defaults.scope, + modified: 'modified' in fields && 'string' === typeof fields.modified ? fields.modified : defaults.modified, + active: 'active' in fields && 'boolean' === typeof fields.active ? fields.active : defaults.active, + network: 'network' in fields && 'boolean' === typeof fields.network ? fields.network : defaults.network, + shared_network: 'shared_network' in fields && 'boolean' === typeof fields.shared_network && fields.shared_network || + defaults.shared_network, + priority: 'priority' in fields && 'number' === typeof fields.priority ? fields.priority : defaults.priority, + conditionId: 'condition_id' in fields && isAbsInt(fields.condition_id) ? fields.condition_id + : 'conditionId' in fields && isAbsInt(fields.conditionId) ? fields.conditionId : defaults.conditionId, + conditions: defaults.conditions + } + + return { ...parsed, ...parseConditions(parsed, fields) } +} diff --git a/src/js/utils/snippets/snippets.ts b/src/js/utils/snippets/snippets.ts new file mode 100644 index 00000000..fa23b233 --- /dev/null +++ b/src/js/utils/snippets/snippets.ts @@ -0,0 +1,73 @@ +import { __ } from '@wordpress/i18n' +import { isNetworkAdmin } from '../screen' +import { parseSnippetObject } from './objects' +import type { Snippet, SnippetType } from '../../types/Snippet' + +const PRO_TYPES = new Set(['css', 'js']) + +const defaults: Omit = { + id: 0, + name: '', + code: '', + desc: '', + scope: 'global', + modified: '', + active: false, + network: isNetworkAdmin(), + shared_network: null, + priority: 10, + conditionId: 0 +} + +export const createSnippetObject = (fields: unknown = null): Snippet => + parseSnippetObject(fields, { ...defaults, tags: [], conditions: {} }) + +export const getSnippetType = ({ scope }: Pick): SnippetType => { + switch (true) { + case scope.endsWith('-css'): + return 'css' + + case scope.endsWith('-js'): + return 'js' + + case scope.endsWith('content'): + return 'html' + + case 'condition' === scope: + return 'cond' + + default: + return 'php' + } +} + +export const validateSnippet = (snippet: Snippet): undefined | string => { + const missingTitle = '' === snippet.name.trim() + + const missingCode = isCondition(snippet) + ? !snippet.conditions + : '' === snippet.code.trim() + + switch (true) { + case missingCode && missingTitle: + return __('This snippet has no code or title.', 'code-snippets') + + case missingCode: + return __('This snippet has no snippet code.', 'code-snippets') + + case missingTitle: + return __('This snippet has no title.', 'code-snippets') + + default: + return undefined + } +} + +export const isCondition = (snippet: Pick): boolean => + 'condition' === snippet.scope + +export const isProSnippet = (snippet: Pick): boolean => + PRO_TYPES.has(getSnippetType(snippet)) + +export const isProType = (type: SnippetType): boolean => + PRO_TYPES.has(type) diff --git a/src/js/utils/text.ts b/src/js/utils/text.ts index aa483698..5ad2a41b 100644 --- a/src/js/utils/text.ts +++ b/src/js/utils/text.ts @@ -14,3 +14,8 @@ export const truncateWords = (text: string, wordCount: number): string => { ? `${words.slice(0, wordCount).join(' ')}…` : text } + +export const stripTags = (text: string): string => + text + .replace(/|<\?(?:php)?[\s\S]*?\?>/gi, '') + .replace(/<\/?[a-z][a-z0-9]*\b[^>]*>/gi, '') diff --git a/src/php/admin-menus/class-edit-menu.php b/src/php/admin-menus/class-edit-menu.php index 9b89b5fc..955fedde 100644 --- a/src/php/admin-menus/class-edit-menu.php +++ b/src/php/admin-menus/class-edit-menu.php @@ -132,6 +132,7 @@ public function load_snippet_data() { 'css' => 'site-css', 'html' => 'content', 'js' => 'site-head-js', + 'cond' => 'condition', ]; if ( isset( $default_scopes[ $type ] ) ) { diff --git a/src/php/admin-menus/class-manage-menu.php b/src/php/admin-menus/class-manage-menu.php index 4e90b8b2..997ae65d 100644 --- a/src/php/admin-menus/class-manage-menu.php +++ b/src/php/admin-menus/class-manage-menu.php @@ -83,7 +83,7 @@ public function register() { * @return void */ public function register_upgrade_menu() { - if ( get_setting( 'general', 'hide_upgrade_menu' ) ) { + if ( code_snippets()->licensing->is_licensed() || get_setting( 'general', 'hide_upgrade_menu' ) ) { return; } @@ -183,7 +183,7 @@ public function load() { } /** - * Enqueue scripts and stylesheets for the admin page + * Enqueue scripts and stylesheets for the admin page. */ public function enqueue_assets() { $plugin = code_snippets(); diff --git a/src/php/admin-menus/class-settings-menu.php b/src/php/admin-menus/class-settings-menu.php index aa87d4dc..1b41dbe2 100644 --- a/src/php/admin-menus/class-settings-menu.php +++ b/src/php/admin-menus/class-settings-menu.php @@ -175,6 +175,9 @@ protected function do_settings_tabs() { echo '

    '; foreach ( $sections as $section ) { + if ( 'license' === $section['id'] ) { + continue; + } if ( $section['title'] ) { printf( diff --git a/src/php/class-active-snippets.php b/src/php/class-active-snippets.php index e60929e7..81b01d4b 100644 --- a/src/php/class-active-snippets.php +++ b/src/php/class-active-snippets.php @@ -2,6 +2,9 @@ namespace Code_Snippets; +use MatthiasMullie\Minify; +use WP_Exception; + /** * Class for loading active snippets of various types. * diff --git a/src/php/class-admin.php b/src/php/class-admin.php index ad1e277b..c78d6252 100644 --- a/src/php/class-admin.php +++ b/src/php/class-admin.php @@ -341,13 +341,11 @@ public static function render_snippet_type_tab( string $type_name, string $label ); } - if ( 'all' === $type_name ) { - $label_class = 'all-snippets-label'; - } else { - $label_class = 'snippet-label'; - } - - echo '', esc_html( $label ), ''; + printf( + '%s', + esc_attr( 'all' === $type_name ? 'all-snippets-label' : 'snippet-label' ), + esc_html( $label ) + ); switch ( $type_name ) { case 'all': diff --git a/src/php/class-data-item.php b/src/php/class-data-item.php index 25cfe75b..31d090b5 100644 --- a/src/php/class-data-item.php +++ b/src/php/class-data-item.php @@ -2,6 +2,8 @@ namespace Code_Snippets; +use WP_Exception; + /** * Base class for representing an item of data without needing to use direct access or individual getter and setter functions. * @@ -134,6 +136,8 @@ public function __isset( string $field ) { * @param string $field The field name. * * @return mixed The field value + * + * @throws WP_Exception If the field name is not allowed. */ public function __get( string $field ) { $field = $this->resolve_field_name( $field ); @@ -143,10 +147,10 @@ public function __get( string $field ) { } if ( ! $this->is_allowed_field( $field ) ) { - if ( WP_DEBUG ) { - $message = sprintf( 'Trying to access invalid property on "%s" class: %s', get_class( $this ), $field ); - // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error - trigger_error( esc_html( $message ), E_USER_WARNING ); + if ( function_exists( 'wp_trigger_error' ) ) { + // translators: 1: class name, 2: field name. + $message = sprintf( 'Trying to access invalid property on "%1$s" class: %2$s', get_class( $this ), $field ); + wp_trigger_error( __FUNCTION__, $message, E_USER_WARNING ); } return null; @@ -160,15 +164,17 @@ public function __get( string $field ) { * * @param string $field The field name. * @param mixed $value The field value. + * + * @throws WP_Exception If the field name is not allowed. */ public function __set( string $field, $value ) { $field = $this->resolve_field_name( $field ); if ( ! $this->is_allowed_field( $field ) ) { - if ( WP_DEBUG ) { + if ( function_exists( 'wp_trigger_error' ) ) { + // translators: 1: class name, 2: field name. $message = sprintf( 'Trying to set invalid property on "%s" class: %s', get_class( $this ), $field ); - // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error - trigger_error( esc_html( $message ), E_USER_ERROR ); + wp_trigger_error( __FUNCTION__, $message, E_USER_ERROR ); } return; diff --git a/src/php/class-db.php b/src/php/class-db.php index 64ea5f47..217ce636 100644 --- a/src/php/class-db.php +++ b/src/php/class-db.php @@ -168,17 +168,18 @@ public static function create_table( string $table_name ): bool { /* Create the database table */ $sql = "CREATE TABLE $table_name ( - id BIGINT(20) NOT NULL AUTO_INCREMENT, - name TINYTEXT NOT NULL, - description TEXT NOT NULL, - code LONGTEXT NOT NULL, - tags LONGTEXT NOT NULL, - scope VARCHAR(15) NOT NULL DEFAULT 'global', - priority SMALLINT NOT NULL DEFAULT 10, - active TINYINT(1) NOT NULL DEFAULT 0, - modified DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - revision BIGINT(20) NOT NULL DEFAULT 1, - cloud_id VARCHAR(255) NULL, + id BIGINT(20) NOT NULL AUTO_INCREMENT, + name TINYTEXT NOT NULL, + description TEXT NOT NULL, + code LONGTEXT NOT NULL, + tags LONGTEXT NOT NULL, + scope VARCHAR(15) NOT NULL DEFAULT 'global', + condition_id BIGINT(20) NOT NULL DEFAULT 0, + priority SMALLINT NOT NULL DEFAULT 10, + active TINYINT(1) NOT NULL DEFAULT 0, + modified DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + revision BIGINT(20) NOT NULL DEFAULT 1, + cloud_id VARCHAR(255) NULL, PRIMARY KEY (id), KEY scope (scope), KEY active (active) diff --git a/src/php/class-list-table.php b/src/php/class-list-table.php index b864c589..330b18c0 100644 --- a/src/php/class-list-table.php +++ b/src/php/class-list-table.php @@ -216,14 +216,65 @@ public function get_action_link( string $action, Snippet $snippet ): string { return wp_nonce_url( $url, 'code_snippets_manage_snippet_' . $snippet->id ); } + /** + * Build the cloud action links for a snippet. + * + * @param Snippet $snippet Current snippet. + * @param Cloud_Link|null $cloud_link Snippet cloud link, if available. + * + * @return array The action links HTML. + */ + private function get_cloud_action_links( Snippet $snippet, ?Cloud_Link $cloud_link ): array { + if ( $snippet->is_condition() ) { + return []; + } + + if ( ! $this->is_cloud_connected ) { + return [ + 'cloud' => sprintf( + '%s', + esc_url( add_query_arg( 'connect-authorise-cloud', true, code_snippets()->get_menu_url( 'settings' ) ) ), + esc_html__( 'Set up cloud', 'code-snippets' ) + ), + ]; + } + + if ( ! $cloud_link || ! $cloud_link->in_codevault ) { + return [ + 'cloud' => sprintf( + '%s', + esc_url( $this->get_action_link( 'cloud', $snippet ) ), + esc_html__( 'Sync to Codevault', 'code-snippets' ) + ), + ]; + } + + $unlink_action = sprintf( + '%s', + esc_url( $this->get_action_link( 'unsync-cloud', $snippet ) ), + esc_html__( 'Unlink from Cloud', 'code-snippets' ) + ); + + return $cloud_link->update_available + ? [ + 'cloud' => $unlink_action, + 'cloud_update' => sprintf( + '%s', + esc_html__( 'Update Available', 'code-snippets' ) + ), + ] + : [ 'cloud' => $unlink_action ]; + } + /** * Build a list of action links for individual snippets * - * @param Snippet $snippet The current snippet. + * @param Snippet $snippet The current snippet. + * @param Cloud_Link|null $cloud_link Snippet cloud link, if available. * * @return array The action links HTML. */ - private function get_snippet_action_links( Snippet $snippet ): array { + private function get_snippet_action_links( Snippet $snippet, ?Cloud_Link $cloud_link ): array { $actions = array(); if ( ! $this->is_network && $snippet->network && ! $snippet->shared_network ) { @@ -275,28 +326,40 @@ protected function column_activate( Snippet $snippet ): string { return ''; } - if ( 'single-use' === $snippet->scope ) { - $class = 'snippet-execution-button'; - $action = 'run-once'; - $label = esc_html__( 'Run Once', 'code-snippets' ); - } else { - $class = 'snippet-activation-switch'; - $action = $snippet->active ? 'deactivate' : 'activate'; - $label = $snippet->network && ! $snippet->shared_network ? - ( $snippet->active ? __( 'Network Deactivate', 'code-snippets' ) : __( 'Network Activate', 'code-snippets' ) ) : - ( $snippet->active ? __( 'Deactivate', 'code-snippets' ) : __( 'Activate', 'code-snippets' ) ); + switch ( $snippet->scope ) { + case 'single-use': + $class = 'snippet-execution-button'; + $action = 'run-once'; + $label = esc_html__( 'Run Once', 'code-snippets' ); + break; + + case 'condition': + $action = null; + $class = null; + $label = null; + break; + + default: + $class = 'snippet-activation-switch'; + $action = $snippet->active ? 'deactivate' : 'activate'; + $label = $snippet->network && ! $snippet->shared_network ? + ( $snippet->active ? __( 'Network Deactivate', 'code-snippets' ) : __( 'Network Activate', 'code-snippets' ) ) : + ( $snippet->active ? __( 'Deactivate', 'code-snippets' ) : __( 'Activate', 'code-snippets' ) ); + break; } - if ( $snippet->shared_network ) { + if ( $action && $snippet->shared_network ) { $action .= '-shared'; } - return sprintf( - '  ', - esc_attr( $class ), - esc_url( $this->get_action_link( $action, $snippet ) ), - esc_attr( $label ) - ); + return $action && $class && $label + ? sprintf( + '  ', + esc_attr( $class ), + esc_url( $this->get_action_link( $action, $snippet ) ), + esc_attr( $label ) + ) + : ''; } /** @@ -328,9 +391,7 @@ protected function column_name( Snippet $snippet ): string { $out .= ' ' . esc_html__( 'Shared on Network', 'code-snippets' ) . ''; } - // Return the name contents. $out = apply_filters( 'code_snippets/list_table/column_name', $out, $snippet ); - return $out . $row_actions; } @@ -751,7 +812,7 @@ public function process_requested_actions() { $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id', 'scope', '_wpnonce' ) ); // If so, then perform the requested action and inform the user of the result. - $result = $this->perform_action( $id, sanitize_key( $_GET['action'] ), $scope ); + $result = $this->perform_action( $id, sanitize_key( $_GET['action'] ) ); if ( $result ) { wp_safe_redirect( esc_url_raw( add_query_arg( 'result', $result ) ) ); diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php index 92bebfa3..c6d2ec32 100644 --- a/src/php/class-plugin.php +++ b/src/php/class-plugin.php @@ -3,6 +3,8 @@ namespace Code_Snippets; use Code_Snippets\Cloud\Cloud_API; +use Code_Snippets\REST_API\Cloud_REST_API; +use Code_Snippets\REST_API\Conditions_REST_API; use Code_Snippets\REST_API\Snippets_REST_Controller; /** @@ -212,7 +214,7 @@ public function get_menu_url( string $menu = '', string $context = 'self' ): str $url = 'admin.php?page=' . $slug; } - if ( 'network' === $context || 'snippets-settings' === $slug ) { + if ( 'network' === $context ) { return network_admin_url( $url ); } elseif ( 'admin' === $context ) { return admin_url( $url ); diff --git a/src/php/class-snippet.php b/src/php/class-snippet.php index 2cbe8615..48a2ca4f 100644 --- a/src/php/class-snippet.php +++ b/src/php/class-snippet.php @@ -18,13 +18,14 @@ * @property string $code The executable code. * @property array $tags An array of the tags. * @property string $scope The scope name. + * @property int $condition_id ID of the condition this snippet is linked to. * @property int $priority Execution priority. * @property bool $active The active status. * @property bool $network true if is multisite-wide snippet, false if site-wide. * @property bool $shared_network Whether the snippet is a shared network snippet. * @property string $modified The date and time when the snippet data was most recently saved to the database. * @property array{string,int}|null $code_error Code error encountered when last testing snippet code. - * @property object|null $conditions Snippet conditionals + * @property object|null $conditions Snippet conditions. * @property int $revision Revision or version number of snippet. * @property string $cloud_id Cloud ID and ownership status of snippet. * @@ -64,12 +65,14 @@ public function __construct( $initial_data = null ) { 'code' => '', 'tags' => array(), 'scope' => 'global', + 'condition_id' => 0, 'active' => false, 'priority' => 10, 'network' => null, 'shared_network' => null, 'modified' => null, 'code_error' => null, + 'conditions' => null, 'revision' => 1, 'cloud_id' => '', ); @@ -77,6 +80,7 @@ public function __construct( $initial_data = null ) { $field_aliases = array( 'description' => 'desc', 'language' => 'lang', + 'conditionId' => 'condition_id', ); parent::__construct( $default_values, $initial_data, $field_aliases ); @@ -91,6 +95,15 @@ public function add_tag( string $tag ) { $this->fields['tags'][] = $tag; } + /** + * Determine if the snippet is a condition. + * + * @return bool + */ + public function is_condition(): bool { + return 'condition' === $this->scope; + } + /** * Prepare a value before it is stored. * @@ -103,19 +116,36 @@ protected function prepare_field( $value, string $field ) { switch ( $field ) { case 'id': case 'priority': + case 'condition_id': return absint( $value ); case 'tags': return code_snippets_build_tags_array( $value ); case 'active': - return is_bool( $value ) ? $value : (bool) $value; + return ( is_bool( $value ) ? $value : (bool) $value ) && ! $this->is_condition(); default: return $value; } } + /** + * Retrieve list of current data fields. + * + * @return array Field names keyed to current values. + */ + public function get_fields(): array { + $fields = parent::get_fields(); + + if ( 'condition' === $this->scope ) { + $fields['conditions'] = json_decode( $fields['code'] ); + $fields['code'] = ''; + } + + return $fields; + } + /** * Prepare the scope by ensuring that it is a valid choice * @@ -153,29 +183,42 @@ protected function prepare_network( bool $network ): bool { } /** - * Determine the type of code this snippet is, based on its scope + * Determine the type of code a given scope will produce. + * + * @param string $scope Scope name. * * @return string The snippet type – will be a filename extension. */ - protected function get_type(): string { - if ( '-css' === substr( $this->scope, -4 ) ) { + public static function get_type_from_scope( string $scope ): string { + if ( '-css' === substr( $scope, -4 ) ) { return 'css'; - } elseif ( '-js' === substr( $this->scope, -3 ) ) { + } elseif ( '-js' === substr( $scope, -3 ) ) { return 'js'; - } elseif ( 'content' === substr( $this->scope, -7 ) ) { + } elseif ( 'content' === substr( $scope, -7 ) ) { return 'html'; + } elseif ( 'condition' === $scope ) { + return 'cond'; } else { return 'php'; } } + /** + * Determine the type of code this snippet is, based on its scope + * + * @return string The snippet type – will be a filename extension. + */ + protected function get_type(): string { + return self::get_type_from_scope( $this->scope ); + } + /** * Retrieve a list of all valid types. * * @return string[] */ public static function get_types(): array { - return [ 'php', 'html', 'css', 'js' ]; + return [ 'php', 'html', 'css', 'js', 'cond' ]; } /** @@ -189,6 +232,7 @@ protected function get_type_desc(): string { 'html' => __( 'Content', 'code-snippets' ), 'css' => __( 'Styles', 'code-snippets' ), 'js' => __( 'Scripts', 'code-snippets' ), + 'cond' => __( 'Conditions', 'code-snippets' ), ]; return isset( $labels[ $this->type ] ) ? $labels[ $this->type ] : strtoupper( $this->type ); @@ -246,8 +290,8 @@ public function update_modified() { * @return string */ protected function get_display_name(): string { - // translators: %d: snippet ID. - return empty( $this->name ) ? sprintf( esc_html__( 'Untitled #%d', 'code-snippets' ), $this->id ) : $this->name; + // translators: %s: snippet identifier. + return empty( $this->name ) ? sprintf( esc_html__( 'Snippet #%d', 'code-snippets' ), $this->id ) : $this->name; } /** @@ -271,7 +315,8 @@ public static function get_all_scopes(): array { 'global', 'admin', 'front-end', 'single-use', 'content', 'head-content', 'footer-content', 'admin-css', 'site-css', - 'site-head-js', 'site-footer-js' + 'site-head-js', 'site-footer-js', + 'condition', ); } @@ -293,6 +338,7 @@ public static function get_scope_icons(): array { 'site-css' => 'admin-customizer', 'site-head-js' => 'media-code', 'site-footer-js' => 'media-code', + 'condition' => 'randomize', ); } @@ -322,7 +368,7 @@ protected function get_scope_name(): string { case 'site-css': return __( 'Front-end styles', 'code-snippets' ); case 'site-head-js': - return __( 'Head styles', 'code-snippets' ); + return __( 'Head scripts', 'code-snippets' ); case 'site-footer-js': return __( 'Footer scripts', 'code-snippets' ); } diff --git a/src/php/cloud/class-cloud-api.php b/src/php/cloud/class-cloud-api.php index 4a6b83cb..f27d4c84 100644 --- a/src/php/cloud/class-cloud-api.php +++ b/src/php/cloud/class-cloud-api.php @@ -4,6 +4,7 @@ use Code_Snippets\Snippet; use WP_Error; +use function Code_Snippets\code_snippets; use function Code_Snippets\get_snippet_by_cloud_id; use function Code_Snippets\get_snippets; use function Code_Snippets\save_snippet; @@ -26,13 +27,6 @@ class Cloud_API { */ private const DAYS_TO_STORE_CS = 1; - /** - * Name of key used to cache cloud settings. - * - * @var string - */ - private const CLOUD_SETTINGS_CACHE_KEY = 'code_snippets_cloud_settings'; - /** * Token used for public API access. * @@ -266,7 +260,6 @@ public static function get_single_snippet_from_cloud( int $cloud_id ): Cloud_Sni $url = self::get_cloud_api_url() . sprintf( 'public/getsnippet/%s', $cloud_id ); $response = wp_remote_get( $url ); $cloud_snippet = self::unpack_request_json( $response ); - return new Cloud_Snippet( $cloud_snippet['snippet'] ); } @@ -302,7 +295,6 @@ public static function get_cloud_snippet_revision( string $cloud_id ): ?string { */ public function download_or_update_snippet( int $cloud_id, string $source, string $action ): array { $cloud_id = intval( $cloud_id ); - $snippet_to_store = $this->get_single_snippet_from_cloud( $cloud_id ); switch ( $action ) { diff --git a/src/php/export/class-export.php b/src/php/export/class-export.php index 28d982c6..ce1bf7b0 100644 --- a/src/php/export/class-export.php +++ b/src/php/export/class-export.php @@ -145,6 +145,10 @@ public function export_snippets_code( ?string $type = null ): string { return $this->export_snippets_php(); } + if ( 'cond' === $type ) { + return $this->export_snippets_basic_json(); + } + foreach ( $this->snippets_list as $snippet ) { $snippet = new Snippet( $snippet ); diff --git a/src/php/export/class-import.php b/src/php/export/class-import.php index 4c2cfb9d..ef5b13d5 100644 --- a/src/php/export/class-import.php +++ b/src/php/export/class-import.php @@ -73,6 +73,7 @@ public function import_json() { 'desc', 'description', 'code', + 'conditions', 'tags', 'scope', 'priority', diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 8d76ae8a..402ecef3 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -172,13 +172,6 @@ function () { public static function register_prism_assets() { $plugin = code_snippets(); - wp_register_style( - self::PRISM_HANDLE, - plugins_url( 'dist/prism.css', $plugin->file ), - array(), - $plugin->version - ); - wp_register_script( self::PRISM_HANDLE, plugins_url( 'dist/prism.js', $plugin->file ), @@ -186,6 +179,13 @@ public static function register_prism_assets() { $plugin->version, true ); + + wp_register_style( + self::PRISM_HANDLE, + plugins_url( 'dist/prism.css', $plugin->file ), + array(), + $plugin->version + ); } /** diff --git a/src/php/load.php b/src/php/load.php index 2de59ce2..86e42bce 100644 --- a/src/php/load.php +++ b/src/php/load.php @@ -65,4 +65,4 @@ function code_snippets(): Plugin { code_snippets()->load_plugin(); // Execute the snippets once the plugins are loaded. -add_action( 'plugins_loaded', __NAMESPACE__ . '\execute_active_snippets', 1 ); +add_action( 'wp', __NAMESPACE__ . '\execute_active_snippets', 1 ); diff --git a/src/php/rest-api/class-snippets-rest-controller.php b/src/php/rest-api/class-snippets-rest-controller.php index 9e59f22a..4a5b0dc3 100644 --- a/src/php/rest-api/class-snippets-rest-controller.php +++ b/src/php/rest-api/class-snippets-rest-controller.php @@ -16,6 +16,7 @@ use function Code_Snippets\get_snippet; use function Code_Snippets\get_snippets; use function Code_Snippets\save_snippet; +use function Code_Snippets\update_snippet_fields; use const Code_Snippets\REST_API_NAMESPACE; /** @@ -516,6 +517,10 @@ public function get_item_schema(): array { 'description' => esc_html__( 'Context in which the snippet is executable.', 'code-snippets' ), 'type' => 'string', ], + 'condition_id' => [ + 'description' => esc_html__( 'Identifier of condition linked to this snippet.', 'code-snippets' ), + 'type' => 'integer', + ], 'active' => [ 'description' => esc_html__( 'Snippet activation status.', 'code-snippets' ), 'type' => 'boolean', diff --git a/src/php/snippet-ops.php b/src/php/snippet-ops.php index ec26eedd..7f3bc759 100644 --- a/src/php/snippet-ops.php +++ b/src/php/snippet-ops.php @@ -522,16 +522,17 @@ function save_snippet( $snippet ) { // Build the list of data to insert. $data = [ - 'name' => $snippet->name, - 'description' => $snippet->desc, - 'code' => $snippet->code, - 'tags' => $snippet->tags_list, - 'scope' => $snippet->scope, - 'priority' => $snippet->priority, - 'active' => intval( $snippet->active ), - 'modified' => $snippet->modified, - 'revision' => $snippet->revision, - 'cloud_id' => $snippet->cloud_id ? $snippet->cloud_id : null, + 'name' => $snippet->name, + 'description' => $snippet->desc, + 'code' => $snippet->code, + 'tags' => $snippet->tags_list, + 'scope' => $snippet->scope, + 'condition_id' => intval( $snippet->condition_id ), + 'priority' => $snippet->priority, + 'active' => intval( $snippet->active ), + 'modified' => $snippet->modified, + 'revision' => $snippet->revision, + 'cloud_id' => $snippet->cloud_id ? $snippet->cloud_id : null, ]; // Create a new snippet if the ID is not set. @@ -610,7 +611,7 @@ function execute_active_snippets(): bool { } $db = code_snippets()->db; - $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ]; + $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end', 'condition' ]; $data = $db->fetch_active_snippets( $scopes ); // Detect if a snippet is currently being edited, and if so, spare it from execution. @@ -633,12 +634,24 @@ function execute_active_snippets(): bool { } foreach ( $data as $table_name => $active_snippets ) { + $conditions = []; + + foreach ( $active_snippets as $snippet ) { + if ( 'condition' === $snippet['scope'] ) { + $snippet_id = intval( $snippet['id'] ); + $conditions[ $snippet_id ] = Conditions\evaluate_condition( $snippet['code'] ); + } + } // Loop through the returned snippets and execute the PHP code. foreach ( $active_snippets as $snippet ) { $snippet_id = intval( $snippet['id'] ); $code = $snippet['code']; + if ( 'condition' === $snippet['scope'] ) { + continue; + } + // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens. if ( 'single-use' === $snippet['scope'] ) { $active_shared_ids = get_option( 'active_shared_network_snippets', array() ); @@ -660,10 +673,20 @@ function execute_active_snippets(): bool { } } - if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) && - ! ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { - execute_snippet( $code, $snippet_id ); + if ( ! apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) || + ( $edit_id === $snippet_id && $table_name === $edit_table ) ) { + continue; } + + if ( $snippet['condition_id'] ) { + $condition_id = intval( $snippet['condition_id'] ); + + if ( isset( $conditions[ $condition_id ] ) && ! $conditions[ $condition_id ] ) { + continue; + } + } + + execute_snippet( $code, $snippet_id ); } } diff --git a/src/php/views/import.php b/src/php/views/import.php index c30d17cf..902d4409 100644 --- a/src/php/views/import.php +++ b/src/php/views/import.php @@ -42,23 +42,22 @@

    All Snippets page to activate the imported snippets.', 'code-snippets' ); - $url = esc_url( code_snippets()->get_menu_url( 'manage' ) ); - - echo wp_kses( - sprintf( $text, $url ), - array( - 'a' => array( - 'href' => array(), - 'target' => array(), - ) - ) - ); - ?> + /* translators: %s: link to snippets admin menu */ + $text = __( 'Afterward, you will need to visit the All Snippets page to activate the imported snippets.', 'code-snippets' ); + $url = esc_url( code_snippets()->get_menu_url( 'manage' ) ); + + echo wp_kses( + sprintf( $text, $url ), + array( + 'a' => array( + 'href' => array(), + 'target' => array(), + ), + ) + ); + ?>

    -
    diff --git a/src/php/views/partials/list-table-notices.php b/src/php/views/partials/list-table-notices.php index bfcb3d3e..e8dbc99d 100644 --- a/src/php/views/partials/list-table-notices.php +++ b/src/php/views/partials/list-table-notices.php @@ -18,17 +18,11 @@ ?>

    + %s %s', - esc_html__( 'Warning:', 'code-snippets' ), - sprintf( - // translators: 1: constant name, 2: file name. - esc_html__( 'Safe mode is active and snippets will not execute! Remove the %1$s constant from %2$s file to turn off safe mode.', 'code-snippets' ) - 'CODE_SNIPPETS_SAFE_MODE', - 'wp-config.php', - ) - ); + // translators: 1: constant name, 2: file name. + $text = __( 'Safe mode is active and snippets will not execute! Remove the %1$s constant from %2$s file to turn off safe mode.', 'code-snippets' ); + printf( esc_html( $text ), 'CODE_SNIPPETS_SAFE_MODE', 'wp-config.php' ); ?> diff --git a/src/readme.txt b/src/readme.txt index 7aeb15ed..1b4dac61 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -4,7 +4,7 @@ Donate link: https://codesnippets.pro Tags: code, snippets, multisite, php, css License: GPL-2.0-or-later License URI: license.txt -Stable tag: 3.7.0-dev.1 +Stable tag: 3.7.0-alpha.1 Tested up to: 6.7.2 An easy, clean and simple way to enhance your site with code snippets. @@ -103,6 +103,16 @@ You can report security bugs found in the source code of this plugin through the == Changelog == += 3.6.9 (2025-02-17) = + +__Changed__ +* Updated `Cloud_API::get_bundles()` to properly check bundle data and return an empty array if no valid bundles are present. +* Refactored `Cloud_List_Table::fetch_snippets()` to always return a valid `Cloud_Snippets` instance. +* Cleaned up bundle iteration code and improved translation handling in the bundles view. + +__Fixed__ +* Fixed errors in bundle iteration by adding a check for the bundles array before iterating. + = 3.6.8 (2025-02-14) = __Added__ From e21dcb4b15f943b0007f5c64f74eb8d7ed8fe531 Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 11 Jul 2025 20:26:07 +0300 Subject: [PATCH 093/268] feat: update build workflow to include version output and upgrade PHP version to 8.3 --- .github/workflows/build.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb930d58..ff922676 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,9 +8,11 @@ on: type: string default: ${{ inputs.ref }} outputs: - artifact_name: + version: + value: ${{ jobs.build.outputs.version }} + artifact_name: value: ${{ jobs.build.outputs.artifact_name }} - artifact_url: + artifact_url: value: ${{ jobs.build.outputs.artifact_url }} jobs: build: @@ -18,6 +20,7 @@ jobs: outputs: artifact_name: ${{ steps.build.outputs.name }} artifact_url: ${{ steps.artifacts.outputs.artifact-url }} + version: ${{ steps.build.outputs.version }} steps: - uses: actions/checkout@v4 with: @@ -26,7 +29,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: "8.3" - name: Set up Node.js uses: actions/setup-node@v4 @@ -38,6 +41,7 @@ jobs: run: | npm install && npm run bundle echo "name=$(jq -r .name package.json)" >> $GITHUB_OUTPUT + echo "version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT - name: Upload id: artifacts From bbf6cf75bb66ead80e713606b945855fe6c339da Mon Sep 17 00:00:00 2001 From: Imants Date: Fri, 11 Jul 2025 20:26:13 +0300 Subject: [PATCH 094/268] feat: add deploy job to release workflow for SVN publishing --- .github/workflows/release.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f10c23f0..c1ad2099 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,8 +44,13 @@ jobs: with: files: ${{ steps.zip.outputs.path }} - # deploy: - # needs: upload - # uses: ./.github/workflows/svn_push.yml - # with: - # ref: ${{ github.event.release.tag_name }} \ No newline at end of file + deploy: + needs: install + uses: codesnippetspro/.github-private/.github/workflows/publish_svn.yml@main + with: + repo: ${{ github.repository }} + branch: ${{ github.ref_name }} + tag: ${{ needs.install.outputs.version}} + slug: ${{ github.repository }} + artifact_url: ${{ needs.upload.outputs.artifact_url }} + artifact_name: ${{ needs.upload.outputs.artifact_name }} From f81ec7453658aa5301af48ca6e80cc29dea83e08 Mon Sep 17 00:00:00 2001 From: Imants Date: Sat, 12 Jul 2025 01:28:47 +0300 Subject: [PATCH 095/268] feat: update release workflow --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1ad2099..2c68fb1e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,9 @@ jobs: upload: needs: install runs-on: ubuntu-latest + outputs: + artifact_url: ${{ steps.zip.outputs.path }} + artifact_name: ${{ needs.install.outputs.artifact_name }} steps: - name: Get artifact url id: artifact @@ -45,7 +48,7 @@ jobs: files: ${{ steps.zip.outputs.path }} deploy: - needs: install + needs: [install, upload] uses: codesnippetspro/.github-private/.github/workflows/publish_svn.yml@main with: repo: ${{ github.repository }} From 988ea0412829b09f03a8d3aa22cf3d039b92476c Mon Sep 17 00:00:00 2001 From: Shea Bunge Date: Mon, 14 Jul 2025 13:01:39 +1000 Subject: [PATCH 096/268] Improve consistency of badges and implement upsell dialog. --- src/composer.lock | 117 +++++++++++------ src/css/common/_badges.scss | 67 ++++++++++ src/css/common/_switch.scss | 22 +++- src/css/common/_theme.scss | 18 ++- src/css/common/_type-badges.scss | 88 ------------- src/css/common/_upsell.scss | 124 ++++++++++++++++++ src/css/edit.scss | 5 +- src/css/edit/_conditions.scss | 6 + src/css/edit/_form.scss | 26 +++- src/css/edit/_sidebar.scss | 6 + src/css/edit/_upgrade-dialog.scss | 2 +- src/css/manage.scss | 26 +--- src/css/manage/_cloud.scss | 46 +------ src/css/welcome.scss | 2 +- .../ConditionModal/ConditionModalButton.tsx | 42 ++++++ .../EditorSidebar/actions/ShortcodeInfo.tsx | 4 +- src/js/components/SnippetForm/SnippetForm.tsx | 24 ++-- .../SnippetForm/fields/CodeEditor.tsx | 8 +- .../fields/SnippetLocationInput.tsx | 3 +- .../SnippetForm/fields/SnippetTypeInput.tsx | 28 ++-- .../SnippetForm/page/UpgradeDialog.tsx | 16 ++- src/js/components/common/Badge.tsx | 26 ++++ src/js/components/common/SnippetTypeBadge.tsx | 14 -- src/js/components/common/UpsellBanner.tsx | 25 ++++ src/js/components/common/UpsellDialog.tsx | 60 +++++++++ src/js/types/Window.ts | 1 + src/js/utils/snippets/snippets.ts | 2 +- src/php/admin-menus/class-edit-menu.php | 3 +- src/php/admin-menus/class-manage-menu.php | 2 +- src/php/class-admin.php | 58 ++++---- src/php/class-list-table.php | 107 ++++++--------- .../cloud/class-cloud-search-list-table.php | 8 +- src/php/cloud/list-table-shared-ops.php | 2 +- src/php/settings/settings-fields.php | 4 +- src/php/views/manage.php | 17 ++- 35 files changed, 643 insertions(+), 366 deletions(-) create mode 100644 src/css/common/_badges.scss delete mode 100644 src/css/common/_type-badges.scss create mode 100644 src/css/common/_upsell.scss create mode 100644 src/css/edit/_conditions.scss create mode 100644 src/js/components/ConditionModal/ConditionModalButton.tsx create mode 100644 src/js/components/common/Badge.tsx delete mode 100644 src/js/components/common/SnippetTypeBadge.tsx create mode 100644 src/js/components/common/UpsellBanner.tsx create mode 100644 src/js/components/common/UpsellDialog.tsx diff --git a/src/composer.lock b/src/composer.lock index 7ab674d3..b256075a 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "483a8311b70d7fb9078831174276ee2e", + "content-hash": "af21aa57ee761a9e969a806ede3a5073", "packages": [ { "name": "composer/installers", @@ -156,28 +156,28 @@ "packages-dev": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" + "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/6e0fa428497bf560152ee73ffbb8af5c6a56b0dd", + "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", + "composer-plugin-api": "^2.2", "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "*", + "composer/composer": "^2.2", "ext-json": "*", "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", + "php-parallel-lint/php-parallel-lint": "^1.4.0", "phpcompatibility/php-compatibility": "^9.0", "yoast/phpunit-polyfills": "^1.0" }, @@ -197,9 +197,9 @@ "authors": [ { "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" }, { "name": "Contributors", @@ -207,7 +207,6 @@ } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -228,9 +227,28 @@ ], "support": { "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2023-01-05T11:28:13+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-06-27T17:24:01+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -368,21 +386,22 @@ }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.6", + "version": "2.1.7", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469" + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/80ccb1a7640995edf1b87a4409fa584cd5869469", - "reference": "80ccb1a7640995edf1b87a4409fa584cd5869469", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", "shasum": "" }, "require": { "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0" @@ -432,35 +451,39 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2025-01-16T22:34:19+00:00" + "time": "2025-05-12T16:38:37+00:00" }, { "name": "phpcsstandards/phpcsextra", - "version": "1.2.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489" + "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", - "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/fa4b8d051e278072928e32d817456a7fdb57b6ca", + "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca", "shasum": "" }, "require": { "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.0.9", - "squizlabs/php_codesniffer": "^3.8.0" + "phpcsstandards/phpcsutils": "^1.1.0", + "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", + "php-parallel-lint/php-parallel-lint": "^1.4.0", "phpcsstandards/phpcsdevcs": "^1.1.6", "phpcsstandards/phpcsdevtools": "^1.2.1", - "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "type": "phpcodesniffer-standard", "extra": { @@ -510,35 +533,39 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2023-12-08T16:49:07+00:00" + "time": "2025-06-14T07:40:39+00:00" }, { "name": "phpcsstandards/phpcsutils", - "version": "1.0.12", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c" + "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c", - "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/65355670ac17c34cd235cf9d3ceae1b9252c4dad", + "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.10.0 || 4.0.x-dev@dev" + "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" }, "require-dev": { "ext-filter": "*", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", + "php-parallel-lint/php-parallel-lint": "^1.4.0", "phpcsstandards/phpcsdevcs": "^1.1.6", - "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0" + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" }, "type": "phpcodesniffer-standard", "extra": { @@ -575,6 +602,7 @@ "phpcodesniffer-standard", "phpcs", "phpcs3", + "phpcs4", "standards", "static analysis", "tokens", @@ -598,22 +626,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-05-20T13:34:27+00:00" + "time": "2025-06-12T04:32:33+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.3", + "version": "3.13.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", "shasum": "" }, "require": { @@ -680,11 +712,11 @@ "type": "open_collective" }, { - "url": "https://thanks.dev/phpcsstandards", + "url": "https://thanks.dev/u/gh/phpcsstandards", "type": "thanks_dev" } ], - "time": "2025-01-23T17:04:15+00:00" + "time": "2025-06-17T22:17:01+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -759,6 +791,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { + "php": ">=7.4", "ext-dom": "*", "ext-json": "*" }, diff --git a/src/css/common/_badges.scss b/src/css/common/_badges.scss new file mode 100644 index 00000000..633ae8fa --- /dev/null +++ b/src/css/common/_badges.scss @@ -0,0 +1,67 @@ +@use 'sass:map'; +@use 'sass:color'; +@use 'theme'; + +.badge { + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + border-radius: 3px; + text-align: center; + inline-size: 43px; + block-size: 24px; + display: flex; + justify-content: center; + align-items: center; +} + +.small-badge { + block-size: auto; + inline-size: auto; + font-size: smaller; + padding: 0 0.5em; +} + +.wp-core-ui .button.nav-tab-button { + margin-inline-start: 0.5em; + float: inline-end; + color: #a7aaad; + background: #f6f7f7; + border-color: #f6f7f7; + align-self: center; + display: flex; + justify-content: center; + align-items: center; + gap: 2px; +} + +@each $name, $color in theme.$badges { + $text-color: theme.contrasting-text-color($color); + + .badge.#{$name}-badge, + .nav-tab-inactive:hover .badge.#{$name}-badge, + :hover > .inverted-badges .#{$name}-badge { + color: $text-color; + background-color: $color; + } + + .badge.#{$name}-badge:hover { + color: color.adjust($text-color, $lightness: 5%); + background-color: color.adjust($color, $lightness: 5%); + } +} + +.nav-tab-inactive .badge, +.inverted-badges .badge { + color: #fff; + background-color: #a7aaad; +} + +.nav-tab-inactive { + background: transparent; +} + +.nav-tab-button .dashicons-external { + font-size: 15px; + color: #666; +} diff --git a/src/css/common/_switch.scss b/src/css/common/_switch.scss index e17185a2..29713774 100644 --- a/src/css/common/_switch.scss +++ b/src/css/common/_switch.scss @@ -57,8 +57,28 @@ input[type="checkbox"].switch:checked { color: #bbb; } +a.snippet-condition-count { + border-radius: 50%; + border: 1.8px solid currentColor; + height: 29px; + width: 29px; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + + .inactive-snippet & { + color: #ccc; + } + + .active-snippet & { + font-weight: bold; + color: theme.$accent; + } +} + .snippet-execution-button { - margin-inline-start: 10px; + margin-inline-start: 11px; margin-block-start: 9px; inline-size: 0; block-size: 0; diff --git a/src/css/common/_theme.scss b/src/css/common/_theme.scss index 37cd27aa..88a7666e 100644 --- a/src/css/common/_theme.scss +++ b/src/css/common/_theme.scss @@ -10,12 +10,18 @@ $visibility-private: (background: #F7E6BE, text: #CA961B); $visibility-public: (background: #DBEBF7, text: #2271B1); $brand-discord: #5865f2; $brand-facebook: #3b5998; -$types: ( - php: #1d97c6, - html: #ef6a36, - css: #9b59b6, - js: #ffeb3b, - cond: #2eae95 + +$badges: ( + php: #1d97c6, + html: #ef6a36, + css: #9b59b6, + js: #ffeb3b, + cond: #2eae95, + pro: #DF9279, + core: #61C5CB, + cloud: #00bcd4, + bundles: #50575e, + cloud_search: #ff9800 ); @function contrasting-text-color($bg-color) { diff --git a/src/css/common/_type-badges.scss b/src/css/common/_type-badges.scss deleted file mode 100644 index 1d508345..00000000 --- a/src/css/common/_type-badges.scss +++ /dev/null @@ -1,88 +0,0 @@ -@use 'sass:map'; -@use 'theme'; - -%badge { - font-size: 12px; - font-weight: 700; - text-transform: uppercase; - padding: 1px 2px; - border-radius: 3px; - text-align: center; - inline-size: 3.5em; - display: flex; - justify-content: center; - align-items: center; -} - -.nav-tab .badge, -.snippet-type-badge, -.go-pro-button .badge, -.button .badge { - @extend %badge; -} - - -.go-pro-badge, .pro-badge, .core-badge { - @extend %badge; - - margin-inline-start: 3px; - border: 1px solid currentcolor; - border-radius: 5px; - font-size: 10px; - padding: 1px 2px; - text-transform: uppercase; -} - -.pro-badge, -.go-pro-badge, -.go-pro-button .badge { - color: map.get(theme.$version-pro, 'text'); - background-color: map.get(theme.$version-pro, 'background'); -} - -.go-pro-badge, -.go-pro-button .badge { - margin-inline-start: 1px; - font-size: 10px; -} - -.core-badge { - color: map.get(theme.$version-core, 'text'); - background: map.get(theme.$version-core, 'background'); -} - -@each $type, $color in theme.$types { - .nav-tab[data-snippet-type=#{$type}] .badge, - .snippet-type-badge[data-snippet-type=#{$type}], - .nav-tab-inactive[data-snippet-type=#{$type}]:hover .badge { - color: theme.contrasting-text-color($color); - background-color: $color; - } -} - -.nav-tab-button .dashicons-external { - font-size: 15px; - color: #666; -} - -.nav-tab.nav-tab-inactive { - background: transparent; - - .badge { - color: #fff; - background-color: #a7aaad; - } -} - -.wp-core-ui .button.nav-tab-button { - margin-inline-start: 0.5em; - float: inline-end; - color: #a7aaad; - background: #f6f7f7; - border-color: #f6f7f7; - - &:hover { - background-color: #fff; - color: #3c434a; - } -} diff --git a/src/css/common/_upsell.scss b/src/css/common/_upsell.scss new file mode 100644 index 00000000..a0d9d1a6 --- /dev/null +++ b/src/css/common/_upsell.scss @@ -0,0 +1,124 @@ +@use 'sass:color'; +@use 'theme'; + +.code-snippets-upsell-dialog { + background: linear-gradient(116.04deg, #EDFCFF -0.75%, #FCDFD4 93.04%); + width: 794px; + box-shadow: 0 4px 80px rgba(0, 0, 0, 0.1); + border-radius: 8px; + font-family: 'SF Pro', sans-serif; + color: #1C3F41; + line-height: 1.5; + max-height: unset; + + .components-modal__content { + margin: 0; + padding: 48px 80px; + } + + .components-modal__content > div:last-child { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 18px; + overflow: auto; + } + + h1 { + font-size: 32px; + margin: 0; + + span { + color: #D46F4D; + } + } + + h1 + p { + font-size: 16px; + } + + p { + margin: 0 2em; + } + + img { + width: 82px; + } + + h2 { + text-transform: uppercase; + font-size: 12px; + } + + ul { + display: grid; + width: 100%; + grid-auto-flow: column; + grid-template-rows: 1fr 1fr 1fr 1fr 1fr; + } + + li { + display: flex; + flex-flow: row; + gap: 11px; + font-size: 14px; + } + + li::before { + content: '✓'; + color: #fff; + width: 24px; + height: 24px; + border-radius: 50%; + background-color: #0CA0A9; + display: flex; + justify-content: center; + align-items: center; + } + + .button.button-large { + background-color: #D46F4D; + padding: 15px 20px; + border: 0; + display: block; + font-size: 16px; + line-height: inherit; + margin: 16px auto 24px; + + &:hover { + background-color: color.adjust(#D46F4D, $lightness: -10%); + } + } +} + +.code-snippets-upsell-banner { + display: flex; + flex-direction: row; + align-items: center; + padding: 12px; + gap: 10px; + background: #FFFFFF; + border: 1px solid #C3C4C7; + border-radius: 5px; + margin-top: 31px; + + p { + margin: 0; + } + + .components-external-link__contents { + text-decoration: none; + } + + .button { + background-color: theme.$secondary; + border: 0; + font-weight: bold; + margin-left: auto; + + &:hover, &:focus { + background-color: color.adjust(theme.$secondary, $lightness: -10%); + } + } +} diff --git a/src/css/edit.scss b/src/css/edit.scss index a823b65a..a456e0db 100644 --- a/src/css/edit.scss +++ b/src/css/edit.scss @@ -3,14 +3,15 @@ */ @use 'common/codemirror'; -@use 'common/type-badges'; +@use 'common/badges'; @use 'common/switch'; @use 'common/select'; @use 'common/tooltips'; +@use 'common/upsell'; @use 'edit/form'; @use 'edit/sidebar'; @use 'edit/editor'; -@use 'edit/upgrade-dialog'; +@use 'edit/conditions'; @use 'edit/gpt'; .notice.error blockquote { diff --git a/src/css/edit/_conditions.scss b/src/css/edit/_conditions.scss new file mode 100644 index 00000000..1981b056 --- /dev/null +++ b/src/css/edit/_conditions.scss @@ -0,0 +1,6 @@ +.snippet-condition-editor-container { + margin-block-start: 33px; + padding: 32px; + border: 1px solid #c3c4c7; + background: #f6f7f7; +} diff --git a/src/css/edit/_form.scss b/src/css/edit/_form.scss index f7a2aa92..9d8812d9 100644 --- a/src/css/edit/_form.scss +++ b/src/css/edit/_form.scss @@ -1,3 +1,5 @@ +@use '../common/theme'; + $sidebar-width: 280px; $sidebar-gap: 30px; @@ -19,12 +21,20 @@ $sidebar-gap: 30px; .badge { margin: 3px 0 3px auto; - text-transform: uppercase; + + + .badge { + margin-inline-start: 5px; + } + } + + .beta-badge { border: 1px solid currentcolor; - border-radius: 4px; - line-height: 1; - padding: 5px; - font-size: smaller; + } + + .pro-badge { + color: currentColor; + border: 1px solid currentcolor; + background: transparent; } } @@ -76,7 +86,11 @@ $sidebar-gap: 30px; justify-content: space-between; flex-flow: row; - div.snippet-type-badge { + .small-badge { + margin-left: 0.5em; + } + + .badge { float: inline-end; } } diff --git a/src/css/edit/_sidebar.scss b/src/css/edit/_sidebar.scss index 4782cbe7..9b308248 100644 --- a/src/css/edit/_sidebar.scss +++ b/src/css/edit/_sidebar.scss @@ -67,3 +67,9 @@ p.submit { margin-block-start: 17px; padding-block-start: 0; } + +.shortcode-tag-wrapper { + .code-snippets-copy-text { + vertical-align: middle; + } +} diff --git a/src/css/edit/_upgrade-dialog.scss b/src/css/edit/_upgrade-dialog.scss index b51b4741..c864f5f2 100644 --- a/src/css/edit/_upgrade-dialog.scss +++ b/src/css/edit/_upgrade-dialog.scss @@ -57,7 +57,7 @@ margin-inline-start: 1ch; } - .components-external-link.button { + .components-external-link__contents { text-decoration: none; } } diff --git a/src/css/manage.scss b/src/css/manage.scss index bd033422..888e8db8 100644 --- a/src/css/manage.scss +++ b/src/css/manage.scss @@ -5,7 +5,7 @@ @use 'sass:map'; @use 'sass:color'; @use 'common/theme'; -@use 'common/type-badges'; +@use 'common/badges'; @use 'common/switch'; @use 'common/select'; @use 'manage/cloud'; @@ -121,23 +121,6 @@ } } - .small-badge { - margin-inline-start: 4px; - padding: 3px 6px; - text-decoration: none; - border: medium none; - border-radius: 2px; - background-color: #e0e0e0; - background-color: rgb(0 0 0 / 8%); - font-size: smaller; - line-height: 1.2; - - /* rtl:ignore */ - .rtl & { - float: inline-start; - } - } - tr.active-snippet + tr.inactive-snippet th, tr.active-snippet + tr.inactive-snippet td { border-block-start: 1px solid rgb(0 0 0 / 3%); @@ -203,13 +186,6 @@ line-height: 1.4; } -.cloud-badge { - border: 1px solid; - border-radius: 5px; - font-size: 15px; - line-height: 19px; -} - .wrap h2.nav-tab-wrapper { display: flex; flex-flow: row wrap-reverse; diff --git a/src/css/manage/_cloud.scss b/src/css/manage/_cloud.scss index b1ea6b6d..4dc0e466 100644 --- a/src/css/manage/_cloud.scss +++ b/src/css/manage/_cloud.scss @@ -28,22 +28,6 @@ font-size: 15px; } -.cloud-badge { - margin-inline-start: 10px; - border: 1px solid; - border-radius: 5px; - font-size: 15px; - line-height: 19px; -} - -.nav-tab-inactive .cloud-badge { - color: #a7aaad; -} - -.nav-tab-inactive[data-snippet-type="cloud"]:hover .cloud-badge { - color: #00bcd4; -} - .cloud-synced-legend { color: #00bcd4; } @@ -56,11 +40,11 @@ color: #e91e63; } -.nav-tab-inactive[data-snippet-type="cloud_search"]:hover .cloud-badge { +.nav-tab-inactive.cloud_search-tab:hover .cloud-badge { color: #e91e63; } -.nav-tab-inactive[data-snippet-type="bundles"]:hover .cloud-badge { +.nav-tab-inactive.bundles-tab:hover .cloud-badge { color: #50575e; } @@ -124,7 +108,7 @@ color: #ff9800 !important; } -.snippet-type-badge { +.badge { white-space: nowrap; } @@ -317,22 +301,22 @@ } } -.ai-verified-snippet .badge, .snippet-type-badge[data-type="ai-verified"] { +.ai-verified-snippet .badge, .ai-verified-badge { background-color: #ccfbf1; color: #0f766e; } -.private-snippet .badge, .snippet-type-badge[data-type="private"] { +.private-snippet .badge, .private-badge { background-color: #eff6ff; color: #1d4ed8; } -.public-snippet .badge, .snippet-type-badge[data-type="public"] { +.public-snippet .badge, .public-badge { background-color: #fefce8; color: #a16207; } -.unverified-snippet .badge, .snippet-type-badge[data-type="unverified"] { +.unverified-snippet .badge, .unverified-badge { background-color: #fff1f2; color: #be123c; @@ -342,22 +326,6 @@ } } -.css-badge { - color: #8000ff !important; -} - -.js-badge { - color: #cd6600 !important; -} - -.php-badge { - color: #0073aa !important; -} - -.html-badge { - color: #548b54 !important; -} - .tooltip-box { position: relative; display: inline-block; diff --git a/src/css/welcome.scss b/src/css/welcome.scss index f557eaf7..762c0484 100644 --- a/src/css/welcome.scss +++ b/src/css/welcome.scss @@ -1,6 +1,6 @@ @use 'sass:color'; @use 'common/theme'; -@use 'common/type-badges'; +@use 'common/badges'; $breakpoint: 1060px; diff --git a/src/js/components/ConditionModal/ConditionModalButton.tsx b/src/js/components/ConditionModal/ConditionModalButton.tsx new file mode 100644 index 00000000..b1f9ccb2 --- /dev/null +++ b/src/js/components/ConditionModal/ConditionModalButton.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import classnames from 'classnames' +import { __ } from '@wordpress/i18n' +import { isLicensed } from '../../utils/screen' +import { isCondition } from '../../utils/snippets/snippets' +import { Button } from '../common/Button' +import { useSnippetForm } from '../../hooks/useSnippetForm' +import type { Dispatch, SetStateAction } from 'react' + +export interface ConditionModalButtonProps { + setIsDialogOpen: Dispatch> +} + +export const ConditionModalButton: React.FC = ({ setIsDialogOpen }) => { + const { snippet, isReadOnly } = useSnippetForm() + + const hasCondition = 0 !== snippet.conditionId + + return ( +

    + ) +} diff --git a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx index 3890c502..1e0aa0cc 100644 --- a/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx +++ b/src/js/components/EditorSidebar/actions/ShortcodeInfo.tsx @@ -16,8 +16,8 @@ interface ShortcodeOptions { } const ShortcodeTag: React.FC<{ atts: ShortcodeAtts }> = ({ atts }) => -

    -

    {buildShortcodeTag(SHORTCODE_TAG, atts)}
    +

    + {buildShortcodeTag(SHORTCODE_TAG, atts)}

    {__('This snippet type is not supported in this version of Code Snippets.')}

    -const ConditionModalButton: React.FC = () => -
    TODO
    - const EditFormWrap: React.FC = () => { const { snippet } = useSnippetForm() const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false) @@ -135,23 +135,27 @@ const EditFormWrap: React.FC = () => {
    -
    - setIsUpgradeDialogOpen(true)} /> - - -
    + {!isCondition(snippet) || 0 === snippet.id + ?
    + + + +
    + : null} {isCondition(snippet) ? : } + {!isLicensed() && } + {window.CODE_SNIPPETS_EDIT?.enableDescription ? : null}
    - +
    ) } diff --git a/src/js/components/SnippetForm/fields/CodeEditor.tsx b/src/js/components/SnippetForm/fields/CodeEditor.tsx index 3e417334..1029ceeb 100644 --- a/src/js/components/SnippetForm/fields/CodeEditor.tsx +++ b/src/js/components/SnippetForm/fields/CodeEditor.tsx @@ -8,16 +8,16 @@ import { CodeEditorShortcuts } from './CodeEditorShortcuts' import type { RefObject} from 'react' interface EditorTextareaProps { - ref: RefObject + textareaRef: RefObject } -const EditorTextarea: React.FC = ({ ref }) => { +const EditorTextarea: React.FC = ({ textareaRef }) => { const { snippet, setSnippet } = useSnippetForm() return (