diff --git a/README.md b/README.md index 71067a6..af5cee1 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ [![PHP](https://img.shields.io/badge/PHP-8.0%2B-purple.svg)](https://php.net/) [![WordPress](https://img.shields.io/badge/WordPress-6.6.4%2B-blue.svg)](https://wordpress.org/) -A WordPress plugin that adds animations, effects, and interactivity to Gutenberg blocks. +A WordPress plugin that adds animations, effects, and interactivity to Gutenberg, Elementor, and Bricks. ## 🔧 Requirements - **WordPress**: 6.6.4 or higher - **PHP**: 8.0 or higher - **Node.js**: 18 or higher -- **Block Editor**: Gutenberg (built-in WordPress editor) +- **Supported Editors**: Gutenberg (built-in WordPress editor), Elementor, and Bricks ## 🛠️ Development @@ -56,7 +56,7 @@ src/ ├── action-types/ # Available action types (PHP + JS) ├── interaction-types/ # Available trigger types (PHP + JS) ├── admin/ # Admin interface -├── editor/ # Block editor integration +├── editor/ # Editor and builder integrations ├── frontend/ # Frontend functionality ├── locations/ # Location rules └── rest-api/ # REST API endpoints diff --git a/interactions.php b/interactions.php index 6f3fcd3..9fc1fd6 100644 --- a/interactions.php +++ b/interactions.php @@ -79,6 +79,15 @@ function interact_on_activation() { if ( is_admin() ) { require_once( plugin_dir_path( __FILE__ ) . 'src/admin/admin.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/editor/editor.php' ); +} else { + add_action( 'after_setup_theme', function() { + if ( + ( function_exists( 'bricks_is_builder_main' ) && bricks_is_builder_main() ) || + ( function_exists( 'bricks_is_builder' ) && bricks_is_builder() ) + ) { + require_once( plugin_dir_path( __FILE__ ) . 'src/editor/editor.php' ); + } + } ); } /** diff --git a/package-lock.json b/package-lock.json index 90050bb..77813be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "interactions", - "version": "1.3.2", + "version": "1.3.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "interactions", - "version": "1.3.2", + "version": "1.3.3", "license": "GPL-2.0-or-later", "dependencies": { + "@wordpress/components": "^33.0.0", "@wordpress/icons": "^10.31.0", "canvas-confetti": "^1.9.3", "classnames": "^2.5.1", @@ -53,11 +54,48 @@ "node": ">=6.0.0" } }, + "node_modules/@ariakit/core": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.20.tgz", + "integrity": "sha512-DJbUnui0fM+2ZgiWLOMuFOmlWSJDNV3f6tqghIYRTWEm51TN/LoU6uM8og6/g7Nrwl4Uo5l8AoQT9Kkr/i/uRg==", + "license": "MIT" + }, + "node_modules/@ariakit/react": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.26.tgz", + "integrity": "sha512-NcoPrYE4vgwyODAhdpNNuA7ldwODDuFqZl6jORPVDY3l+oRjl/OYwtQyyC3ZhC/4mjntYBYuKKrPJEizLmoxpg==", + "license": "MIT", + "dependencies": { + "@ariakit/react-core": "0.4.26" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ariakit" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-core": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.26.tgz", + "integrity": "sha512-/Peh1KiVpjj79nCJIa6lEdzSTT9P9FZoy+CxByIFKL3YKdlXmDIIhS1E/tAqKbDq4ODVdynnqmrIDxE5wCoZYw==", + "license": "MIT", + "dependencies": { + "@ariakit/core": "0.4.20", + "@floating-ui/dom": "^1.0.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -128,7 +166,6 @@ "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", @@ -226,7 +263,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -248,7 +284,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -346,7 +381,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -355,7 +389,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -400,7 +433,6 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", - "dev": true, "dependencies": { "@babel/types": "^7.28.4" }, @@ -1874,7 +1906,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", @@ -1888,7 +1919,6 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1906,7 +1936,6 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" @@ -2022,6 +2051,18 @@ "@csstools/css-tokenizer": "^3.0.1" } }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", + "license": "MIT" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", + "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==", + "license": "MIT" + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2041,6 +2082,180 @@ "url": "https://github.com/sponsors/JounQin" } }, + "node_modules/@emotion/babel-plugin": { + "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.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", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "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.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/css": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", + "dependencies": { + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" + } + }, + "node_modules/@emotion/hash": { + "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.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "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.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.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": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "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.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.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.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@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", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "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.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.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.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/@es-joy/jsdoccomment": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz", @@ -2166,6 +2381,44 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", @@ -2709,7 +2962,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -2729,7 +2981,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2747,14 +2998,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4401,6 +4650,52 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@tabby_ai/hijri-converter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", + "integrity": "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@tannin/compile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", + "integrity": "sha512-n8m9eNDfoNZoxdvWiTfW/hSPhehzLJ3zW7f8E7oT6mCROoMNWCB4TYtv041+2FMAxweiE0j7i1jubQU4MEC/Gg==", + "license": "MIT", + "dependencies": { + "@tannin/evaluate": "^1.2.0", + "@tannin/postfix": "^1.1.0" + } + }, + "node_modules/@tannin/evaluate": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@tannin/evaluate/-/evaluate-1.2.0.tgz", + "integrity": "sha512-3ioXvNowbO/wSrxsDG5DKIMxC81P0QrQTYai8zFNY+umuoHWRPbQ/TuuDEOju9E+jQDXmj6yI5GyejNuh8I+eg==", + "license": "MIT" + }, + "node_modules/@tannin/plural-forms": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/plural-forms/-/plural-forms-1.1.0.tgz", + "integrity": "sha512-xl9R2mDZO/qiHam1AgMnAES6IKIg7OBhcXqy6eDsRCdXuxAFPcjrej9HMjyCLE0DJ/8cHf0i5OQTstuBRhpbHw==", + "license": "MIT", + "dependencies": { + "@tannin/compile": "^1.1.0" + } + }, + "node_modules/@tannin/postfix": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", + "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==", + "license": "MIT" + }, + "node_modules/@tannin/sprintf": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz", + "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==", + "license": "MIT" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -4575,6 +4870,18 @@ "@types/node": "*" } }, + "node_modules/@types/gradient-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-1.1.0.tgz", + "integrity": "sha512-SaEcbgQscHtGJ1QL+ajgDTmmqU2f6T+00jZRcFlVHUW2Asivc84LNUev/UQFyu117AsdyrtI+qpwLvgjJXJxmw==", + "license": "MIT" + }, + "node_modules/@types/highlight-words-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.1.tgz", + "integrity": "sha512-9VZUA5omXBfn+hDxFjUDu1FOJTBM3LmvqfDey+Z6Aa8B8/JmF5SMj6FBrjfgJ/Q3YXOZd3qyTDfJyMZSs/wCUA==", + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -4649,6 +4956,12 @@ "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, + "node_modules/@types/mousetrap": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz", + "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==", + "license": "MIT" + }, "node_modules/@types/mysql": { "version": "2.15.26", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz", @@ -4685,8 +4998,7 @@ "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/pg": { "version": "8.6.1", @@ -4726,12 +5038,13 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.3.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", - "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { @@ -5098,6 +5411,24 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -5288,6 +5619,20 @@ } } }, + "node_modules/@wordpress/a11y": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.45.0.tgz", + "integrity": "sha512-KOgdBsZP34nAi+UfrhIAZDt2I1ZDb3DXAgIeQk7QxTIc9OlQKMNfrYwPG0jidgfKwmjFxh8vV8HbZcBzTD29Rw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/dom-ready": "^4.45.0", + "@wordpress/i18n": "^6.18.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/babel-preset-default": { "version": "8.31.0", "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.31.0.tgz", @@ -5361,6 +5706,199 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/components": { + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-33.0.0.tgz", + "integrity": "sha512-VeLDtfz8612bdRqgQiSMtIIEGDi4ZByj0XUvjT7E6RVLgczQyV9DTpGOPyL6PbTyAluIx6hjt9bzsaC+bM6G+w==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@ariakit/react": "^0.4.22", + "@date-fns/utc": "^2.1.1", + "@emotion/cache": "^11.14.0", + "@emotion/css": "^11.13.5", + "@emotion/react": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/styled": "^11.14.1", + "@emotion/utils": "^1.4.2", + "@floating-ui/react-dom": "2.0.8", + "@types/gradient-parser": "1.1.0", + "@types/highlight-words-core": "1.2.1", + "@types/react": "^18.3.27", + "@use-gesture/react": "^10.3.1", + "@wordpress/a11y": "^4.45.0", + "@wordpress/base-styles": "^7.0.0", + "@wordpress/compose": "^7.45.0", + "@wordpress/date": "^5.45.0", + "@wordpress/deprecated": "^4.45.0", + "@wordpress/dom": "^4.45.0", + "@wordpress/element": "^6.45.0", + "@wordpress/escape-html": "^3.45.0", + "@wordpress/hooks": "^4.45.0", + "@wordpress/html-entities": "^4.45.0", + "@wordpress/i18n": "^6.18.0", + "@wordpress/icons": "^13.0.0", + "@wordpress/is-shallow-equal": "^5.45.0", + "@wordpress/keycodes": "^4.45.0", + "@wordpress/primitives": "^4.45.0", + "@wordpress/private-apis": "^1.45.0", + "@wordpress/rich-text": "^7.45.0", + "@wordpress/warning": "^3.45.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "csstype": "^3.2.3", + "date-fns": "^3.6.0", + "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.15.0", + "gradient-parser": "1.1.1", + "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.6.1", + "react-day-picker": "^9.7.0", + "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/components/node_modules/@wordpress/base-styles": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-7.0.0.tgz", + "integrity": "sha512-Q0BbZzfeYbQZKHnyNT4RF8RGVugN5jStGtpRKhBYQW7ut7sS61LbbpP7jR0D0sDPYoEEC8jKZQSZwSM23B4jow==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/components/node_modules/@wordpress/icons": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-13.0.0.tgz", + "integrity": "sha512-+CLbvNdzMUHxQK5I6gFdHb3X6EVAH6SOSIj0xtMWm6PZO+Nnf7tXHfNBuxqTnGfxT5grtfb6D3A9ZMBU+Tpv+Q==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^6.45.0", + "@wordpress/primitives": "^4.45.0", + "change-case": "4.1.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/components/node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/@wordpress/components/node_modules/path-to-regexp": { + "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/@wordpress/components/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@wordpress/compose": { + "version": "7.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.45.0.tgz", + "integrity": "sha512-/keWdRFUe7bnzh2ZtOYLexknpj0K0G56WFw7RLZehl54a9EmzjYjAODBOF9DB3c07pJuNuy7c5QgqMPi0cqLlw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^4.45.0", + "@wordpress/dom": "^4.45.0", + "@wordpress/element": "^6.45.0", + "@wordpress/is-shallow-equal": "^5.45.0", + "@wordpress/keycodes": "^4.45.0", + "@wordpress/priority-queue": "^3.45.0", + "@wordpress/undo-manager": "^1.45.0", + "change-case": "^4.1.2", + "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/data": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.45.0.tgz", + "integrity": "sha512-OR/uMpcEbCh1aBkbzateXffNrL829M+N92qtuD+Gt08Mey129WIEVR9kBC2Tf02VtXs644OKZD6cz77KlxH8XA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/compose": "^7.45.0", + "@wordpress/deprecated": "^4.45.0", + "@wordpress/element": "^6.45.0", + "@wordpress/is-shallow-equal": "^5.45.0", + "@wordpress/priority-queue": "^3.45.0", + "@wordpress/private-apis": "^1.45.0", + "@wordpress/redux-routine": "^5.45.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", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/date": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.45.0.tgz", + "integrity": "sha512-34v3hCxn68kYzWs8bhuAt8cfMxdFX9ukKn3a3FB+tAJXpxafnPCcZoWfJHn4I8hepCbreFrf3UiGdA+id2kQ4A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^4.45.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.40" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/dependency-extraction-webpack-plugin": { "version": "6.31.0", "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-6.31.0.tgz", @@ -5383,6 +5921,42 @@ "integrity": "sha512-dnSoUiLAoVaMXxFsVi4CrPVYMKOuDBXTghXSmMINX44RZ8WM9cXlY7UqrQnlAcODCVO7FV3+8t/5nDKAjimLfg==", "dev": true }, + "node_modules/@wordpress/deprecated": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.45.0.tgz", + "integrity": "sha512-qer/fk/lgmmisb8/hj1xZtsbJbZhCoOblhyxI2k7RRul7rQDdk+fm28LJYV+eIF0ldSVX30f4dmz1pvcVHQEEg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/hooks": "^4.45.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/dom": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.45.0.tgz", + "integrity": "sha512-6RObr/KEZS1FnZwpcDAsKlJ3qw2KLF5+A/LsxlM9fSWDGSO05CEaTp+VmWgx9pwjQWbPEa7N73ijEy8cCNSZWA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^4.45.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/dom-ready": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.45.0.tgz", + "integrity": "sha512-0lFImpg9DGXcGCDQePdoU8haz7QYsKOFXUMTpRvi/Te38LFXzgZtOUBQbY8fRBlLxrgrj4FsAIc7bzdLn73wNQ==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/e2e-test-utils-playwright": { "version": "1.31.0", "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.31.0.tgz", @@ -5405,14 +5979,14 @@ } }, "node_modules/@wordpress/element": { - "version": "6.31.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.31.0.tgz", - "integrity": "sha512-KOier6Y4b4Y5yEV1GYen81R9gCEOvJT6eVbsc93w2fFEKi2FK/oI7IKzGv9GeJMkoCWvTSX6C/ZYTWk6fCUfeA==", + "version": "6.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.45.0.tgz", + "integrity": "sha512-WFrGNPEnj8uE+XhFW9NVbxvqraYpConaEokLv9IszFYVfyg8juXSQcHOAfEnxjC08HBPfVcayr2igu/XUgGOAw==", + "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.31.0", + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/escape-html": "^3.45.0", "change-case": "^4.1.2", "is-plain-object": "^5.0.0", "react": "^18.3.0", @@ -5424,12 +5998,10 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.31.0.tgz", - "integrity": "sha512-9g9qd7Q16PWDeYEa2dU+84d1SvjP4LfS7n7AuXkwl5+F7KfL2nZTmDTHWutw9jVjdDAGmjm1VNIj4ydQk9vaLA==", - "dependencies": { - "@babel/runtime": "7.25.7" - }, + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.45.0.tgz", + "integrity": "sha512-IW4mnA+65XKhABuBkwrQNAlbq97luC6ZIBfdSq0Tkq+AFPqE1lJTMlLo7iBkTpsHsBLyznViPXultq40fz8L7w==", + "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -5478,6 +6050,46 @@ } } }, + "node_modules/@wordpress/hooks": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.45.0.tgz", + "integrity": "sha512-+gOlu8TdohqL1INQNxS/7CxhM4T4MuYnKietWV9zWDmNQV2ysM0SdamNk5pWERJ4w0yY9XhtMBcwR/piJtePZg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/html-entities": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.45.0.tgz", + "integrity": "sha512-7W95xaOv4UgMSWlEmyO7YkBsUae3QlQu3GKENVH7Pt/osbJGSPInAJ1ruO4oeUwGPygWOL7b7IzRsgTNP0M/Wg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/i18n": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.18.0.tgz", + "integrity": "sha512-6dYCih4wUwi7Csu4RNfHiAKkgWhpSQdl8YthvQUF59Sfsoia3RCdtd4K2l7W4f18ldFA/RXjShMjvSexWy6OyQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@tannin/sprintf": "^1.3.2", + "@wordpress/hooks": "^4.45.0", + "gettext-parser": "^1.3.1", + "memize": "^2.1.0", + "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.31.0", "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.31.0.tgz", @@ -5492,6 +6104,16 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/is-shallow-equal": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.45.0.tgz", + "integrity": "sha512-saamGjAuhZOiFOyznsriPGrO8GRDremImMO4q92qjQqmDqssC+FRDQnwr9D8BaedSnVvUDcriGeYBObEEnIJ2A==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/jest-console": { "version": "8.31.0", "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-8.31.0.tgz", @@ -5527,6 +6149,19 @@ "jest": ">=29" } }, + "node_modules/@wordpress/keycodes": { + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.45.0.tgz", + "integrity": "sha512-N+Wp572xZovLM45cYo6HfUNTQNDfEqakAYIOcY8bUqA2iFelN6AUkNfUIkIxmrE0EqkQAQ5odES03g8ym7e1IA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^6.18.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/npm-package-json-lint-config": { "version": "5.31.0", "resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-5.31.0.tgz", @@ -5571,12 +6206,12 @@ } }, "node_modules/@wordpress/primitives": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.31.0.tgz", - "integrity": "sha512-cY4EKYQRqHu9NZuoWchxc/KWiofwGskzxz0oCfgbdkRlfTag8yBjWMayz+fRNaenw0l5pzLyIg3rcNDN8xLezw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.45.0.tgz", + "integrity": "sha512-x+i6EKUvz96EkUb2KuBTLNGm8d5+ZS0FYjUEnIhp5dtWxjMe8dJT6LS+n363vg+K28LVvjptiTAaByccnNKc9w==", + "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "7.25.7", - "@wordpress/element": "^6.31.0", + "@wordpress/element": "^6.45.0", "clsx": "^2.1.1" }, "engines": { @@ -5587,6 +6222,74 @@ "react": "^18.0.0" } }, + "node_modules/@wordpress/priority-queue": { + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.45.0.tgz", + "integrity": "sha512-0sIX2PRPzo5nk252f60xpPj3/BUZxEOLcabCC7FuvQDYPGZrRyS6Dy0vDDzozZxHGuUYCT65t8ubBwXx37wXCw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "requestidlecallback": "^0.3.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/private-apis": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.45.0.tgz", + "integrity": "sha512-UjhIDpoyKKUghPM0tkqd5Whsuk4kqfAfhb5VYGoEYtunDs0rB8IxgFO7hE0PhimHL74QVgaJOlprRZVRCCoQ6w==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/redux-routine": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.45.0.tgz", + "integrity": "sha512-6ShpBns4jIBFXrYFBcKA5pnFm/kjr1SqFvLj5DwLgMV61eI3Rr9LyZwIzNR2BGg067ryxu4W172Uqjke/mZjcQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "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/rich-text": { + "version": "7.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.45.0.tgz", + "integrity": "sha512-C5+JQqNzA3fiQq0hN9pQPKsjcwO/fczouHqubq3847kAUrClROqqI1GJHE34WLl1Vp+/tWQuBkIjQ/95olKteA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.45.0", + "@wordpress/compose": "^7.45.0", + "@wordpress/data": "^10.45.0", + "@wordpress/deprecated": "^4.45.0", + "@wordpress/dom": "^4.45.0", + "@wordpress/element": "^6.45.0", + "@wordpress/escape-html": "^3.45.0", + "@wordpress/i18n": "^6.18.0", + "@wordpress/keycodes": "^4.45.0", + "@wordpress/private-apis": "^1.45.0", + "colord": "2.9.3", + "memize": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@wordpress/scripts": { "version": "30.24.0", "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-30.24.0.tgz", @@ -5777,11 +6480,24 @@ "stylelint-scss": "^6.4.0" } }, + "node_modules/@wordpress/undo-manager": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.45.0.tgz", + "integrity": "sha512-BqclZIPjzBYIjLqLZFihs+Ce+w+yBQuj44VYSrRDOj56AbMtwmClIUqgIVBZAe2En/2ncixTTWOZG9KluvEXfA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/is-shallow-equal": "^5.45.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/warning": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.31.0.tgz", - "integrity": "sha512-Npw1Apa6r+K+jtX40ABWAXv7J1bVnOi6h9VPiMY8l/iZoRHBXao8HTgQnIoCm+GzymaQs6NQoH4X8UAClggeXA==", - "dev": true, + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.45.0.tgz", + "integrity": "sha512-NQ9tAhPdwhfceVIzWra1rbumvgAFAEDTgZlWsX880zLiq1F8JTwBouwW6wfIhA3XLcY6Yj7cBBYLa8vnNiDZDw==", + "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -6547,6 +7263,21 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", @@ -7046,7 +7777,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -7415,8 +8145,7 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" }, "node_modules/colorette": { "version": "2.0.20", @@ -7760,7 +8489,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -8093,9 +8821,10 @@ "dev": true }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/cwd": { "version": "0.10.0", @@ -8206,6 +8935,12 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", @@ -8216,7 +8951,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -8302,7 +9036,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8655,6 +9388,15 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -8723,11 +9465,16 @@ "node": ">=4" } }, + "node_modules/equivalent-key-map": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz", + "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==", + "license": "MIT" + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -8935,7 +9682,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -10049,8 +10795,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -10350,6 +11095,12 @@ "find-process": "bin/find-process.js" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -10524,6 +11275,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -10572,7 +11350,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10737,6 +11514,16 @@ "node": ">= 14" } }, + "node_modules/gettext-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.4.0.tgz", + "integrity": "sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA==", + "license": "MIT", + "dependencies": { + "encoding": "^0.1.12", + "safe-buffer": "^5.1.1" + } + }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -10902,6 +11689,14 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/gradient-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.1.1.tgz", + "integrity": "sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -11017,7 +11812,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -11034,6 +11828,27 @@ "tslib": "^2.0.3" } }, + "node_modules/highlight-words-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz", + "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==", + "license": "MIT" + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -11291,7 +12106,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -11390,7 +12204,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -11406,7 +12219,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -11577,8 +12389,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { "version": "2.1.1", @@ -11679,7 +12490,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -11903,6 +12713,12 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, + "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": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -12992,7 +13808,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -13009,8 +13824,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -13416,8 +14230,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/linkify-it": { "version": "3.0.3", @@ -13844,6 +14657,12 @@ "node": ">= 4.0.0" } }, + "node_modules/memize": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz", + "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==", + "license": "MIT" + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -14145,6 +14964,48 @@ "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", "dev": true }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/mousetrap": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", + "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==", + "license": "Apache-2.0 WITH LLVM-exception" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -14157,8 +15018,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -14899,7 +15759,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -14917,7 +15776,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -15021,8 +15879,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -15065,7 +15922,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -15110,8 +15966,7 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -16359,6 +17214,16 @@ "node": ">=0.10.0" } }, + "node_modules/re-resizable": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", + "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -16370,6 +17235,48 @@ "node": ">=0.10.0" } }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-day-picker": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.14.0.tgz", + "integrity": "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "@tabby_ai/hijri-converter": "1.0.5", + "date-fns": "^4.1.0", + "date-fns-jalali": "4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-day-picker/node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -16546,6 +17453,12 @@ "node": ">=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": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -16658,6 +17571,24 @@ "node": ">=6" } }, + "node_modules/rememo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz", + "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==", + "license": "MIT" + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", + "license": "MIT" + }, + "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", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16709,7 +17640,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -16929,6 +17859,12 @@ "queue-microtask": "^1.2.2" } }, + "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/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -16961,7 +17897,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -17013,8 +17948,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { "version": "1.92.1", @@ -18581,6 +19515,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -18613,7 +19553,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -18745,6 +19684,15 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/tannin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tannin/-/tannin-1.2.0.tgz", + "integrity": "sha512-U7GgX/RcSeUETbV7gYgoz8PD7Ni4y95pgIP/Z6ayI3CfhSujwKEBlGFTCRN+Aqnuyf4AN2yHL+L8x+TCGjb9uA==", + "license": "MIT", + "dependencies": { + "@tannin/plural-forms": "^1.1.0" + } + }, "node_modules/tapable": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", @@ -19555,6 +20503,24 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -20314,7 +21280,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, "engines": { "node": ">= 6" } diff --git a/package.json b/package.json index 65b5d93..138e1ea 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,10 @@ "build": "node scripts/update-build-type.js free && node scripts/sync-version.js ${npm_config_suffix} && npx wp-scripts build && npm run build:css && npm run build:frontend-php && npm run optimize-videos && BUILD_TYPE=free npm run package ${npm_config_suffix}", "build:frontend-php": "node scripts/generate-frontend-php-scripts.mjs production free", "build:frontend-php:premium": "node scripts/generate-frontend-php-scripts.mjs production premium", - "build:css": "webpack --config webpack.css.config.js && npm run clean:css-js", + "build:css": "webpack --config webpack.css.config.js && npm run build:css:wp-components-scoped && npm run clean:css-js", + "build:css:wp-components-scoped": "node scripts/build-scoped-wp-components-css.mjs", "clean:css-js": "rm -f dist/*.css.js", - "start": "node scripts/update-build-type.js free && concurrently \"wp-scripts start\" \"webpack --config webpack.css.config.js --watch\"", + "start": "npm run build:css:wp-components-scoped && node scripts/update-build-type.js free && concurrently \"wp-scripts start\" \"webpack --config webpack.css.config.js --watch\"", "lint:js": "wp-scripts lint-js", "lint:js:fix": "wp-scripts lint-js --fix", "lint:css": "wp-scripts lint-style", @@ -25,7 +26,7 @@ "optimize-videos": "node scripts/optimize-videos.js", "package": "node scripts/package.js", "sync-version": "node scripts/sync-version.js", - "build:premium": "node scripts/update-build-type.js premium && node scripts/sync-version.js ${npm_config_suffix} && npm run clean:css-js && npx wp-scripts build --config pro__premium_only/webpack.config.js && npx webpack --config pro__premium_only/webpack.css.config.js && npm run build:frontend-php:premium && npm run optimize-videos && BUILD_TYPE=premium npm run package ${npm_config_suffix}", + "build:premium": "node scripts/update-build-type.js premium && node scripts/sync-version.js ${npm_config_suffix} && npm run clean:css-js && npx wp-scripts build --config pro__premium_only/webpack.config.js && npx webpack --config pro__premium_only/webpack.css.config.js && npm run build:css:wp-components-scoped && npm run build:frontend-php:premium && npm run optimize-videos && BUILD_TYPE=premium npm run package ${npm_config_suffix}", "start:premium": "cd pro__premium_only && npm run start", "lint:premium": "cd pro__premium_only && npm run lint", "lint:premium:fix": "cd pro__premium_only && npm run lint:fix" @@ -34,28 +35,29 @@ "extends @wordpress/browserslist-config" ], "devDependencies": { + "@node-minify/core": "^8.0.6", + "@node-minify/uglify-js": "^8.0.6", + "@svgr/webpack": "^8.1.0", + "@wordpress/babel-preset-default": "^8.19.0", "@wordpress/eslint-plugin": "^22.15.0", "@wordpress/prettier-config": "^4.29.0", "@wordpress/scripts": "^30.0.0", "@wordpress/stylelint-config": "^23.21.0", - "@wordpress/babel-preset-default": "^8.19.0", - "@svgr/webpack": "^8.1.0", - "@node-minify/core": "^8.0.6", - "@node-minify/uglify-js": "^8.0.6", + "archiver": "^6.0.1", "babel-loader": "^9.2.1", "concurrently": "^8.2.2", + "eslint": "^8.57.0", + "eslint-plugin-compat": "^6.0.2", "file-loader": "^6.2.0", "fluent-ffmpeg": "^2.1.3", "mini-css-extract-plugin": "^2.7.6", + "prettier": "^3.3.3", "sass": "^1.69.0", "sass-loader": "^14.0.0", - "style-loader": "^3.3.3", - "archiver": "^6.0.1", - "eslint": "^8.57.0", - "eslint-plugin-compat": "^6.0.2", - "prettier": "^3.3.3" + "style-loader": "^3.3.3" }, "dependencies": { + "@wordpress/components": "^33.0.0", "@wordpress/icons": "^10.31.0", "canvas-confetti": "^1.9.3", "classnames": "^2.5.1", diff --git a/readme.txt b/readme.txt index 7f01f92..348cdf0 100644 --- a/readme.txt +++ b/readme.txt @@ -1,4 +1,4 @@ -=== Interactions - Create Interactive Experiences in the Block Editor === +=== Interactions - Create Interactive Experiences in WordPress Editors and Builders === Contributors: bfintal, gambitph Tags: animation, interaction, interactivity, blocks, gutenberg Requires at least: 6.7.5 @@ -8,15 +8,15 @@ Stable tag: 1.3.3 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html -Add animations and interactivity to your blocks. Choose from ready-made effects like scroll & hover in the Interactions Library, or build your own. +Add animations and interactivity to your WordPress content. Choose from ready-made effects like scroll and hover in the Interactions Library, or build your own. == Description == -**Interactions – WordPress Animations, Interactive Experiences for Gutenberg Blocks** +**Interactions – WordPress Animations and Interactive Experiences for Gutenberg, Elementor, and Bricks** [Visit our website](https://wpinteractions.com) to learn more about how Interactions work. -Want to make your website feel alive and interactive? **Interactions** is the easiest way to add animations, effects, interactivity, and functional features to WordPress — directly inside the block editor. Check our [samples page here](https://wpinteractions.com/samples/) to see a glimpse of what type of interactions you can create. +Want to make your website feel alive and interactive? **Interactions** is the easiest way to add animations, effects, interactivity, and functional features to WordPress — directly inside the editor or builder you already use. Check our [samples page here](https://wpinteractions.com/samples/) to see a glimpse of what type of interactions you can create. You don't need coding skills or complex tools. With Interactions, you can: @@ -65,7 +65,7 @@ Create [custom interactions](https://docs.wpinteractions.com/article/571-what-ar - Designers who want **scroll animations** without code - Marketers who want **attention-grabbing hover effects** - Bloggers who want **dynamic storytelling** with animations -- Site builders who want **to bring their block designs to life, or create unique micro-interactions** +- Site builders who want **to bring their designs to life, or create unique micro-interactions** - Developers who need **functional features** like post meta updates and data handling - Anyone building **modern interactive websites** in WordPress @@ -110,11 +110,11 @@ https://github.com/gambitph/Interactions 1. Install “Interactions” from the WordPress Plugin Directory, or upload it to `/wp-content/plugins/interactions/`. 2. Activate the plugin from the “Plugins” menu. -3. Edit a post or page with the block editor. -4. Open the **Interactions Library** panel from the top and pick an effect. +3. Edit a post or page with Gutenberg, Elementor, or Bricks. +4. In Gutenberg, open the **Interactions Library** panel from the top and pick an effect. – OR – Create your own using the **Trigger → Action builder**. -5. Save and preview your interactive blocks! +5. Save and preview your interactive elements. == Frequently Asked Questions == @@ -122,7 +122,8 @@ https://github.com/gambitph/Interactions No! While Interactions excels at animations, it's much more than that. It's a comprehensive interaction system that includes functional features like updating post meta, triggering DOM events, copying text to clipboard, and much more. You can build both visual effects and powerful functional features. = Does it work with Elementor or other page builders? = -No. Interactions is built specifically for the **WordPress block editor (Gutenberg)**. +Yes. Interactions works in the **WordPress block editor (Gutenberg)** and also includes editor integrations for **Elementor** and **Bricks**. +The full **Interactions Library** browsing flow is currently available in Gutenberg, while Elementor and Bricks support creating and managing interactions directly from their builder panels. = Can I add scroll animations to WordPress with this plugin? = Yes. You can animate blocks when they enter the viewport, fade in, slide in, or trigger other effects on scroll. diff --git a/scripts/build-scoped-wp-components-css.mjs b/scripts/build-scoped-wp-components-css.mjs new file mode 100644 index 0000000..1ce224a --- /dev/null +++ b/scripts/build-scoped-wp-components-css.mjs @@ -0,0 +1,101 @@ +import fs from 'fs' +import path from 'path' +import postcss from 'postcss' + +const ROOT = process.cwd() +const INPUT_CSS = path.join( + ROOT, + 'node_modules', + '@wordpress', + 'components', + 'build-style', + 'style.css' +) +const OUTPUT_CSS = path.join( ROOT, 'dist', 'wp-components-scoped.css' ) + +const COMPONENT_SCOPES = [ + '.interact-builder-root', + '.interact-popover', +] +const BODY_SCOPE = 'body.interact-builder-editor' +const PORTAL_PATTERNS = [ + '.components-modal', + '.components-snackbar', + '.components-tooltip', + '.components-guide', +] + +// Wrap scope selectors in :where() so scoping does not increase specificity. +const wrapScope = scope => `:where(${ scope })` + +// Some WordPress component UI is rendered in portals outside our sidebar root. +const isPortalSelector = selector => { + return PORTAL_PATTERNS.some( pattern => selector.includes( pattern ) ) +} + +// Check if a rule is inside a keyframes block, which should not be scoped. +const isInsideKeyframes = rule => { + let current = rule.parent + while ( current ) { + if ( current.type === 'atrule' && current.name.includes( 'keyframes' ) ) { + return true + } + current = current.parent + } + return false +} + +// Scope each selector to the Interactions Elementor UI while keeping popovers +// and other portal-based components reachable outside the sidebar root. +const scopeRootSelector = selector => { + if ( selector.startsWith( ':root' ) ) { + return [ ...COMPONENT_SCOPES.map( wrapScope ), wrapScope( BODY_SCOPE ) ] + } + + if ( selector.startsWith( 'body' ) ) { + return [ selector.replace( /^body\b/, wrapScope( BODY_SCOPE ) ) ] + } + + if ( selector.startsWith( 'html' ) ) { + return [ `${ wrapScope( BODY_SCOPE ) } ${ selector }` ] + } + + if ( isPortalSelector( selector ) ) { + return [ `${ wrapScope( BODY_SCOPE ) } ${ selector }` ] + } + + if ( /(^|[\s>+~])\.components-popover(?![a-zA-Z0-9_-])/.test( selector ) ) { + return [ selector.replace( /(^|[\s>+~])\.components-popover(?![a-zA-Z0-9_-])/g, `$1${ wrapScope( '.interact-popover.components-popover' ) }` ) ] + } + + return COMPONENT_SCOPES.map( scope => `${ wrapScope( scope ) } ${ selector }` ) +} + +const css = fs.readFileSync( INPUT_CSS, 'utf8' ) +const root = postcss.parse( css ) + +root.walkRules( rule => { + if ( ! Array.isArray( rule.selectors ) ) { + return + } + + if ( isInsideKeyframes( rule ) ) { + return + } + + rule.selectors = rule.selectors.flatMap( selector => scopeRootSelector( selector ) ) +} ) + +fs.mkdirSync( path.dirname( OUTPUT_CSS ), { recursive: true } ) +fs.writeFileSync( OUTPUT_CSS, root.toString() ) + +process.stdout.write( + JSON.stringify( + { + input: path.relative( ROOT, INPUT_CSS ), + output: path.relative( ROOT, OUTPUT_CSS ), + }, + null, + 2 + ) + '\n' +) diff --git a/src/editor/app.js b/src/editor/app.js new file mode 100644 index 0000000..f263317 --- /dev/null +++ b/src/editor/app.js @@ -0,0 +1,423 @@ +import ElementSVG from './assets/element.svg' +import PageSVG from './assets/page.svg' +import { + AddInteractionButton, + InteractionButton, + InteractionPanel, + ImportExportModal, +} from './components' +import { createNewInteraction, createNewAction } from './util' +import { useInteractions } from './hooks' +import { interactions as interactionsConfig, manageInteractionsUrl } from 'interactions' +import { InteractionLibrary } from './interaction-library' +import { isGutenbergEditor } from '~interact/editor/editors' + +import { __ } from '@wordpress/i18n' +import { upload } from '@wordpress/icons' +import { + PanelBody, + Button, + BaseControl, + Notice, +} from '@wordpress/components' +import { + useState, + useCallback, + useRef, + useEffect, + createInterpolateElement, +} from '@wordpress/element' +import { useSelect, useDispatch } from '@wordpress/data' + +import useOnPostPreview from './use-on-post-save' + +// Get dismissed errors from localStorage with error handling. +const getDismissedErrors = () => { + try { + const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' ) + return Array.isArray( dismissedErrors ) ? dismissedErrors : [] + } catch ( error ) { + return [] + } +} + +// Normalize imported interaction data to ensure it has the expected structure, even if some fields are missing. +const normalizeImportedInteraction = data => { + const timelines = data.timelines || [] + + return { + ...data, + timelines: timelines.map( timeline => { + const actionsToImport = timeline.actions || [] + const actions = actionsToImport.map( action => ( + createNewAction( { + actionType: action.type ?? '', + start: action.timing?.start ?? 0, + targetType: action.target?.type ?? '', + props: { ...action }, + } ) + ) ) + + return { + ...timeline, + actions, + } + } ), + } +} + +const InteractionsApp = ( { + selectedBlockAnchor = null, + enablePostPreviewGuard = true, +} ) => { + const isGutenberg = isGutenbergEditor() + const interactionLibraryMode = useSelect( select => + select( 'interact/interaction-library-modal' ).getMode(), + [] ) + // Interaction library open modal and set target function. + const { + setMode: setInteractionLibraryMode, + } = useDispatch( 'interact/interaction-library-modal' ) + + const [ selectedInteraction, setSelectedInteraction ] = useState( null ) + const [ editPropsPassed, setEditPropsPassed ] = useState( {} ) + const [ editMode, setEditMode ] = useState( 'edit' ) + const [ isShowingError, setIsShowingError ] = useState( true ) + const [ importExportModalProps, setImportExportModalProps ] = useState( null ) + + const sidebarRef = useRef() + // This gets updated when the current interaction being edited is dirty. + const isDirtyRef = useRef( false ) + + const { + interactions: allInteractions, + interactionsFiltered: interactions, + loadingError, + updateInteraction, + deleteInteraction, + } = useInteractions() + + // This listens to the post preview button and publish button and asks the + // user to save the interaction if it's dirty. + const onPostSaveCallback = proceedSaveCallback => { + if ( isDirtyRef.current ) { + // eslint-disable-next-line no-alert + if ( confirm( __( 'You have unsaved changes in your interaction. Do you want to save it before continuing?', 'interactions' ) ) ) { + window?.dispatchEvent( new CustomEvent( 'interact/save-interaction', { + detail: { + // This callback will be called after the interaction is saved. + callback: proceedSaveCallback, + }, + } ) ) + return true + } + } + } + + useOnPostPreview( enablePostPreviewGuard ? onPostSaveCallback : () => {} ) + + const getInteractionFromKey = useCallback( key => { + return allInteractions.find( interaction => interaction.key === key ) + }, [ allInteractions ] ) + + const onAddInteractionHandler = useCallback( ( interactionType, target = null, props = {} ) => { + if ( selectedInteraction && isDirtyRef.current ) { + alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert + return + } + setEditMode( 'new' ) + const newInteraction = createNewInteraction( interactionType, target, props ) + setSelectedInteraction( newInteraction ) + }, [ selectedInteraction ] ) + + const onEditInteractionHandler = useCallback( ( keyOrInteraction, editProps ) => { + if ( selectedInteraction && isDirtyRef.current ) { + alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert + return + } + // If editMode is provided (e.g. when duplicating), set the editMode state accordingly. + if ( typeof editProps.editMode !== 'undefined' ) { + setEditMode( editProps.editMode ) + } + setEditPropsPassed( editProps ) + setSelectedInteraction( typeof keyOrInteraction === 'string' ? getInteractionFromKey( keyOrInteraction ) : keyOrInteraction ) + }, [ getInteractionFromKey, selectedInteraction ] ) + + // Listen to external adds of interactions from the main toolbar button. + useEffect( () => { + const onAddInteractionEventHandler = event => { + onAddInteractionHandler( event.detail.type, event.detail.target, event.detail.props ) + } + const onEditInteractionEventHandler = event => { + const { + key, interaction, ...editProps + } = event.detail + onEditInteractionHandler( key || interaction, editProps ) + } + + window?.addEventListener( 'interact/add-interaction', onAddInteractionEventHandler ) + window?.addEventListener( 'interact/edit-interaction', onEditInteractionEventHandler ) + + return () => { + window?.removeEventListener( 'interact/add-interaction', onAddInteractionEventHandler ) + window?.removeEventListener( 'interact/edit-interaction', onEditInteractionEventHandler ) + } + }, [ onAddInteractionHandler, onEditInteractionHandler ] ) + + useEffect( () => { + if ( ! selectedInteraction ) { + setEditPropsPassed( {} ) + } + }, [ selectedInteraction ] ) + + useEffect( () => { + const dismissedErrors = getDismissedErrors() + const errorKey = loadingError?.interactionKey + + if ( ! loadingError?.interactionKey ) { + return + } + + if ( dismissedErrors.includes( errorKey ) ) { + setIsShowingError( false ) + } else { + setIsShowingError( true ) + } + }, [ loadingError ] ) + + // Interaction library can only be opened if the current interaction is not dirty. + useEffect( () => { + if ( ! isGutenberg ) { + return + } + + if ( selectedInteraction && isDirtyRef.current && interactionLibraryMode ) { + setInteractionLibraryMode( null ) + alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert + } + }, [ isGutenberg, selectedInteraction, isDirtyRef, interactionLibraryMode, setInteractionLibraryMode ] ) + + const { elementInteractions, pageInteractions } = interactions.reduce( ( acc, interaction ) => { + const interactionConfig = interactionsConfig[ interaction.type ] + if ( interactionConfig?.type === 'element' ) { + acc.elementInteractions.push( interaction ) + } else if ( interactionConfig?.type === 'page' ) { + acc.pageInteractions.push( interaction ) + } + return acc + }, { elementInteractions: [], pageInteractions: [] } ) + + const onOpenImportExportModal = props => { + setImportExportModalProps( props ) + } + + const onCloseImportExportModal = () => { + setImportExportModalProps( null ) + } + + return <> + { selectedInteraction === null && loadingError && isShowingError && + + setIsShowingError( false ) } + isDismissible={ false } + > +

{ loadingError.message }

+

{ __( 'Check the browser console for more details.', 'interactions' ) }

+
+ +
+
+
+ } + { allInteractions.length > 0 && selectedInteraction === null && + + { interactions.length > 0 &&

{ __( 'These interactions are on this page because of their location rules.', 'interactions' ) }

} + { interactions.length === 0 &&

{ __( 'There are no interactions on this page because no matches were found in the location rules.', 'interactions' ) }

} + +
+ } + { selectedInteraction === null && +
+ +

+ { __( 'Animate or trigger actions on any button, image, text or widget.', 'interactions' ) } +   + + { __( 'Learn more', 'interactions' ) } + +

+ +
+
+
+ { elementInteractions.map( interaction => { + return ( + { + setSelectedInteraction( interaction ) + } } + onDelete={ () => { + deleteInteraction( interaction.key ) + } } + /> + ) + } ) } + { ! elementInteractions.length && ( +
+ +

{ __( 'Define actions that occur when user interacts with elements on your page', 'interactions' ) }

+
+ ) } +
+
+
+ +

+ { __( 'Launch page-wide transitions, backgrounds or state-based effects.', 'interactions' ) } +   + + { __( 'Learn more', 'interactions' ) } + +

+ +
+
+
+ { pageInteractions.map( interaction => { + return ( + { + setSelectedInteraction( interaction ) + } } + onDelete={ () => { + deleteInteraction( interaction.key ) + } } + /> + ) + } ) } + { ! pageInteractions.length && ( +
+ +

{ __( 'Define actions that occur when there\'s a change in your page\'s state', 'interactions' ) }

+
+ ) } +
+
+ +
+ } + { selectedInteraction !== null && + { + return updateInteraction( newInteraction ).then( () => { + setEditMode( 'edit' ) + } ) + } } + onClose={ ( focusOnInteractionButton = false ) => { + if ( focusOnInteractionButton ) { + setTimeout( () => { + sidebarRef.current?.querySelector( `.interact-list__item-button--${ selectedInteraction.key }` )?.focus() + } ) + } + setSelectedInteraction( null ) + setEditMode( 'edit' ) + } } + onDelete={ () => deleteInteraction( selectedInteraction.key ) } + onDirtyChange={ isDirty => isDirtyRef.current = isDirty } + onOpenImportExportModal={ onOpenImportExportModal } + /> + } + { importExportModalProps && + + } + { isGutenberg && interactionLibraryMode && } + +} + +export default InteractionsApp diff --git a/src/editor/components/add-interaction-popover/editor.scss b/src/editor/components/add-interaction-popover/editor.scss index 2835b08..975385f 100644 --- a/src/editor/components/add-interaction-popover/editor.scss +++ b/src/editor/components/add-interaction-popover/editor.scss @@ -56,9 +56,8 @@ .interact-target-block-button { align-self: end; - margin-bottom: 8px; - width: 30px; - height: 30px; + width: 32px; + height: 32px; min-width: 30px !important; + * { flex: 1; diff --git a/src/editor/components/add-interaction-popover/index.js b/src/editor/components/add-interaction-popover/index.js index 0301258..6294a22 100644 --- a/src/editor/components/add-interaction-popover/index.js +++ b/src/editor/components/add-interaction-popover/index.js @@ -10,12 +10,15 @@ import { import { useInteractions } from '~interact/editor/hooks' import { getOrGenerateBlockAnchor, - getOrGenerateBlockClass, getLocationForCurrentPage, duplicateInteraction, setBlockAnchorIfPossible, openInteractionsSidebar, } from '~interact/editor/util' +import { + getCurrentSelectedTarget, + isBuilderEditor, +} from '~interact/editor/editors' import { cloneDeep, first } from 'lodash' import { @@ -31,9 +34,8 @@ import { __experimentalToggleGroupControlOption as ToggleGroupControlOption, } from '@wordpress/components' import { useState } from '@wordpress/element' -import { useSelect, select } from '@wordpress/data' +import { useSelect } from '@wordpress/data' import { __, sprintf } from '@wordpress/i18n' -import { BlockPickerPopover } from '../target-selector' import { ProUpsell } from '../pro-crown' const NOOP = () => {} @@ -55,22 +57,29 @@ const AddInteractionPopover = props => { const [ selected, setSelected ] = useState( initialSelected ) const [ showDescription, setShowDescription ] = useState( null ) - const [ hidden, setHidden ] = useState( false ) + const isBuilder = isBuilderEditor() const { getBlockNamesByClientId, getSelectedBlockClientId, } = useSelect( select => { + const blockEditorStore = select( 'core/block-editor' ) return { - getBlockNamesByClientId: select( 'core/block-editor' ).getBlockNamesByClientId, - getSelectedBlockClientId: select( 'core/block-editor' ).getSelectedBlockClientId, + getBlockNamesByClientId: blockEditorStore?.getBlockNamesByClientId || ( () => [] ), + getSelectedBlockClientId: blockEditorStore?.getSelectedBlockClientId || ( () => null ), } } ) - const [ target, setTarget ] = useState( { - type: 'block', - value: getOrGenerateBlockAnchor( getSelectedBlockClientId(), false ) || '', - blockName: first( getBlockNamesByClientId( getSelectedBlockClientId() ) ) || '', + const [ target, setTarget ] = useState( () => { + const current = getCurrentSelectedTarget() + const clientId = getSelectedBlockClientId() + + return { + type: current?.type || ( isBuilder ? 'selector' : 'block' ), + value: current?.value || getOrGenerateBlockAnchor( clientId, false ) || '', + blockName: current?.blockName || first( getBlockNamesByClientId( clientId ) ) || '', + options: current?.options || '', + } } ) const libraryTitle = ! showElementOption && showPageOption ? __( 'My Page Interactions', 'interactions' ) @@ -94,45 +103,6 @@ const AddInteractionPopover = props => { return acc }, { elementInteractions: [], pageInteractions: [] } ) - if ( hidden ) { - return ( - { - const valueArgs = { - ...target, - blockName, - } - - let pickerMode = target === 'block' ? 'id' : 'class' - if ( pickerMode === 'id' ) { - // If id, use the block id as the anchor. If the - // block doesn't support anchors is not supported, - // then use picker mode class. - const hasAnchorAttribute = !! select( 'core/blocks' ).getBlockType( blockName )?.attributes?.anchor - if ( hasAnchorAttribute ) { - valueArgs.value = getOrGenerateBlockAnchor( clientId, true ) - } else { - pickerMode = 'class' - } - } - - if ( pickerMode === 'class' ) { - // If class, use the first class name if there is one, or create a new one. - valueArgs.value = getOrGenerateBlockClass( clientId, true ) - valueArgs.type = 'class' - } - - setTarget( valueArgs ) - setHidden( false ) - } } - onClose={ () => setHidden( false ) } - /> - ) - } - return ( { setHidden( true ) } /> ) } diff --git a/src/editor/components/import-export-modal/editor.scss b/src/editor/components/import-export-modal/editor.scss index e779c88..a195ecd 100644 --- a/src/editor/components/import-export-modal/editor.scss +++ b/src/editor/components/import-export-modal/editor.scss @@ -1,7 +1,19 @@ +.components-modal__screen-overlay:has(.interact-import-export-modal) { + position: fixed !important; + inset: 0 !important; + display: flex !important; + align-items: center; + justify-content: center; + padding: 24px; + z-index: 100001; +} + .interact-import-export-modal { width: 100%; max-width: 50%; max-height: 90%; + margin: 0 !important; + z-index: 100002; .components-modal__content { margin-top: 56px; diff --git a/src/editor/components/import-export-modal/index.js b/src/editor/components/import-export-modal/index.js index e092f85..44727f7 100644 --- a/src/editor/components/import-export-modal/index.js +++ b/src/editor/components/import-export-modal/index.js @@ -6,6 +6,7 @@ * External deprendencies */ import { getOrGenerateBlockAnchor } from '~interact/editor/util' +import { getCurrentSelectedTarget } from '~interact/editor/editors' import { first } from 'lodash' /** @@ -52,9 +53,10 @@ const ImportExportModal = props => { getBlockNamesByClientId, getSelectedBlockClientId, } = useSelect( select => { + const blockEditorStore = select( 'core/block-editor' ) return { - getBlockNamesByClientId: select( 'core/block-editor' ).getBlockNamesByClientId, - getSelectedBlockClientId: select( 'core/block-editor' ).getSelectedBlockClientId, + getBlockNamesByClientId: blockEditorStore?.getBlockNamesByClientId || ( () => [] ), + getSelectedBlockClientId: blockEditorStore?.getSelectedBlockClientId || ( () => null ), } } ) @@ -83,9 +85,14 @@ const ImportExportModal = props => { target = null, } = data + const selectedTarget = getCurrentSelectedTarget() + if ( selectedTarget ) { + target = selectedTarget + } + // If the currently selected block is valid, overwrite the interaction trigger. const clientId = getSelectedBlockClientId() - if ( clientId ) { + if ( clientId && ! selectedTarget ) { target = { type: 'block', value: getOrGenerateBlockAnchor( clientId, true ) || '', diff --git a/src/editor/components/interaction-panel/editor.scss b/src/editor/components/interaction-panel/editor.scss index 20922fb..7673ca6 100644 --- a/src/editor/components/interaction-panel/editor.scss +++ b/src/editor/components/interaction-panel/editor.scss @@ -1,6 +1,6 @@ .interact-interaction-card { padding: 16px; - margin-top: 70px; // Push the contents down because the controls are fixed on top + margin-top: 60px; // Push the contents down because the controls are fixed on top h2 { margin: 0 0 8px; } diff --git a/src/editor/components/location-rules/index.js b/src/editor/components/location-rules/index.js index f9bb975..3bf05ba 100644 --- a/src/editor/components/location-rules/index.js +++ b/src/editor/components/location-rules/index.js @@ -9,12 +9,15 @@ import { import { Fragment, useEffect, useState, } from '@wordpress/element' -import { select } from '@wordpress/data' import { __, sprintf } from '@wordpress/i18n' import apiFetch from '@wordpress/api-fetch' +import { getCurrentEditorPostContext } from '~interact/editor/editors' const NOOP = () => {} +const getCurrentPostId = () => getCurrentEditorPostContext().postId +const getCurrentPostType = () => getCurrentEditorPostContext().postType + const updateLocation = ( locations, index1, index2, newLocation ) => { const newLocations = cloneDeep( locations ) newLocations[ index1 ][ index2 ] = newLocation @@ -94,7 +97,7 @@ const LocationRules = props => { onChange( removeLocation( locations, i, k ) ) } } onClickAnd={ () => { - const value = locations.length === 0 ? select( 'core/editor' ).getCurrentPostId() : '' + const value = locations.length === 0 ? getCurrentPostId() : '' onChange( addLocation( locations, i, k + 1, { param: 'post', operator: '==', @@ -111,7 +114,7 @@ const LocationRules = props => { label={ __( 'Add rule group', 'interactions' ) } variant="secondary" onClick={ () => { - const value = locations.length === 0 ? select( 'core/editor' ).getCurrentPostId() : '' + const value = locations.length === 0 ? getCurrentPostId() : '' onChange( addLocation( locations, locations.length, 0, { param: 'post', operator: '==', @@ -151,10 +154,10 @@ const LocationRule = props => { // If param is a post/page, then the post_id doesn't exist yet, then we need to add it near the top as "Current Post" or "Current Page" if ( param === 'post' || param === 'page' ) { - const postType = select( 'core/editor' ).getCurrentPostType() + const postType = getCurrentPostType() options.some( ( { post_type, options } ) => { if ( post_type === postType ) { - const currentPostId = select( 'core/editor' ).getCurrentPostId() + const currentPostId = getCurrentPostId() // Check if the current post is already in the list const exists = options.some( ( { value } ) => { return value === currentPostId diff --git a/src/editor/components/target-selector/editor.scss b/src/editor/components/target-selector/editor.scss index 757e00b..94d6a8b 100644 --- a/src/editor/components/target-selector/editor.scss +++ b/src/editor/components/target-selector/editor.scss @@ -21,6 +21,12 @@ display: none; } +.interact-target-block-button.is-picking { + border-color: var(--wp-components-color-accent, #05f) !important; + box-shadow: inset 0 0 0 1px var(--wp-components-color-accent, #05f); + background: color-mix(in srgb, var(--wp-components-color-accent, #05f) 10%, #fff); +} + .interact-target-selector__warn { background: #fff4e6; border-left: 3px solid #f90; @@ -69,10 +75,3 @@ } } } - -/* Wordpress 7.0 compatibility */ - -// Text control losses its margin bottom. -.interact-target-block-input .components-base-control__field { - margin-bottom: 8px; -} \ No newline at end of file diff --git a/src/editor/components/target-selector/index.js b/src/editor/components/target-selector/index.js index ca37d6b..5b4e6a3 100644 --- a/src/editor/components/target-selector/index.js +++ b/src/editor/components/target-selector/index.js @@ -1,6 +1,12 @@ import TargetSVG from '~interact/editor/assets/target.svg' import { GridLayout, FlexLayout } from '~interact/editor/components' +import { + getSelectedBlockAnchor, + isBricksEditor, + isElementorEditor, + startEditorElementPicker, +} from '~interact/editor/editors' import { getOrGenerateBlockAnchor, getOrGenerateBlockClass } from '~interact/editor/util' import { SelectControl, @@ -38,26 +44,46 @@ const TargetSelector = props => { } = props const [ isPopoverOpen, setIsPopoverOpen ] = useState( false ) + const [ isPickerActive, setIsPickerActive ] = useState( false ) const [ buttonRef, setButtonRef ] = useState( null ) const prevValueRef = useRef( {} ) + const elementPickerStopRef = useRef( null ) + + const isBuilder = isBricksEditor() || isElementorEditor() + const hasBlockEditor = !! select( 'core/block-editor' )?.getSelectedBlockClientId const targetButton = ( <> - - - - } - { allInteractions.length > 0 && selectedInteraction === null && - - { interactions.length > 0 &&

{ __( 'These interactions are on this page because of their location rules.', 'interactions' ) }

} - { interactions.length === 0 &&

{ __( 'There are no interactions on this page because no matches were found in the location rules.', 'interactions' ) }

} - -
- } - { selectedInteraction === null && -
- -

- { __( 'Animate or trigger actions on any button, image, text or widget.', 'interactions' ) } -   - - { __( 'Learn more', 'interactions' ) } - -

- -
-
-
- { elementInteractions - .map( interaction => { - return ( - { - setSelectedInteraction( interaction ) - } } - onDelete={ () => { - deleteInteraction( interaction.key ) - } } - /> - ) - } ) } - { ! elementInteractions.length && ( -
- -

{ __( 'Define actions that occur when user interacts with elements on your page', 'interactions' ) }

-
- ) } -
-
- - - -

- { __( 'Launch page‑wide transitions, backgrounds or state‑based effects.', 'interactions' ) } -   - - { __( 'Learn more', 'interactions' ) } - -

- -
-
-
- { pageInteractions - .map( interaction => { - return ( - { - setSelectedInteraction( interaction ) - } } - onDelete={ () => { - deleteInteraction( interaction.key ) - } } - /> - ) - } ) } - { ! pageInteractions.length && ( -
- -

{ __( 'Define actions that occur when there\'s a change in your page\'s state', 'interactions' ) }

-
- ) } -
-
- - { /* - - */ } -
- } - { selectedInteraction !== null && - { - return updateInteraction( newInteraction ).then( () => { - setEditMode( 'edit' ) - } ) - } } - onClose={ ( focusOnInteractionButton = false ) => { - if ( focusOnInteractionButton ) { - // Focus on the previous interaction button so we can go back to it. - setTimeout( () => { - sidebarRef.current?.querySelector( `.interact-list__item-button--${ selectedInteraction.key }` )?.focus() - } ) - } - setSelectedInteraction( null ) - setEditMode( 'edit' ) - } } - onDelete={ () => deleteInteraction( selectedInteraction.key ) } - onDirtyChange={ isDirty => isDirtyRef.current = isDirty } - onOpenImportExportModal={ onOpenImportExportModal } - /> - } - { importExportModalProps && - - } - - { /* Render the Interaction Library modal in the root editor component */ - interactionLibraryMode && - } - -} +import { getInteractionsEditor } from './editors' +import { domReady } from '~interact/shared/dom-ready.js' -registerPlugin( 'interact-editor', { - render: InteractionsEditor, +domReady( () => { + getInteractionsEditor().init() } ) diff --git a/src/editor/editor.php b/src/editor/editor.php index a964602..88a501d 100644 --- a/src/editor/editor.php +++ b/src/editor/editor.php @@ -18,17 +18,72 @@ class Interact_Editor { */ function __construct() { if ( is_admin() ) { - add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor' ) ); + add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_editor' ) ); add_action( 'enqueue_block_assets', array( $this, 'enqueue_assets' ) ); } + add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_bricks_editor' ) ); + } + + /** + * Loads the editor script inside the Gutenberg editor. + * + * @return void + */ + public function enqueue_gutenberg_editor() { + $this->enqueue_editor( 'gutenberg' ); + } + + /** + * Loads the editor script inside the Elementor editor. + * + * @return void + */ + public function enqueue_elementor_editor() { + $this->enqueue_builder_editor_styles(); + + $this->enqueue_editor( 'elementor' ); + } + + /** + * Loads the editor script inside the Bricks builder. + * + * @return void + */ + public function enqueue_bricks_editor() { + $is_bricks_builder = ( function_exists( 'bricks_is_builder_main' ) && bricks_is_builder_main() ) || + ( function_exists( 'bricks_is_builder' ) && bricks_is_builder() ); + + if ( ! $is_bricks_builder ) { + return; + } + + $this->enqueue_builder_editor_styles(); + $this->enqueue_editor( 'bricks' ); + } + + /** + * Loads the scoped wp-components styles for visual builders. + * + * @return void + */ + private function enqueue_builder_editor_styles() { + wp_enqueue_style( + 'interact-editor-wp-components-scoped', + plugins_url( 'dist/wp-components-scoped.css', INTERACT_FILE ), + array(), + INTERACT_VERSION + ); } /** * Loads the editor script. * + * @param string $editor_mode Current editor mode. + * * @return void */ - public function enqueue_editor() { + public function enqueue_editor( $editor_mode = 'gutenberg' ) { // Load the required interaciton and action types. interact_require_types(); @@ -63,6 +118,7 @@ public function enqueue_editor() { [ $actions, $action_categories ] = $this->get_action_types_config(); global $wp_version; + $post = get_post(); $args = apply_filters( 'interact/localize_script', array( 'interactions' => $interactions, 'interactionCategories' => $interaction_categories, @@ -77,6 +133,14 @@ public function enqueue_editor() { 'restNonce' => wp_create_nonce( 'wp_rest' ), // This needs to be 'wp_rest' to use the built-in nonce verification. 'srcUrl' => untrailingslashit( plugins_url( '/', INTERACT_FILE ) ), 'currentUserCanUnfilteredHtml' => current_user_can( 'unfiltered_html' ), + // Keep the current editor and document context available to the + // shared editor app so integrations can resolve targets and + // location rules correctly. + 'editorMode' => $editor_mode, + 'currentPostId' => $post ? (int) $post->ID : 0, + 'currentPostType' => $post ? $post->post_type : '', + 'currentPostTemplate' => $post ? get_page_template_slug( $post->ID ) : '', + 'currentPostParent' => $post ? (int) $post->post_parent : 0, ) ); wp_localize_script( 'interact-editor', 'interactions', $args ); } diff --git a/src/editor/editor.scss b/src/editor/editor.scss index a07a736..ded9334 100644 --- a/src/editor/editor.scss +++ b/src/editor/editor.scss @@ -139,6 +139,124 @@ --wp-components-color-accent: #05f; } +/* Interaction Elementor Editor Panel Styles */ + +.interact-elementor-launcher, +.interact-bricks-launcher { + position: fixed !important; + right: 32px; + bottom: 32px; + z-index: 100000; + border-radius: 999px !important; + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18); +} + +.interact-elementor-launcher.is-hidden, +.interact-bricks-launcher.is-hidden { + opacity: 0; + pointer-events: none; +} + +.interact-elementor-panel, +.interact-bricks-panel { + position: fixed; + top: 0; + right: 0; + bottom: 0; + width: 280px; + background: #fff; + color: #1e1e1e; + line-height: 18px; + box-shadow: -24px 0 48px rgba(15, 23, 42, 0.18); + transform: translateX(100%); + transition: transform 180ms ease; + z-index: 99999; + display: flex; + flex-direction: column; + + h2 { + font-size: 13px; + margin: 1.33em; + } + + p { + margin: 1em 0; + } +} + +.interact-elementor-panel.is-open, +.interact-bricks-panel.is-open { + transform: translateX(0); +} + +.interact-elementor-panel__header, +.interact-bricks-panel__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 18px; + border-bottom: 1px solid #e5e7eb; + background: #f8fafc; +} + +.interact-elementor-panel__title, +.interact-bricks-panel__title { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; +} + +.interact-elementor-panel__body, +.interact-bricks-panel__body { + flex: 1; + overflow: auto; +} + +.interact-elementor-sidebar, +.interact-bricks-sidebar { + min-height: 100%; + + h2, h4 { + font-size: 1em; + margin: 1.33em 0; + } + + .components-panel__body > .components-panel__body-title { + display: block; + font-size: inherit; + margin-bottom: 0; + margin-top: 0; + padding: 0; + } + + .components-panel__body.is-opened > .components-panel__body-title { + margin: -16px -16px 5px; + } +} + +.interact-bricks-sidebar { + .components-toggle-control__label, + .components-base-control__label { + color: inherit; + margin-bottom: 0; + } +} + +.interact-popover { + color: #3c434a; + line-height: 1.4em; + + h2 { + font-size: 13px; + margin: 1.33em; + } + + p { + margin: 1em 0; + } +} + /* Wordpress 7.0 compatibility */ .interact-sidebar, .interact-popover { @@ -160,4 +278,4 @@ .interact-property-control .components-base-control__field { margin-bottom: 8px; } -} \ No newline at end of file +} diff --git a/src/editor/editors/abstract.js b/src/editor/editors/abstract.js new file mode 100644 index 0000000..2446991 --- /dev/null +++ b/src/editor/editors/abstract.js @@ -0,0 +1,95 @@ +import { + currentPostId, + currentPostParent, + currentPostTemplate, + currentPostType, +} from 'interactions' +import { select } from '@wordpress/data' + +const NOOP = () => {} + +// Base editor adapter that defines the shared editor contract. +class InteractionsEditorAbstract { + constructor() { + this.initialized = false + } + + // Boot the current editor integration. + init() { + this.initialized = true + return this + } + + // Return the current editor mode. + getEditorMode() { + throw new Error( 'InteractionsEditorAbstract#getEditorMode must be implemented.' ) + } + + isElementor() { + return this.getEditorMode() === 'elementor' + } + + isBricks() { + return this.getEditorMode() === 'bricks' + } + + isGutenberg() { + return this.getEditorMode() === 'gutenberg' + } + + isBuilder() { + return this.isElementor() || this.isBricks() + } + + // Return the current document context for location rule matching. + getCurrentPostContext() { + const editorStore = select( 'core/editor' ) + return { + postId: editorStore?.getCurrentPostId?.() || currentPostId || 0, + postType: editorStore?.getCurrentPostType?.() || currentPostType || '', + postTemplate: editorStore?.getCurrentPost?.()?.template || currentPostTemplate || '', + postParent: editorStore?.getCurrentPost?.()?.parent || currentPostParent || 0, + } + } + + getCanvasDocument() { + return document + } + + getCanvasElement() { + const canvasDocument = this.getCanvasDocument() + return canvasDocument?.querySelector( '.editor-styles-wrapper' ) || canvasDocument?.body || document.body + } + + // Open the editor panel when supported by the current integration. + openPanel() { + return null + } + + openInteractionsPanel() { + return this.openPanel() + } + + getSelectedBlockAnchor() { + return null + } + + getCurrentSelectedTarget() { + return null + } + + registerSelectionTracking() { + return NOOP + } + + // Start an editor-specific target picker. + startElementPicker( args = {} ) { + const { + onCancel = NOOP, + } = args + onCancel() + return NOOP + } +} + +export default InteractionsEditorAbstract diff --git a/src/editor/editors/bricks.js b/src/editor/editors/bricks.js new file mode 100644 index 0000000..77e3a43 --- /dev/null +++ b/src/editor/editors/bricks.js @@ -0,0 +1,290 @@ +import IconSVG from '../assets/icon.svg' +import InteractionsApp from '../app' +import InteractionsEditorAbstract from './abstract' + +import { __ } from '@wordpress/i18n' +import { Button } from '@wordpress/components' +import { + useEffect, + useState, + createRoot, +} from '@wordpress/element' + +const NOOP = () => {} + +class BricksInteractionsEditor extends InteractionsEditorAbstract { + constructor() { + super() + this.selectedElement = null + } + + getEditorMode() { + return 'bricks' + } + + init() { + if ( this.initialized ) { + return this + } + + // Bricks loads the editor script in both the builder document and the + // preview iframe. Only mount the Interactions UI in the top-level shell. + if ( window.frameElement ) { + return super.init() + } + + const mountNodeId = 'interact-bricks-root' + if ( document.getElementById( mountNodeId ) ) { + return super.init() + } + + const BricksInteractionsEditorComponent = () => { + const [ isOpen, setIsOpen ] = useState( false ) + + useEffect( () => { + const openHandler = () => setIsOpen( true ) + window.addEventListener( 'interact/open-bricks-sidebar', openHandler ) + return () => window.removeEventListener( 'interact/open-bricks-sidebar', openHandler ) + }, [] ) + + return ( + <> + +
+
+
+ + { __( 'Interactions', 'interactions' ) } +
+
+
+
+ +
+
+
+ + ) + } + + const mountNode = document.createElement( 'div' ) + mountNode.id = mountNodeId + mountNode.className = 'interact-builder-root interact-bricks-root' + document.body.appendChild( mountNode ) + document.body.classList.add( 'interact-builder-editor' ) + document.body.classList.add( 'interact-bricks-editor' ) + this.registerSelectionTracking() + createRoot( mountNode ).render( ) + + return super.init() + } + + getCanvasDocument() { + const iframe = document.querySelector( '#bricks-builder-iframe' ) + return iframe?.contentDocument || null + } + + openPanel() { + window.dispatchEvent( new CustomEvent( 'interact/open-bricks-sidebar' ) ) + return null + } + + getSelectedElementId( element ) { + if ( ! element ) { + return '' + } + + if ( element.dataset?.id ) { + return element.dataset.id + } + + const bricksNode = element.closest( '[data-id]' ) + if ( bricksNode?.dataset?.id ) { + return bricksNode.dataset.id + } + + const frontendNode = element.closest( '[id^="brxe-"]' ) + if ( frontendNode?.id ) { + return frontendNode.id.replace( /^brxe-/, '' ) + } + + return '' + } + + buildTargetFromElement( element ) { + const elementId = this.getSelectedElementId( element ) + if ( ! elementId ) { + return null + } + + const label = element?.dataset?.elementType || element?.tagName?.toLowerCase() || 'bricks-element' + + return { + type: 'selector', + value: `#brxe-${ elementId }`, + blockName: label, + } + } + + getCurrentSelectedTarget() { + if ( ! this.selectedElement ) { + return null + } + + return this.buildTargetFromElement( this.selectedElement.element ) + } + + registerSelectionTracking() { + let isBound = false + let observer = null + + const handleSelection = element => { + this.selectedElement = { + element, + } + } + + const bindListeners = () => { + if ( isBound ) { + return true + } + + const previewDocument = this.getCanvasDocument() + if ( ! previewDocument?.body ) { + return false + } + + previewDocument.addEventListener( 'click', event => { + const candidate = event.target.closest( '[data-id], [id^="brxe-"]' ) + if ( candidate ) { + handleSelection( candidate ) + } + }, true ) + + const structurePanel = document.querySelector( '#bricks-structure' ) + if ( structurePanel ) { + structurePanel.addEventListener( 'click', event => { + const candidate = event.target.closest( '[data-id]' ) + if ( candidate ) { + handleSelection( candidate ) + } + }, true ) + } + + isBound = true + return true + } + + if ( ! bindListeners() ) { + observer = new MutationObserver( () => { + if ( bindListeners() ) { + observer?.disconnect() + } + } ) + + observer.observe( document.body, { + childList: true, + subtree: true, + } ) + } + + return () => observer?.disconnect() + } + + startElementPicker( { + onPick = NOOP, + onCancel = NOOP, + } = {} ) { + const previewDocument = this.getCanvasDocument() + if ( ! previewDocument ) { + onCancel() + return NOOP + } + + let highlightedElement = null + + const clearHighlight = () => { + if ( highlightedElement ) { + highlightedElement.style.outline = highlightedElement.dataset.interactPrevOutline || '' + highlightedElement.style.outlineOffset = highlightedElement.dataset.interactPrevOutlineOffset || '' + delete highlightedElement.dataset.interactPrevOutline + delete highlightedElement.dataset.interactPrevOutlineOffset + } + highlightedElement = null + } + + const getCandidate = element => element?.closest?.( '[data-id], [id^="brxe-"]' ) || null + + const mouseMoveHandler = event => { + const candidate = getCandidate( event.target ) + if ( candidate === highlightedElement ) { + return + } + + clearHighlight() + if ( candidate ) { + highlightedElement = candidate + highlightedElement.dataset.interactPrevOutline = highlightedElement.style.outline || '' + highlightedElement.dataset.interactPrevOutlineOffset = highlightedElement.style.outlineOffset || '' + highlightedElement.style.outline = '2px solid #05f' + highlightedElement.style.outlineOffset = '2px' + } + } + + const clickHandler = event => { + const candidate = getCandidate( event.target ) + if ( ! candidate ) { + return + } + + event.preventDefault() + event.stopPropagation() + const target = this.buildTargetFromElement( candidate ) + stop() + + if ( target ) { + onPick( target ) + } else { + onCancel() + } + } + + const keyHandler = event => { + if ( event.key === 'Escape' ) { + stop() + onCancel() + } + } + + const stop = () => { + clearHighlight() + previewDocument.removeEventListener( 'mousemove', mouseMoveHandler, true ) + previewDocument.removeEventListener( 'click', clickHandler, true ) + previewDocument.removeEventListener( 'keydown', keyHandler, true ) + document.removeEventListener( 'keydown', keyHandler, true ) + } + + previewDocument.addEventListener( 'mousemove', mouseMoveHandler, true ) + previewDocument.addEventListener( 'click', clickHandler, true ) + previewDocument.addEventListener( 'keydown', keyHandler, true ) + document.addEventListener( 'keydown', keyHandler, true ) + + return stop + } +} + +export default BricksInteractionsEditor diff --git a/src/editor/editors/elementor.js b/src/editor/editors/elementor.js new file mode 100644 index 0000000..aa43b5a --- /dev/null +++ b/src/editor/editors/elementor.js @@ -0,0 +1,299 @@ +import IconSVG from '../assets/icon.svg' +import InteractionsApp from '../app' +import InteractionsEditorAbstract from './abstract' + +import { __ } from '@wordpress/i18n' +import { Button } from '@wordpress/components' +import { + useEffect, + useState, + createRoot, +} from '@wordpress/element' + +const NOOP = () => {} + +// Elementor editor adapter. +class ElementorInteractionsEditor extends InteractionsEditorAbstract { + constructor() { + super() + this.selectedElement = null + } + + getEditorMode() { + return 'elementor' + } + + // Mount the Elementor launcher and side panel shell. + init() { + if ( this.initialized ) { + return this + } + + const mountNodeId = 'interact-elementor-root' + if ( document.getElementById( mountNodeId ) ) { + return super.init() + } + + const ElementorInteractionsEditorComponent = () => { + const [ isOpen, setIsOpen ] = useState( false ) + + useEffect( () => { + const openHandler = () => setIsOpen( true ) + window.addEventListener( 'interact/open-elementor-sidebar', openHandler ) + return () => window.removeEventListener( 'interact/open-elementor-sidebar', openHandler ) + }, [] ) + + return ( + <> + + { /* { isOpen && ( +
setIsOpen( false ) } + aria-hidden="true" + /> + ) } */ } +
+
+
+ + { __( 'Interactions', 'interactions' ) } +
+
+
+
+ +
+
+
+ + ) + } + + const mountNode = document.createElement( 'div' ) + mountNode.id = mountNodeId + mountNode.className = 'interact-builder-root interact-elementor-root' + document.body.appendChild( mountNode ) + document.body.classList.add( 'interact-builder-editor' ) + document.body.classList.add( 'interact-elementor-editor' ) + this.registerSelectionTracking() + createRoot( mountNode ).render( ) + + return super.init() + } + + // Return the Elementor preview canvas document. + getCanvasDocument() { + const iframe = document.querySelector( '#elementor-preview-iframe' ) + return iframe?.contentDocument || null + } + + // Open the Interactions sidebar in Elementor. + openPanel() { + window.dispatchEvent( new CustomEvent( 'interact/open-elementor-sidebar' ) ) + return null + } + + getWidgetContentTargetSelector( targetElement, elementId ) { + if ( ! targetElement || ! elementId ) { + return '' + } + + const interactiveDescendant = targetElement.querySelector( `[data-interaction-id="${ elementId }"]` ) + if ( interactiveDescendant ) { + return `[data-interaction-id="${ elementId }"]` + } + + return '' + } + + // Build an interaction target from a selected Elementor element. + buildTargetFromElement( element, targetType = 'selector' ) { + if ( ! element ) { + return null + } + + const targetElement = element.closest( '.elementor-element[data-id]' ) + if ( ! targetElement ) { + return null + } + + const elementId = targetElement.getAttribute( 'data-id' ) + if ( ! elementId ) { + return null + } + + const elementType = targetElement.getAttribute( 'data-element_type' ) || '' + const widgetType = targetElement.getAttribute( 'data-widget_type' ) || '' + const label = widgetType || elementType || 'elementor-element' + const wrapperSelector = `.elementor-element.elementor-element-${ elementId }` + const classValue = `elementor-element-${ elementId }` + const widgetContentSelector = elementType === 'widget' + ? this.getWidgetContentTargetSelector( targetElement, elementId ) + : '' + const targetTargetType = targetType === 'class' && widgetContentSelector + ? 'selector' + : targetType + const targetValue = targetTargetType === 'class' + ? classValue + : elementType === 'widget' + ? widgetContentSelector || wrapperSelector + : wrapperSelector + + return { + type: targetTargetType, + value: targetValue, + blockName: label, + } + } + + getHighlightElement( element ) { + if ( ! element ) { + return null + } + + const targetElement = element.closest( '.elementor-element[data-id]' ) + if ( ! targetElement ) { + return null + } + + const elementType = targetElement.getAttribute( 'data-element_type' ) || '' + if ( elementType !== 'widget' ) { + return targetElement + } + + return targetElement.firstElementChild || targetElement + } + + // Return the currently selected Elementor target. + getCurrentSelectedTarget() { + return this.buildTargetFromElement( this.selectedElement?.element || null ) + } + + // Track the current Elementor selection from the editor panel. + registerSelectionTracking() { + if ( ! window.elementor?.hooks?.addAction ) { + return NOOP + } + + const register = action => { + window.elementor.hooks.addAction( action, ( panel, model, view ) => { + this.selectedElement = { + model, + view, + element: view?.$el?.get?.( 0 ) || null, + } + } ) + } + + const actions = [ + 'panel/open_editor/section', + 'panel/open_editor/column', + 'panel/open_editor/container', + 'panel/open_editor/widget', + ] + actions.forEach( register ) + + return NOOP + } + + // Start an Elementor preview picker for selector or class targets. + startElementPicker( { + targetType = 'selector', + onPick = NOOP, + onCancel = NOOP, + } = {} ) { + const previewDocument = this.getCanvasDocument() + if ( ! previewDocument ) { + onCancel() + return NOOP + } + + let highlightedElement = null + + // Restore the previously highlighted element back to its original outline. + const clearHighlight = () => { + if ( highlightedElement ) { + highlightedElement.style.outline = highlightedElement.dataset.interactPrevOutline || '' + highlightedElement.style.outlineOffset = highlightedElement.dataset.interactPrevOutlineOffset || '' + delete highlightedElement.dataset.interactPrevOutline + delete highlightedElement.dataset.interactPrevOutlineOffset + } + highlightedElement = null + } + + // Follow the pointer inside the preview and visually mark the current pick candidate. + const mouseMoveHandler = event => { + const candidate = event.target.closest( '.elementor-element[data-id]' ) + const highlightCandidate = this.getHighlightElement( candidate ) + if ( highlightCandidate === highlightedElement ) { + return + } + clearHighlight() + if ( highlightCandidate ) { + highlightedElement = highlightCandidate + highlightedElement.dataset.interactPrevOutline = highlightedElement.style.outline || '' + highlightedElement.dataset.interactPrevOutlineOffset = highlightedElement.style.outlineOffset || '' + highlightedElement.style.outline = '2px solid #05f' + highlightedElement.style.outlineOffset = '2px' + } + } + + // Convert the clicked Elementor element into an interaction target and stop pick mode. + const clickHandler = event => { + const candidate = event.target.closest( '.elementor-element[data-id]' ) + if ( ! candidate ) { + return + } + event.preventDefault() + event.stopPropagation() + const target = this.buildTargetFromElement( candidate, targetType ) + stop() + if ( target ) { + onPick( target ) + } else { + onCancel() + } + } + + // Allow canceling the picker with Escape. + const keyHandler = event => { + if ( event.key === 'Escape' ) { + stop() + onCancel() + } + } + + // Remove all temporary picker listeners and preview highlighting. + const stop = () => { + clearHighlight() + previewDocument.removeEventListener( 'mousemove', mouseMoveHandler, true ) + previewDocument.removeEventListener( 'click', clickHandler, true ) + previewDocument.removeEventListener( 'keydown', keyHandler, true ) + document.removeEventListener( 'keydown', keyHandler, true ) + } + + previewDocument.addEventListener( 'mousemove', mouseMoveHandler, true ) + previewDocument.addEventListener( 'click', clickHandler, true ) + previewDocument.addEventListener( 'keydown', keyHandler, true ) + document.addEventListener( 'keydown', keyHandler, true ) + + return stop + } +} + +export default ElementorInteractionsEditor diff --git a/src/editor/editors/gutenberg.js b/src/editor/editors/gutenberg.js new file mode 100644 index 0000000..4edf85d --- /dev/null +++ b/src/editor/editors/gutenberg.js @@ -0,0 +1,119 @@ +import IconSVG from '../assets/icon.svg' +import InteractionsApp from '../app' +import InteractionsEditorAbstract from './abstract' + +import { registerPlugin } from '@wordpress/plugins' +import { __ } from '@wordpress/i18n' +import { + useSelect, + dispatch, + select, +} from '@wordpress/data' + +// Gutenberg editor adapter. +class GutenbergInteractionsEditor extends InteractionsEditorAbstract { + getEditorMode() { + return 'gutenberg' + } + + // Register the Gutenberg sidebar plugin. + init() { + if ( this.initialized ) { + return this + } + + const GutenbergInteractionsEditorComponent = () => { + const SideEditorPluginSidebar = window.wp.editSite?.PluginSidebar + const PostEditorPluginSidebar = window.wp.editPost?.PluginSidebar + const SideBar = SideEditorPluginSidebar ? SideEditorPluginSidebar + : PostEditorPluginSidebar ? PostEditorPluginSidebar : null + + const selectedBlockAnchor = useSelect( select => { + const clientId = select( 'core/block-editor' )?.getSelectedBlockClientId?.() + return clientId ? select( 'core/block-editor' ).getBlockAttributes( clientId )?.anchor : null + }, [] ) + + if ( ! SideBar ) { + return null + } + + return ( + } + > + + + ) + } + + registerPlugin( 'interact-editor', { + render: GutenbergInteractionsEditorComponent, + } ) + + return super.init() + } + + // Return the Gutenberg editor canvas document. + getCanvasDocument() { + const iframe = document.querySelector( 'iframe[name="editor-canvas"]' ) + return iframe?.contentDocument || document + } + + // Open the Interactions sidebar in Gutenberg. + openPanel() { + if ( dispatch( 'core/edit-post' ) ) { + return dispatch( 'core/edit-post' ).openGeneralSidebar( 'interact-editor/sidebar' ) + } + return dispatch( 'core/edit-site' ).openGeneralSidebar( 'interact-editor/sidebar' ) + } + + // Return the currently selected block anchor. + getSelectedBlockAnchor() { + const blockEditorStore = select( 'core/block-editor' ) + if ( ! blockEditorStore?.getSelectedBlockClientId ) { + return null + } + const clientId = blockEditorStore.getSelectedBlockClientId() + return clientId ? blockEditorStore.getBlockAttributes( clientId )?.anchor : null + } + + // Return the current Gutenberg selection as an interaction target. + getCurrentSelectedTarget() { + const blockEditorStore = select( 'core/block-editor' ) + const clientId = blockEditorStore?.getSelectedBlockClientId?.() + if ( ! clientId ) { + return null + } + + const block = blockEditorStore.getBlock?.( clientId ) + if ( ! block ) { + return null + } + + const hasAnchorAttribute = !! select( 'core/blocks' ).getBlockType( block.name )?.attributes?.anchor + if ( hasAnchorAttribute ) { + return { + type: 'block', + value: this.getSelectedBlockAnchor() || '', + blockName: block.name || '', + options: { clientId }, + } + } + + const className = block.attributes?.className?.split( ' ' )?.[ 0 ] || '' + return { + type: 'class', + value: className, + blockName: block.name || '', + options: { clientId }, + } + } +} + +export default GutenbergInteractionsEditor diff --git a/src/editor/editors/index.js b/src/editor/editors/index.js new file mode 100644 index 0000000..fba10b5 --- /dev/null +++ b/src/editor/editors/index.js @@ -0,0 +1,49 @@ +import { editorMode } from 'interactions' +import GutenbergInteractionsEditor from './gutenberg' +import ElementorInteractionsEditor from './elementor' +import BricksInteractionsEditor from './bricks' + +let activeEditor = null + +// Create the active editor adapter for the current editor environment. +const createInteractionsEditor = () => { + return editorMode === 'elementor' + ? new ElementorInteractionsEditor() + : editorMode === 'bricks' + ? new BricksInteractionsEditor() + : new GutenbergInteractionsEditor() +} + +// Return the memoized editor adapter instance. +export const getInteractionsEditor = () => { + if ( ! activeEditor ) { + activeEditor = createInteractionsEditor() + } + return activeEditor +} + +export const getEditorMode = () => getInteractionsEditor().getEditorMode() + +export const isElementorEditor = () => getInteractionsEditor().isElementor() + +export const isBricksEditor = () => getInteractionsEditor().isBricks() + +export const isGutenbergEditor = () => getInteractionsEditor().isGutenberg() + +export const isBuilderEditor = () => getInteractionsEditor().isBuilder() + +export const getCurrentEditorPostContext = () => getInteractionsEditor().getCurrentPostContext() + +export const getSelectedBlockAnchor = () => getInteractionsEditor().getSelectedBlockAnchor() + +export const getEditorCanvasDocument = () => getInteractionsEditor().getCanvasDocument() + +export const getEditorCanvasElement = () => getInteractionsEditor().getCanvasElement() + +export const openInteractionsSidebar = () => getInteractionsEditor().openInteractionsPanel() + +export const getCurrentSelectedTarget = () => getInteractionsEditor().getCurrentSelectedTarget() + +export const registerEditorSelectionTracking = () => getInteractionsEditor().registerSelectionTracking() + +export const startEditorElementPicker = args => getInteractionsEditor().startElementPicker( args ) diff --git a/src/editor/hooks/use-interactions.js b/src/editor/hooks/use-interactions.js index c007ebb..a8ac4d9 100644 --- a/src/editor/hooks/use-interactions.js +++ b/src/editor/hooks/use-interactions.js @@ -10,6 +10,7 @@ import { domReady } from '~interact/shared/dom-ready.js' import apiFetch from '@wordpress/api-fetch' import { __ } from '@wordpress/i18n' import { ensureInteractionDefaults } from '../util' +import { getCurrentEditorPostContext, getEditorMode } from '~interact/editor/editors' const DEFAULT_STATE = { interactions: [], @@ -92,16 +93,12 @@ register( createReduxStore( 'interact/interactions', { * Whether or not the interaction should be shown in the editor based on what's * currently beign edited in the Block Editor. * - * @param {Array} interaction - * @param {Object} select wp.data.select + * @param {Array} interaction * * @return {boolean} Whether or not the interaction should be shown in the editor. */ -export const isInteractionShown = ( interaction, select ) => { - // If the editor is not available (e.g. in Widgets editor), don't do anything. - if ( ! select( 'core/editor' ) ) { - return false - } +export const isInteractionShown = interaction => { + const currentContext = getCurrentEditorPostContext() return interaction.locations.some( locationGroup => { return locationGroup.every( location => { const { @@ -113,15 +110,15 @@ export const isInteractionShown = ( interaction, select ) => { case 'page': { // If blank, then it's all posts/pages. if ( ! value || isNaN( +value ) ) { - const postType = select( 'core/editor' ).getCurrentPostType() + const postType = currentContext.postType const postTypeParam = value || param return operator === '==' ? postType === postTypeParam : postType !== postTypeParam } - const match = value.toString() === select( 'core/editor' ).getCurrentPostId()?.toString() + const match = value.toString() === currentContext.postId?.toString() return operator === '==' ? match : ! match } case 'post_type': { - const match = value.toString() === select( 'core/editor' ).getCurrentPostType()?.toString() + const match = value.toString() === currentContext.postType?.toString() return operator === '==' ? match : ! match } case 'post_status': @@ -131,20 +128,20 @@ export const isInteractionShown = ( interaction, select ) => { return true case 'post_template': case 'page_template': { - const match = value.toString() === select( 'core/editor' ).getCurrentPost()?.template.toString() + const match = value.toString() === currentContext.postTemplate?.toString() return operator === '==' ? match : ! match } case 'post_parent': case 'page_parent': { - const match = value.toString() === select( 'core/editor' ).getCurrentPost()?.parent.toString() + const match = value.toString() === currentContext.postParent?.toString() return operator === '==' ? match : ! match } case 'all': // Entire website return true case 'wp_template': // Site editor templates: home, 404, etc - const currentPostType = select( 'core/editor' ).getCurrentPostType() + const currentPostType = currentContext.postType if ( currentPostType === 'wp_template' ) { - const match = value.toString() === select( 'core/editor' ).getCurrentPostId()?.toString() + const match = value.toString() === currentContext.postId?.toString() return operator === '==' ? match : ! match } break @@ -177,7 +174,7 @@ const useInteractions = () => { const updateInteraction = newInteraction => { // Check if we updated any anchors/attributes, if we did, then we need to ask whether to also update the post. const didModifyPostContent = select( 'interact/interactions' ).didModifyPostContent() - if ( didModifyPostContent ) { + if ( didModifyPostContent && getEditorMode() === 'gutenberg' ) { if ( confirm( __( 'Some block anchors have been updated for your interactions to work correctly. Do you want to save these post changes? (Any modified synced patterns will also be saved)', 'interactions' ) ) ) { // eslint-disable-line no-alert dispatch( 'interact/interactions' ).setDidModifyPostContent( false ) // Save the post. diff --git a/src/editor/plugins/index.js b/src/editor/plugins/index.js index c448b35..f3af43f 100644 --- a/src/editor/plugins/index.js +++ b/src/editor/plugins/index.js @@ -1,4 +1,8 @@ -import './block-toolbar-button' -import './top-toolbar-button' -import './block-highlight' -import './block-select' +import { editorMode } from 'interactions' + +if ( editorMode === 'gutenberg' ) { + require( './block-toolbar-button' ) + require( './top-toolbar-button' ) + require( './block-highlight' ) + require( './block-select' ) +} diff --git a/src/editor/util/index.js b/src/editor/util/index.js index d34bf05..c2356e5 100644 --- a/src/editor/util/index.js +++ b/src/editor/util/index.js @@ -6,6 +6,10 @@ import { import { select, dispatch } from '@wordpress/data' import { sprintf, __ } from '@wordpress/i18n' import { addClientIdAnchorPair } from '../components/timeline/with-tracked-anchors' +import { + getCurrentEditorPostContext, + openInteractionsSidebar, +} from '~interact/editor/editors' const getUniqueTitle = title => { const interactions = select( 'interact/interactions' ).getInteractions() @@ -52,12 +56,17 @@ const getBlockNameFromAnchor = anchor => { // Returns the current page export const getLocationForCurrentPage = () => { - const currentPostType = select( 'core/editor' ).getCurrentPostType() + const { + postId, + postType, + } = getCurrentEditorPostContext() + + const currentPostType = postType let locationParam = currentPostType === 'page' ? 'page' : currentPostType === 'wp_template' ? 'wp_template' // Site editor templates : !! currentPostType ? 'post' : null - let locationValue = select( 'core/editor' ).getCurrentPostId() + let locationValue = postId if ( ! locationParam ) { locationParam = 'all' @@ -382,14 +391,4 @@ export const getOrGenerateBlockClass = ( clientId, updateAttribute = true ) => { return className } -/** - * Utility function to open the Interactions sidebar. - * - * @return {Object} Dispatch action object - */ -export const openInteractionsSidebar = () => { - if ( dispatch( 'core/edit-post' ) ) { - return dispatch( 'core/edit-post' ).openGeneralSidebar( 'interact-editor/sidebar' ) - } - return dispatch( 'core/edit-site' ).openGeneralSidebar( 'interact-editor/sidebar' ) -} +export { openInteractionsSidebar }