@xleddyl/nuxt-cms 0.9.4 → 0.10.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/README.md +1 -1
- package/dist/module.d.mts +20 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1037 -148
- package/dist/runtime/app/blocks/CmsBlocks.d.vue.ts +1 -1
- package/dist/runtime/app/blocks/CmsBlocks.vue +57 -9
- package/dist/runtime/app/blocks/CmsBlocks.vue.d.ts +1 -1
- package/dist/runtime/app/blocks/CmsField.d.ts +24 -0
- package/dist/runtime/app/blocks/CmsField.js +16 -0
- package/dist/runtime/app/blocks/node.d.ts +37 -0
- package/dist/runtime/app/blocks/node.js +17 -0
- package/dist/runtime/app/blocks/resolve.d.ts +18 -0
- package/dist/runtime/app/blocks/resolve.js +34 -0
- package/dist/runtime/app/components/cms/BlockPicker.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue +87 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.d.vue.ts +38 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue +44 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue.d.ts +38 -0
- package/dist/runtime/app/components/cms/BlockTree.d.vue.ts +29 -0
- package/dist/runtime/app/components/cms/BlockTree.vue +322 -0
- package/dist/runtime/app/components/cms/BlockTree.vue.d.ts +29 -0
- package/dist/runtime/app/components/cms/BlocksField.vue +569 -219
- package/dist/runtime/app/components/cms/BrandMark.vue +7 -13
- package/dist/runtime/app/components/cms/ConfirmGroups.vue +15 -13
- package/dist/runtime/app/components/cms/{BlockPalette.vue.d.ts → EditorHistory.d.vue.ts} +6 -5
- package/dist/runtime/app/components/cms/EditorHistory.vue +34 -0
- package/dist/runtime/app/components/cms/{BlockPalette.d.vue.ts → EditorHistory.vue.d.ts} +6 -5
- package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +11 -5
- package/dist/runtime/app/components/cms/EntryDrawer.vue +183 -152
- package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +11 -5
- package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +16 -4
- package/dist/runtime/app/components/cms/EntryForm.vue +106 -147
- package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +16 -4
- package/dist/runtime/app/components/cms/FieldInput.vue +10 -2
- package/dist/runtime/app/components/cms/FormSections.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/FormSections.vue +71 -0
- package/dist/runtime/app/components/cms/FormSections.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/ImportModal.vue +46 -58
- package/dist/runtime/app/components/cms/LinkField.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/LinkField.vue +250 -0
- package/dist/runtime/app/components/cms/LinkField.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/ListFilters.vue +10 -12
- package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +2 -1
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +19 -11
- package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +2 -1
- package/dist/runtime/app/components/cms/MediaDetail.d.vue.ts +2 -0
- package/dist/runtime/app/components/cms/MediaDetail.vue +112 -83
- package/dist/runtime/app/components/cms/MediaDetail.vue.d.ts +2 -0
- package/dist/runtime/app/components/cms/MediaField.vue +35 -25
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue +26 -71
- package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/MediaGallery.vue +442 -419
- package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue +9 -25
- package/dist/runtime/app/components/cms/MediaUpload.vue +1 -1
- package/dist/runtime/app/components/cms/{BlockOutline.d.vue.ts → NestedBlockList.d.vue.ts} +5 -5
- package/dist/runtime/app/components/cms/NestedBlockList.vue +87 -0
- package/dist/runtime/app/components/cms/{BlockOutline.vue.d.ts → NestedBlockList.vue.d.ts} +5 -5
- package/dist/runtime/app/components/cms/PoweredBy.vue +12 -5
- package/dist/runtime/app/components/cms/PresetModal.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/PresetModal.vue +70 -0
- package/dist/runtime/app/components/cms/PresetModal.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/RelationField.vue +2 -8
- package/dist/runtime/app/components/cms/SettingsAccount.vue +60 -71
- package/dist/runtime/app/components/cms/SettingsColumns.vue +47 -69
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue +50 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntry.vue +11 -95
- package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntryActions.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue +40 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue +17 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue +18 -14
- package/dist/runtime/app/components/cms/SettingsPresetForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue +60 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresets.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue +65 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.d.vue.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue +45 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue.d.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue +59 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue +77 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue +117 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue +43 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSite.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue +140 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.d.vue.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue +82 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue.d.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue +179 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +9 -6
- package/dist/runtime/app/components/cms/SettingsUsers.vue +88 -234
- package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +9 -6
- package/dist/runtime/app/components/cms/SharePreview.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/SharePreview.vue +219 -0
- package/dist/runtime/app/components/cms/SharePreview.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/SlugField.vue +5 -3
- package/dist/runtime/app/components/cms/StatusBadge.vue +8 -8
- package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue +44 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationMenu.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue +28 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/VisualEditor.d.vue.ts +22 -4
- package/dist/runtime/app/components/cms/VisualEditor.vue +1153 -338
- package/dist/runtime/app/components/cms/VisualEditor.vue.d.ts +22 -4
- package/dist/runtime/app/components/cms/WelcomeModal.vue +32 -50
- package/dist/runtime/app/composables/cms-clipboard.d.ts +8 -0
- package/dist/runtime/app/composables/cms-clipboard.js +38 -0
- package/dist/runtime/app/composables/cms-confirm.d.ts +6 -8
- package/dist/runtime/app/composables/cms-confirm.js +5 -7
- package/dist/runtime/app/composables/cms-entry-disabled.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry-disabled.js +7 -0
- package/dist/runtime/app/composables/cms-entry-form.d.ts +15 -0
- package/dist/runtime/app/composables/cms-entry-form.js +40 -0
- package/dist/runtime/app/composables/cms-entry-settings.d.ts +30 -0
- package/dist/runtime/app/composables/cms-entry-settings.js +122 -0
- package/dist/runtime/app/composables/cms-entry.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry.js +36 -11
- package/dist/runtime/app/composables/cms-history.d.ts +10 -0
- package/dist/runtime/app/composables/cms-history.js +24 -0
- package/dist/runtime/app/composables/cms-link-targets.d.ts +10 -0
- package/dist/runtime/app/composables/cms-link-targets.js +40 -0
- package/dist/runtime/app/composables/cms-link-usage.d.ts +1 -0
- package/dist/runtime/app/composables/cms-link-usage.js +45 -0
- package/dist/runtime/app/composables/cms-media-library.d.ts +19 -9
- package/dist/runtime/app/composables/cms-media-library.js +31 -13
- package/dist/runtime/app/composables/cms-media-uploader.js +6 -20
- package/dist/runtime/app/composables/cms-permissions.d.ts +18 -0
- package/dist/runtime/app/composables/cms-permissions.js +54 -0
- package/dist/runtime/app/composables/cms-presets.d.ts +11 -0
- package/dist/runtime/app/composables/cms-presets.js +40 -0
- package/dist/runtime/app/composables/cms-preview-mode.d.ts +4 -0
- package/dist/runtime/app/composables/cms-preview-mode.js +8 -0
- package/dist/runtime/app/composables/cms-query.d.ts +3 -0
- package/dist/runtime/app/composables/cms-query.js +45 -20
- package/dist/runtime/app/composables/cms-record-list.d.ts +31 -0
- package/dist/runtime/app/composables/cms-record-list.js +75 -0
- package/dist/runtime/app/composables/cms-route.d.ts +6 -0
- package/dist/runtime/app/composables/cms-route.js +36 -0
- package/dist/runtime/app/composables/cms-runtime.d.ts +1 -0
- package/dist/runtime/app/composables/cms-save-handoff.d.ts +4 -0
- package/dist/runtime/app/composables/cms-save-handoff.js +19 -0
- package/dist/runtime/app/composables/cms-seo.d.ts +12 -0
- package/dist/runtime/app/composables/cms-seo.js +69 -0
- package/dist/runtime/app/composables/cms-settings-form.d.ts +13 -0
- package/dist/runtime/app/composables/cms-settings-form.js +29 -0
- package/dist/runtime/app/composables/cms-settings-sections.d.ts +26 -0
- package/dist/runtime/app/composables/cms-settings-sections.js +181 -0
- package/dist/runtime/app/composables/cms-settings-stores.d.ts +52 -0
- package/dist/runtime/app/composables/cms-settings-stores.js +156 -0
- package/dist/runtime/app/composables/cms-settings.d.ts +3 -3
- package/dist/runtime/app/composables/cms-settings.js +14 -8
- package/dist/runtime/app/composables/cms-site-disabled.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site-disabled.js +8 -0
- package/dist/runtime/app/composables/cms-site.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site.js +22 -0
- package/dist/runtime/app/composables/cms-toast.d.ts +2 -8
- package/dist/runtime/app/composables/cms-toast.js +3 -9
- package/dist/runtime/app/composables/cms-translation-actions.d.ts +15 -0
- package/dist/runtime/app/composables/cms-translation-actions.js +108 -0
- package/dist/runtime/app/css-url.d.ts +4 -0
- package/dist/runtime/app/image/CmsImage.d.vue.ts +89 -0
- package/dist/runtime/app/image/CmsImage.vue +91 -0
- package/dist/runtime/app/image/CmsImage.vue.d.ts +89 -0
- package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
- package/dist/runtime/app/layouts/cms-admin.vue +98 -76
- package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
- package/dist/runtime/app/layouts/cms-editor.vue +7 -2
- package/dist/runtime/app/links/CmsLink.d.vue.ts +18 -0
- package/dist/runtime/app/links/CmsLink.vue +18 -0
- package/dist/runtime/app/links/CmsLink.vue.d.ts +18 -0
- package/dist/runtime/app/middleware/cms-auth.js +13 -1
- package/dist/runtime/app/pages/admin-collection.vue +473 -297
- package/dist/runtime/app/pages/admin-content.vue +191 -54
- package/dist/runtime/app/pages/admin-entry.vue +79 -75
- package/dist/runtime/app/pages/admin-index.vue +18 -5
- package/dist/runtime/app/pages/admin-login.vue +1 -1
- package/dist/runtime/app/pages/admin-logout.vue +1 -1
- package/dist/runtime/app/pages/admin-media.vue +3 -3
- package/dist/runtime/app/pages/admin-preview.vue +112 -24
- package/dist/runtime/app/plugins/cms-field.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-field.js +8 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.js +19 -0
- package/dist/runtime/app/plugins/cms-preview.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview.js +27 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.d.vue.ts +7 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue +63 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue.d.ts +7 -0
- package/dist/runtime/app/preview/PreviewOverlay.d.vue.ts +17 -3
- package/dist/runtime/app/preview/PreviewOverlay.vue +629 -166
- package/dist/runtime/app/preview/PreviewOverlay.vue.d.ts +17 -3
- package/dist/runtime/app/preview/field-marker.d.ts +12 -0
- package/dist/runtime/app/preview/field-marker.js +20 -0
- package/dist/runtime/app/preview/inline-text.d.ts +15 -0
- package/dist/runtime/app/preview/inline-text.js +158 -0
- package/dist/runtime/app/utils/block-editor.d.ts +22 -0
- package/dist/runtime/app/utils/block-editor.js +12 -0
- package/dist/runtime/app/utils/keyboard.d.ts +6 -0
- package/dist/runtime/app/utils/keyboard.js +52 -0
- package/dist/runtime/app/utils/redirects.d.ts +6 -0
- package/dist/runtime/app/utils/redirects.js +7 -0
- package/dist/runtime/app/utils/ui.d.ts +8 -0
- package/dist/runtime/app/utils/ui.js +15 -1
- package/dist/runtime/app/utils/users.d.ts +15 -0
- package/dist/runtime/app/utils/users.js +14 -0
- package/dist/runtime/assets/cms.css +1 -1
- package/dist/runtime/generate.js +16 -2
- package/dist/runtime/server/api/collection-export.get.js +3 -2
- package/dist/runtime/server/api/collection-import.post.js +39 -11
- package/dist/runtime/server/api/collection.get.js +27 -9
- package/dist/runtime/server/api/collection.post.js +28 -11
- package/dist/runtime/server/api/collection.put.js +35 -13
- package/dist/runtime/server/api/item.delete.js +19 -7
- package/dist/runtime/server/api/item.get.js +3 -2
- package/dist/runtime/server/api/item.put.js +52 -18
- package/dist/runtime/server/api/link-usage.post.d.ts +5 -0
- package/dist/runtime/server/api/link-usage.post.js +21 -0
- package/dist/runtime/server/api/media-delete.post.js +4 -2
- package/dist/runtime/server/api/media-download.get.js +2 -2
- package/dist/runtime/server/api/media-folder.delete.js +8 -2
- package/dist/runtime/server/api/media-folder.patch.js +10 -2
- package/dist/runtime/server/api/media-folder.post.js +4 -2
- package/dist/runtime/server/api/media-move.post.js +7 -4
- package/dist/runtime/server/api/media-presign.post.js +2 -2
- package/dist/runtime/server/api/media-upload.put.js +2 -2
- package/dist/runtime/server/api/media-usage.post.d.ts +2 -4
- package/dist/runtime/server/api/media-usage.post.js +13 -9
- package/dist/runtime/server/api/media.delete.js +4 -2
- package/dist/runtime/server/api/media.get.js +22 -7
- package/dist/runtime/server/api/media.post.js +4 -2
- package/dist/runtime/server/api/media.put.js +33 -16
- package/dist/runtime/server/api/preset.delete.d.ts +4 -0
- package/dist/runtime/server/api/preset.delete.js +9 -0
- package/dist/runtime/server/api/preset.put.d.ts +3 -0
- package/dist/runtime/server/api/preset.put.js +23 -0
- package/dist/runtime/server/api/presets-field.get.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.get.js +13 -0
- package/dist/runtime/server/api/presets-field.post.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.post.js +36 -0
- package/dist/runtime/server/api/presets.get.d.ts +3 -0
- package/dist/runtime/server/api/presets.get.js +7 -0
- package/dist/runtime/server/api/preview-link.delete.d.ts +4 -0
- package/dist/runtime/server/api/preview-link.delete.js +15 -0
- package/dist/runtime/server/api/preview-links.get.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.get.js +10 -0
- package/dist/runtime/server/api/preview-links.post.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.post.js +17 -0
- package/dist/runtime/server/api/preview.get.d.ts +3 -0
- package/dist/runtime/server/api/preview.get.js +7 -0
- package/dist/runtime/server/api/redirect.delete.d.ts +4 -0
- package/dist/runtime/server/api/redirect.delete.js +10 -0
- package/dist/runtime/server/api/redirect.put.d.ts +3 -0
- package/dist/runtime/server/api/redirect.put.js +8 -0
- package/dist/runtime/server/api/redirects-import.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects-import.post.js +13 -0
- package/dist/runtime/server/api/redirects.get.d.ts +3 -0
- package/dist/runtime/server/api/redirects.get.js +7 -0
- package/dist/runtime/server/api/redirects.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects.post.js +7 -0
- package/dist/runtime/server/api/search.get.js +8 -3
- package/dist/runtime/server/api/settings.delete.js +2 -2
- package/dist/runtime/server/api/settings.get.js +4 -2
- package/dist/runtime/server/api/settings.put.js +2 -2
- package/dist/runtime/server/api/site.get.d.ts +3 -0
- package/dist/runtime/server/api/site.get.js +7 -0
- package/dist/runtime/server/api/site.put.d.ts +3 -0
- package/dist/runtime/server/api/site.put.js +16 -0
- package/dist/runtime/server/api/tags.get.js +3 -2
- package/dist/runtime/server/api/user.delete.js +8 -2
- package/dist/runtime/server/api/user.put.js +14 -4
- package/dist/runtime/server/api/users.get.js +2 -2
- package/dist/runtime/server/api/users.post.js +8 -4
- package/dist/runtime/server/middleware/hidden-site.d.ts +2 -0
- package/dist/runtime/server/middleware/hidden-site.js +8 -0
- package/dist/runtime/server/middleware/preview-links.d.ts +2 -0
- package/dist/runtime/server/middleware/preview-links.js +6 -0
- package/dist/runtime/server/middleware/redirects.d.ts +2 -0
- package/dist/runtime/server/middleware/redirects.js +18 -0
- package/dist/runtime/server/plugins/session-validate.js +4 -1
- package/dist/runtime/server/plugins/webhooks.d.ts +3 -0
- package/dist/runtime/server/plugins/webhooks.js +27 -0
- package/dist/runtime/server/routes/graphql.js +5 -3
- package/dist/runtime/server/routes/robots.d.ts +2 -0
- package/dist/runtime/server/routes/robots.js +16 -0
- package/dist/runtime/server/routes/sitemap.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.js +67 -0
- package/dist/runtime/server/utils/cached-value.d.ts +5 -0
- package/dist/runtime/server/utils/cached-value.js +24 -0
- package/dist/runtime/server/utils/content-routes.d.ts +5 -0
- package/dist/runtime/server/utils/content-routes.js +29 -0
- package/dist/runtime/server/utils/field-rules.d.ts +19 -0
- package/dist/runtime/server/utils/field-rules.js +72 -0
- package/dist/runtime/server/utils/graphql.d.ts +3 -1
- package/dist/runtime/server/utils/graphql.js +143 -28
- package/dist/runtime/server/utils/hooks.d.ts +14 -0
- package/dist/runtime/server/utils/hooks.js +71 -0
- package/dist/runtime/server/utils/import.d.ts +7 -2
- package/dist/runtime/server/utils/import.js +108 -14
- package/dist/runtime/server/utils/link-usage.d.ts +4 -0
- package/dist/runtime/server/utils/link-usage.js +28 -0
- package/dist/runtime/server/utils/media-references.d.ts +6 -1
- package/dist/runtime/server/utils/media-references.js +49 -20
- package/dist/runtime/server/utils/media-usage.d.ts +2 -0
- package/dist/runtime/server/utils/media-usage.js +5 -3
- package/dist/runtime/server/utils/page-rows.d.ts +2 -0
- package/dist/runtime/server/utils/page-rows.js +42 -29
- package/dist/runtime/server/utils/permissions.d.ts +14 -0
- package/dist/runtime/server/utils/permissions.js +43 -0
- package/dist/runtime/server/utils/presets.d.ts +18 -0
- package/dist/runtime/server/utils/presets.js +68 -0
- package/dist/runtime/server/utils/preview-links.d.ts +207 -0
- package/dist/runtime/server/utils/preview-links.js +161 -0
- package/dist/runtime/server/utils/redirects.d.ts +31 -0
- package/dist/runtime/server/utils/redirects.js +240 -0
- package/dist/runtime/server/utils/registry.js +4 -1
- package/dist/runtime/server/utils/relations.d.ts +8 -0
- package/dist/runtime/server/utils/relations.js +25 -6
- package/dist/runtime/server/utils/require-admin.d.ts +0 -1
- package/dist/runtime/server/utils/require-admin.js +4 -8
- package/dist/runtime/server/utils/search.d.ts +1 -0
- package/dist/runtime/server/utils/search.js +44 -19
- package/dist/runtime/server/utils/site.d.ts +23 -0
- package/dist/runtime/server/utils/site.js +79 -0
- package/dist/runtime/server/utils/sitemap.d.ts +12 -0
- package/dist/runtime/server/utils/sitemap.js +109 -0
- package/dist/runtime/server/utils/user-management.d.ts +4 -0
- package/dist/runtime/server/utils/user-management.js +20 -0
- package/dist/runtime/server/utils/users.d.ts +1 -1
- package/dist/runtime/server/utils/users.js +1 -1
- package/dist/runtime/server/utils/visibility.d.ts +1 -0
- package/dist/runtime/server/utils/visibility.js +6 -1
- package/dist/runtime/server/utils/webhooks.d.ts +24 -0
- package/dist/runtime/server/utils/webhooks.js +90 -0
- package/dist/runtime/shared/block-paths.d.ts +66 -0
- package/dist/runtime/shared/block-paths.js +310 -0
- package/dist/runtime/shared/block-picker.d.ts +18 -0
- package/dist/runtime/shared/block-picker.js +31 -0
- package/dist/runtime/shared/block-signature.d.ts +4 -0
- package/dist/runtime/shared/block-signature.js +46 -0
- package/dist/runtime/shared/clipboard.d.ts +31 -0
- package/dist/runtime/shared/clipboard.js +206 -0
- package/dist/runtime/shared/conditions.d.ts +36 -0
- package/dist/runtime/shared/conditions.js +100 -0
- package/dist/runtime/shared/content-routes.d.ts +42 -0
- package/dist/runtime/shared/content-routes.js +237 -0
- package/dist/runtime/shared/duplicate.js +2 -1
- package/dist/runtime/shared/editor-layout.d.ts +18 -0
- package/dist/runtime/shared/editor-layout.js +9 -0
- package/dist/runtime/shared/field-options.d.ts +21 -0
- package/dist/runtime/shared/field-options.js +90 -0
- package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
- package/dist/runtime/shared/graphql-sdl.js +50 -26
- package/dist/runtime/shared/history.d.ts +27 -0
- package/dist/runtime/shared/history.js +123 -0
- package/dist/runtime/shared/hooks.d.ts +45 -0
- package/dist/runtime/shared/hooks.js +28 -0
- package/dist/runtime/shared/index.d.ts +90 -21
- package/dist/runtime/shared/index.js +121 -37
- package/dist/runtime/shared/inline-text.d.ts +38 -0
- package/dist/runtime/shared/inline-text.js +85 -0
- package/dist/runtime/shared/layout.d.ts +2 -1
- package/dist/runtime/shared/layout.js +3 -0
- package/dist/runtime/shared/links.d.ts +48 -0
- package/dist/runtime/shared/links.js +155 -0
- package/dist/runtime/shared/list-filters.d.ts +2 -0
- package/dist/runtime/shared/list-filters.js +2 -0
- package/dist/runtime/shared/media-focal.d.ts +30 -0
- package/dist/runtime/shared/media-focal.js +52 -0
- package/dist/runtime/shared/migrations-dir.d.ts +14 -0
- package/dist/runtime/shared/migrations-dir.js +48 -2
- package/dist/runtime/shared/presets.d.ts +67 -0
- package/dist/runtime/shared/presets.js +127 -0
- package/dist/runtime/shared/preview-links.d.ts +64 -0
- package/dist/runtime/shared/preview-links.js +54 -0
- package/dist/runtime/shared/preview.d.ts +124 -21
- package/dist/runtime/shared/preview.js +419 -81
- package/dist/runtime/shared/redirects.d.ts +75 -0
- package/dist/runtime/shared/redirects.js +230 -0
- package/dist/runtime/shared/roles.d.ts +57 -0
- package/dist/runtime/shared/roles.js +293 -0
- package/dist/runtime/shared/save-state.d.ts +8 -0
- package/dist/runtime/shared/save-state.js +15 -0
- package/dist/runtime/shared/search.d.ts +16 -1
- package/dist/runtime/shared/search.js +41 -7
- package/dist/runtime/shared/seo.d.ts +167 -0
- package/dist/runtime/shared/seo.js +335 -0
- package/dist/runtime/shared/transfer.js +2 -1
- package/dist/runtime/shared/translations.d.ts +29 -0
- package/dist/runtime/shared/translations.js +123 -0
- package/dist/runtime/shared/users.d.ts +1 -1
- package/dist/runtime/shared/validation.d.ts +23 -1
- package/dist/runtime/shared/validation.js +214 -64
- package/dist/runtime/webhooks.d.ts +33 -0
- package/dist/runtime/webhooks.js +57 -0
- package/dist/types.d.mts +5 -1
- package/package.json +6 -2
- package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
- package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
- package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
- package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
- package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
- package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
- package/dist/runtime/app/composables/cms-field-focus.js +0 -20
|
@@ -1,123 +1,142 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<KStack gap="sm" class="cms-blocks-field">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
<KTileGrid
|
|
4
|
+
:items="items"
|
|
5
|
+
:item-key="(item2) => uidFor(item2)"
|
|
6
|
+
:item-label="barTitle"
|
|
7
|
+
:label="field.label"
|
|
8
|
+
:columns="4"
|
|
9
|
+
min-item-width="9rem"
|
|
10
|
+
sortable
|
|
11
|
+
:handle="mediaOnly ? void 0 : '[data-cms-grip]'"
|
|
12
|
+
:disabled="disabled"
|
|
13
|
+
@update:items="onReorder"
|
|
14
|
+
@keydown="onTileKey"
|
|
15
|
+
@copy="onClipboard"
|
|
16
|
+
@cut="onClipboard"
|
|
17
|
+
@paste="onClipboard"
|
|
18
|
+
>
|
|
19
|
+
<template #tile="{ item, index }">
|
|
20
|
+
<KCard
|
|
21
|
+
variant="outline"
|
|
22
|
+
padding="none"
|
|
23
|
+
:animated="false"
|
|
24
|
+
class="cms-block-tile"
|
|
25
|
+
:class="{
|
|
9
26
|
'is-muted': isHidden(item),
|
|
10
|
-
'is-dragging': dragIndex === index,
|
|
11
|
-
'is-drop-target': dropIndex === index && dragIndex !== index,
|
|
12
27
|
'is-missing': hasMissingMedia(item),
|
|
13
|
-
'is-
|
|
28
|
+
'is-invalid': tileInvalid(index)
|
|
14
29
|
}"
|
|
15
|
-
|
|
16
|
-
:tabindex="mediaOnly ? 0 : void 0"
|
|
17
|
-
:aria-label="mediaOnly ? `${barTitle(item)}, use the arrow keys to reorder` : void 0"
|
|
18
|
-
@dragstart="onDragStart(index, $event)"
|
|
19
|
-
@dragover.prevent="dropIndex = index"
|
|
20
|
-
@dragleave="onDragLeave(index)"
|
|
21
|
-
@drop.prevent="onDrop(index)"
|
|
22
|
-
@dragend="onDragEnd"
|
|
23
|
-
@keydown.self="onTileKey($event, index)"
|
|
24
|
-
>
|
|
25
|
-
<component
|
|
26
|
-
:is="mediaOnly ? 'div' : 'button'"
|
|
27
|
-
:type="mediaOnly ? void 0 : 'button'"
|
|
28
|
-
class="cms-block-preview"
|
|
29
|
-
:aria-label="mediaOnly ? void 0 : `Edit ${labelOf(item)}`"
|
|
30
|
-
@click="onPreviewClick(index)"
|
|
30
|
+
:data-cms-tile="index"
|
|
31
31
|
>
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<KIcon v-else :name="cmsIcon(iconOf(item))" class="cms-block-icon" />
|
|
49
|
-
|
|
50
|
-
<KIcon v-if="isHidden(item)" name="eye-slash" class="cms-block-hidden" />
|
|
51
|
-
|
|
52
|
-
<template v-if="mediaOnly">
|
|
53
|
-
<span
|
|
54
|
-
v-if="hasMissingMedia(item)"
|
|
55
|
-
class="cms-block-missing"
|
|
56
|
-
title="Not found in the media library"
|
|
57
|
-
>
|
|
58
|
-
<KIcon name="exclamation-triangle" />
|
|
59
|
-
</span>
|
|
60
|
-
<button
|
|
61
|
-
type="button"
|
|
62
|
-
class="cms-block-remove"
|
|
63
|
-
:aria-label="`Remove ${barTitle(item)}`"
|
|
64
|
-
title="Remove"
|
|
65
|
-
@click.stop="remove(index)"
|
|
32
|
+
<component
|
|
33
|
+
:is="mediaOnly ? 'div' : 'button'"
|
|
34
|
+
:type="mediaOnly ? void 0 : 'button'"
|
|
35
|
+
class="cms-tile-preview"
|
|
36
|
+
:aria-label="mediaOnly ? void 0 : `Edit ${labelOf(item)}`"
|
|
37
|
+
@click="onPreviewClick(index)"
|
|
38
|
+
>
|
|
39
|
+
<KThumbnail
|
|
40
|
+
:src="previewOf(item)?.url"
|
|
41
|
+
:kind="previewOf(item)?.kind ?? 'icon'"
|
|
42
|
+
:icon="previewOf(item) ? void 0 : cmsIcon(iconOf(item))"
|
|
43
|
+
:aspect="mediaOnly ? 1 : '4/3'"
|
|
44
|
+
rounded="none"
|
|
45
|
+
:missing="mediaKeys.isMissing(mediaKeyOf(item))"
|
|
46
|
+
missing-text="Not found in the media library"
|
|
47
|
+
:reveal="['top-end']"
|
|
66
48
|
>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
49
|
+
<template v-if="!previewOf(item) && summaryOf(item)" #default>
|
|
50
|
+
<span class="cms-block-summary">{{ summaryOf(item) }}</span>
|
|
51
|
+
</template>
|
|
52
|
+
<template v-if="isHidden(item)" #top-start>
|
|
53
|
+
<KIcon name="eye-slash" label="Hidden" />
|
|
54
|
+
</template>
|
|
55
|
+
<template v-if="mediaOnly && !disabled" #top-end>
|
|
56
|
+
<KButton
|
|
57
|
+
icon="x-mark"
|
|
58
|
+
size="xs"
|
|
59
|
+
variant="solid"
|
|
60
|
+
tone="neutral"
|
|
61
|
+
square
|
|
62
|
+
tooltip="Remove"
|
|
63
|
+
:aria-label="`Remove ${barTitle(item)}`"
|
|
64
|
+
@click.stop="remove(index)"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
</KThumbnail>
|
|
68
|
+
</component>
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
70
|
+
<div v-if="!mediaOnly" class="cms-tile-bar">
|
|
71
|
+
<KButton
|
|
72
|
+
v-if="!disabled"
|
|
73
|
+
data-cms-grip
|
|
74
|
+
:icon="cmsIcon('bars-2')"
|
|
75
|
+
size="xs"
|
|
76
|
+
variant="ghost"
|
|
77
|
+
tone="neutral"
|
|
78
|
+
:aria-label="`Reorder ${labelOf(item)}, Alt with the arrow keys moves it`"
|
|
79
|
+
tooltip="Drag to reorder"
|
|
80
|
+
/>
|
|
81
|
+
<KTooltip :text="barTitle(item)">
|
|
82
|
+
<span class="cms-tile-name">{{ barLabel(item) }}</span>
|
|
83
|
+
</KTooltip>
|
|
84
|
+
<KTooltip v-if="hasMissingMedia(item)" text="Not found in the media library">
|
|
85
|
+
<span class="cms-block-missing-icon">
|
|
86
|
+
<KIcon name="exclamation-triangle" label="Not found in the media library" />
|
|
87
|
+
</span>
|
|
88
|
+
</KTooltip>
|
|
89
|
+
<KDropdownMenu :items="tileMenu(index)" placement="bottom-end">
|
|
90
|
+
<KButton
|
|
91
|
+
:icon="cmsIcon('ellipsis-horizontal')"
|
|
92
|
+
size="xs"
|
|
93
|
+
variant="ghost"
|
|
94
|
+
tone="neutral"
|
|
95
|
+
:aria-label="`${labelOf(item)} actions`"
|
|
96
|
+
/>
|
|
97
|
+
</KDropdownMenu>
|
|
98
|
+
<KButton
|
|
99
|
+
v-if="!disabled"
|
|
100
|
+
icon="trash"
|
|
101
|
+
size="xs"
|
|
102
|
+
variant="ghost"
|
|
103
|
+
tone="danger"
|
|
104
|
+
aria-label="Remove block"
|
|
105
|
+
@click="remove(index)"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
</KCard>
|
|
109
|
+
</template>
|
|
100
110
|
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
<template #add>
|
|
112
|
+
<CmsBlockPickerPopover
|
|
113
|
+
v-if="
|
|
114
|
+
canAdd && !disabled && !mediaOnly && (Object.keys(blocks).length > 1 || presets.length)
|
|
115
|
+
"
|
|
116
|
+
v-model:open="pickerMenuOpen"
|
|
117
|
+
:blocks="blocks"
|
|
118
|
+
:paste-count="pasteCount"
|
|
119
|
+
:presets="presets"
|
|
120
|
+
@pick="add"
|
|
121
|
+
@paste="paste(items.length, null)"
|
|
122
|
+
@preset="insertPreset"
|
|
123
|
+
>
|
|
124
|
+
<KCard variant="dashed" as="button" icon="plus" title="Add" />
|
|
125
|
+
</CmsBlockPickerPopover>
|
|
126
|
+
<KCard
|
|
127
|
+
v-else
|
|
128
|
+
variant="dashed"
|
|
129
|
+
as="button"
|
|
130
|
+
icon="plus"
|
|
131
|
+
:title="mediaOnly ? addMediaLabel : 'Add'"
|
|
132
|
+
:disabled="!canAdd || disabled"
|
|
133
|
+
:disabled-reason="disabled ? void 0 : fullReason"
|
|
134
|
+
@click="addFirst"
|
|
135
|
+
/>
|
|
136
|
+
</template>
|
|
137
|
+
</KTileGrid>
|
|
119
138
|
|
|
120
|
-
<
|
|
139
|
+
<KFieldMessage v-if="countHint" tone="neutral" :icon="false" :message="countHint" />
|
|
121
140
|
|
|
122
141
|
<div v-if="visual">
|
|
123
142
|
<KButton
|
|
@@ -145,6 +164,7 @@
|
|
|
145
164
|
:field-key="fieldKey"
|
|
146
165
|
:fields="{ [fieldKey]: field }"
|
|
147
166
|
:title="field.label"
|
|
167
|
+
:readonly="disabled"
|
|
148
168
|
@update:state="onVisualUpdate"
|
|
149
169
|
>
|
|
150
170
|
<template #start>
|
|
@@ -154,7 +174,6 @@
|
|
|
154
174
|
variant="ghost"
|
|
155
175
|
tone="neutral"
|
|
156
176
|
aria-label="Back to the form"
|
|
157
|
-
title="Back to the form"
|
|
158
177
|
@click="visualOpen = false"
|
|
159
178
|
/>
|
|
160
179
|
</template>
|
|
@@ -164,6 +183,15 @@
|
|
|
164
183
|
</CmsVisualEditor>
|
|
165
184
|
</KModal>
|
|
166
185
|
|
|
186
|
+
<CmsPresetModal
|
|
187
|
+
v-if="presetsEnabled"
|
|
188
|
+
v-model:open="presetOpen"
|
|
189
|
+
title="Save as preset"
|
|
190
|
+
submit-label="Save preset"
|
|
191
|
+
:hint="presetHint"
|
|
192
|
+
:save="savePreset"
|
|
193
|
+
/>
|
|
194
|
+
|
|
167
195
|
<KModal v-if="mediaOnly" v-model:open="pickerOpen" title="Media" size="lg">
|
|
168
196
|
<CmsMediaGallery
|
|
169
197
|
v-model:folder="pickerFolder"
|
|
@@ -174,26 +202,49 @@
|
|
|
174
202
|
/>
|
|
175
203
|
</KModal>
|
|
176
204
|
|
|
177
|
-
<KModal
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
205
|
+
<KModal
|
|
206
|
+
v-if="!nested"
|
|
207
|
+
v-model:open="editorOpen"
|
|
208
|
+
:title="editorTitle"
|
|
209
|
+
:trail="editorTrail"
|
|
210
|
+
:size="editorWide ? 'lg' : 'md'"
|
|
211
|
+
@navigate="navigateEditor"
|
|
212
|
+
>
|
|
213
|
+
<CmsBlockEditorProvider :scope="editorScope">
|
|
214
|
+
<CmsFormSections
|
|
215
|
+
v-if="editorBlock"
|
|
216
|
+
:key="editorKey"
|
|
217
|
+
:layout="editorLayout"
|
|
218
|
+
:shown="isEditorFieldShown"
|
|
185
219
|
>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
220
|
+
<template #field="{ fieldKey: blockKey }">
|
|
221
|
+
<KFormField
|
|
222
|
+
:name="editorFieldName(blockKey)"
|
|
223
|
+
:label="editorBlock.fields[blockKey].label"
|
|
224
|
+
:help="editorBlock.fields[blockKey].description"
|
|
225
|
+
:required="editorBlock.fields[blockKey].required"
|
|
226
|
+
>
|
|
227
|
+
<CmsFieldInput
|
|
228
|
+
:model-value="editorItem?.[blockKey]"
|
|
229
|
+
:field="editorBlock.fields[blockKey]"
|
|
230
|
+
:locale="locale"
|
|
231
|
+
:entry-name="entryName"
|
|
232
|
+
:field-key="
|
|
233
|
+
editorBlock.fields[blockKey].type === 'blocks' ? blockKey : void 0
|
|
234
|
+
"
|
|
235
|
+
@update:model-value="(value) => updateEditorField(blockKey, value)"
|
|
236
|
+
/>
|
|
237
|
+
</KFormField>
|
|
238
|
+
</template>
|
|
239
|
+
</CmsFormSections>
|
|
240
|
+
</CmsBlockEditorProvider>
|
|
194
241
|
|
|
195
242
|
<template #footer>
|
|
243
|
+
<KStack v-if="disabled" direction="row" justify="end">
|
|
244
|
+
<KButton label="Done" @click="editorOpen = false" />
|
|
245
|
+
</KStack>
|
|
196
246
|
<KStack
|
|
247
|
+
v-else
|
|
197
248
|
direction="row"
|
|
198
249
|
justify="between"
|
|
199
250
|
gap="sm"
|
|
@@ -206,7 +257,7 @@
|
|
|
206
257
|
variant="soft"
|
|
207
258
|
tone="neutral"
|
|
208
259
|
aria-label="Move earlier"
|
|
209
|
-
:disabled="
|
|
260
|
+
:disabled="editorPosition === 0"
|
|
210
261
|
@click="moveCurrent(-1)"
|
|
211
262
|
/>
|
|
212
263
|
<KButton
|
|
@@ -214,7 +265,7 @@
|
|
|
214
265
|
variant="soft"
|
|
215
266
|
tone="neutral"
|
|
216
267
|
aria-label="Move later"
|
|
217
|
-
:disabled="
|
|
268
|
+
:disabled="editorPosition === editorList.length - 1"
|
|
218
269
|
@click="moveCurrent(1)"
|
|
219
270
|
/>
|
|
220
271
|
<KButton
|
|
@@ -222,11 +273,11 @@
|
|
|
222
273
|
:icon="cmsIcon('document-duplicate')"
|
|
223
274
|
variant="soft"
|
|
224
275
|
tone="neutral"
|
|
225
|
-
:disabled="!
|
|
276
|
+
:disabled="!editorCanAdd"
|
|
226
277
|
@click="duplicateCurrent"
|
|
227
278
|
/>
|
|
228
279
|
<KButton
|
|
229
|
-
v-if="editorItem && !declaresHiddenField(
|
|
280
|
+
v-if="editorItem && !declaresHiddenField(editorBlock)"
|
|
230
281
|
:label="isHidden(editorItem) ? 'Show' : 'Hide'"
|
|
231
282
|
:icon="cmsIcon(isHidden(editorItem) ? 'eye' : 'eye-slash')"
|
|
232
283
|
variant="soft"
|
|
@@ -252,10 +303,25 @@
|
|
|
252
303
|
|
|
253
304
|
<script setup>
|
|
254
305
|
import {
|
|
306
|
+
blockAt,
|
|
307
|
+
blockFormLayout,
|
|
308
|
+
blockListAt,
|
|
309
|
+
blockListPath,
|
|
310
|
+
blockPathIndex,
|
|
311
|
+
blockSchemaPath,
|
|
255
312
|
canAddBlock,
|
|
313
|
+
formatBlockPath,
|
|
314
|
+
parseBlockPath,
|
|
315
|
+
insertBlocks,
|
|
316
|
+
moveBlock,
|
|
317
|
+
removeBlock,
|
|
318
|
+
resolveBlockPath,
|
|
319
|
+
updateBlockAt,
|
|
256
320
|
declaresHiddenField,
|
|
321
|
+
isFieldVisible,
|
|
257
322
|
isHiddenBlock,
|
|
258
323
|
isTranslatableMediaField,
|
|
324
|
+
linkDisplayText,
|
|
259
325
|
mediaFilename,
|
|
260
326
|
mediaIconFor,
|
|
261
327
|
mediaPublicUrl,
|
|
@@ -264,16 +330,33 @@ import {
|
|
|
264
330
|
} from "#nuxt-cms";
|
|
265
331
|
import {
|
|
266
332
|
Button as KButton,
|
|
333
|
+
Card as KCard,
|
|
267
334
|
DropdownMenu as KDropdownMenu,
|
|
335
|
+
FieldMessage as KFieldMessage,
|
|
268
336
|
FormField as KFormField,
|
|
269
337
|
Icon as KIcon,
|
|
270
338
|
Modal as KModal,
|
|
271
|
-
Stack as KStack
|
|
339
|
+
Stack as KStack,
|
|
340
|
+
Thumbnail as KThumbnail,
|
|
341
|
+
TileGrid as KTileGrid,
|
|
342
|
+
Tooltip as KTooltip,
|
|
343
|
+
useFieldControl,
|
|
344
|
+
useFormContext,
|
|
345
|
+
useRouteState
|
|
272
346
|
} from "@xleddyl/443-components-vue";
|
|
273
|
-
import { computed, ref, useState } from "#imports";
|
|
347
|
+
import { computed, inject, nextTick, onMounted, ref, useState, watch } from "#imports";
|
|
348
|
+
import { copyNotice, pasteBlocks, pasteNotice, pasteableCount } from "../../../shared/clipboard";
|
|
349
|
+
import { CMS_PRESET_ICON, presetInsertNotice } from "../../../shared/presets";
|
|
350
|
+
import { blockSummary, clipboardActionApplies, emptyBlock } from "../../../shared/preview";
|
|
351
|
+
import { useCmsClipboard } from "../../composables/cms-clipboard";
|
|
274
352
|
import { useCmsMediaKeys } from "../../composables/cms-media-keys";
|
|
353
|
+
import { useCmsPermissions } from "../../composables/cms-permissions";
|
|
354
|
+
import { useCmsPresets } from "../../composables/cms-presets";
|
|
275
355
|
import { useCmsRuntime } from "../../composables/cms-runtime";
|
|
276
|
-
import {
|
|
356
|
+
import { useCmsOverlayFlag } from "../../composables/cms-route";
|
|
357
|
+
import { CMS_BLOCK_EDITOR, CmsBlockEditorProvider } from "../../utils/block-editor";
|
|
358
|
+
import { clipboardTarget } from "../../utils/keyboard";
|
|
359
|
+
import { cmsIcon } from "../../utils/ui";
|
|
277
360
|
const props = defineProps({
|
|
278
361
|
field: { type: Object, required: true },
|
|
279
362
|
locale: { type: String, required: false },
|
|
@@ -283,9 +366,38 @@ const props = defineProps({
|
|
|
283
366
|
const model = defineModel({ type: [Array, null], ...{ required: true } });
|
|
284
367
|
const { mediaBaseUrl, i18n } = useCmsRuntime();
|
|
285
368
|
const mediaKeys = useCmsMediaKeys();
|
|
369
|
+
const { disabled } = useFieldControl();
|
|
370
|
+
const parentEditor = inject(CMS_BLOCK_EDITOR, null);
|
|
371
|
+
const nested = computed(() => !!parentEditor && !!props.fieldKey);
|
|
372
|
+
const nestedListPath = computed(
|
|
373
|
+
() => parentEditor && props.fieldKey ? [...parentEditor.path.value, props.fieldKey] : null
|
|
374
|
+
);
|
|
375
|
+
const presetFieldKey = computed(() => {
|
|
376
|
+
if (!nested.value) return props.fieldKey;
|
|
377
|
+
const schema = parentEditor?.schemaPath.value;
|
|
378
|
+
return schema && props.fieldKey ? [...schema, props.fieldKey].join(".") : void 0;
|
|
379
|
+
});
|
|
380
|
+
const form = useFormContext();
|
|
381
|
+
const formPath = computed(() => {
|
|
382
|
+
if (!nested.value) return props.fieldKey ?? null;
|
|
383
|
+
const base = parentEditor?.formPath.value;
|
|
384
|
+
return base && nestedListPath.value ? `${base}.${formatBlockPath(nestedListPath.value)}` : null;
|
|
385
|
+
});
|
|
386
|
+
function tileInvalid(index) {
|
|
387
|
+
const base = formPath.value;
|
|
388
|
+
if (!base || !form) return false;
|
|
389
|
+
const prefix = `${base}.${index}`;
|
|
390
|
+
return Object.entries(form.errors).some(
|
|
391
|
+
([key, message]) => !!message && (key === prefix || key.startsWith(`${prefix}.`))
|
|
392
|
+
);
|
|
393
|
+
}
|
|
286
394
|
const blocks = computed(() => props.field.blocks ?? {});
|
|
287
395
|
const items = computed(() => Array.isArray(model.value) ? model.value : []);
|
|
288
396
|
const canAdd = computed(() => canAddBlock(props.field, items.value.length));
|
|
397
|
+
const fullReason = computed(() => {
|
|
398
|
+
const max = props.field.max ?? 0;
|
|
399
|
+
return canAdd.value ? void 0 : `This field holds at most ${blockCount(max)}.`;
|
|
400
|
+
});
|
|
289
401
|
function blockCount(count) {
|
|
290
402
|
return `${count} ${count === 1 ? "block" : "blocks"}`;
|
|
291
403
|
}
|
|
@@ -310,10 +422,11 @@ const mediaOnly = computed(() => {
|
|
|
310
422
|
});
|
|
311
423
|
const visual = computed(() => {
|
|
312
424
|
const list = Object.values(blocks.value);
|
|
313
|
-
return !mediaOnly.value && !!props.entryName && !!props.fieldKey && !!list.length && list.every((block) => typeof block.component === "string" && !!block.component.trim());
|
|
425
|
+
return !mediaOnly.value && !nested.value && !!props.entryName && !!props.fieldKey && !!list.length && list.every((block) => typeof block.component === "string" && !!block.component.trim());
|
|
314
426
|
});
|
|
315
|
-
const visualOpen =
|
|
427
|
+
const visualOpen = useCmsOverlayFlag("visual", !!props.fieldKey, props.fieldKey);
|
|
316
428
|
function onVisualUpdate(state) {
|
|
429
|
+
if (disabled.value) return;
|
|
317
430
|
const value = props.fieldKey ? state[props.fieldKey] : null;
|
|
318
431
|
model.value = Array.isArray(value) && value.length ? value : null;
|
|
319
432
|
}
|
|
@@ -325,7 +438,7 @@ const pickerOpen = ref(false);
|
|
|
325
438
|
const pickerFolder = useState("cms-media-picker-folder", () => null);
|
|
326
439
|
function addMedia(media) {
|
|
327
440
|
const target = mediaOnly.value;
|
|
328
|
-
if (!target || !canAdd.value) return;
|
|
441
|
+
if (!target || !canAdd.value || disabled.value) return;
|
|
329
442
|
mediaKeys.remember(media.key);
|
|
330
443
|
model.value = [...items.value, { type: target.type, [target.key]: media.key }];
|
|
331
444
|
pickerOpen.value = false;
|
|
@@ -383,45 +496,49 @@ function iconOf(item) {
|
|
|
383
496
|
return key ? mediaIconFor(mediaTypeForKey(key)) : "rectangle-stack";
|
|
384
497
|
}
|
|
385
498
|
function summaryOf(item) {
|
|
499
|
+
const locale = props.locale ?? i18n.defaultLocale;
|
|
386
500
|
for (const [key, field] of Object.entries(blockOf(item)?.fields ?? {})) {
|
|
501
|
+
if (field.type === "link") {
|
|
502
|
+
const text2 = linkDisplayText(item[key], locale, i18n.defaultLocale);
|
|
503
|
+
if (text2) return text2.length > 80 ? `${text2.slice(0, 77)}\u2026` : text2;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
387
506
|
if (field.type !== "text" && field.type !== "richtext") continue;
|
|
388
507
|
const raw = item[key];
|
|
389
|
-
const value = typeof raw === "string" ? raw : raw?.[
|
|
508
|
+
const value = typeof raw === "string" ? raw : raw?.[locale] ?? "";
|
|
390
509
|
const text = value.replace(/<[^>]*>/g, " ").trim();
|
|
391
510
|
if (text) return text.length > 80 ? `${text.slice(0, 77)}\u2026` : text;
|
|
392
511
|
}
|
|
393
|
-
|
|
512
|
+
const block = blockOf(item);
|
|
513
|
+
const hasNested = Object.values(block?.fields ?? {}).some((field) => field.type === "blocks");
|
|
514
|
+
return hasNested ? blockSummary(block, item, locale, i18n.defaultLocale) : "";
|
|
394
515
|
}
|
|
395
516
|
function isHidden(item) {
|
|
396
517
|
return isHiddenBlock(item);
|
|
397
518
|
}
|
|
398
|
-
function updateField(index, key, value) {
|
|
399
|
-
const current = items.value[index];
|
|
400
|
-
if (!current) return;
|
|
401
|
-
const updated = { ...current, [key]: value };
|
|
402
|
-
uids.set(updated, uidFor(current));
|
|
403
|
-
model.value = items.value.map((item, i) => i === index ? updated : item);
|
|
404
|
-
}
|
|
405
519
|
function add(type) {
|
|
406
520
|
const block = blocks.value[type];
|
|
407
|
-
if (!block || !canAdd.value) return;
|
|
408
|
-
const empty = Object.fromEntries(Object.keys(block.fields).map((k) => [k, null]));
|
|
521
|
+
if (!block || !canAdd.value || disabled.value) return;
|
|
409
522
|
const index = items.value.length;
|
|
410
|
-
model.value = [...items.value,
|
|
523
|
+
model.value = [...items.value, emptyBlock(type, block)];
|
|
411
524
|
openEditor(index);
|
|
412
525
|
}
|
|
413
|
-
function
|
|
414
|
-
if (!
|
|
415
|
-
|
|
416
|
-
|
|
526
|
+
function onReorder(next) {
|
|
527
|
+
if (!disabled.value) model.value = next;
|
|
528
|
+
}
|
|
529
|
+
function onTileKey(event) {
|
|
530
|
+
if (!mediaOnly.value || disabled.value || event.altKey) return;
|
|
531
|
+
if (event.key !== "Delete" && event.key !== "Backspace") return;
|
|
532
|
+
const index = focusedTileIndex(event.target);
|
|
533
|
+
if (index === null) return;
|
|
417
534
|
event.preventDefault();
|
|
418
|
-
|
|
535
|
+
remove(index);
|
|
419
536
|
}
|
|
420
537
|
function onPreviewClick(index) {
|
|
421
538
|
if (!mediaOnly.value) openEditor(index);
|
|
422
539
|
}
|
|
423
540
|
function addFirst() {
|
|
424
|
-
if (!canAdd.value) return;
|
|
541
|
+
if (!canAdd.value || disabled.value) return;
|
|
425
542
|
if (mediaOnly.value) {
|
|
426
543
|
pickerOpen.value = true;
|
|
427
544
|
return;
|
|
@@ -430,96 +547,329 @@ function addFirst() {
|
|
|
430
547
|
if (first) add(first);
|
|
431
548
|
}
|
|
432
549
|
function remove(index) {
|
|
550
|
+
if (disabled.value) return;
|
|
433
551
|
const next = items.value.filter((_, i) => i !== index);
|
|
434
552
|
model.value = next.length ? next : null;
|
|
435
553
|
}
|
|
436
554
|
function duplicate(index) {
|
|
437
555
|
const current = items.value[index];
|
|
438
|
-
if (!current || !canAdd.value) return;
|
|
439
|
-
const
|
|
440
|
-
const next = [...items.value];
|
|
441
|
-
next.splice(index + 1, 0, copy);
|
|
442
|
-
model.value = next;
|
|
443
|
-
}
|
|
444
|
-
function move(index, delta) {
|
|
445
|
-
moveTo(index, index + delta);
|
|
446
|
-
}
|
|
447
|
-
function moveTo(from, to) {
|
|
448
|
-
if (from === to || to < 0 || to >= items.value.length) return;
|
|
556
|
+
if (!current || !canAdd.value || disabled.value) return;
|
|
557
|
+
const copy2 = JSON.parse(JSON.stringify(current));
|
|
449
558
|
const next = [...items.value];
|
|
450
|
-
|
|
451
|
-
next.splice(to, 0, item);
|
|
559
|
+
next.splice(index + 1, 0, copy2);
|
|
452
560
|
model.value = next;
|
|
453
561
|
}
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
562
|
+
const ROOT = "_";
|
|
563
|
+
const localEditorPath = ref(null);
|
|
564
|
+
const editorRoute = useRouteState("editblock", { location: "hash" });
|
|
565
|
+
const editorRouted = computed(() => !nested.value && !!props.fieldKey);
|
|
566
|
+
const editorPath = computed({
|
|
567
|
+
get: () => {
|
|
568
|
+
if (!editorRouted.value) return localEditorPath.value;
|
|
569
|
+
const path = parseBlockPath(editorRoute.value);
|
|
570
|
+
return path && path[0] === props.fieldKey && path.length > 1 ? path.slice(1) : null;
|
|
571
|
+
},
|
|
572
|
+
set: (path) => {
|
|
573
|
+
if (!editorRouted.value) localEditorPath.value = path;
|
|
574
|
+
else editorRoute.value = path ? formatBlockPath([props.fieldKey, ...path]) : void 0;
|
|
461
575
|
}
|
|
576
|
+
});
|
|
577
|
+
const rootFields = computed(() => ({ [ROOT]: props.field }));
|
|
578
|
+
const rootValues = computed(() => ({ [ROOT]: items.value }));
|
|
579
|
+
function fullPath(path) {
|
|
580
|
+
return [ROOT, ...path];
|
|
462
581
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
function onDrop(index) {
|
|
467
|
-
if (dragIndex.value !== null) moveTo(dragIndex.value, index);
|
|
468
|
-
onDragEnd();
|
|
469
|
-
}
|
|
470
|
-
function onDragEnd() {
|
|
471
|
-
dragIndex.value = null;
|
|
472
|
-
dropIndex.value = null;
|
|
473
|
-
}
|
|
474
|
-
const addItems = computed(
|
|
475
|
-
() => Object.entries(blocks.value).map(([type, block]) => ({
|
|
476
|
-
label: block.label,
|
|
477
|
-
onSelect: () => add(type)
|
|
478
|
-
}))
|
|
582
|
+
const editorLocation = computed(
|
|
583
|
+
() => editorPath.value ? resolveBlockPath(rootFields.value, rootValues.value, fullPath(editorPath.value)) : null
|
|
479
584
|
);
|
|
480
|
-
const editorIndex = ref(null);
|
|
481
585
|
const editorOpen = computed({
|
|
482
|
-
get: () =>
|
|
586
|
+
get: () => editorPath.value !== null,
|
|
483
587
|
set: (value) => {
|
|
484
|
-
if (!value)
|
|
588
|
+
if (!value) editorPath.value = null;
|
|
485
589
|
}
|
|
486
590
|
});
|
|
487
|
-
const editorItem = computed(
|
|
488
|
-
|
|
591
|
+
const editorItem = computed(() => editorLocation.value?.item);
|
|
592
|
+
const editorBlock = computed(() => editorLocation.value?.block);
|
|
593
|
+
const editorPosition = computed(() => editorLocation.value?.index ?? -1);
|
|
594
|
+
const editorList = computed(
|
|
595
|
+
() => editorPath.value ? blockListAt(rootValues.value, blockListPath(fullPath(editorPath.value))) ?? [] : []
|
|
596
|
+
);
|
|
597
|
+
const editorCanAdd = computed(
|
|
598
|
+
() => editorLocation.value ? canAddBlock(editorLocation.value.field, editorList.value.length) : canAdd.value
|
|
489
599
|
);
|
|
490
|
-
const
|
|
491
|
-
const
|
|
600
|
+
const editorKey = computed(() => editorPath.value ? formatBlockPath(editorPath.value) : "");
|
|
601
|
+
const editorLayout = computed(
|
|
602
|
+
() => editorBlock.value ? blockFormLayout(editorBlock.value) : { main: [] }
|
|
603
|
+
);
|
|
604
|
+
const editorWide = computed(
|
|
605
|
+
() => editorLayout.value.main.some((section) => section.rows.some((row) => row.length > 1)) || Object.values(editorBlock.value?.fields ?? {}).some((field) => field.type === "blocks")
|
|
606
|
+
);
|
|
607
|
+
function isEditorFieldShown(key) {
|
|
608
|
+
const fields = editorBlock.value?.fields ?? {};
|
|
609
|
+
const field = fields[key];
|
|
610
|
+
return !!field && isFieldVisible(field, editorItem.value, fields);
|
|
611
|
+
}
|
|
612
|
+
function itemLabel(item, block) {
|
|
613
|
+
return block?.label ?? String(item.type);
|
|
614
|
+
}
|
|
615
|
+
const editorTitle = computed(
|
|
616
|
+
() => editorItem.value ? itemLabel(editorItem.value, editorBlock.value) : ""
|
|
617
|
+
);
|
|
618
|
+
const editorAncestors = computed(() => {
|
|
619
|
+
const path = editorPath.value;
|
|
620
|
+
if (!path) return [];
|
|
621
|
+
const ancestors = [];
|
|
622
|
+
for (let end = 1; end < path.length - 1; end += 2) ancestors.push(path.slice(0, end));
|
|
623
|
+
return ancestors;
|
|
624
|
+
});
|
|
625
|
+
const editorTrail = computed(
|
|
626
|
+
() => editorAncestors.value.map((path) => {
|
|
627
|
+
const location = resolveBlockPath(rootFields.value, rootValues.value, fullPath(path));
|
|
628
|
+
return location ? itemLabel(location.item, location.block) : "";
|
|
629
|
+
})
|
|
630
|
+
);
|
|
631
|
+
function navigateEditor(index) {
|
|
632
|
+
const target = editorAncestors.value[index];
|
|
633
|
+
if (target) editorPath.value = target;
|
|
634
|
+
}
|
|
635
|
+
const editorScope = {
|
|
636
|
+
path: computed(() => editorPath.value ?? []),
|
|
637
|
+
schemaPath: computed(() => {
|
|
638
|
+
if (!editorPath.value || !props.fieldKey) return null;
|
|
639
|
+
const schema = blockSchemaPath(rootFields.value, rootValues.value, fullPath(editorPath.value));
|
|
640
|
+
const type = editorItem.value?.type;
|
|
641
|
+
return schema && type ? [props.fieldKey, ...schema.slice(1), String(type)] : null;
|
|
642
|
+
}),
|
|
643
|
+
formPath: computed(() => props.fieldKey && !nested.value ? props.fieldKey : null),
|
|
644
|
+
open: (path) => {
|
|
645
|
+
editorPath.value = path;
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
watch(editorLocation, (location) => {
|
|
649
|
+
if (location || !editorPath.value) return;
|
|
650
|
+
const path = editorPath.value;
|
|
651
|
+
void nextTick(() => {
|
|
652
|
+
if (editorPath.value === path && !blockAt(rootValues.value, fullPath(path)))
|
|
653
|
+
editorPath.value = null;
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
function editorFieldName(key) {
|
|
657
|
+
const base = editorScope.formPath.value;
|
|
658
|
+
return base && editorPath.value ? `${base}.${formatBlockPath(editorPath.value)}.${key}` : void 0;
|
|
659
|
+
}
|
|
660
|
+
function commitRoot(values) {
|
|
661
|
+
const next = values[ROOT];
|
|
662
|
+
const list = Array.isArray(next) ? next : [];
|
|
663
|
+
const previous = items.value;
|
|
664
|
+
if (list.length === previous.length) {
|
|
665
|
+
list.forEach((item, index) => {
|
|
666
|
+
const before = previous[index];
|
|
667
|
+
if (before && item !== before && !uids.has(item)) uids.set(item, uidFor(before));
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
model.value = list.length ? list : null;
|
|
671
|
+
}
|
|
492
672
|
function openEditor(index) {
|
|
493
|
-
|
|
673
|
+
if (nested.value && parentEditor && nestedListPath.value) {
|
|
674
|
+
parentEditor.open([...nestedListPath.value, index]);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
editorPath.value = [index];
|
|
494
678
|
}
|
|
495
679
|
function updateEditorField(key, value) {
|
|
496
|
-
if (
|
|
497
|
-
|
|
680
|
+
if (!editorPath.value || disabled.value) return;
|
|
681
|
+
commitRoot(
|
|
682
|
+
updateBlockAt(rootValues.value, fullPath(editorPath.value), (item) => ({
|
|
683
|
+
...item,
|
|
684
|
+
[key]: value
|
|
685
|
+
}))
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
function leaveEditorItem() {
|
|
689
|
+
const path = editorPath.value;
|
|
690
|
+
editorPath.value = path && path.length > 2 ? path.slice(0, -2) : null;
|
|
498
691
|
}
|
|
499
692
|
function removeCurrent() {
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
693
|
+
if (!editorPath.value || disabled.value) return;
|
|
694
|
+
const path = editorPath.value;
|
|
695
|
+
commitRoot(removeBlock(rootValues.value, fullPath(path)));
|
|
696
|
+
leaveEditorItem();
|
|
503
697
|
}
|
|
504
698
|
function moveCurrent(delta) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
699
|
+
const path = editorPath.value;
|
|
700
|
+
if (!path || disabled.value) return;
|
|
701
|
+
const index = blockPathIndex(path);
|
|
702
|
+
const target = index + delta;
|
|
703
|
+
if (target < 0 || target >= editorList.value.length) return;
|
|
704
|
+
const full = fullPath(path);
|
|
705
|
+
commitRoot(
|
|
706
|
+
moveBlock(rootValues.value, full, blockListPath(full), delta > 0 ? target + 1 : target)
|
|
707
|
+
);
|
|
708
|
+
editorPath.value = [...path.slice(0, -1), target];
|
|
509
709
|
}
|
|
510
710
|
function toggleHiddenCurrent() {
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
model.value = items.value.map((item, i) => i === index ? updated : item);
|
|
711
|
+
if (!editorPath.value || !editorItem.value || disabled.value) return;
|
|
712
|
+
commitRoot(
|
|
713
|
+
updateBlockAt(rootValues.value, fullPath(editorPath.value), (current) => {
|
|
714
|
+
const { hidden: _hidden, ...rest } = current;
|
|
715
|
+
return isHidden(current) ? rest : { ...rest, hidden: true };
|
|
716
|
+
})
|
|
717
|
+
);
|
|
519
718
|
}
|
|
520
719
|
function duplicateCurrent() {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
720
|
+
const path = editorPath.value;
|
|
721
|
+
const current = editorItem.value;
|
|
722
|
+
if (!path || !current || !editorCanAdd.value || disabled.value) return;
|
|
723
|
+
const copy2 = JSON.parse(JSON.stringify(current));
|
|
724
|
+
const full = fullPath(path);
|
|
725
|
+
commitRoot(insertBlocks(rootValues.value, blockListPath(full), blockPathIndex(path) + 1, [copy2]));
|
|
726
|
+
leaveEditorItem();
|
|
727
|
+
}
|
|
728
|
+
const clipboard = useCmsClipboard();
|
|
729
|
+
const pasteCount = computed(() => pasteableCount(clipboard.items.value, props.field));
|
|
730
|
+
function copy(index, event) {
|
|
731
|
+
const item = items.value[index];
|
|
732
|
+
if (!item) return;
|
|
733
|
+
clipboard.copy([item], event);
|
|
734
|
+
clipboard.notify(copyNotice(1));
|
|
735
|
+
}
|
|
736
|
+
function cut(index, event) {
|
|
737
|
+
const item = items.value[index];
|
|
738
|
+
if (!item || disabled.value) return;
|
|
739
|
+
clipboard.copy([item], event);
|
|
740
|
+
remove(index);
|
|
741
|
+
clipboard.notify(copyNotice(1, true));
|
|
742
|
+
}
|
|
743
|
+
async function paste(index, text) {
|
|
744
|
+
if (disabled.value) return;
|
|
745
|
+
const pasted = await clipboard.read(text);
|
|
746
|
+
const result = pasted ? pasteBlocks(items.value, index, pasted, props.field, i18n) : null;
|
|
747
|
+
if (result?.pasted) model.value = result.list;
|
|
748
|
+
clipboard.notify(pasteNotice(result, props.field));
|
|
749
|
+
}
|
|
750
|
+
function tileIndex(tile) {
|
|
751
|
+
const index = tile ? Number(tile.dataset.cmsTile) : NaN;
|
|
752
|
+
return Number.isInteger(index) ? index : null;
|
|
753
|
+
}
|
|
754
|
+
function focusedTileIndex(target) {
|
|
755
|
+
return target instanceof Element ? tileIndex(target.querySelector(":scope > * > [data-cms-tile]")) : null;
|
|
756
|
+
}
|
|
757
|
+
function tileIndexOf(target) {
|
|
758
|
+
if (!(target instanceof Element)) return null;
|
|
759
|
+
return tileIndex(target.closest("[data-cms-tile]")) ?? focusedTileIndex(target);
|
|
760
|
+
}
|
|
761
|
+
function onClipboard(event) {
|
|
762
|
+
if (event.defaultPrevented) return;
|
|
763
|
+
const action = event.type;
|
|
764
|
+
if (disabled.value && action !== "copy") return;
|
|
765
|
+
const index = tileIndexOf(event.target);
|
|
766
|
+
const target = { ...clipboardTarget(event, index !== null), interactive: false };
|
|
767
|
+
if (!clipboardActionApplies(action, target)) return;
|
|
768
|
+
if (action === "paste") {
|
|
769
|
+
event.preventDefault();
|
|
770
|
+
void paste(
|
|
771
|
+
index === null ? items.value.length : index + 1,
|
|
772
|
+
event.clipboardData?.getData("text/plain") ?? null
|
|
773
|
+
);
|
|
774
|
+
} else if (index !== null) {
|
|
775
|
+
if (action === "copy") copy(index, event);
|
|
776
|
+
else cut(index, event);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const { can } = useCmsPermissions();
|
|
780
|
+
const presetsEnabled = computed(
|
|
781
|
+
() => !mediaOnly.value && !disabled.value && !!props.entryName && !!presetFieldKey.value && can(`${props.entryName}.update`)
|
|
782
|
+
);
|
|
783
|
+
const {
|
|
784
|
+
presets: fieldPresets,
|
|
785
|
+
load: loadPresets,
|
|
786
|
+
create: createPreset
|
|
787
|
+
} = useCmsPresets(
|
|
788
|
+
() => presetsEnabled.value ? props.entryName : void 0,
|
|
789
|
+
() => presetFieldKey.value
|
|
790
|
+
);
|
|
791
|
+
const presets = computed(() => presetsEnabled.value ? fieldPresets.value : []);
|
|
792
|
+
const pickerMenuOpen = ref(false);
|
|
793
|
+
const presetOpen = ref(false);
|
|
794
|
+
const presetItems = ref([]);
|
|
795
|
+
const presetHint = computed(() => {
|
|
796
|
+
const types = presetItems.value.map((item) => labelOf(item));
|
|
797
|
+
return `A copy of ${types.join(
|
|
798
|
+
", "
|
|
799
|
+
)}. Insert it from the block picker of any field with the same blocks.`;
|
|
800
|
+
});
|
|
801
|
+
function openPresetSave(index) {
|
|
802
|
+
const item = items.value[index];
|
|
803
|
+
if (!item) return;
|
|
804
|
+
presetItems.value = [JSON.parse(JSON.stringify(item))];
|
|
805
|
+
presetOpen.value = true;
|
|
806
|
+
}
|
|
807
|
+
async function savePreset(details) {
|
|
808
|
+
const preset = await createPreset({ ...details, items: presetItems.value });
|
|
809
|
+
clipboard.notify({ title: `Preset "${preset.name}" saved`, color: "success" });
|
|
810
|
+
}
|
|
811
|
+
function insertPreset(id) {
|
|
812
|
+
const preset = presets.value.find((candidate) => candidate.id === id);
|
|
813
|
+
if (!preset || !canAdd.value || disabled.value) return;
|
|
814
|
+
const result = pasteBlocks(
|
|
815
|
+
items.value,
|
|
816
|
+
items.value.length,
|
|
817
|
+
preset.items,
|
|
818
|
+
props.field,
|
|
819
|
+
i18n
|
|
820
|
+
);
|
|
821
|
+
if (result.pasted) model.value = result.list;
|
|
822
|
+
clipboard.notify(presetInsertNotice(result, props.field, preset));
|
|
823
|
+
}
|
|
824
|
+
watch(pickerMenuOpen, (open) => {
|
|
825
|
+
if (open) void loadPresets();
|
|
826
|
+
});
|
|
827
|
+
onMounted(() => {
|
|
828
|
+
if (presetsEnabled.value) void loadPresets();
|
|
829
|
+
});
|
|
830
|
+
function tileMenu(index) {
|
|
831
|
+
const copyItem = {
|
|
832
|
+
label: "Copy",
|
|
833
|
+
icon: cmsIcon("clipboard-document"),
|
|
834
|
+
shortcut: "mod+c",
|
|
835
|
+
onSelect: () => copy(index)
|
|
836
|
+
};
|
|
837
|
+
if (disabled.value) return [[copyItem]];
|
|
838
|
+
const full = canAdd.value ? void 0 : countHint.value;
|
|
839
|
+
return [
|
|
840
|
+
[
|
|
841
|
+
copyItem,
|
|
842
|
+
{
|
|
843
|
+
label: "Cut",
|
|
844
|
+
icon: cmsIcon("scissors"),
|
|
845
|
+
shortcut: "mod+x",
|
|
846
|
+
onSelect: () => cut(index)
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
label: "Paste after",
|
|
850
|
+
icon: cmsIcon("clipboard"),
|
|
851
|
+
shortcut: "mod+v",
|
|
852
|
+
disabled: !!full,
|
|
853
|
+
disabledReason: full,
|
|
854
|
+
onSelect: () => void paste(index + 1)
|
|
855
|
+
}
|
|
856
|
+
],
|
|
857
|
+
[
|
|
858
|
+
{
|
|
859
|
+
label: "Duplicate",
|
|
860
|
+
icon: cmsIcon("document-duplicate"),
|
|
861
|
+
disabled: !!full,
|
|
862
|
+
disabledReason: full,
|
|
863
|
+
onSelect: () => duplicate(index)
|
|
864
|
+
},
|
|
865
|
+
...presetsEnabled.value ? [
|
|
866
|
+
{
|
|
867
|
+
label: "Save as preset",
|
|
868
|
+
icon: cmsIcon(CMS_PRESET_ICON),
|
|
869
|
+
onSelect: () => openPresetSave(index)
|
|
870
|
+
}
|
|
871
|
+
] : []
|
|
872
|
+
]
|
|
873
|
+
];
|
|
524
874
|
}
|
|
525
875
|
</script>
|