@next-buildr/editor 0.0.0-stage → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/app/builder-editor.d.ts +8 -0
- package/dist/app/builder-editor.d.ts.map +1 -0
- package/dist/app/builder-editor.js +14 -0
- package/dist/app/builder-editor.test.d.ts +2 -0
- package/dist/app/builder-editor.test.d.ts.map +1 -0
- package/dist/app/builder-editor.test.js +199 -0
- package/dist/app/config.d.ts +59 -0
- package/dist/app/config.d.ts.map +1 -0
- package/dist/app/config.js +18 -0
- package/dist/app/content-language.test.d.ts +2 -0
- package/dist/app/content-language.test.d.ts.map +1 -0
- package/dist/app/content-language.test.js +157 -0
- package/dist/app/editor-app.d.ts +17 -0
- package/dist/app/editor-app.d.ts.map +1 -0
- package/dist/app/editor-app.js +206 -0
- package/dist/app/editor-app.test.d.ts +2 -0
- package/dist/app/editor-app.test.d.ts.map +1 -0
- package/dist/app/editor-app.test.js +135 -0
- package/dist/app/layout.d.ts +32 -0
- package/dist/app/layout.d.ts.map +1 -0
- package/dist/app/layout.js +82 -0
- package/dist/app/manifest.d.ts +12 -0
- package/dist/app/manifest.d.ts.map +1 -0
- package/dist/app/manifest.js +17 -0
- package/dist/app/shell-panels.d.ts +15 -0
- package/dist/app/shell-panels.d.ts.map +1 -0
- package/dist/app/shell-panels.js +5 -0
- package/dist/app/small-screen.d.ts +12 -0
- package/dist/app/small-screen.d.ts.map +1 -0
- package/dist/app/small-screen.js +33 -0
- package/dist/app/small-screen.test.d.ts +2 -0
- package/dist/app/small-screen.test.d.ts.map +1 -0
- package/dist/app/small-screen.test.js +108 -0
- package/dist/app/status-info.d.ts +14 -0
- package/dist/app/status-info.d.ts.map +1 -0
- package/dist/app/status-info.js +34 -0
- package/dist/app/use-resizable.d.ts +34 -0
- package/dist/app/use-resizable.d.ts.map +1 -0
- package/dist/app/use-resizable.js +84 -0
- package/dist/canvas-host/canvas-frame.d.ts +53 -0
- package/dist/canvas-host/canvas-frame.d.ts.map +1 -0
- package/dist/canvas-host/canvas-frame.js +146 -0
- package/dist/canvas-host/canvas-frame.test.d.ts +2 -0
- package/dist/canvas-host/canvas-frame.test.d.ts.map +1 -0
- package/dist/canvas-host/canvas-frame.test.js +91 -0
- package/dist/canvas-host/host.d.ts +109 -0
- package/dist/canvas-host/host.d.ts.map +1 -0
- package/dist/canvas-host/host.js +293 -0
- package/dist/canvas-host/host.test.d.ts +2 -0
- package/dist/canvas-host/host.test.d.ts.map +1 -0
- package/dist/canvas-host/host.test.js +398 -0
- package/dist/canvas-host/index.d.ts +5 -0
- package/dist/canvas-host/index.d.ts.map +1 -0
- package/dist/canvas-host/index.js +2 -0
- package/dist/clipboard/clipboard.d.ts +38 -0
- package/dist/clipboard/clipboard.d.ts.map +1 -0
- package/dist/clipboard/clipboard.js +160 -0
- package/dist/clipboard/clipboard.test.d.ts +2 -0
- package/dist/clipboard/clipboard.test.d.ts.map +1 -0
- package/dist/clipboard/clipboard.test.js +457 -0
- package/dist/clipboard/format.d.ts +27 -0
- package/dist/clipboard/format.d.ts.map +1 -0
- package/dist/clipboard/format.js +37 -0
- package/dist/clipboard/index.d.ts +6 -0
- package/dist/clipboard/index.d.ts.map +1 -0
- package/dist/clipboard/index.js +3 -0
- package/dist/clipboard/react.d.ts +20 -0
- package/dist/clipboard/react.d.ts.map +1 -0
- package/dist/clipboard/react.js +71 -0
- package/dist/dialogs/index.d.ts +2 -0
- package/dist/dialogs/index.d.ts.map +1 -0
- package/dist/dialogs/index.js +1 -0
- package/dist/dialogs/media-picker/index.d.ts +7 -0
- package/dist/dialogs/media-picker/index.d.ts.map +1 -0
- package/dist/dialogs/media-picker/index.js +3 -0
- package/dist/dialogs/media-picker/library.d.ts +17 -0
- package/dist/dialogs/media-picker/library.d.ts.map +1 -0
- package/dist/dialogs/media-picker/library.js +9 -0
- package/dist/dialogs/media-picker/media-picker.d.ts +18 -0
- package/dist/dialogs/media-picker/media-picker.d.ts.map +1 -0
- package/dist/dialogs/media-picker/media-picker.js +99 -0
- package/dist/dialogs/media-picker/media-picker.test.d.ts +2 -0
- package/dist/dialogs/media-picker/media-picker.test.d.ts.map +1 -0
- package/dist/dialogs/media-picker/media-picker.test.js +161 -0
- package/dist/dialogs/media-picker/media-ref.d.ts +28 -0
- package/dist/dialogs/media-picker/media-ref.d.ts.map +1 -0
- package/dist/dialogs/media-picker/media-ref.js +52 -0
- package/dist/dnd/check.d.ts +8 -0
- package/dist/dnd/check.d.ts.map +1 -0
- package/dist/dnd/check.js +12 -0
- package/dist/dnd/destinations.d.ts +15 -0
- package/dist/dnd/destinations.d.ts.map +1 -0
- package/dist/dnd/destinations.js +43 -0
- package/dist/dnd/dnd-react.test.d.ts +2 -0
- package/dist/dnd/dnd-react.test.d.ts.map +1 -0
- package/dist/dnd/dnd-react.test.js +93 -0
- package/dist/dnd/dnd.test.d.ts +2 -0
- package/dist/dnd/dnd.test.d.ts.map +1 -0
- package/dist/dnd/dnd.test.js +327 -0
- package/dist/dnd/engine.d.ts +85 -0
- package/dist/dnd/engine.d.ts.map +1 -0
- package/dist/dnd/engine.js +185 -0
- package/dist/dnd/fragment.d.ts +6 -0
- package/dist/dnd/fragment.d.ts.map +1 -0
- package/dist/dnd/fragment.js +18 -0
- package/dist/dnd/geometry.d.ts +30 -0
- package/dist/dnd/geometry.d.ts.map +1 -0
- package/dist/dnd/geometry.js +31 -0
- package/dist/dnd/index.d.ts +14 -0
- package/dist/dnd/index.d.ts.map +1 -0
- package/dist/dnd/index.js +7 -0
- package/dist/dnd/move-to-dialog.d.ts +13 -0
- package/dist/dnd/move-to-dialog.d.ts.map +1 -0
- package/dist/dnd/move-to-dialog.js +30 -0
- package/dist/dnd/react.d.ts +43 -0
- package/dist/dnd/react.d.ts.map +1 -0
- package/dist/dnd/react.js +185 -0
- package/dist/dnd/tree-target.d.ts +29 -0
- package/dist/dnd/tree-target.d.ts.map +1 -0
- package/dist/dnd/tree-target.js +97 -0
- package/dist/fonts/LICENSE-inter.txt +93 -0
- package/dist/fonts/inter-latin-400-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-500-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-600-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-ext-400-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-ext-500-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-ext-600-normal.woff2 +0 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/messages/en.d.ts +335 -0
- package/dist/messages/en.d.ts.map +1 -0
- package/dist/messages/en.js +332 -0
- package/dist/messages/index.d.ts +16 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +22 -0
- package/dist/messages/pl.d.ts +3 -0
- package/dist/messages/pl.d.ts.map +1 -0
- package/dist/messages/pl.js +327 -0
- package/dist/panels/breadcrumbs/breadcrumbs.d.ts +6 -0
- package/dist/panels/breadcrumbs/breadcrumbs.d.ts.map +1 -0
- package/dist/panels/breadcrumbs/breadcrumbs.js +24 -0
- package/dist/panels/breadcrumbs/breadcrumbs.test.d.ts +2 -0
- package/dist/panels/breadcrumbs/breadcrumbs.test.d.ts.map +1 -0
- package/dist/panels/breadcrumbs/breadcrumbs.test.js +56 -0
- package/dist/panels/index.d.ts +9 -0
- package/dist/panels/index.d.ts.map +1 -0
- package/dist/panels/index.js +6 -0
- package/dist/panels/insert/catalog.d.ts +30 -0
- package/dist/panels/insert/catalog.d.ts.map +1 -0
- package/dist/panels/insert/catalog.js +76 -0
- package/dist/panels/insert/catalog.order.test.d.ts +2 -0
- package/dist/panels/insert/catalog.order.test.d.ts.map +1 -0
- package/dist/panels/insert/catalog.order.test.js +17 -0
- package/dist/panels/insert/category-name.d.ts +4 -0
- package/dist/panels/insert/category-name.d.ts.map +1 -0
- package/dist/panels/insert/category-name.js +8 -0
- package/dist/panels/insert/index.d.ts +3 -0
- package/dist/panels/insert/index.d.ts.map +1 -0
- package/dist/panels/insert/index.js +2 -0
- package/dist/panels/insert/insert-panel.d.ts +7 -0
- package/dist/panels/insert/insert-panel.d.ts.map +1 -0
- package/dist/panels/insert/insert-panel.js +155 -0
- package/dist/panels/insert/insert-panel.test.d.ts +2 -0
- package/dist/panels/insert/insert-panel.test.d.ts.map +1 -0
- package/dist/panels/insert/insert-panel.test.js +323 -0
- package/dist/panels/insert/target.d.ts +17 -0
- package/dist/panels/insert/target.d.ts.map +1 -0
- package/dist/panels/insert/target.js +48 -0
- package/dist/panels/inspector/controls/boolean.d.ts +4 -0
- package/dist/panels/inspector/controls/boolean.d.ts.map +1 -0
- package/dist/panels/inspector/controls/boolean.js +4 -0
- package/dist/panels/inspector/controls/compound.d.ts +15 -0
- package/dist/panels/inspector/controls/compound.d.ts.map +1 -0
- package/dist/panels/inspector/controls/compound.js +99 -0
- package/dist/panels/inspector/controls/compound.test.d.ts +2 -0
- package/dist/panels/inspector/controls/compound.test.d.ts.map +1 -0
- package/dist/panels/inspector/controls/compound.test.js +195 -0
- package/dist/panels/inspector/controls/icon.d.ts +5 -0
- package/dist/panels/inspector/controls/icon.d.ts.map +1 -0
- package/dist/panels/inspector/controls/icon.js +6 -0
- package/dist/panels/inspector/controls/index.d.ts +13 -0
- package/dist/panels/inspector/controls/index.d.ts.map +1 -0
- package/dist/panels/inspector/controls/index.js +11 -0
- package/dist/panels/inspector/controls/label.d.ts +4 -0
- package/dist/panels/inspector/controls/label.d.ts.map +1 -0
- package/dist/panels/inspector/controls/label.js +8 -0
- package/dist/panels/inspector/controls/link.d.ts +5 -0
- package/dist/panels/inspector/controls/link.d.ts.map +1 -0
- package/dist/panels/inspector/controls/link.js +6 -0
- package/dist/panels/inspector/controls/media.d.ts +9 -0
- package/dist/panels/inspector/controls/media.d.ts.map +1 -0
- package/dist/panels/inspector/controls/media.js +21 -0
- package/dist/panels/inspector/controls/number.d.ts +10 -0
- package/dist/panels/inspector/controls/number.d.ts.map +1 -0
- package/dist/panels/inspector/controls/number.js +33 -0
- package/dist/panels/inspector/controls/rich-text-model.d.ts +20 -0
- package/dist/panels/inspector/controls/rich-text-model.d.ts.map +1 -0
- package/dist/panels/inspector/controls/rich-text-model.js +91 -0
- package/dist/panels/inspector/controls/rich-text.d.ts +10 -0
- package/dist/panels/inspector/controls/rich-text.d.ts.map +1 -0
- package/dist/panels/inspector/controls/rich-text.js +159 -0
- package/dist/panels/inspector/controls/select.d.ts +5 -0
- package/dist/panels/inspector/controls/select.d.ts.map +1 -0
- package/dist/panels/inspector/controls/select.js +16 -0
- package/dist/panels/inspector/controls/select.test.d.ts +2 -0
- package/dist/panels/inspector/controls/select.test.d.ts.map +1 -0
- package/dist/panels/inspector/controls/select.test.js +43 -0
- package/dist/panels/inspector/controls/text.d.ts +4 -0
- package/dist/panels/inspector/controls/text.d.ts.map +1 -0
- package/dist/panels/inspector/controls/text.js +5 -0
- package/dist/panels/inspector/controls/textarea.d.ts +4 -0
- package/dist/panels/inspector/controls/textarea.d.ts.map +1 -0
- package/dist/panels/inspector/controls/textarea.js +4 -0
- package/dist/panels/inspector/controls/types.d.ts +16 -0
- package/dist/panels/inspector/controls/types.d.ts.map +1 -0
- package/dist/panels/inspector/controls/types.js +1 -0
- package/dist/panels/inspector/index.d.ts +17 -0
- package/dist/panels/inspector/index.d.ts.map +1 -0
- package/dist/panels/inspector/index.js +9 -0
- package/dist/panels/inspector/inspector.d.ts +16 -0
- package/dist/panels/inspector/inspector.d.ts.map +1 -0
- package/dist/panels/inspector/inspector.js +116 -0
- package/dist/panels/inspector/inspector.test.d.ts +2 -0
- package/dist/panels/inspector/inspector.test.d.ts.map +1 -0
- package/dist/panels/inspector/inspector.test.js +328 -0
- package/dist/panels/inspector/props-panel.d.ts +26 -0
- package/dist/panels/inspector/props-panel.d.ts.map +1 -0
- package/dist/panels/inspector/props-panel.js +117 -0
- package/dist/panels/inspector/styles/box-model.d.ts +20 -0
- package/dist/panels/inspector/styles/box-model.d.ts.map +1 -0
- package/dist/panels/inspector/styles/box-model.js +31 -0
- package/dist/panels/inspector/styles/index.d.ts +5 -0
- package/dist/panels/inspector/styles/index.d.ts.map +1 -0
- package/dist/panels/inspector/styles/index.js +2 -0
- package/dist/panels/inspector/styles/model.d.ts +55 -0
- package/dist/panels/inspector/styles/model.d.ts.map +1 -0
- package/dist/panels/inspector/styles/model.js +111 -0
- package/dist/panels/inspector/styles/segments.d.ts +18 -0
- package/dist/panels/inspector/styles/segments.d.ts.map +1 -0
- package/dist/panels/inspector/styles/segments.js +75 -0
- package/dist/panels/inspector/styles/sides-editor.d.ts +34 -0
- package/dist/panels/inspector/styles/sides-editor.d.ts.map +1 -0
- package/dist/panels/inspector/styles/sides-editor.js +86 -0
- package/dist/panels/inspector/styles/style-inspector.d.ts +15 -0
- package/dist/panels/inspector/styles/style-inspector.d.ts.map +1 -0
- package/dist/panels/inspector/styles/style-inspector.js +219 -0
- package/dist/panels/inspector/styles/style-inspector.test.d.ts +2 -0
- package/dist/panels/inspector/styles/style-inspector.test.d.ts.map +1 -0
- package/dist/panels/inspector/styles/style-inspector.test.js +265 -0
- package/dist/panels/inspector/styles/token-picker.d.ts +15 -0
- package/dist/panels/inspector/styles/token-picker.d.ts.map +1 -0
- package/dist/panels/inspector/styles/token-picker.js +23 -0
- package/dist/panels/inspector/styles/use-style-value.d.ts +28 -0
- package/dist/panels/inspector/styles/use-style-value.d.ts.map +1 -0
- package/dist/panels/inspector/styles/use-style-value.js +51 -0
- package/dist/panels/inspector/value.d.ts +20 -0
- package/dist/panels/inspector/value.d.ts.map +1 -0
- package/dist/panels/inspector/value.js +32 -0
- package/dist/panels/inspector/values/analyze.d.ts +49 -0
- package/dist/panels/inspector/values/analyze.d.ts.map +1 -0
- package/dist/panels/inspector/values/analyze.js +115 -0
- package/dist/panels/inspector/values/data.d.ts +18 -0
- package/dist/panels/inspector/values/data.d.ts.map +1 -0
- package/dist/panels/inspector/values/data.js +10 -0
- package/dist/panels/inspector/values/translation.d.ts +20 -0
- package/dist/panels/inspector/values/translation.d.ts.map +1 -0
- package/dist/panels/inspector/values/translation.js +25 -0
- package/dist/panels/inspector/values/value-editor.d.ts +30 -0
- package/dist/panels/inspector/values/value-editor.d.ts.map +1 -0
- package/dist/panels/inspector/values/value-editor.js +191 -0
- package/dist/panels/inspector/values/values.test.d.ts +2 -0
- package/dist/panels/inspector/values/values.test.d.ts.map +1 -0
- package/dist/panels/inspector/values/values.test.js +173 -0
- package/dist/panels/issues/collect.d.ts +70 -0
- package/dist/panels/issues/collect.d.ts.map +1 -0
- package/dist/panels/issues/collect.js +96 -0
- package/dist/panels/issues/index.d.ts +5 -0
- package/dist/panels/issues/index.d.ts.map +1 -0
- package/dist/panels/issues/index.js +2 -0
- package/dist/panels/issues/issues-panel.d.ts +17 -0
- package/dist/panels/issues/issues-panel.d.ts.map +1 -0
- package/dist/panels/issues/issues-panel.js +77 -0
- package/dist/panels/layers/flatten.d.ts +28 -0
- package/dist/panels/layers/flatten.d.ts.map +1 -0
- package/dist/panels/layers/flatten.js +61 -0
- package/dist/panels/layers/layers-panel.d.ts +14 -0
- package/dist/panels/layers/layers-panel.d.ts.map +1 -0
- package/dist/panels/layers/layers-panel.js +428 -0
- package/dist/panels/layers/layers-panel.test.d.ts +2 -0
- package/dist/panels/layers/layers-panel.test.d.ts.map +1 -0
- package/dist/panels/layers/layers-panel.test.js +315 -0
- package/dist/persistence/controller.d.ts +55 -0
- package/dist/persistence/controller.d.ts.map +1 -0
- package/dist/persistence/controller.js +341 -0
- package/dist/persistence/index.d.ts +6 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +3 -0
- package/dist/persistence/load.d.ts +35 -0
- package/dist/persistence/load.d.ts.map +1 -0
- package/dist/persistence/load.js +67 -0
- package/dist/persistence/persistence.test.d.ts +2 -0
- package/dist/persistence/persistence.test.d.ts.map +1 -0
- package/dist/persistence/persistence.test.js +483 -0
- package/dist/persistence/react.d.ts +18 -0
- package/dist/persistence/react.d.ts.map +1 -0
- package/dist/persistence/react.js +105 -0
- package/dist/persistence/types.d.ts +182 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +1 -0
- package/dist/preview/index.d.ts +3 -0
- package/dist/preview/index.d.ts.map +1 -0
- package/dist/preview/index.js +2 -0
- package/dist/preview/prepare.d.ts +16 -0
- package/dist/preview/prepare.d.ts.map +1 -0
- package/dist/preview/prepare.js +21 -0
- package/dist/preview/preview.d.ts +27 -0
- package/dist/preview/preview.d.ts.map +1 -0
- package/dist/preview/preview.js +51 -0
- package/dist/preview/preview.test.d.ts +2 -0
- package/dist/preview/preview.test.d.ts.map +1 -0
- package/dist/preview/preview.test.js +129 -0
- package/dist/shortcuts/actions.d.ts +17 -0
- package/dist/shortcuts/actions.d.ts.map +1 -0
- package/dist/shortcuts/actions.js +85 -0
- package/dist/shortcuts/index.d.ts +11 -0
- package/dist/shortcuts/index.d.ts.map +1 -0
- package/dist/shortcuts/index.js +5 -0
- package/dist/shortcuts/keys.d.ts +29 -0
- package/dist/shortcuts/keys.d.ts.map +1 -0
- package/dist/shortcuts/keys.js +100 -0
- package/dist/shortcuts/map.d.ts +17 -0
- package/dist/shortcuts/map.d.ts.map +1 -0
- package/dist/shortcuts/map.js +31 -0
- package/dist/shortcuts/registry.d.ts +32 -0
- package/dist/shortcuts/registry.d.ts.map +1 -0
- package/dist/shortcuts/registry.js +53 -0
- package/dist/shortcuts/shortcuts.d.ts +32 -0
- package/dist/shortcuts/shortcuts.d.ts.map +1 -0
- package/dist/shortcuts/shortcuts.js +95 -0
- package/dist/shortcuts/shortcuts.test.d.ts +2 -0
- package/dist/shortcuts/shortcuts.test.d.ts.map +1 -0
- package/dist/shortcuts/shortcuts.test.js +263 -0
- package/dist/store/create-store.d.ts +13 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/create-store.js +248 -0
- package/dist/store/index.d.ts +9 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +5 -0
- package/dist/store/locale.d.ts +23 -0
- package/dist/store/locale.d.ts.map +1 -0
- package/dist/store/locale.js +31 -0
- package/dist/store/react.d.ts +17 -0
- package/dist/store/react.d.ts.map +1 -0
- package/dist/store/react.js +32 -0
- package/dist/store/selection.d.ts +32 -0
- package/dist/store/selection.d.ts.map +1 -0
- package/dist/store/selection.js +84 -0
- package/dist/store/selection.test.d.ts +2 -0
- package/dist/store/selection.test.d.ts.map +1 -0
- package/dist/store/selection.test.js +95 -0
- package/dist/store/selectors.d.ts +18 -0
- package/dist/store/selectors.d.ts.map +1 -0
- package/dist/store/selectors.js +39 -0
- package/dist/store/store.test.d.ts +2 -0
- package/dist/store/store.test.d.ts.map +1 -0
- package/dist/store/store.test.js +331 -0
- package/dist/store/types.d.ts +114 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +1 -0
- package/dist/styles/tokens.test.d.ts +2 -0
- package/dist/styles/tokens.test.d.ts.map +1 -0
- package/dist/styles/tokens.test.js +199 -0
- package/dist/styles.css +2885 -0
- package/dist/toolbar/brand.d.ts +7 -0
- package/dist/toolbar/brand.d.ts.map +1 -0
- package/dist/toolbar/brand.js +11 -0
- package/dist/toolbar/breakpoint-control.d.ts +9 -0
- package/dist/toolbar/breakpoint-control.d.ts.map +1 -0
- package/dist/toolbar/breakpoint-control.js +23 -0
- package/dist/toolbar/editable-title.d.ts +14 -0
- package/dist/toolbar/editable-title.d.ts.map +1 -0
- package/dist/toolbar/editable-title.js +46 -0
- package/dist/toolbar/editable-title.test.d.ts +2 -0
- package/dist/toolbar/editable-title.test.d.ts.map +1 -0
- package/dist/toolbar/editable-title.test.js +53 -0
- package/dist/toolbar/index.d.ts +12 -0
- package/dist/toolbar/index.d.ts.map +1 -0
- package/dist/toolbar/index.js +5 -0
- package/dist/toolbar/locale-switcher.d.ts +10 -0
- package/dist/toolbar/locale-switcher.d.ts.map +1 -0
- package/dist/toolbar/locale-switcher.js +20 -0
- package/dist/toolbar/more-menu.d.ts +12 -0
- package/dist/toolbar/more-menu.d.ts.map +1 -0
- package/dist/toolbar/more-menu.js +47 -0
- package/dist/toolbar/panel-toggles.d.ts +9 -0
- package/dist/toolbar/panel-toggles.d.ts.map +1 -0
- package/dist/toolbar/panel-toggles.js +19 -0
- package/dist/toolbar/publish-dialog.d.ts +18 -0
- package/dist/toolbar/publish-dialog.d.ts.map +1 -0
- package/dist/toolbar/publish-dialog.js +49 -0
- package/dist/toolbar/publish.test.d.ts +2 -0
- package/dist/toolbar/publish.test.d.ts.map +1 -0
- package/dist/toolbar/publish.test.js +258 -0
- package/dist/toolbar/sample-picker.d.ts +18 -0
- package/dist/toolbar/sample-picker.d.ts.map +1 -0
- package/dist/toolbar/sample-picker.js +72 -0
- package/dist/toolbar/sample-picker.test.d.ts +2 -0
- package/dist/toolbar/sample-picker.test.d.ts.map +1 -0
- package/dist/toolbar/sample-picker.test.js +78 -0
- package/dist/toolbar/save-indicator.d.ts +8 -0
- package/dist/toolbar/save-indicator.d.ts.map +1 -0
- package/dist/toolbar/save-indicator.js +30 -0
- package/dist/toolbar/theme.d.ts +22 -0
- package/dist/toolbar/theme.d.ts.map +1 -0
- package/dist/toolbar/theme.js +46 -0
- package/dist/toolbar/theme.test.d.ts +2 -0
- package/dist/toolbar/theme.test.d.ts.map +1 -0
- package/dist/toolbar/theme.test.js +49 -0
- package/dist/toolbar/toolbar-centre.d.ts +13 -0
- package/dist/toolbar/toolbar-centre.d.ts.map +1 -0
- package/dist/toolbar/toolbar-centre.js +7 -0
- package/dist/toolbar/toolbar-left.d.ts +12 -0
- package/dist/toolbar/toolbar-left.d.ts.map +1 -0
- package/dist/toolbar/toolbar-left.js +16 -0
- package/dist/toolbar/toolbar-right.d.ts +19 -0
- package/dist/toolbar/toolbar-right.d.ts.map +1 -0
- package/dist/toolbar/toolbar-right.js +21 -0
- package/dist/toolbar/toolbar.d.ts +38 -0
- package/dist/toolbar/toolbar.d.ts.map +1 -0
- package/dist/toolbar/toolbar.js +27 -0
- package/dist/toolbar/toolbar.test.d.ts +2 -0
- package/dist/toolbar/toolbar.test.d.ts.map +1 -0
- package/dist/toolbar/toolbar.test.js +289 -0
- package/dist/toolbar/use-overflow-level.d.ts +9 -0
- package/dist/toolbar/use-overflow-level.d.ts.map +1 -0
- package/dist/toolbar/use-overflow-level.js +40 -0
- package/dist/toolbar/zoom-menu.d.ts +10 -0
- package/dist/toolbar/zoom-menu.d.ts.map +1 -0
- package/dist/toolbar/zoom-menu.js +17 -0
- package/dist/ui/icon.d.ts +102 -0
- package/dist/ui/icon.d.ts.map +1 -0
- package/dist/ui/icon.js +182 -0
- package/dist/ui/icon.test.d.ts +2 -0
- package/dist/ui/icon.test.d.ts.map +1 -0
- package/dist/ui/icon.test.js +115 -0
- package/dist/ui/index.d.ts +11 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/panel.d.ts +18 -0
- package/dist/ui/panel.d.ts.map +1 -0
- package/dist/ui/panel.js +21 -0
- package/dist/ui/portal.d.ts +14 -0
- package/dist/ui/portal.d.ts.map +1 -0
- package/dist/ui/portal.js +14 -0
- package/dist/ui/primitives.d.ts +190 -0
- package/dist/ui/primitives.d.ts.map +1 -0
- package/dist/ui/primitives.js +164 -0
- package/dist/ui/primitives.test.d.ts +2 -0
- package/dist/ui/primitives.test.d.ts.map +1 -0
- package/dist/ui/primitives.test.js +215 -0
- package/dist/ui/toast.d.ts +39 -0
- package/dist/ui/toast.d.ts.map +1 -0
- package/dist/ui/toast.js +76 -0
- package/dist/ui/toast.test.d.ts +2 -0
- package/dist/ui/toast.test.d.ts.map +1 -0
- package/dist/ui/toast.test.js +95 -0
- package/package.json +67 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { DocumentAdapter } from '../../persistence/index.ts';
|
|
3
|
+
/** The media the editor can offer: what the adapter's `media` gives, and where a picked entry lives. */
|
|
4
|
+
export interface MediaLibrary {
|
|
5
|
+
readonly media: DocumentAdapter['media'];
|
|
6
|
+
/** The collection a `MediaRef` names (`media` in the Payload plugin's defaults). */
|
|
7
|
+
readonly collection: string;
|
|
8
|
+
}
|
|
9
|
+
export interface MediaLibraryProviderProps {
|
|
10
|
+
readonly media: DocumentAdapter['media'];
|
|
11
|
+
readonly collection?: string | undefined;
|
|
12
|
+
readonly children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare function MediaLibraryProvider({ media, collection, children }: MediaLibraryProviderProps): import("react").JSX.Element;
|
|
15
|
+
/** The media library, or `undefined` when the editor was given none (the media control then says so). */
|
|
16
|
+
export declare const useMediaLibrary: () => MediaLibrary | undefined;
|
|
17
|
+
//# sourceMappingURL=library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../../src/dialogs/media-picker/library.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAID,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,yBAAyB,+BAG9F;AAED,yGAAyG;AACzG,eAAO,MAAM,eAAe,QAAO,YAAY,GAAG,SAAuC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
3
|
+
const LibraryContext = createContext(undefined);
|
|
4
|
+
export function MediaLibraryProvider({ media, collection, children }) {
|
|
5
|
+
const value = useMemo(() => ({ media, collection: collection ?? 'media' }), [media, collection]);
|
|
6
|
+
return _jsx(LibraryContext.Provider, { value: value, children: children });
|
|
7
|
+
}
|
|
8
|
+
/** The media library, or `undefined` when the editor was given none (the media control then says so). */
|
|
9
|
+
export const useMediaLibrary = () => useContext(LibraryContext);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MediaAsset } from '@next-buildr/core';
|
|
2
|
+
export interface MediaPickerProps {
|
|
3
|
+
readonly open: boolean;
|
|
4
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
5
|
+
/** Called with the entry the author chose (or just uploaded). */
|
|
6
|
+
readonly onPick: (asset: MediaAsset) => void;
|
|
7
|
+
/** The prop's `accept`; narrows the kinds offered. */
|
|
8
|
+
readonly accept?: readonly string[] | undefined;
|
|
9
|
+
/** How long after typing the search runs; `0` searches at once. */
|
|
10
|
+
readonly searchDelayMs?: number | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The media picker (docs/editor.md#media): a searchable, filterable grid of the library, paged with
|
|
14
|
+
* "Load more", plus an upload that will not go without alternative text. Every part is a native
|
|
15
|
+
* control, so it is operable with Tab, Enter and Space alone; the dialog traps focus and Escape closes it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function MediaPicker(props: MediaPickerProps): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=media-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-picker.d.ts","sourceRoot":"","sources":["../../../src/dialogs/media-picker/media-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAOpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAChD,mEAAmE;IACnE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAcD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA4HlD"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
3
|
+
import { useT } from '../../messages/index.js';
|
|
4
|
+
import { Button, Dialog, Input, Select } from '../../ui/index.js';
|
|
5
|
+
import { useMediaLibrary } from './library.js';
|
|
6
|
+
import { kindsFor, mimeTypesFor } from './media-ref.js';
|
|
7
|
+
const KIND_KEY = {
|
|
8
|
+
all: 'media.kind.all',
|
|
9
|
+
image: 'media.kind.image',
|
|
10
|
+
video: 'media.kind.video',
|
|
11
|
+
document: 'media.kind.document',
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The media picker (docs/editor.md#media): a searchable, filterable grid of the library, paged with
|
|
15
|
+
* "Load more", plus an upload that will not go without alternative text. Every part is a native
|
|
16
|
+
* control, so it is operable with Tab, Enter and Space alone; the dialog traps focus and Escape closes it.
|
|
17
|
+
*/
|
|
18
|
+
export function MediaPicker(props) {
|
|
19
|
+
const { open, onOpenChange, onPick, accept, searchDelayMs = 250 } = props;
|
|
20
|
+
const t = useT();
|
|
21
|
+
const library = useMediaLibrary();
|
|
22
|
+
const kinds = kindsFor(accept);
|
|
23
|
+
const [text, setText] = useState('');
|
|
24
|
+
const [kind, setKind] = useState(kinds[0] ?? 'all');
|
|
25
|
+
const [load, setLoad] = useState({ state: 'loading' });
|
|
26
|
+
const [more, setMore] = useState(false);
|
|
27
|
+
// A reply that arrives after a newer search was made must not replace it.
|
|
28
|
+
const request = useRef(0);
|
|
29
|
+
const run = useCallback(async (cursor, previous) => {
|
|
30
|
+
if (library === undefined)
|
|
31
|
+
return;
|
|
32
|
+
const mine = ++request.current;
|
|
33
|
+
if (cursor === undefined)
|
|
34
|
+
setLoad({ state: 'loading' });
|
|
35
|
+
else
|
|
36
|
+
setMore(true);
|
|
37
|
+
try {
|
|
38
|
+
const mimeTypes = mimeTypesFor(kind);
|
|
39
|
+
const result = await library.media.search({
|
|
40
|
+
...(text.trim() !== '' ? { text: text.trim() } : {}),
|
|
41
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
42
|
+
...(mimeTypes !== undefined ? { mimeTypes } : {}),
|
|
43
|
+
});
|
|
44
|
+
if (mine !== request.current)
|
|
45
|
+
return;
|
|
46
|
+
setLoad({
|
|
47
|
+
state: 'ready',
|
|
48
|
+
items: [...previous, ...result.items],
|
|
49
|
+
...(result.nextCursor !== undefined ? { next: result.nextCursor } : {}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
if (mine === request.current)
|
|
54
|
+
setLoad({ state: 'error' });
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (mine === request.current)
|
|
58
|
+
setMore(false);
|
|
59
|
+
}
|
|
60
|
+
}, [library, kind, text]);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!open || library === undefined)
|
|
63
|
+
return;
|
|
64
|
+
// Opening, changing the kind and clearing the search load at once; only typing waits for a pause.
|
|
65
|
+
const timer = setTimeout(() => void run(undefined, []), text === '' ? 0 : searchDelayMs);
|
|
66
|
+
return () => clearTimeout(timer);
|
|
67
|
+
}, [open, library, run, searchDelayMs, text]);
|
|
68
|
+
const pick = (asset) => {
|
|
69
|
+
onPick(asset);
|
|
70
|
+
onOpenChange(false);
|
|
71
|
+
};
|
|
72
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, title: t('media.picker.title'), description: t('media.picker.description'), children: library === undefined ? (_jsx("p", { className: "bd-field-hint", children: t('media.unavailable') })) : (_jsxs("div", { className: "bd-media-picker", children: [_jsxs("div", { className: "bd-media-tools", children: [_jsx(Input, { type: "search", "aria-label": t('media.search'), placeholder: t('media.search'), value: text, onChange: (event) => setText(event.target.value) }), kinds.length > 1 ? (_jsx(Select, { label: t('media.kind'), value: kind, onValueChange: (next) => setKind(next), options: kinds.map((entry) => ({ value: entry, label: t(KIND_KEY[entry]) })) })) : null] }), load.state === 'loading' ? (_jsx("p", { role: "status", className: "bd-field-hint", children: t('media.loading') })) : null, load.state === 'error' ? (_jsxs("p", { role: "alert", className: "bd-media-error", children: [t('media.error'), ' ', _jsx(Button, { variant: "ghost", onClick: () => void run(undefined, []), children: t('media.retry') })] })) : null, load.state === 'ready' && load.items.length === 0 ? (_jsx("p", { className: "bd-field-hint", children: t('media.empty') })) : null, load.state === 'ready' ? (_jsx("ul", { className: "bd-media-grid", "aria-label": t('media.picker.title'), children: load.items.map((asset) => (_jsx("li", { children: _jsxs("button", { type: "button", className: "bd-media-item", onClick: () => pick(asset), children: [asset.mimeType.startsWith('image/') ? (_jsx("img", { src: asset.url, alt: "", loading: "lazy" })) : (_jsx("span", { className: "bd-media-file", children: asset.mimeType })), _jsx("span", { className: "bd-media-name", children: asset.alt ?? asset.id })] }) }, asset.id))) })) : null, load.state === 'ready' && load.next !== undefined ? (_jsx(Button, { disabled: more, onClick: () => void run(load.next, load.items), children: t('media.more') })) : null, library.media.upload !== undefined ? (_jsx(UploadForm, { upload: library.media.upload, onUploaded: pick })) : null] })) }));
|
|
73
|
+
}
|
|
74
|
+
/** Uploads a file. The alternative text is required: an image without one is an accessibility error. */
|
|
75
|
+
function UploadForm(props) {
|
|
76
|
+
const t = useT();
|
|
77
|
+
const id = useId();
|
|
78
|
+
const [file, setFile] = useState();
|
|
79
|
+
const [alt, setAlt] = useState('');
|
|
80
|
+
const [busy, setBusy] = useState(false);
|
|
81
|
+
const [failed, setFailed] = useState(false);
|
|
82
|
+
const ready = file !== undefined && alt.trim() !== '' && !busy;
|
|
83
|
+
const submit = async () => {
|
|
84
|
+
if (file === undefined || alt.trim() === '')
|
|
85
|
+
return;
|
|
86
|
+
setBusy(true);
|
|
87
|
+
setFailed(false);
|
|
88
|
+
try {
|
|
89
|
+
props.onUploaded(await props.upload(file, alt.trim()));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
setFailed(true);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setBusy(false);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
return (_jsxs("fieldset", { className: "bd-media-upload", children: [_jsx("legend", { children: t('media.upload') }), _jsx(Input, { id: `${id}-file`, type: "file", "aria-label": t('media.upload.file'), onChange: (event) => setFile(event.target.files?.[0]) }), _jsx(Input, { id: `${id}-alt`, type: "text", required: true, "aria-label": t('media.upload.alt'), placeholder: t('media.upload.alt'), "aria-invalid": file !== undefined && alt.trim() === '', value: alt, onChange: (event) => setAlt(event.target.value) }), _jsx(Button, { variant: "primary", disabled: !ready, onClick: () => void submit(), children: busy ? t('media.upload.busy') : t('media.upload.submit') }), failed ? (_jsx("p", { role: "alert", className: "bd-media-error", children: t('media.upload.failed') })) : null] }));
|
|
99
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-picker.test.d.ts","sourceRoot":"","sources":["../../../src/dialogs/media-picker/media-picker.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment jsdom
|
|
3
|
+
import { p } from '@next-buildr/core';
|
|
4
|
+
import { act } from 'react';
|
|
5
|
+
import { createRoot } from 'react-dom/client';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { axe } from 'vitest-axe';
|
|
8
|
+
import * as matchers from 'vitest-axe/matchers';
|
|
9
|
+
import { MessagesProvider } from '../../messages/index.js';
|
|
10
|
+
import { MediaControl } from '../../panels/inspector/controls/index.js';
|
|
11
|
+
import { MediaLibraryProvider } from './library.js';
|
|
12
|
+
import { MediaPicker } from './media-picker.js';
|
|
13
|
+
import { kindsFor, mimeTypesFor, parseMediaRef, toMediaRef } from './media-ref.js';
|
|
14
|
+
expect.extend(matchers);
|
|
15
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
16
|
+
const photo = {
|
|
17
|
+
id: 'm1',
|
|
18
|
+
url: '/a.jpg',
|
|
19
|
+
alt: 'A cat',
|
|
20
|
+
width: 10,
|
|
21
|
+
height: 8,
|
|
22
|
+
mimeType: 'image/jpeg',
|
|
23
|
+
};
|
|
24
|
+
const manual = { id: 'm2', url: '/b.pdf', mimeType: 'application/pdf' };
|
|
25
|
+
describe('media references', () => {
|
|
26
|
+
it('stores a reference with a snapshot and reads it back', () => {
|
|
27
|
+
const ref = toMediaRef(photo, 'media');
|
|
28
|
+
expect(ref).toEqual({
|
|
29
|
+
source: 'payload',
|
|
30
|
+
collection: 'media',
|
|
31
|
+
id: 'm1',
|
|
32
|
+
snapshot: { url: '/a.jpg', alt: 'A cat', width: 10, height: 8, mimeType: 'image/jpeg' },
|
|
33
|
+
});
|
|
34
|
+
expect(parseMediaRef(ref)).toEqual(ref);
|
|
35
|
+
expect(parseMediaRef(null)).toBeUndefined();
|
|
36
|
+
expect(parseMediaRef({ id: 'x' })).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
it('narrows the kinds by what the prop accepts', () => {
|
|
39
|
+
expect(kindsFor(undefined)).toContain('video');
|
|
40
|
+
expect(kindsFor(['image'])).toEqual(['image']);
|
|
41
|
+
expect(kindsFor(['nonsense'])).toContain('all');
|
|
42
|
+
expect(mimeTypesFor('all')).toBeUndefined();
|
|
43
|
+
expect(mimeTypesFor('image')).toEqual(['image/*']);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('the media picker and control', () => {
|
|
47
|
+
let container;
|
|
48
|
+
let root;
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
container = document.createElement('div');
|
|
51
|
+
document.body.append(container);
|
|
52
|
+
root = createRoot(container);
|
|
53
|
+
Element.prototype.scrollIntoView ??= () => { };
|
|
54
|
+
Element.prototype.hasPointerCapture ??= () => false;
|
|
55
|
+
Element.prototype.setPointerCapture ??= () => { };
|
|
56
|
+
Element.prototype.releasePointerCapture ??= () => { };
|
|
57
|
+
});
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
act(() => root.unmount());
|
|
60
|
+
document.body.innerHTML = '';
|
|
61
|
+
});
|
|
62
|
+
function library(overrides = {}) {
|
|
63
|
+
const search = vi.fn(async (query) => {
|
|
64
|
+
if (query.cursor === 'page2')
|
|
65
|
+
return { items: [manual] };
|
|
66
|
+
if (query.text === 'none')
|
|
67
|
+
return { items: [] };
|
|
68
|
+
return { items: [photo], nextCursor: 'page2' };
|
|
69
|
+
});
|
|
70
|
+
const media = { search, ...overrides };
|
|
71
|
+
return { media, search };
|
|
72
|
+
}
|
|
73
|
+
const render = async (node) => act(async () => root.render(_jsx(MessagesProvider, { locale: "en", children: node })));
|
|
74
|
+
const byText = (text) => [...document.querySelectorAll('button')].find((b) => b.textContent?.includes(text));
|
|
75
|
+
const settle = () => act(async () => void (await new Promise((done) => setTimeout(done, 10))));
|
|
76
|
+
const setValue = (element, value) => act(async () => {
|
|
77
|
+
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set?.call(element, value);
|
|
78
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
79
|
+
});
|
|
80
|
+
it('lists the library, pages it and picks with the keyboard-reachable buttons', async () => {
|
|
81
|
+
const { media, search } = library();
|
|
82
|
+
const onPick = vi.fn();
|
|
83
|
+
const onOpenChange = vi.fn();
|
|
84
|
+
await render(_jsx(MediaLibraryProvider, { media: media, children: _jsx(MediaPicker, { open: true, onOpenChange: onOpenChange, onPick: onPick, searchDelayMs: 0 }) }));
|
|
85
|
+
await settle();
|
|
86
|
+
expect(document.querySelectorAll('.bd-media-item')).toHaveLength(1);
|
|
87
|
+
await act(async () => byText('Load more')?.click());
|
|
88
|
+
expect(search).toHaveBeenLastCalledWith({ cursor: 'page2' });
|
|
89
|
+
expect(document.querySelectorAll('.bd-media-item')).toHaveLength(2);
|
|
90
|
+
await act(async () => document.querySelector('.bd-media-item').click());
|
|
91
|
+
expect(onPick).toHaveBeenCalledWith(photo);
|
|
92
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
93
|
+
});
|
|
94
|
+
it('searches and says when nothing matches', async () => {
|
|
95
|
+
const { media, search } = library();
|
|
96
|
+
await render(_jsx(MediaLibraryProvider, { media: media, children: _jsx(MediaPicker, { open: true, onOpenChange: () => undefined, onPick: () => undefined, searchDelayMs: 0 }) }));
|
|
97
|
+
await setValue(document.querySelector('input[type=search]'), 'none');
|
|
98
|
+
await settle();
|
|
99
|
+
expect(search).toHaveBeenLastCalledWith({ text: 'none' });
|
|
100
|
+
expect(document.body.textContent).toContain('No files found.');
|
|
101
|
+
});
|
|
102
|
+
it('will not upload without alternative text, then picks the new file', async () => {
|
|
103
|
+
const upload = vi.fn(async () => photo);
|
|
104
|
+
const { media } = library({ upload });
|
|
105
|
+
const onPick = vi.fn();
|
|
106
|
+
await render(_jsx(MediaLibraryProvider, { media: media, children: _jsx(MediaPicker, { open: true, onOpenChange: () => undefined, onPick: onPick, searchDelayMs: 0 }) }));
|
|
107
|
+
const submit = byText('Upload and use');
|
|
108
|
+
const file = new File(['x'], 'cat.jpg', { type: 'image/jpeg' });
|
|
109
|
+
const fileInput = document.querySelector('input[type=file]');
|
|
110
|
+
await act(async () => {
|
|
111
|
+
Object.defineProperty(fileInput, 'files', { value: [file], configurable: true });
|
|
112
|
+
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
113
|
+
});
|
|
114
|
+
expect(submit.disabled).toBe(true);
|
|
115
|
+
await setValue(document.querySelector('input[aria-label^="Alternative"]'), 'A cat');
|
|
116
|
+
expect(submit.disabled).toBe(false);
|
|
117
|
+
await act(async () => submit.click());
|
|
118
|
+
expect(upload).toHaveBeenCalledWith(file, 'A cat');
|
|
119
|
+
expect(onPick).toHaveBeenCalledWith(photo);
|
|
120
|
+
});
|
|
121
|
+
it('shows a failed library as a retryable error', async () => {
|
|
122
|
+
const search = vi
|
|
123
|
+
.fn()
|
|
124
|
+
.mockRejectedValueOnce(new Error('down'))
|
|
125
|
+
.mockResolvedValue({ items: [] });
|
|
126
|
+
await render(_jsx(MediaLibraryProvider, { media: { search }, children: _jsx(MediaPicker, { open: true, onOpenChange: () => undefined, onPick: () => undefined, searchDelayMs: 0 }) }));
|
|
127
|
+
await settle();
|
|
128
|
+
expect(document.querySelector('[role=alert]')?.textContent).toContain('could not be reached');
|
|
129
|
+
await act(async () => byText('Try again')?.click());
|
|
130
|
+
expect(document.body.textContent).toContain('No files found.');
|
|
131
|
+
});
|
|
132
|
+
it('the control chooses, replaces and removes', async () => {
|
|
133
|
+
const { media } = library();
|
|
134
|
+
const onChange = vi.fn();
|
|
135
|
+
const def = p.media({ accept: ['image'] });
|
|
136
|
+
const mount = (value) => render(_jsx(MediaLibraryProvider, { media: media, collection: "uploads", children: _jsx(MediaControl, { id: "c", def: def, label: "Image", describedBy: undefined, value: value, disabled: false, onChange: onChange }) }));
|
|
137
|
+
await mount(null);
|
|
138
|
+
expect(container.textContent).toContain('No file chosen.');
|
|
139
|
+
await act(async () => byText('Choose file')?.click());
|
|
140
|
+
await settle();
|
|
141
|
+
await act(async () => document.querySelector('.bd-media-item').click());
|
|
142
|
+
expect(onChange).toHaveBeenLastCalledWith(toMediaRef(photo, 'uploads'));
|
|
143
|
+
await mount(toMediaRef(photo, 'uploads'));
|
|
144
|
+
expect(container.querySelector('img')?.getAttribute('alt')).toBe('A cat');
|
|
145
|
+
expect(byText('Replace')).toBeDefined();
|
|
146
|
+
await act(async () => byText('Remove')?.click());
|
|
147
|
+
expect(onChange).toHaveBeenLastCalledWith(null);
|
|
148
|
+
});
|
|
149
|
+
it('the control says so without a library', async () => {
|
|
150
|
+
await render(_jsx(MediaControl, { id: "c", def: p.media(), label: "Image", describedBy: undefined, value: null, disabled: false, onChange: () => undefined }));
|
|
151
|
+
expect(container.textContent).toContain('No media library is connected.');
|
|
152
|
+
expect(byText('Choose file').disabled).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
it('has no accessibility violations', async () => {
|
|
155
|
+
const { media } = library();
|
|
156
|
+
await render(_jsx(MediaLibraryProvider, { media: media, children: _jsx(MediaPicker, { open: true, onOpenChange: () => undefined, onPick: () => undefined, searchDelayMs: 0 }) }));
|
|
157
|
+
await settle();
|
|
158
|
+
const dialog = document.querySelector('[role=dialog]');
|
|
159
|
+
expect((await axe(dialog)).violations).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MediaAsset } from '@next-buildr/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/** What a media prop stores (docs/payload.md#media): a reference, plus a snapshot to show while the library is not asked. */
|
|
4
|
+
export interface MediaRef {
|
|
5
|
+
readonly source: 'payload';
|
|
6
|
+
readonly collection: string;
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly snapshot?: {
|
|
9
|
+
readonly url: string;
|
|
10
|
+
readonly alt?: string | undefined;
|
|
11
|
+
readonly width?: number | undefined;
|
|
12
|
+
readonly height?: number | undefined;
|
|
13
|
+
readonly mimeType?: string | undefined;
|
|
14
|
+
} | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare const mediaRefSchema: z.ZodType<MediaRef>;
|
|
17
|
+
/** The value of a media prop, when it is a reference; anything else (`null`, a stale shape) is `undefined`. */
|
|
18
|
+
export declare function parseMediaRef(value: unknown): MediaRef | undefined;
|
|
19
|
+
/** The reference to store for a library entry; the snapshot carries only what a thumbnail needs. */
|
|
20
|
+
export declare function toMediaRef(asset: MediaAsset, collection: string): MediaRef;
|
|
21
|
+
/** Which kinds of file the picker offers. The prop's `accept` (`['image']`) narrows the choice. */
|
|
22
|
+
export type MediaKind = 'all' | 'image' | 'video' | 'document';
|
|
23
|
+
export declare const MEDIA_KINDS: readonly MediaKind[];
|
|
24
|
+
/** The MIME filter the library is asked with; `undefined` for no filter. */
|
|
25
|
+
export declare function mimeTypesFor(kind: MediaKind): readonly string[] | undefined;
|
|
26
|
+
/** The kinds a prop's `accept` allows: all of them when it names none. */
|
|
27
|
+
export declare function kindsFor(accept: readonly string[] | undefined): readonly MediaKind[];
|
|
28
|
+
//# sourceMappingURL=media-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-ref.d.ts","sourceRoot":"","sources":["../../../src/dialogs/media-picker/media-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,6HAA6H;AAC7H,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,CAAC,EACd;QACE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACxC,GACD,SAAS,CAAC;CACf;AAED,eAAO,MAAM,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAa7C,CAAC;AAEH,+GAA+G;AAC/G,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAGlE;AAED,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAa1E;AAED,mGAAmG;AACnG,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AAE/D,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAA0C,CAAC;AAQvF,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAE3E;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,SAAS,SAAS,EAAE,CAIpF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const mediaRefSchema = z.object({
|
|
3
|
+
source: z.literal('payload'),
|
|
4
|
+
collection: z.string().min(1),
|
|
5
|
+
id: z.string().min(1),
|
|
6
|
+
snapshot: z
|
|
7
|
+
.object({
|
|
8
|
+
url: z.string(),
|
|
9
|
+
alt: z.string().optional(),
|
|
10
|
+
width: z.number().optional(),
|
|
11
|
+
height: z.number().optional(),
|
|
12
|
+
mimeType: z.string().optional(),
|
|
13
|
+
})
|
|
14
|
+
.optional(),
|
|
15
|
+
});
|
|
16
|
+
/** The value of a media prop, when it is a reference; anything else (`null`, a stale shape) is `undefined`. */
|
|
17
|
+
export function parseMediaRef(value) {
|
|
18
|
+
const parsed = mediaRefSchema.safeParse(value);
|
|
19
|
+
return parsed.success ? parsed.data : undefined;
|
|
20
|
+
}
|
|
21
|
+
/** The reference to store for a library entry; the snapshot carries only what a thumbnail needs. */
|
|
22
|
+
export function toMediaRef(asset, collection) {
|
|
23
|
+
return {
|
|
24
|
+
source: 'payload',
|
|
25
|
+
collection,
|
|
26
|
+
id: asset.id,
|
|
27
|
+
snapshot: {
|
|
28
|
+
url: asset.url,
|
|
29
|
+
...(asset.alt !== undefined ? { alt: asset.alt } : {}),
|
|
30
|
+
...(asset.width !== undefined ? { width: asset.width } : {}),
|
|
31
|
+
...(asset.height !== undefined ? { height: asset.height } : {}),
|
|
32
|
+
mimeType: asset.mimeType,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export const MEDIA_KINDS = ['all', 'image', 'video', 'document'];
|
|
37
|
+
const MIME_PATTERNS = {
|
|
38
|
+
image: ['image/*'],
|
|
39
|
+
video: ['video/*'],
|
|
40
|
+
document: ['application/pdf'],
|
|
41
|
+
};
|
|
42
|
+
/** The MIME filter the library is asked with; `undefined` for no filter. */
|
|
43
|
+
export function mimeTypesFor(kind) {
|
|
44
|
+
return kind === 'all' ? undefined : MIME_PATTERNS[kind];
|
|
45
|
+
}
|
|
46
|
+
/** The kinds a prop's `accept` allows: all of them when it names none. */
|
|
47
|
+
export function kindsFor(accept) {
|
|
48
|
+
if (accept === undefined || accept.length === 0)
|
|
49
|
+
return MEDIA_KINDS;
|
|
50
|
+
const allowed = MEDIA_KINDS.filter((kind) => kind !== 'all' && accept.includes(kind));
|
|
51
|
+
return allowed.length === 0 ? MEDIA_KINDS : allowed;
|
|
52
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BuilderDocument, type DocumentIndex, type DropTarget, type NodeId, type Reason, type RegistryMeta, type Result } from '@next-buildr/core';
|
|
2
|
+
/**
|
|
3
|
+
* `canMove` for a gap as `node.move` counts it (among the children as they are before the move).
|
|
4
|
+
* `canMove` itself counts without the node being moved when it stays in its slot, so the end gap of
|
|
5
|
+
* its own slot is one past what it accepts; the gap is clamped for the check, as the command does.
|
|
6
|
+
*/
|
|
7
|
+
export declare function moveVerdict(doc: BuilderDocument, index: DocumentIndex, registry: RegistryMeta, id: NodeId, target: DropTarget): Result<true, Reason>;
|
|
8
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/dnd/check.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,YAAY,EACtB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,UAAU,GACjB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAKtB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { canMove, } from '@next-buildr/core';
|
|
2
|
+
/**
|
|
3
|
+
* `canMove` for a gap as `node.move` counts it (among the children as they are before the move).
|
|
4
|
+
* `canMove` itself counts without the node being moved when it stays in its slot, so the end gap of
|
|
5
|
+
* its own slot is one past what it accepts; the gap is clamped for the check, as the command does.
|
|
6
|
+
*/
|
|
7
|
+
export function moveVerdict(doc, index, registry, id, target) {
|
|
8
|
+
const sameSlot = index.parentOf[id] === target.parentId && index.slotOf[id] === target.slot;
|
|
9
|
+
const count = doc.nodes[target.parentId]?.slots?.[target.slot]?.length ?? 0;
|
|
10
|
+
const at = sameSlot ? Math.min(target.index, Math.max(0, count - 1)) : target.index;
|
|
11
|
+
return canMove(doc, index, registry, id, { parentId: target.parentId, slot: target.slot, at });
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type BuilderDocument, type DropTarget, type NodeId, type RegistryMeta } from '@next-buildr/core';
|
|
2
|
+
export interface Destination {
|
|
3
|
+
readonly target: DropTarget;
|
|
4
|
+
/** The node the nodes would go into. */
|
|
5
|
+
readonly parentId: NodeId;
|
|
6
|
+
readonly slot: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Every place the nodes could be moved to, in document order: the end of each slot of each node the
|
|
10
|
+
* rules accept (`canMove` for each of them). The keyboard counterpart of dragging; the list is what
|
|
11
|
+
* the "Move to" dialog shows. Never more than `limit` entries, so a huge document cannot make the
|
|
12
|
+
* dialog huge.
|
|
13
|
+
*/
|
|
14
|
+
export declare function moveDestinations(doc: BuilderDocument, registry: RegistryMeta, ids: readonly NodeId[], limit?: number): Destination[];
|
|
15
|
+
//# sourceMappingURL=destinations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destinations.d.ts","sourceRoot":"","sources":["../../src/dnd/destinations.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAcD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,KAAK,SAAM,GACV,WAAW,EAAE,CAqBf"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createIndex, } from '@next-buildr/core';
|
|
2
|
+
import { moveVerdict } from './check.js';
|
|
3
|
+
/** Nodes with no ancestor in the list: moving a node moves what is inside it too. */
|
|
4
|
+
function topmost(doc, ids) {
|
|
5
|
+
const index = createIndex(doc);
|
|
6
|
+
const set = new Set(ids);
|
|
7
|
+
return ids.filter((id) => {
|
|
8
|
+
for (let up = index.parentOf[id]; up !== undefined; up = index.parentOf[up]) {
|
|
9
|
+
if (set.has(up))
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Every place the nodes could be moved to, in document order: the end of each slot of each node the
|
|
17
|
+
* rules accept (`canMove` for each of them). The keyboard counterpart of dragging; the list is what
|
|
18
|
+
* the "Move to" dialog shows. Never more than `limit` entries, so a huge document cannot make the
|
|
19
|
+
* dialog huge.
|
|
20
|
+
*/
|
|
21
|
+
export function moveDestinations(doc, registry, ids, limit = 200) {
|
|
22
|
+
const movable = topmost(doc, ids.filter((id) => id !== doc.root && Object.hasOwn(doc.nodes, id)));
|
|
23
|
+
if (movable.length === 0)
|
|
24
|
+
return [];
|
|
25
|
+
const index = createIndex(doc);
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const parentId of index.order) {
|
|
28
|
+
const node = doc.nodes[parentId];
|
|
29
|
+
if (node === undefined)
|
|
30
|
+
continue;
|
|
31
|
+
const slots = new Set(['default', ...Object.keys(node.slots ?? {})]);
|
|
32
|
+
for (const slot of slots) {
|
|
33
|
+
const at = (node.slots?.[slot] ?? []).length;
|
|
34
|
+
const target = { parentId, slot, index: at };
|
|
35
|
+
if (!movable.every((id) => moveVerdict(doc, index, registry, id, target).ok))
|
|
36
|
+
continue;
|
|
37
|
+
out.push({ target, parentId, slot });
|
|
38
|
+
if (out.length >= limit)
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dnd-react.test.d.ts","sourceRoot":"","sources":["../../src/dnd/dnd-react.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment jsdom
|
|
3
|
+
import { createRegistryMeta, createSeededIdGenerator, } from '@next-buildr/core';
|
|
4
|
+
import { act } from 'react';
|
|
5
|
+
import { createRoot } from 'react-dom/client';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
+
import { MessagesProvider } from '../messages/index.js';
|
|
8
|
+
import { LayersPanel } from '../panels/layers/layers-panel.js';
|
|
9
|
+
import { createEditorStore, EditorStoreProvider } from '../store/index.js';
|
|
10
|
+
import { DragProvider } from './react.js';
|
|
11
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
12
|
+
const meta = {
|
|
13
|
+
version: 1,
|
|
14
|
+
category: 'content',
|
|
15
|
+
props: {},
|
|
16
|
+
contentCategories: ['flow'],
|
|
17
|
+
styles: { groups: [] },
|
|
18
|
+
runtime: 'shared',
|
|
19
|
+
};
|
|
20
|
+
const registry = createRegistryMeta({
|
|
21
|
+
components: [
|
|
22
|
+
{
|
|
23
|
+
...meta,
|
|
24
|
+
type: 'buildr/page',
|
|
25
|
+
label: 'Page',
|
|
26
|
+
capabilities: { root: true },
|
|
27
|
+
slots: { default: {} },
|
|
28
|
+
},
|
|
29
|
+
{ ...meta, type: 'buildr/box', label: 'Box', slots: { default: {} } },
|
|
30
|
+
{ ...meta, type: 'buildr/leaf', label: 'Leaf' },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
const fixture = () => ({
|
|
34
|
+
schemaVersion: 1,
|
|
35
|
+
root: 'root',
|
|
36
|
+
nodes: {
|
|
37
|
+
root: { id: 'root', type: 'buildr/page', slots: { default: ['boxAAAAAA1', 'leafBBBBB1'] } },
|
|
38
|
+
boxAAAAAA1: { id: 'boxAAAAAA1', type: 'buildr/box' },
|
|
39
|
+
leafBBBBB1: { id: 'leafBBBBB1', type: 'buildr/leaf' },
|
|
40
|
+
},
|
|
41
|
+
components: {},
|
|
42
|
+
});
|
|
43
|
+
describe('drag and drop in the layers panel', () => {
|
|
44
|
+
let container;
|
|
45
|
+
let root;
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
container = document.createElement('div');
|
|
48
|
+
document.body.append(container);
|
|
49
|
+
root = createRoot(container);
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
act(() => root.unmount());
|
|
53
|
+
document.body.innerHTML = '';
|
|
54
|
+
});
|
|
55
|
+
async function mount(withProvider) {
|
|
56
|
+
const store = createEditorStore({
|
|
57
|
+
doc: fixture(),
|
|
58
|
+
registry,
|
|
59
|
+
generateId: createSeededIdGenerator(5),
|
|
60
|
+
validationDelayMs: null,
|
|
61
|
+
});
|
|
62
|
+
store.select('leafBBBBB1');
|
|
63
|
+
const panel = _jsx(LayersPanel, {});
|
|
64
|
+
await act(async () => root.render(_jsx(MessagesProvider, { locale: "en", children: _jsx(EditorStoreProvider, { store: store, children: withProvider ? _jsx(DragProvider, { children: panel }) : panel }) })));
|
|
65
|
+
return store;
|
|
66
|
+
}
|
|
67
|
+
const rowMenu = async () => {
|
|
68
|
+
const row = container.querySelector('[data-node-id="leafBBBBB1"]');
|
|
69
|
+
await act(async () => {
|
|
70
|
+
row.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }));
|
|
71
|
+
});
|
|
72
|
+
return [...document.querySelectorAll('[role=menuitem]')];
|
|
73
|
+
};
|
|
74
|
+
it('offers "Move to" only where dragging is set up, and moves with it', async () => {
|
|
75
|
+
const plain = await mount(false);
|
|
76
|
+
expect((await rowMenu()).map((i) => i.textContent)).not.toContain('Move to…');
|
|
77
|
+
expect(plain.getState().doc.nodes['boxAAAAAA1']?.slots).toBeUndefined();
|
|
78
|
+
await act(async () => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })));
|
|
79
|
+
act(() => root.unmount());
|
|
80
|
+
root = createRoot(container);
|
|
81
|
+
const store = await mount(true);
|
|
82
|
+
const items = await rowMenu();
|
|
83
|
+
const move = items.find((i) => i.textContent === 'Move to…');
|
|
84
|
+
expect(move).toBeDefined();
|
|
85
|
+
await act(async () => move?.click());
|
|
86
|
+
const dialog = document.querySelector('[role=dialog]');
|
|
87
|
+
const into = [...(dialog?.querySelectorAll('button') ?? [])].find((b) => b.textContent?.includes('Box'));
|
|
88
|
+
expect(into).toBeDefined();
|
|
89
|
+
await act(async () => into?.click());
|
|
90
|
+
expect(store.getState().doc.nodes['boxAAAAAA1']?.slots?.['default']).toEqual(['leafBBBBB1']);
|
|
91
|
+
expect(store.getState().doc.nodes['root']?.slots?.['default']).toEqual(['boxAAAAAA1']);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dnd.test.d.ts","sourceRoot":"","sources":["../../src/dnd/dnd.test.ts"],"names":[],"mappings":""}
|