diff --git a/docs/docs/CustomActions.mdx b/docs/docs/CustomActions.mdx new file mode 100644 index 00000000..d2e6cbc4 --- /dev/null +++ b/docs/docs/CustomActions.mdx @@ -0,0 +1,17 @@ +--- +sidebar_position: 5 +--- + +# Custom actions + +For more specific use case you can add custom buttons. + +```js +// Register a new button +editor.registerButton({ + position: 'header', // 'footer' or 'header' + icon: '', + action: () => alert('Hello world'), + title: 'Title' +}) +``` diff --git a/docs/docs/Fields/CustomField.md b/docs/docs/Fields/CustomField.md index c2a62912..b281cf2b 100644 --- a/docs/docs/Fields/CustomField.md +++ b/docs/docs/Fields/CustomField.md @@ -76,4 +76,18 @@ export const Number = defineField({ }) ``` -You can check how [it is used internally](https://github.com/boxraiser/visual-editor/tree/main/visual-editor/src/fields) +You can check how [it is used internally](https://github.com/ciklik/visual-editor/tree/main/visual-editor/src/fields) + +## UIKit + +The library exposes an object `UI` with some components used internally. You can use these components to keep a coherent design. + +```jsx +import { Field, defineField, React, UI } from '@boxraiser/visual-editor' + +const Component = ({ value, onChange, options }) => { + return
+ onChange(e.target.value)} /> +
+} +``` diff --git a/docs/docs/Examples/Laravel.md b/docs/docs/Preview/Examples/Laravel.md similarity index 100% rename from docs/docs/Examples/Laravel.md rename to docs/docs/Preview/Examples/Laravel.md diff --git a/docs/docs/Examples/_category_.json b/docs/docs/Preview/Examples/_category_.json similarity index 100% rename from docs/docs/Examples/_category_.json rename to docs/docs/Preview/Examples/_category_.json diff --git a/docs/docs/Preview/preview-responsive.mdx b/docs/docs/Preview/preview-responsive.mdx new file mode 100644 index 00000000..60d67d8f --- /dev/null +++ b/docs/docs/Preview/preview-responsive.mdx @@ -0,0 +1,23 @@ +--- +sidebar_position: 3 +--- + +# Responsive + +You can customize the breakpoints used for the preview when initializing the VisualEditor. + +```js +const editor = new VisualEditor({ + // ... + devices: [ + { name: 'Mobile', width: 390, height: '100%', icon: 'mobile' }, + { name: 'Tablet', width: 1180, height: 820, icon: 'tablet' }, + { name: 'Desktop', width: '100%', height: '100%', icon: 'desktop' } + ] +}) +``` + +If the preview size exceeds the viewport, the iframe will be scaled automatically to fit the screen. + +- `width` and `height` can be a number of `"100%"` +- `icon` can be `tablet`, `mobile` or `desktop` diff --git a/docs/docs/changelog.mdx b/docs/docs/changelog.mdx index 11f7b06d..b11e9325 100644 --- a/docs/docs/changelog.mdx +++ b/docs/docs/changelog.mdx @@ -4,6 +4,17 @@ sidebar_position: 10 # Changelog +## 0.2 - 2024/02/01 + +- Feat: export UI components +- Feat: registerButton() method for adding custom button +- Feat: Add control in the preview +- Feat: New responsive controls +- Feat: Add a new `templates` event (cancelable) +- Feat: Add a new `components` event (cancelable) +- Chore: Remove `detail` in the change event +- Fix: key missing on templates listing + ## 0.1.0 - 2023/03/16 - Feat: Add a new `postMessagePreview` option for VisualEditor diff --git a/docs/docs/events.mdx b/docs/docs/events.mdx new file mode 100644 index 00000000..a9666f9d --- /dev/null +++ b/docs/docs/events.mdx @@ -0,0 +1,14 @@ +--- +sidebar_position: 7 +--- + +# Events + +If you need to create custom behaviour you can listen for some events on the editor + +| **Event** | **Description** | **Cancellable** | **Methods** | +|-----------|-----------------|-------------|-------------| +| `change` | Fires when the value of the editor is changed by the user | No | | +| `templates` | Fires when the user click on the template button | Yes | | +| `components` | Fires when the user click on the add component button | Yes | | +| `removeitem` | Fires when a bloc is removed | Yes | **detail.confirm** : `() => void`, triggers the intended action (usefull if event was prevented) | diff --git a/docs/docs/intro.mdx b/docs/docs/intro.mdx index f945cc5f..d9028230 100644 --- a/docs/docs/intro.mdx +++ b/docs/docs/intro.mdx @@ -24,7 +24,7 @@ let editor = new VisualEditor() Then register your page components using the `registerComponent` method that expect 2 parameters - A unique identifier for the block (string) -- An option object (see [EditorComponentDefinition](https://github.com/boxraiser/visual-editor/blob/main/visual-editor/src/types.ts#L38) for more information) +- An option object (see [EditorComponentDefinition](https://github.com/ciklik/visual-editor/blob/main/visual-editor/src/types.ts#L38) for more information) ```js import { HTMLText, Repeater, Text, Row, Select } from '@boxraiser/visual-editor' diff --git a/docs/docs/translation.mdx b/docs/docs/translation.mdx index 67b4e203..8108c487 100644 --- a/docs/docs/translation.mdx +++ b/docs/docs/translation.mdx @@ -14,4 +14,4 @@ const editor = new VisualEditor({ }) ``` -You can see all the available keys in the [source code](https://github.com/boxraiser/visual-editor/blob/main/visual-editor/src/langs/en.ts). \ No newline at end of file +You can see all the available keys in the [source code](https://github.com/ciklik/visual-editor/blob/main/visual-editor/src/langs/en.ts). diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index e940141d..ca587c64 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -8,12 +8,12 @@ const darkCodeTheme = themes.dracula; const config = { title: "Visual Editor", tagline: "Edit your page block by block", - url: "https://boxraiser.github.io", + url: "https://ciklik.github.io", baseUrl: "/visual-editor/", onBrokenLinks: "warn", onBrokenMarkdownLinks: "warn", favicon: "img/favicon.ico", - organizationName: "BoxRaiser", // Usually your GitHub org/user name. + organizationName: "Ciklik", // Usually your GitHub org/user name. projectName: "visual-editor", // Usually your repo name. presets: [ [ @@ -23,7 +23,7 @@ const config = { docs: { sidebarPath: require.resolve("./sidebars.js"), // Please change this to your repo. - editUrl: "https://github.com/boxraiser/visual-editor/tree/main/docs/", + editUrl: "https://github.com/ciklik/visual-editor/tree/main/docs/", }, blog: false, theme: { @@ -50,7 +50,7 @@ const config = { label: "Pricing", }, { - href: "https://github.com/boxraiser/visual-editor", + href: "https://github.com/ciklik/visual-editor", label: "GitHub", position: "right", }, @@ -73,12 +73,12 @@ const config = { items: [ { label: "Github", - href: "https://github.com/boxraiser/visual-editor", + href: "https://github.com/ciklik/visual-editor", }, ], }, ], - copyright: `Copyright © ${new Date().getFullYear()} Boxraiser. Built with Docusaurus.`, + copyright: `Copyright © ${new Date().getFullYear()} Boxraiser. Built with Docusaurus.`, }, prism: { theme: lightCodeTheme, diff --git a/docs/src/pages/pricing.jsx b/docs/src/pages/pricing.jsx index cfd5ac15..5eed6405 100644 --- a/docs/src/pages/pricing.jsx +++ b/docs/src/pages/pricing.jsx @@ -31,7 +31,7 @@ export default function Pricing() {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c9a4cab..4d2a5852 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,9 +140,6 @@ importers: '@tiptap/react': specifier: ^3.0.7 version: 3.0.7(@floating-ui/dom@1.7.2)(@tiptap/core@3.0.7(@tiptap/pm@2.26.1))(@tiptap/pm@2.26.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@vitejs/plugin-react': - specifier: ^4.7.0 - version: 4.7.0(vite@7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1)) clsx: specifier: ^2.0.0 version: 2.1.1 @@ -161,12 +158,6 @@ importers: react-use: specifier: ^17.4.0 version: 17.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - vite: - specifier: ^7.0.6 - version: 7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) - vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@18.19.120)(jiti@1.21.7)(jsdom@16.7.0)(terser@5.43.1) zustand: specifier: ^4.4.2 version: 4.5.7(@types/react@18.3.23)(immer@9.0.21)(react@18.3.1) @@ -186,6 +177,9 @@ importers: '@types/react-dom': specifier: ^18.2.10 version: 18.3.7(@types/react@18.3.23) + '@vitejs/plugin-react': + specifier: ^4.7.0 + version: 4.7.0(vite@7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1)) concurrently: specifier: ^7.6.0 version: 7.6.0 @@ -193,11 +187,11 @@ importers: specifier: ^15.6.0 version: 15.6.0 esbuild: - specifier: ^0.19.4 - version: 0.19.12 + specifier: ^0.27.3 + version: 0.27.3 esbuild-node-externals: specifier: ^1.9.0 - version: 1.18.0(esbuild@0.19.12) + version: 1.18.0(esbuild@0.27.3) prettier: specifier: ^3.0.3 version: 3.6.2 @@ -219,6 +213,12 @@ importers: typescript: specifier: ^4.9.5 version: 4.9.5 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@18.19.120)(jiti@1.21.7)(jsdom@16.7.0)(terser@5.43.1) packages: @@ -1431,296 +1431,158 @@ packages: '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -3804,13 +3666,8 @@ packages: peerDependencies: esbuild: 0.12 - 0.25 - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -3992,6 +3849,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + feed@4.2.2: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} @@ -6616,6 +6482,10 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.1.1: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -6888,8 +6758,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.0.6: - resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7055,6 +6925,7 @@ packages: whatwg-encoding@1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@2.3.0: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} @@ -9262,151 +9133,82 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} - '@esbuild/aix-ppc64@0.19.12': + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/aix-ppc64@0.25.8': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm64@0.19.12': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-arm64@0.25.8': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/android-arm@0.19.12': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/android-arm@0.25.8': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/android-x64@0.19.12': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/android-x64@0.25.8': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.19.12': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.25.8': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/darwin-x64@0.19.12': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/darwin-x64@0.25.8': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.19.12': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.25.8': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.19.12': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.25.8': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-arm64@0.19.12': + '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/linux-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.19.12': + '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/linux-arm@0.25.8': + '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/linux-ia32@0.19.12': + '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/linux-ia32@0.25.8': + '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/linux-loong64@0.19.12': + '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/linux-loong64@0.25.8': + '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/linux-mips64el@0.19.12': + '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/linux-mips64el@0.25.8': - optional: true - - '@esbuild/linux-ppc64@0.19.12': - optional: true - - '@esbuild/linux-ppc64@0.25.8': - optional: true - - '@esbuild/linux-riscv64@0.19.12': - optional: true - - '@esbuild/linux-riscv64@0.25.8': - optional: true - - '@esbuild/linux-s390x@0.19.12': - optional: true - - '@esbuild/linux-s390x@0.25.8': - optional: true - - '@esbuild/linux-x64@0.19.12': - optional: true - - '@esbuild/linux-x64@0.25.8': - optional: true - - '@esbuild/netbsd-arm64@0.25.8': - optional: true - - '@esbuild/netbsd-x64@0.19.12': - optional: true - - '@esbuild/netbsd-x64@0.25.8': - optional: true - - '@esbuild/openbsd-arm64@0.25.8': - optional: true - - '@esbuild/openbsd-x64@0.19.12': - optional: true - - '@esbuild/openbsd-x64@0.25.8': - optional: true - - '@esbuild/openharmony-arm64@0.25.8': - optional: true - - '@esbuild/sunos-x64@0.19.12': - optional: true - - '@esbuild/sunos-x64@0.25.8': - optional: true - - '@esbuild/win32-arm64@0.19.12': - optional: true - - '@esbuild/win32-arm64@0.25.8': - optional: true - - '@esbuild/win32-ia32@0.19.12': - optional: true - - '@esbuild/win32-ia32@0.25.8': - optional: true - - '@esbuild/win32-x64@0.19.12': - optional: true - - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.27.3': optional: true '@floating-ui/core@1.7.2': @@ -10403,7 +10205,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1))': + '@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -10411,7 +10213,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) + vite: 7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) transitivePeerDependencies: - supports-color @@ -10423,13 +10225,13 @@ snapshots: chai: 5.2.1 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1))': + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) + vite: 7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -11639,65 +11441,39 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.2 - esbuild-node-externals@1.18.0(esbuild@0.19.12): + esbuild-node-externals@1.18.0(esbuild@0.27.3): dependencies: - esbuild: 0.19.12 + esbuild: 0.27.3 find-up: 5.0.0 - esbuild@0.19.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - - esbuild@0.25.8: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 escalade@3.2.0: {} @@ -11921,6 +11697,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + feed@4.2.2: dependencies: xml-js: 1.6.11 @@ -15121,6 +14901,11 @@ snapshots: fdir: 6.4.6(picomatch@4.0.3) picomatch: 4.0.3 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tinypool@1.1.1: {} tinyrainbow@2.0.0: {} @@ -15382,7 +15167,7 @@ snapshots: debug: 4.4.1(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) + vite: 7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - jiti @@ -15397,14 +15182,14 @@ snapshots: - tsx - yaml - vite@7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1): + vite@7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1): dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.45.1 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 18.19.120 fsevents: 2.3.3 @@ -15415,7 +15200,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1)) + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -15433,7 +15218,7 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.6(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) + vite: 7.3.1(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) vite-node: 3.2.4(@types/node@18.19.120)(jiti@1.21.7)(terser@5.43.1) why-is-node-running: 2.3.0 optionalDependencies: diff --git a/visual-editor/demo/csr-preview.html b/visual-editor/demo/csr-preview.html new file mode 100644 index 00000000..1a2a537e --- /dev/null +++ b/visual-editor/demo/csr-preview.html @@ -0,0 +1,17 @@ + + + + + + + Document + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur deleniti dolore eum maiores perferendis quos ratione repudiandae soluta ut voluptates! Debitis incidunt magnam molestias mollitia. Alias autem iste libero sunt. +

+ + + diff --git a/visual-editor/demo/csr-preview.tsx b/visual-editor/demo/csr-preview.tsx new file mode 100644 index 00000000..6c07f90e --- /dev/null +++ b/visual-editor/demo/csr-preview.tsx @@ -0,0 +1,45 @@ +import { createRoot } from 'react-dom/client' +import { + type EditorMessageEvents, + PreviewWrapper, + AddButton, +} from '../src/VisualEditor' +import { useEffect, useState } from 'react' + +customElements.define('ve-wrapper', PreviewWrapper) +customElements.define('ve-add', AddButton) + +declare global { + namespace JSX { + interface IntrinsicElements { + 've-wrapper': any + 've-add': any + } + } +} + +function App() { + const [data, setData] = useState([]) + useEffect(() => { + const listener = (message: MessageEvent) => { + if (message.data.type === 've-data') { + setData(message.data.payload) + } + } + window.addEventListener('message', listener, false) + return () => window.removeEventListener('message', listener) + }, []) + + return ( +
+ {data.map((item) => ( + +
Bloc name: {item._name}
+
+ ))} + +
+ ) +} + +createRoot(document.querySelector('#app')).render() diff --git a/visual-editor/src/demo.tsx b/visual-editor/demo/main.tsx similarity index 85% rename from visual-editor/src/demo.tsx rename to visual-editor/demo/main.tsx index 7ddd9766..dd60a97f 100644 --- a/visual-editor/src/demo.tsx +++ b/visual-editor/demo/main.tsx @@ -13,7 +13,7 @@ import { Text, TextAlign, VisualEditor, -} from 'src/VisualEditor' +} from '../src/VisualEditor' import { ButtonField, ButtonsField, @@ -22,11 +22,16 @@ import { ImageField, TitleField, WithStyles, -} from './shared' +} from '../src/shared' let editor = new VisualEditor({ lang: EN, - postMessagePreview: false + postMessagePreview: false, + devices: [ + { name: 'Mobile', width: 390, height: 820, icon: 'mobile' }, + { name: 'Tablet', width: 1180, height: 820, icon: 'tablet' }, + { name: 'Desktop', width: '100%', height: '100%', icon: 'desktop' }, + ], }) editor.registerComponent('hero', { @@ -208,3 +213,19 @@ editor.registerTemplate({ }) editor.defineElement() + +document + .querySelector('#postmessagepreview')! + .addEventListener('click', (e) => { + VisualEditor.postMessagePreview = !VisualEditor.postMessagePreview + ;(e.currentTarget as HTMLElement).querySelector('span')!.innerText = + VisualEditor.postMessagePreview ? 'on' : 'off' + document.querySelectorAll('visual-editor').forEach((button) => { + button.setAttribute( + 'preview', + VisualEditor.postMessagePreview + ? 'http://localhost:3000/demo/csr-preview.html' + : 'http://localhost:3000/preview' + ) + }) + }) diff --git a/visual-editor/index.html b/visual-editor/index.html index d1aab6a3..734456e2 100644 --- a/visual-editor/index.html +++ b/visual-editor/index.html @@ -74,6 +74,9 @@

+

@@ -96,10 +99,13 @@

value="[]" > - + diff --git a/visual-editor/package.json b/visual-editor/package.json index d57e639c..6dbc54d0 100644 --- a/visual-editor/package.json +++ b/visual-editor/package.json @@ -1,15 +1,16 @@ { - "name": "@boxraiser/visual-editor", - "version": "0.1.4", + "name": "@ciklik/visual-editor", + "version": "0.2.0", "type": "module", "main": "VisualEditor.js", "types": "./VisualEditor.d.ts", "repository": { "type": "git", - "url": "https://github.com/boxraiser/visual-editor" + "url": "https://github.com/ciklik/visual-editor" }, "scripts": { "dev": "concurrently \"vite\" \"php -S 0.0.0.0:8000 -ddisplay_errors=1 -t server/\"", + "check": "tsc", "build": "tsc && vite build && node esbuild.js && npm run declaration && cp package.json dist/package.json && cp README.md dist/README.md", "serve": "vite preview", "format": "prettier --config .prettierrc 'src/**/*.{ts,tsx}' --write", @@ -57,15 +58,12 @@ "@tiptap/extension-text-style": "^3.0.7", "@tiptap/extension-underline": "^3.0.7", "@tiptap/react": "^3.0.7", - "@vitejs/plugin-react": "^4.7.0", "clsx": "^2.0.0", "framesync": "^6.1.2", "react-datepicker": "^4.18.0", "react-flip-toolkit": "^7.1.0", "react-popper": "^2.3.0", "react-use": "^17.4.0", - "vite": "^7.0.6", - "vitest": "^3.2.4", "zustand": "^4.4.2" }, "devDependencies": { @@ -76,7 +74,7 @@ "@types/react-dom": "^18.2.10", "concurrently": "^7.6.0", "cypress": "^15.6.0", - "esbuild": "^0.19.4", + "esbuild": "^0.27.3", "esbuild-node-externals": "^1.9.0", "prettier": "^3.0.3", "react": "^18.2.0", @@ -84,7 +82,10 @@ "rollup-plugin-analyzer": "^4.0.0", "start-server-and-test": "^2.0.1", "tsc-alias": "^1.8.8", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "vite": "^7.3.1", + "vitest": "^3.2.4", + "@vitejs/plugin-react": "^4.7.0" }, "peerDependencies": { "react": "^18.0.0", diff --git a/visual-editor/src/VisualEditor.tsx b/visual-editor/src/VisualEditor.tsx index 88526f1e..cae43b18 100644 --- a/visual-editor/src/VisualEditor.tsx +++ b/visual-editor/src/VisualEditor.tsx @@ -1,6 +1,8 @@ -import React, { useEffect, useMemo, useRef } from 'react' -import { createRoot } from 'react-dom/client' +import React, { useMemo, useRef } from 'react' +import { createRoot, type Root } from 'react-dom/client' import type { + Action, + Device, EditorComponentData, EditorComponentDefinition, EditorComponentDefinitions, @@ -8,19 +10,34 @@ import type { Translation, } from 'src/types' import { Layout } from 'src/components/Layout' -import { StoreProvider, useData, useUpdateData } from 'src/store' +import { Store, StoreProvider, usePartialStore } from 'src/store' import { indexify, stringifyFields } from 'src/functions/object' import { useClipboardPaste } from 'src/hooks/useClipboardPaste' -import { useUpdateEffect } from 'src/hooks/useUpdateEffect' import { fillDefaults } from 'src/functions/fields' import { useStateDelayed } from 'src/hooks/useStateDelayed' import { BaseStyles } from 'src/components/BaseStyles' import { Translations as EN } from 'src/langs/en' import { useStopPropagation } from 'src/hooks/useStopPropagation' import { InsertPosition } from 'src/enum' +import { + Button, + ButtonIcon, + Card, + Flex, + Input, + Field as FieldUI, + Modal, + Spinner, + Label, +} from 'src/components/ui' const components: EditorComponentDefinitions = {} const templates: EditorComponentTemplate[] = [] +const actions: Action[] = [] +const defaultDevices: Device[] = [ + { name: 'Mobile', width: 390, height: '100%', icon: 'mobile' }, + { name: 'Desktop', width: '100%', height: '100%', icon: 'desktop' }, +] export { PreviewWrapper } from './elements/PreviewWrapper' export { AddButton } from './elements/AddButton' @@ -32,127 +49,159 @@ export type { EditorMessageEvents } from './components/Preview/PreviewPostMessag export class VisualEditor { static i18n: Translation = EN static postMessagePreview: boolean = false + static devices: Device[] - constructor (options: { lang?: Translation, postMessagePreview?: boolean } = {}) { + constructor( + options: { + lang?: Translation + postMessagePreview?: boolean + devices?: Device[] + } = {} + ) { VisualEditor.i18n = options.lang ?? EN + VisualEditor.devices = options.devices ?? defaultDevices VisualEditor.postMessagePreview = options.postMessagePreview ?? false } - registerComponent (name: string, definition: EditorComponentDefinition) { + registerComponent(name: string, definition: EditorComponentDefinition) { components[name] = { label: 'title', ...definition } } - registerTemplate (template: EditorComponentTemplate) { + registerTemplate(template: EditorComponentTemplate) { templates.push(template) } - defineElement (elementName: string = 'visual-editor') { + registerButton(action: Action) { + actions.push(action) + } + + defineElement(elementName: string = 'visual-editor') { // We only declare the class in this function to avoid any problem with SSR class VisualEditorElement extends HTMLElement { static changeEventName = 'change' - private _mounted: boolean = false - private _data: EditorComponentData[] | null = null - private _value = '' + // React root + private _root: Root | null = null + // Access zustand store used by the VisualEditor + private _store: Store | null = null - static get observedAttributes () { + static get observedAttributes() { return ['hidden', 'value'] } - get value (): string { - return this._value + get value(): string { + return stringifyFields(this.valueAsArray) + } + + get valueAsArray(): EditorComponentData[] { + return this._store?.getState().data ?? [] } - set value (v: string) { - if (v === this._value) { + set value( + v: + | string + | EditorComponentData[] + | ((v: EditorComponentData[]) => EditorComponentData[]) + ) { + if (!this._store) { + if (!v) { + this.removeAttribute('value') + } else { + this.setAttribute( + 'value', + typeof v === 'string' ? v : JSON.stringify(v) + ) + } return } - this._value = v - this._data = null - this.render() + const state = this._store.getState() + if (typeof v === 'string') { + state.setDataFromOutside(this.parseValue(v)) + return + } + if (typeof v === 'function') { + state.setDataFromOutside(v(state.data)) + return + } + state.setDataFromOutside(indexify(v)) } - connectedCallback () { - this._value = this.getAttribute('value') || '[]' + connectedCallback() { this.render() - this._mounted = true } - attributeChangedCallback ( + attributeChangedCallback( name: string, oldValue?: string, - newValue?: string, + newValue?: string ) { - if (!this._mounted) { + if (!this._root) { return false } // Si la valeur change, on réinitialise la version traduite du JSON - if (name === 'value') { - // Saute le nouveau rendu si la valeur n'est pas nouvelle - if (newValue === this._value) { - return - } - this._value = newValue! + if (name === 'value' && newValue) { + this.value = newValue + return } this.render() } - disconnectedCallback () { - this._mounted = false + disconnectedCallback() { + if (!this._root) { + return + } + this._root.unmount() + this._store = null + this._root = null } - private parseValue (value: string): EditorComponentData[] { - if (this._data === null) { - try { - const json = JSON.parse(value) - this._data = indexify(json).map((value: EditorComponentData) => { - return fillDefaults(value, components[value._name]?.fields ?? []) - }) - } catch (e) { - console.error('Impossible de parser les données', value, e) - alert('Impossible de parser les données de l\'éditeur visuel') - this._data = [] - } + private parseValue(value?: string): EditorComponentData[] { + if (!value) { + return [] + } + try { + const json = JSON.parse(value) + return indexify(json).map((value: EditorComponentData) => { + return fillDefaults(value, components[value._name]?.fields ?? []) + }) + } catch (e) { + console.error('Impossible de parser les données', value, e) + alert("Impossible de parser les données de l'éditeur visuel") + return [] } - return this._data! } - private render () { - const data = this.parseValue(this._value) + private render() { + const data = this.parseValue(this.getAttribute('value')?.toString()) const hiddenCategories = this.getAttribute('hidden-categories')?.split(';') ?? [] - createRoot(this).render( + if (!this._root) { + this._root = createRoot(this) + } + + this._root.render( (this._store = store)} > { - if (this._value === value) { - return - } - this._value = value - this.dispatchEvent( - new CustomEvent('change', { - detail: value, - }), - ) - }} /> - , + ) } } @@ -162,73 +211,70 @@ export class VisualEditor { } type VisualEditorProps = { - value: EditorComponentData[] previewUrl: string name: string iconsUrl: string visible: boolean element: Element - onChange: (v: string) => void } -export function VisualEditorComponent ({ - value, - previewUrl, - name, - element, - iconsUrl, - visible: visibleProps, - onChange, - }: VisualEditorProps) { - const skipNextChange = useRef(true) // Skip emitting a change event on the next update (usefull for external changes) - const updateData = useUpdateData() - const data = useData() +export function VisualEditorComponent({ + previewUrl, + name, + element, + iconsUrl, + visible: visibleProps, +}: VisualEditorProps) { const visible = useStateDelayed(visibleProps) const handleClose = () => { element.dispatchEvent(new Event('close')) } - const doNothing = () => null // React wants handler :( - // JSON nettoyé - const cleanedData = useMemo(() => stringifyFields(data), [data]) - // Synchronise l'état du composant avec la prop value - useUpdateEffect(() => { - skipNextChange.current = true - updateData(value) - }, [value]) useClipboardPaste(visible) - useEffect(() => { - if (skipNextChange.current) { - skipNextChange.current = false - } else { - onChange(cleanedData) - } - }, [cleanedData]) // We want to avoid bubbling change & close event const div = useRef(null) useStopPropagation(div, ['change', 'close']) if (!visible) { - return ( -