@alfakit/vue 0.1.1 → 0.1.6
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/CHANGELOG.md +16 -0
- package/dist/AlfakitRoot.js.map +1 -1
- package/dist/AlfakitRoot.vue.d.ts.map +1 -1
- package/dist/AlfakitRoot.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/AlfakitRoot.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/actions/ActionButton.js.map +1 -1
- package/dist/components/actions/ActionButton.vue.d.ts.map +1 -1
- package/dist/components/actions/ActionButton.vue_vue_type_script_setup_true_lang.js +160 -159
- package/dist/components/actions/ActionButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/actions/ActionGroupMenu.js.map +1 -1
- package/dist/components/actions/ActionGroupMenu.vue.d.ts.map +1 -1
- package/dist/components/actions/ActionGroupMenu.vue_vue_type_script_setup_true_lang.js +69 -65
- package/dist/components/actions/ActionGroupMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/actions/ActionModal.js.map +1 -1
- package/dist/components/actions/ActionModal.vue.d.ts.map +1 -1
- package/dist/components/actions/ActionModal.vue_vue_type_script_setup_true_lang.js +221 -219
- package/dist/components/actions/ActionModal.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ai/AiCopilot.js.map +1 -1
- package/dist/components/ai/AiCopilot.vue.d.ts +2 -0
- package/dist/components/ai/AiCopilot.vue.d.ts.map +1 -1
- package/dist/components/ai/AiCopilot.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/components/ai/AiCopilot.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cms/CmsPage.vue_vue_type_script_setup_true_lang.js +19 -19
- package/dist/components/cms/CmsPage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/cms/LivePreview.js.map +1 -1
- package/dist/components/cms/LivePreview.vue.d.ts.map +1 -1
- package/dist/components/cms/LivePreview.vue_vue_type_script_setup_true_lang.js +83 -83
- package/dist/components/cms/LivePreview.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/BuilderField.js.map +1 -1
- package/dist/components/fields/BuilderField.vue.d.ts.map +1 -1
- package/dist/components/fields/BuilderField.vue_vue_type_script_setup_true_lang.js +162 -157
- package/dist/components/fields/BuilderField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/ColorPickerField.js.map +1 -1
- package/dist/components/fields/ColorPickerField.vue.d.ts.map +1 -1
- package/dist/components/fields/ColorPickerField.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/fields/ColorPickerField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/ComboboxSelect.js.map +1 -1
- package/dist/components/fields/ComboboxSelect.vue.d.ts.map +1 -1
- package/dist/components/fields/ComboboxSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/fields/ComboboxSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/FileUploadField.js.map +1 -1
- package/dist/components/fields/FileUploadField.vue.d.ts.map +1 -1
- package/dist/components/fields/FileUploadField.vue_vue_type_script_setup_true_lang.js +183 -183
- package/dist/components/fields/FileUploadField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/ImageEditorDialog.js.map +1 -1
- package/dist/components/fields/ImageEditorDialog.vue.d.ts.map +1 -1
- package/dist/components/fields/ImageEditorDialog.vue_vue_type_script_setup_true_lang.js +46 -46
- package/dist/components/fields/ImageEditorDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/KeyValueField.js.map +1 -1
- package/dist/components/fields/KeyValueField.vue.d.ts.map +1 -1
- package/dist/components/fields/KeyValueField.vue_vue_type_script_setup_true_lang.js +66 -66
- package/dist/components/fields/KeyValueField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/ModalTablePicker.js.map +1 -1
- package/dist/components/fields/ModalTablePicker.vue.d.ts.map +1 -1
- package/dist/components/fields/ModalTablePicker.vue_vue_type_script_setup_true_lang.js +113 -113
- package/dist/components/fields/ModalTablePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/OneTimeCodeInputField.js.map +1 -1
- package/dist/components/fields/OneTimeCodeInputField.vue_vue_type_script_setup_true_lang.js +1 -2
- package/dist/components/fields/OneTimeCodeInputField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RepeaterField.js.map +1 -1
- package/dist/components/fields/RepeaterField.vue.d.ts.map +1 -1
- package/dist/components/fields/RepeaterField.vue_vue_type_script_setup_true_lang.js +12 -12
- package/dist/components/fields/RepeaterField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RichEditorInput.js.map +1 -1
- package/dist/components/fields/RichEditorInput.vue.d.ts.map +1 -1
- package/dist/components/fields/RichEditorInput.vue_vue_type_script_setup_true_lang.js +166 -165
- package/dist/components/fields/RichEditorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/SelectField.js.map +1 -1
- package/dist/components/fields/SelectField.vue.d.ts.map +1 -1
- package/dist/components/fields/SelectField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/fields/SelectField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/SliderField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/TableColumnHeaders.js.map +1 -1
- package/dist/components/fields/TableColumnHeaders.vue.d.ts.map +1 -1
- package/dist/components/fields/TableColumnHeaders.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/components/fields/TableColumnHeaders.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/item-actions.d.ts +2 -2
- package/dist/components/fields/item-actions.d.ts.map +1 -1
- package/dist/components/fields/item-actions.js +31 -31
- package/dist/components/fields/item-actions.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/file-manager/FileManagerView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/FormBlock.js.map +1 -1
- package/dist/components/form/FormBlock.vue.d.ts.map +1 -1
- package/dist/components/form/FormBlock.vue_vue_type_script_setup_true_lang.js +81 -77
- package/dist/components/form/FormBlock.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/form-context.js +25 -25
- package/dist/components/form/form-context.js.map +1 -1
- package/dist/components/kanban/KanbanBoard.js.map +1 -1
- package/dist/components/kanban/KanbanBoard.vue.d.ts.map +1 -1
- package/dist/components/kanban/KanbanBoard.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/components/kanban/KanbanBoard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/kanban/KanbanColumnHeader.js.map +1 -1
- package/dist/components/kanban/KanbanColumnHeader.vue.d.ts.map +1 -1
- package/dist/components/kanban/KanbanColumnHeader.vue_vue_type_script_setup_true_lang.js +64 -64
- package/dist/components/kanban/KanbanColumnHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/FusedGroupComponent.js.map +1 -1
- package/dist/components/layout/FusedGroupComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/FusedGroupComponent.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/layout/FusedGroupComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/SectionComponent.js.map +1 -1
- package/dist/components/layout/SectionComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/SectionComponent.vue_vue_type_script_setup_true_lang.js +23 -23
- package/dist/components/layout/SectionComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/TabsComponent.js.map +1 -1
- package/dist/components/layout/TabsComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsComponent.vue_vue_type_script_setup_true_lang.js +76 -55
- package/dist/components/layout/TabsComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/notifications/NotificationsMenu.js.map +1 -1
- package/dist/components/notifications/NotificationsMenu.vue.d.ts.map +1 -1
- package/dist/components/notifications/NotificationsMenu.vue_vue_type_script_setup_true_lang.js +44 -42
- package/dist/components/notifications/NotificationsMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/DebugPanel.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/panel/DebugPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/GlobalSearch.js.map +1 -1
- package/dist/components/panel/GlobalSearch.vue.d.ts.map +1 -1
- package/dist/components/panel/GlobalSearch.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/components/panel/GlobalSearch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/GlobalSearchPalette.js.map +1 -1
- package/dist/components/panel/GlobalSearchPalette.vue.d.ts.map +1 -1
- package/dist/components/panel/GlobalSearchPalette.vue_vue_type_script_setup_true_lang.js +102 -96
- package/dist/components/panel/GlobalSearchPalette.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js +10 -10
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavGroupFlyout.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/panel/NavGroupFlyout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js +16 -16
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js +10 -10
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/ShortcutKeys.js.map +1 -1
- package/dist/components/panel/ShortcutKeys.vue.d.ts.map +1 -1
- package/dist/components/panel/ShortcutKeys.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/components/panel/ShortcutKeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/TenantMenu.js.map +1 -1
- package/dist/components/panel/TenantMenu.vue_vue_type_script_setup_true_lang.js +37 -37
- package/dist/components/panel/TenantMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/Topbar.vue_vue_type_script_setup_true_lang.js +11 -11
- package/dist/components/panel/Topbar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/realtime/use-live-updates.d.ts.map +1 -1
- package/dist/components/realtime/use-live-updates.js +24 -23
- package/dist/components/realtime/use-live-updates.js.map +1 -1
- package/dist/components/table/BooleanCell.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/table/BooleanCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/ColumnHeader.js.map +1 -1
- package/dist/components/table/ColumnHeader.vue.d.ts.map +1 -1
- package/dist/components/table/ColumnHeader.vue_vue_type_script_setup_true_lang.js +55 -49
- package/dist/components/table/ColumnHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/ColumnManager.js.map +1 -1
- package/dist/components/table/ColumnManager.vue.d.ts.map +1 -1
- package/dist/components/table/ColumnManager.vue_vue_type_script_setup_true_lang.js +5 -4
- package/dist/components/table/ColumnManager.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/ContentLayoutSort.js.map +1 -1
- package/dist/components/table/ContentLayoutSort.vue.d.ts.map +1 -1
- package/dist/components/table/ContentLayoutSort.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/table/ContentLayoutSort.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/GroupHeaderRow.js.map +1 -1
- package/dist/components/table/GroupHeaderRow.vue.d.ts.map +1 -1
- package/dist/components/table/GroupHeaderRow.vue_vue_type_script_setup_true_lang.js +55 -34
- package/dist/components/table/GroupHeaderRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/SelectCell.js.map +1 -1
- package/dist/components/table/SelectCell.vue.d.ts.map +1 -1
- package/dist/components/table/SelectCell.vue_vue_type_script_setup_true_lang.js +26 -20
- package/dist/components/table/SelectCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/SelectComboboxCell.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/table/SelectComboboxCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/TableCellContent.js.map +1 -1
- package/dist/components/table/TableCellContent.vue.d.ts.map +1 -1
- package/dist/components/table/TableCellContent.vue_vue_type_script_setup_true_lang.js +4 -3
- package/dist/components/table/TableCellContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/TablePagination.js.map +1 -1
- package/dist/components/table/TablePagination.vue.d.ts.map +1 -1
- package/dist/components/table/TablePagination.vue_vue_type_script_setup_true_lang.js +87 -87
- package/dist/components/table/TablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/TextInputCell.js.map +1 -1
- package/dist/components/table/TextInputCell.vue.d.ts.map +1 -1
- package/dist/components/table/TextInputCell.vue_vue_type_script_setup_true_lang.js +40 -34
- package/dist/components/table/TextInputCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/cell-update.d.ts +6 -0
- package/dist/components/table/cell-update.d.ts.map +1 -1
- package/dist/components/table/cell-update.js.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/CommandAutoHighlight.js +7 -0
- package/dist/components/ui/CommandAutoHighlight.js.map +1 -0
- package/dist/components/ui/CommandAutoHighlight.vue.d.ts +21 -0
- package/dist/components/ui/CommandAutoHighlight.vue.d.ts.map +1 -0
- package/dist/components/ui/CommandAutoHighlight.vue_vue_type_script_setup_true_lang.js +18 -0
- package/dist/components/ui/CommandAutoHighlight.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ui/DialogContent.js.map +1 -1
- package/dist/components/ui/DialogContent.vue.d.ts.map +1 -1
- package/dist/components/ui/DialogContent.vue_vue_type_script_setup_true_lang.js +13 -12
- package/dist/components/ui/DialogContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/PopoverContent.js.map +1 -1
- package/dist/components/ui/PopoverContent.vue.d.ts.map +1 -1
- package/dist/components/ui/PopoverContent.vue_vue_type_script_setup_true_lang.js +9 -8
- package/dist/components/ui/PopoverContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/SheetContent.js.map +1 -1
- package/dist/components/ui/SheetContent.vue.d.ts.map +1 -1
- package/dist/components/ui/SheetContent.vue_vue_type_script_setup_true_lang.js +13 -12
- package/dist/components/ui/SheetContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/TooltipContent.js.map +1 -1
- package/dist/components/ui/TooltipContent.vue.d.ts.map +1 -1
- package/dist/components/ui/TooltipContent.vue_vue_type_script_setup_true_lang.js +9 -8
- package/dist/components/ui/TooltipContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components.css +347 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -26
- package/dist/context.js.map +1 -1
- package/dist/create-alfakit.d.ts.map +1 -1
- package/dist/create-alfakit.js +33 -31
- package/dist/create-alfakit.js.map +1 -1
- package/dist/eject-manifest.json +11 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -217
- package/dist/layouts/AppLayout.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/layouts/AppLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/layouts/AuthLayout.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/layouts/FullscreenLayout.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/layouts/FullscreenLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js +15 -15
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/layouts/lazy-layouts.d.ts +10 -0
- package/dist/layouts/lazy-layouts.d.ts.map +1 -0
- package/dist/layouts/lazy-layouts.js +8 -0
- package/dist/layouts/lazy-layouts.js.map +1 -0
- package/dist/layouts/panel-layouts.d.ts +8 -0
- package/dist/layouts/panel-layouts.d.ts.map +1 -0
- package/dist/layouts/panel-layouts.js +15 -0
- package/dist/layouts/panel-layouts.js.map +1 -0
- package/dist/lib/lazy.d.ts.map +1 -1
- package/dist/lib/lazy.js +2 -2
- package/dist/lib/lazy.js.map +1 -1
- package/dist/pages/PageShell.js.map +1 -1
- package/dist/pages/PageShell.vue.d.ts.map +1 -1
- package/dist/pages/PageShell.vue_vue_type_script_setup_true_lang.js +39 -42
- package/dist/pages/PageShell.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone/AlfakitComponent.js.map +1 -1
- package/dist/standalone/AlfakitComponent.vue.d.ts.map +1 -1
- package/dist/standalone/AlfakitComponent.vue_vue_type_script_setup_true_lang.js +56 -52
- package/dist/standalone/AlfakitComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone/AlfakitToaster.js.map +1 -1
- package/dist/standalone/AlfakitToaster.vue.d.ts.map +1 -1
- package/dist/standalone/AlfakitToaster.vue_vue_type_script_setup_true_lang.js +23 -15
- package/dist/standalone/AlfakitToaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone.css +54 -0
- package/dist/starter-kit/AlfakitStarterKitRoot.js.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue.d.ts +2 -2
- package/dist/starter-kit/AlfakitStarterKitRoot.vue.d.ts.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js +17 -12
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/starter-kit/dashboard-widgets.d.ts +54 -0
- package/dist/starter-kit/dashboard-widgets.d.ts.map +1 -0
- package/dist/starter-kit/dashboard-widgets.js +54 -0
- package/dist/starter-kit/dashboard-widgets.js.map +1 -0
- package/dist/starter-kit/index.d.ts +2 -1
- package/dist/starter-kit/index.d.ts.map +1 -1
- package/dist/starter-kit/index.js +5 -4
- package/dist/starter-kit/runtime.d.ts +6 -0
- package/dist/starter-kit/runtime.d.ts.map +1 -1
- package/dist/starter-kit/runtime.js +19 -15
- package/dist/starter-kit/runtime.js.map +1 -1
- package/dist/starter-kit/starter-kit.d.ts.map +1 -1
- package/dist/starter-kit/starter-kit.js +8 -8
- package/dist/starter-kit/starter-kit.js.map +1 -1
- package/dist/starter-kit/widgets/CopilotTrigger.js +7 -0
- package/dist/starter-kit/widgets/CopilotTrigger.js.map +1 -0
- package/dist/starter-kit/widgets/CopilotTrigger.vue.d.ts +12 -0
- package/dist/starter-kit/widgets/CopilotTrigger.vue.d.ts.map +1 -0
- package/dist/starter-kit/widgets/CopilotTrigger.vue_vue_type_script_setup_true_lang.js +23 -0
- package/dist/starter-kit/widgets/CopilotTrigger.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/starter-kit/widgets/LoadedCopilot.js +7 -0
- package/dist/starter-kit/widgets/LoadedCopilot.js.map +1 -0
- package/dist/starter-kit/widgets/LoadedCopilot.vue.d.ts +8 -0
- package/dist/starter-kit/widgets/LoadedCopilot.vue.d.ts.map +1 -0
- package/dist/starter-kit/widgets/LoadedCopilot.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/starter-kit/widgets/LoadedCopilot.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/starter-kit/widgets/SearchTrigger.js.map +1 -1
- package/dist/starter-kit/widgets/SearchTrigger.vue.d.ts.map +1 -1
- package/dist/starter-kit/widgets/SearchTrigger.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/starter-kit/widgets/SearchTrigger.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.js +7 -0
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.js.map +1 -0
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.vue.d.ts +8 -0
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.vue.d.ts.map +1 -0
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.vue_vue_type_script_setup_true_lang.js +34 -0
- package/dist/starter-kit/widgets/StarterKitImpersonationBanner.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/starter-kit/widgets/TenantChooser.js.map +1 -1
- package/dist/starter-kit/widgets/TenantChooser.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/starter-kit/widgets/TenantChooser.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/starter-kit/widgets/index.d.ts +40 -9
- package/dist/starter-kit/widgets/index.d.ts.map +1 -1
- package/dist/starter-kit/widgets/index.js +21 -18
- package/dist/starter-kit/widgets/index.js.map +1 -1
- package/dist/starter-kit/widgets/loaded.d.ts +10 -0
- package/dist/starter-kit/widgets/loaded.d.ts.map +1 -0
- package/dist/starter-kit/widgets/loaded.js +5 -0
- package/dist/styles.css +9 -2
- package/dist/themes.css +8 -335
- package/package.json +3 -3
- package/src/AlfakitRoot.vue +9 -1
- package/src/components/actions/ActionButton.vue +14 -6
- package/src/components/actions/ActionGroupMenu.vue +13 -1
- package/src/components/actions/ActionModal.vue +12 -1
- package/src/components/ai/AiCopilot.vue +7 -1
- package/src/components/cms/LivePreview.vue +8 -2
- package/src/components/fields/BuilderField.vue +15 -2
- package/src/components/fields/ColorPickerField.vue +1 -0
- package/src/components/fields/ComboboxSelect.vue +1 -1
- package/src/components/fields/FileUploadField.vue +14 -11
- package/src/components/fields/ImageEditorDialog.vue +7 -3
- package/src/components/fields/KeyValueField.vue +3 -2
- package/src/components/fields/ModalTablePicker.vue +7 -2
- package/src/components/fields/OneTimeCodeInputField.vue +1 -1
- package/src/components/fields/RepeaterField.vue +2 -3
- package/src/components/fields/RichEditorInput.vue +3 -0
- package/src/components/fields/SelectField.vue +4 -2
- package/src/components/fields/TableColumnHeaders.vue +6 -1
- package/src/components/fields/item-actions.ts +31 -10
- package/src/components/file-manager/FileManagerView.vue +1 -1
- package/src/components/form/FormBlock.vue +26 -1
- package/src/components/form/form-context.ts +3 -3
- package/src/components/kanban/KanbanBoard.vue +15 -3
- package/src/components/kanban/KanbanColumnHeader.vue +6 -4
- package/src/components/layout/FusedGroupComponent.vue +1 -0
- package/src/components/layout/SectionComponent.vue +10 -4
- package/src/components/layout/TabsComponent.vue +61 -5
- package/src/components/notifications/NotificationsMenu.vue +11 -0
- package/src/components/panel/GlobalSearch.vue +1 -0
- package/src/components/panel/GlobalSearchPalette.vue +2 -0
- package/src/components/panel/ShortcutKeys.vue +1 -0
- package/src/components/panel/TenantMenu.vue +2 -2
- package/src/components/realtime/use-live-updates.ts +3 -1
- package/src/components/table/ColumnHeader.vue +23 -12
- package/src/components/table/ColumnManager.vue +12 -8
- package/src/components/table/ContentLayoutSort.vue +7 -1
- package/src/components/table/GroupHeaderRow.vue +27 -0
- package/src/components/table/SelectCell.vue +5 -2
- package/src/components/table/TableCellContent.vue +4 -2
- package/src/components/table/TablePagination.vue +12 -7
- package/src/components/table/TextInputCell.vue +11 -5
- package/src/components/table/cell-update.ts +6 -0
- package/src/components/ui/CommandAutoHighlight.vue +27 -0
- package/src/components/ui/DialogContent.vue +5 -1
- package/src/components/ui/PopoverContent.vue +5 -1
- package/src/components/ui/SheetContent.vue +5 -1
- package/src/components/ui/TooltipContent.vue +5 -1
- package/src/context.ts +10 -0
- package/src/create-alfakit.ts +5 -0
- package/src/index.ts +2 -0
- package/src/layouts/lazy-layouts.ts +14 -0
- package/src/layouts/panel-layouts.ts +17 -0
- package/src/lib/lazy.ts +11 -1
- package/src/pages/PageShell.vue +5 -6
- package/src/standalone/AlfakitComponent.vue +17 -6
- package/src/standalone/AlfakitToaster.vue +12 -2
- package/src/starter-kit/AlfakitStarterKitRoot.vue +12 -3
- package/src/starter-kit/dashboard-widgets.ts +93 -0
- package/src/starter-kit/index.ts +7 -1
- package/src/starter-kit/runtime.ts +17 -1
- package/src/starter-kit/starter-kit.ts +2 -1
- package/src/starter-kit/widgets/CopilotTrigger.vue +58 -0
- package/src/starter-kit/widgets/LoadedCopilot.vue +29 -0
- package/src/starter-kit/widgets/SearchTrigger.vue +1 -0
- package/src/starter-kit/widgets/StarterKitImpersonationBanner.vue +57 -0
- package/src/starter-kit/widgets/TenantChooser.vue +1 -1
- package/src/starter-kit/widgets/index.ts +56 -11
- package/src/starter-kit/widgets/loaded.ts +9 -0
- package/src/styles.css +9 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuilderField.vue_vue_type_script_setup_true_lang.js","names":["_resolveComponent","_unref","_openBlock","_createBlock","DeferredSchema","_withCtx","_createVNode","FieldWrapper","_createElementVNode","_toDisplayString","_createElementBlock","BuilderBlockPickerPanel","_createCommentVNode","CollapseAllActions","_normalizeStyle","_normalizeClass","_Fragment","_renderList","Collapsible","Icon","ItemHeaderActions","Button","BuilderBlockActionsMenu","CollapsibleContent","_withKeys","_withModifiers","_resolveDynamicComponent","SchemaRenderer","AddActionRow","BuilderBlockPicker","_createTextVNode","Icon_default","Button_default","SchemaRenderer_default","Collapsible_default","CollapsibleContent_default","DeferredSchema_default","FieldWrapper_default","AddActionRow_default","CollapseAllActions_default","ItemHeaderActions_default","BuilderBlockActionsMenu_default","BuilderBlockPickerPanel_default","BuilderBlockPicker_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","BuilderField_vue_vue_type_script_setup_true_lang_default","props","__props","t","useT","formatter","useFormatter","registry","useOptionalRegistry","form","useOptionalFormStore","context","useFieldActionContext","runItemAction","useRunItemAction","fallbacks","useItemActionFallbacks","serverItemOf","server","index","items","resolve","resolveItemAction","name","fieldDisabled","useField","locked","computed","readOnly","builderItems","value","collapsed","ref","editing","dragIndex","virtual","useVirtualList","virtualizeOptions","VIRTUAL_ESTIMATES","visibleItems","blockOf","block","type","isCollapsed","canAdd","canDelete","canClone","item","canAddBuilderBlock","draggable","buttons","addAction","showEmptyPicker","menu","runner","useActionRunner","onMenuAction","moves","cloneItem","removeItem","onMenuExtra","action","itemActionContext","update","setValue","next","moveItem","moveRepeaterItem","from","to","moveIndexedState","useItemMoves","builderItemLabel","moveAnnouncement","moveContainer","removeRepeaterItem","removeIndexedState","cloneRepeaterItem","cloneIndexedState","addBlock","insertBuilderItem","onDrop","reorderedItemKeys","setAllCollapsed","previewComponentOf","hasPreview","showPreview","toggleEditing","open","openEditor","previewIsButton","_component_BuilderField","resolveComponent","unref","isDeferredPlaceholder","openBlock","createBlock","withCtx","loaded","createVNode","id","error","createElementVNode","toDisplayString","createElementBlock","createCommentVNode","_cache","fieldIds","aria","normalizeStyle","normalizeClass","cn","Fragment","renderList","$event","PencilIcon","withKeys","withModifiers","resolveDynamicComponent","builderItemSchema","createTextVNode"],"sources":["../../../src/components/fields/BuilderField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { BuilderBlockSchema, BuilderItemState, BuilderSchema } from '@alfakit/core'\nimport {\n isDeferredPlaceholder,\n VIRTUAL_ESTIMATES,\n builderItemLabel,\n builderItems,\n builderItemSchema,\n canAddBuilderBlock,\n cloneIndexedState,\n cloneRepeaterItem,\n fieldIds,\n insertBuilderItem,\n itemActionContext,\n moveIndexedState,\n moveRepeaterItem,\n removeIndexedState,\n removeRepeaterItem,\n reorderedItemKeys,\n resolveItemAction,\n virtualizeOptions,\n type ActionSchema,\n type ItemActionName,\n} from '@alfakit/core'\nimport { PencilIcon } from '@lucide/vue'\nimport { computed, ref } from 'vue'\nimport { useScrollToErroredItem, useVirtualList } from '../../composables/use-virtual-list'\nimport { useActionRunner, useFormatter, useOptionalRegistry, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport DeferredSchema from '../layout/DeferredSchema.vue'\nimport { useField, useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport AddActionRow from './AddActionRow.vue'\nimport CollapseAllActions from './CollapseAllActions.vue'\nimport ItemHeaderActions from './ItemHeaderActions.vue'\nimport { useItemActionFallbacks, useItemMoves, useRunItemAction } from './item-actions'\nimport BuilderBlockActionsMenu from './BuilderBlockActionsMenu.vue'\nimport BuilderBlockPicker from './BuilderBlockPicker.vue'\nimport BuilderBlockPickerPanel from './BuilderBlockPickerPanel.vue'\n\n/**\n * A list of blocks, each with its own schema. Items use the server's per-item schema\n * when it describes the same block, else the block's template (fresh, moved or cloned\n * items the server has not seen yet).\n */\nconst props = defineProps<{ component: BuilderSchema }>()\nconst t = useT()\nconst formatter = useFormatter()\nconst registry = useOptionalRegistry()\nconst form = useOptionalFormStore()\nconst context = useFieldActionContext(() => props.component)\nconst runItemAction = useRunItemAction(context)\nconst fallbacks = useItemActionFallbacks()\nconst serverItemOf = (index: number) => {\n const server = props.component.items[index]\n\n return server && server.type === items.value[index]?.type ? server : undefined\n}\nconst resolve = (name: ItemActionName, index?: number) =>\n resolveItemAction(\n name,\n props.component,\n index === undefined ? undefined : serverItemOf(index),\n name === 'add' ? { ...fallbacks.add, label: props.component.addActionLabel } : fallbacks[name],\n )\n\nconst {\n value,\n setValue,\n error,\n id,\n disabled: fieldDisabled,\n readOnly,\n aria,\n} = useField<unknown>(() => props.component)\nconst locked = computed(() => fieldDisabled.value || readOnly.value)\nconst items = computed(() => builderItems(value.value))\nconst collapsed = ref<Record<number, boolean>>({})\nconst editing = ref<Record<number, boolean>>({})\nconst dragIndex = ref<number | null>(null)\n// Long lists render only the visible blocks (plus a few around them).\nconst virtual = useVirtualList(\n () => items.value.length,\n () => virtualizeOptions(props.component.virtualize, VIRTUAL_ESTIMATES.item, 12),\n)\nuseScrollToErroredItem(virtual, () => props.component.statePath)\nconst visibleItems = computed(() =>\n virtual.indexes.value.map((index) => ({ item: items.value[index] as BuilderItemState, index })),\n)\n\nconst blockOf = (type: string) => props.component.blocks.find((block) => block.name === type)\nconst isCollapsed = (index: number) => collapsed.value[index] ?? props.component.collapsed ?? false\nconst canAdd = computed(\n () =>\n props.component.addable &&\n !locked.value &&\n (props.component.maxItems == null || items.value.length < props.component.maxItems),\n)\nconst canDelete = computed(\n () =>\n props.component.deletable &&\n !locked.value &&\n (props.component.minItems == null || items.value.length > props.component.minItems),\n)\nconst canClone = (item: BuilderItemState) => {\n const block = blockOf(item.type)\n\n return Boolean(\n props.component.cloneable &&\n canAdd.value &&\n block &&\n canAddBuilderBlock(props.component, block, items.value),\n )\n}\n\nconst draggable = computed(\n () =>\n props.component.reorderable &&\n props.component.reorderableWithDragAndDrop !== false &&\n !locked.value &&\n resolve('reorder') !== null,\n)\nconst buttons = computed(\n () =>\n props.component.reorderable &&\n props.component.reorderableWithButtons !== false &&\n !locked.value,\n)\nconst addAction = computed(() => resolve('add'))\nconst showEmptyPicker = computed(\n () =>\n canAdd.value &&\n addAction.value !== null &&\n !addAction.value.disabled &&\n props.component.blocks.length > 0,\n)\nconst menu = computed(() => Boolean(props.component.blockActionsMenu) && !locked.value)\nconst runner = useActionRunner()\n// `blockActionsMenu()`: built-in actions run in the browser unless customised.\nconst onMenuAction = (name: ItemActionName, index: number) =>\n runItemAction(resolve(name, index), { item: String(index) }, () => {\n if (name === 'moveUp') moves.moveWithButton(index, index - 1)\n else if (name === 'moveDown') moves.moveWithButton(index, index + 1)\n else if (name === 'clone') cloneItem(index)\n else if (name === 'delete') removeItem(index)\n })\nconst onMenuExtra = (action: ActionSchema, index: number) =>\n void runner.trigger(action, itemActionContext(context.value, { item: String(index) }))\n\nconst update = (next: unknown[]) => setValue(next as BuilderItemState[])\nconst moveItem = (from: number, to: number) => {\n update(moveRepeaterItem(items.value, from, to))\n collapsed.value = moveIndexedState(collapsed.value, from, to)\n editing.value = moveIndexedState(editing.value, from, to)\n}\n// Move buttons: announced, one at a time, focus kept on the moved item.\nconst moves = useItemMoves(\n value,\n (from, to) => moveItem(from, to),\n (index) => builderItemLabel(props.component, items.value, index),\n () => items.value.length,\n)\nconst moveAnnouncement = moves.announcement\nconst moveContainer = moves.container\nconst removeItem = (index: number) => {\n update(removeRepeaterItem(items.value, index))\n collapsed.value = removeIndexedState(collapsed.value, index)\n editing.value = removeIndexedState(editing.value, index)\n}\nconst cloneItem = (index: number) => {\n update(cloneRepeaterItem(items.value, index))\n collapsed.value = cloneIndexedState(collapsed.value, index)\n editing.value = cloneIndexedState(editing.value, index)\n}\nconst addBlock = (block: BuilderBlockSchema) =>\n runItemAction(addAction.value, { block: block.name }, () =>\n update(insertBuilderItem(items.value, block)),\n )\n// A drop runs the reorder action: in the browser, or on the server when customised.\nconst onDrop = (index: number) => {\n const from = dragIndex.value\n dragIndex.value = null\n if (from === null || from === index) return\n runItemAction(\n resolve('reorder'),\n { items: reorderedItemKeys(items.value.length, from, index) },\n () => moveItem(from, index),\n )\n}\nconst setAllCollapsed = (value: boolean) => {\n collapsed.value = Object.fromEntries(items.value.map((_, index) => [index, value]))\n}\n\n// Previews (`blockPreviews()`): server HTML or a registered `builder-preview:{name}`.\nconst previewComponentOf = (index: number) => {\n const name =\n serverItemOf(index)?.previewComponent ??\n blockOf(items.value[index]?.type ?? '')?.previewComponent\n\n return name ? registry?.resolve(`builder-preview:${name}`) : undefined\n}\nconst hasPreview = (index: number) =>\n props.component.blockPreviews === true &&\n (previewComponentOf(index) !== undefined || Boolean(serverItemOf(index)?.preview))\nconst showPreview = (index: number) => hasPreview(index) && !editing.value[index]\n// Closing the editor re-renders the previews from the server.\nconst toggleEditing = (index: number) => {\n const open = !editing.value[index]\n editing.value = { ...editing.value, [index]: open }\n if (!open) void form?.refresh(props.component.statePath)\n}\nconst openEditor = (index: number) => {\n if (!locked.value) toggleEditing(index)\n}\nconst previewIsButton = computed(() => !props.component.interactivePreviews && !locked.value)\n</script>\n\n<template>\n <!-- `deferLoading()`: a skeleton until the field scrolls into view. -->\n <DeferredSchema\n v-if=\"isDeferredPlaceholder(props.component)\"\n v-slot=\"{ component: loaded }\"\n :component=\"props.component\"\n >\n <BuilderField :component=\"loaded\" />\n </DeferredSchema>\n <FieldWrapper v-else :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div ref=\"moveContainer\" class=\"flex flex-col gap-3\" data-slot=\"builder\">\n <span aria-live=\"polite\" aria-atomic=\"true\" class=\"sr-only\" data-slot=\"item-move-status\">{{\n moveAnnouncement\n }}</span>\n <!-- No blocks yet: the picker is right there instead of behind a button. -->\n <div\n v-if=\"items.length === 0 && showEmptyPicker\"\n data-slot=\"builder-empty\"\n class=\"bg-muted/30 flex flex-col gap-3 rounded-lg border border-dashed p-3 sm:p-4\"\n >\n <div class=\"px-1\">\n <p class=\"text-sm font-medium\">\n {{ t('builder.empty_heading', {}, 'Add your first block') }}\n </p>\n <p class=\"text-muted-foreground text-sm\">\n {{\n t(\n 'builder.empty_description',\n {},\n 'Pick one to start. You can add more, reorder and remove them at any time.',\n )\n }}\n </p>\n </div>\n <BuilderBlockPickerPanel\n :component=\"props.component\"\n :items=\"items\"\n class=\"bg-card max-h-[28rem] rounded-md border\"\n @pick=\"addBlock\"\n />\n </div>\n <p\n v-else-if=\"items.length === 0\"\n class=\"text-muted-foreground rounded-lg border border-dashed p-4 text-center text-sm\"\n >\n {{ t('builder.empty', {}, 'No blocks yet.') }}\n </p>\n <CollapseAllActions\n v-if=\"props.component.collapsible && items.length > 0\"\n :context=\"context\"\n :collapse-all=\"resolve('collapseAll')\"\n :expand-all=\"resolve('expandAll')\"\n @collapse-all=\"setAllCollapsed(true)\"\n @expand-all=\"setAllCollapsed(false)\"\n />\n <ul\n :id=\"id\"\n :ref=\"virtual.attach\"\n :aria-labelledby=\"props.component.label ? fieldIds(id).label : undefined\"\n :aria-describedby=\"aria['aria-describedby']\"\n :style=\"virtual.style.value\"\n :class=\"cn('grid gap-3', items.length === 0 && 'hidden')\"\n >\n <li\n v-for=\"{ item, index } in visibleItems\"\n :key=\"index\"\n :ref=\"virtual.item\"\n :data-index=\"index\"\n :aria-setsize=\"virtual.range.value.active ? items.length : undefined\"\n :aria-posinset=\"virtual.range.value.active ? index + 1 : undefined\"\n :data-block=\"item.type\"\n :draggable=\"draggable\"\n :class=\"\n cn('bg-card min-w-0 rounded-lg border shadow-xs', dragIndex === index && 'opacity-50')\n \"\n @dragstart=\"dragIndex = index\"\n @dragover=\"draggable && $event.preventDefault()\"\n @drop=\"onDrop(index)\"\n @dragend=\"dragIndex = null\"\n >\n <Collapsible :open=\"!isCollapsed(index)\">\n <div\n class=\"flex flex-wrap items-center gap-1 border-b px-2 py-1.5\"\n data-slot=\"item-header\"\n >\n <span\n v-if=\"draggable\"\n class=\"text-muted-foreground cursor-grab\"\n :title=\"resolve('reorder')?.label ?? undefined\"\n aria-hidden=\"true\"\n ><Icon :name=\"resolve('reorder')?.icon ?? 'grip-vertical'\"\n /></span>\n <Icon\n v-if=\"\n props.component.blockIcons &&\n (blockOf(item.type)?.icon ?? props.component.items[index]?.icon)\n \"\n :name=\"blockOf(item.type)?.icon ?? props.component.items[index]?.icon\"\n class=\"text-muted-foreground size-4\"\n />\n <span\n v-if=\"props.component.blockNumbers\"\n class=\"text-muted-foreground text-xs tabular-nums\"\n >{{ formatter.number(index + 1) }}</span\n >\n <span class=\"flex min-w-0 flex-1 items-baseline gap-2\">\n <span\n v-if=\"props.component.blockLabels\"\n dir=\"auto\"\n class=\"min-w-0 shrink-0 truncate text-sm font-medium sm:max-w-[60%]\"\n >{{ builderItemLabel(props.component, items, index) }}</span\n >\n <span\n v-if=\"serverItemOf(index)?.summary\"\n dir=\"auto\"\n class=\"text-muted-foreground min-w-0 truncate text-sm\"\n data-slot=\"builder-item-summary\"\n >{{ serverItemOf(index)?.summary }}</span\n >\n </span>\n <ItemHeaderActions\n :context=\"context\"\n :item-key=\"String(index)\"\n :index=\"index\"\n :count=\"items.length\"\n :resolve=\"(name: ItemActionName) => resolve(name, index)\"\n :extra-actions=\"menu || locked ? undefined : serverItemOf(index)?.actions\"\n :buttons=\"buttons && !menu\"\n :can-clone=\"canClone(item) && !menu\"\n :can-delete=\"canDelete && !menu\"\n :collapsible=\"Boolean(props.component.collapsible)\"\n :collapsed=\"isCollapsed(index)\"\n @move=\"moves.moveWithButton\"\n @clone=\"cloneItem(index)\"\n @delete=\"removeItem(index)\"\n @toggle=\"collapsed = { ...collapsed, [index]: !isCollapsed(index) }\"\n >\n <Button\n v-if=\"hasPreview(index) && !locked\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('builder.edit_block', {}, 'Edit block')\"\n :aria-pressed=\"Boolean(editing[index])\"\n @click=\"toggleEditing(index)\"\n ><Icon\n name=\"alfakit::forms.components.builder.actions.edit\"\n :fallback=\"PencilIcon\"\n /></Button>\n <BuilderBlockActionsMenu\n v-if=\"menu\"\n :label=\"builderItemLabel(props.component, items, index) ?? ''\"\n :resolve=\"(name: ItemActionName) => resolve(name, index)\"\n :can-move-up=\"buttons && index > 0\"\n :can-move-down=\"buttons && index < items.length - 1\"\n :can-clone=\"canClone(item)\"\n :can-delete=\"canDelete\"\n :extra-actions=\"serverItemOf(index)?.actions\"\n @action=\"(name: ItemActionName) => onMenuAction(name, index)\"\n @extra=\"(action: ActionSchema) => onMenuExtra(action, index)\"\n />\n </ItemHeaderActions>\n </div>\n <CollapsibleContent>\n <div\n v-if=\"showPreview(index)\"\n data-slot=\"builder-preview\"\n :class=\"cn('p-4', previewIsButton && 'cursor-pointer')\"\n :role=\"previewIsButton ? 'button' : undefined\"\n :tabindex=\"previewIsButton ? 0 : undefined\"\n :aria-label=\"\n previewIsButton ? t('builder.edit_block', {}, 'Edit block') : undefined\n \"\n @click=\"previewIsButton && openEditor(index)\"\n @keydown.enter.prevent=\"previewIsButton && openEditor(index)\"\n @keydown.space.prevent=\"previewIsButton && openEditor(index)\"\n >\n <div :class=\"cn(!props.component.interactivePreviews && 'pointer-events-none')\">\n <component\n :is=\"previewComponentOf(index)\"\n v-if=\"previewComponentOf(index)\"\n :data=\"item.data\"\n :block=\"item.type\"\n :index=\"index\"\n />\n <!-- Server-rendered `Block::preview()` HTML (Blade escapes the data). -->\n <!-- eslint-disable-next-line vue/no-v-html -->\n <div v-else v-html=\"serverItemOf(index)?.preview ?? ''\" />\n </div>\n </div>\n <div v-else class=\"p-4\">\n <SchemaRenderer\n :components=\"builderItemSchema(props.component, items, index)\"\n :columns=\"\n blockOf(item.type)?.columns ??\n props.component.items[index]?.columns ?? { default: 1 }\n \"\n />\n </div>\n </CollapsibleContent>\n </Collapsible>\n </li>\n </ul>\n <AddActionRow\n v-if=\"canAdd && addAction && props.component.blocks.length > 0 && items.length > 0\"\n :alignment=\"props.component.addActionAlignment\"\n >\n <BuilderBlockPicker :component=\"props.component\" :items=\"items\" @pick=\"addBlock\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n :disabled=\"addAction?.disabled\"\n data-slot=\"builder-add\"\n ><Icon v-if=\"addAction?.icon\" :name=\"addAction.icon\" /> {{ addAction?.label }}</Button\n >\n </BuilderBlockPicker>\n </AddActionRow>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDA,IAAM8D,IAAQC,GACRC,IAAIC,GAAK,GACTC,KAAYC,EAAa,GACzBC,KAAWC,EAAoB,GAC/BC,KAAOC,GAAqB,GAC5BC,IAAUC,SAA4BX,EAAM,SAAS,GACrDY,IAAgBC,GAAiBH,CAAO,GACxCI,IAAYC,GAAuB,GACnCC,KAAgB,MAAkB;GACtC,IAAMC,IAASjB,EAAM,UAAU,MAAMkB;GAErC,OAAOD,KAAUA,EAAO,SAASE,EAAM,MAAMD,EAAM,EAAE,OAAOD,IAAS,KAAA;EACvE,GACMG,KAAW,GAAsB,MACrCC,GACEC,GACAtB,EAAM,WACNkB,MAAU,KAAA,IAAY,KAAA,IAAYF,EAAaE,CAAK,GACpDI,MAAS,QAAQ;GAAE,GAAGR,EAAU;GAAK,OAAOd,EAAM,UAAU;EAAe,IAAIc,EAAUQ,EAC3F,GAEI,EACJ,UACA,cACA,WACA,OACA,UAAUC,IACV,cACA,aACEC,SAAwBxB,EAAM,SAAS,GACrCyB,IAASC,QAAeH,GAAc,SAASI,GAAS,KAAK,GAC7DR,IAAQO,QAAeE,GAAaC,EAAM,KAAK,CAAC,GAChDC,IAAYC,EAA6B,CAAC,CAAC,GAC3CC,IAAUD,EAA6B,CAAC,CAAC,GACzCE,IAAYF,EAAmB,IAAI,GAEnCG,IAAUC,SACRhB,EAAM,MAAM,cACZiB,GAAkBpC,EAAM,UAAU,YAAYqC,GAAkB,MAAM,EAAE,CAChF;EACA,GAAuBH,SAAelC,EAAM,UAAU,SAAS;EAC/D,IAAMsC,KAAeZ,QACnBQ,EAAQ,QAAQ,MAAM,KAAK,OAAW;GAAE,MAAMf,EAAM,MAAMD;GAA4B;EAAM,EAAE,CAChG,GAEMqB,KAAW,MAAiBvC,EAAM,UAAU,OAAO,MAAM,MAAUwC,EAAM,SAASC,CAAI,GACtFC,KAAe,MAAkBZ,EAAU,MAAMZ,MAAUlB,EAAM,UAAU,aAAa,IACxF2C,IAASjB,QAEX1B,EAAM,UAAU,WAChB,CAACyB,EAAO,UACPzB,EAAM,UAAU,YAAY,QAAQmB,EAAM,MAAM,SAASnB,EAAM,UAAU,SAC9E,GACM4C,IAAYlB,QAEd1B,EAAM,UAAU,aAChB,CAACyB,EAAO,UACPzB,EAAM,UAAU,YAAY,QAAQmB,EAAM,MAAM,SAASnB,EAAM,UAAU,SAC9E,GACM6C,KAAY,MAA2B;GAC3C,IAAML,IAAQD,EAAQO,EAAK,IAAI;GAE/B,OAAO,GACL9C,EAAM,UAAU,aAChB2C,EAAO,SACPH,KACAO,GAAmB/C,EAAM,WAAWwC,GAAOrB,EAAM,KAAK;EAE1D,GAEM6B,IAAYtB,QAEd1B,EAAM,UAAU,eAChBA,EAAM,UAAU,+BAA+B,MAC/C,CAACyB,EAAO,SACRL,EAAQ,SAAS,MAAM,IAC3B,GACM6B,IAAUvB,QAEZ1B,EAAM,UAAU,eAChBA,EAAM,UAAU,2BAA2B,MAC3C,CAACyB,EAAO,KACZ,GACMyB,IAAYxB,QAAeN,EAAQ,KAAK,CAAC,GACzC+B,KAAkBzB,QAEpBiB,EAAO,SACPO,EAAU,UAAU,QACpB,CAACA,EAAU,MAAM,YACjBlD,EAAM,UAAU,OAAO,SAAS,CACpC,GACMoD,IAAO1B,QAAe,EAAQ1B,EAAM,UAAU,oBAAqB,CAACyB,EAAO,KAAK,GAChF4B,KAASC,EAAgB,GAEzBC,MAAgB,GAAsB,MAC1C3C,EAAcQ,EAAQE,GAAMJ,CAAK,GAAG,EAAE,MAAM,OAAOA,CAAK,EAAE,SAAS;GACjE,AAAII,MAAS,WAAUkC,EAAM,eAAetC,GAAOA,IAAQ,CAAC,IACnDI,MAAS,aAAYkC,EAAM,eAAetC,GAAOA,IAAQ,CAAC,IAC1DI,MAAS,UAASmC,GAAUvC,CAAK,IACjCI,MAAS,YAAUoC,GAAWxC,CAAK;EAC9C,CAAC,GACGyC,MAAe,GAAsB,MACzC,KAAKN,GAAO,QAAQO,GAAQC,GAAkBnD,EAAQ,OAAO,EAAE,MAAM,OAAOQ,CAAK,EAAE,CAAC,CAAC,GAEjF4C,KAAU,MAAoBC,GAASC,CAA0B,GACjEC,MAAY,GAAc,MAAe;GAG7C,AAFAH,EAAOI,GAAiB/C,EAAM,OAAOgD,GAAMC,CAAE,CAAC,GAC9C,EAAU,QAAQC,EAAiBvC,EAAU,OAAOqC,GAAMC,CAAE,GAC5D,EAAQ,QAAQC,EAAiBrC,EAAQ,OAAOmC,GAAMC,CAAE;EAC1D,GAEMZ,IAAQc,GACZzC,IACC,GAAM,MAAOoC,GAASE,GAAMC,CAAE,IAC9B,MAAUG,EAAiBvE,EAAM,WAAWmB,EAAM,OAAOD,CAAK,SACzDC,EAAM,MAAM,MACpB,GACMqD,KAAmBhB,EAAM,cACzBiB,KAAgBjB,EAAM,WACtBE,MAAc,MAAkB;GAGpC,AAFAI,EAAOY,GAAmBvD,EAAM,OAAOD,CAAK,CAAC,GAC7C,EAAU,QAAQyD,EAAmB7C,EAAU,OAAOZ,CAAK,GAC3D,EAAQ,QAAQyD,EAAmB3C,EAAQ,OAAOd,CAAK;EACzD,GACMuC,MAAa,MAAkB;GAGnC,AAFAK,EAAOc,GAAkBzD,EAAM,OAAOD,CAAK,CAAC,GAC5C,EAAU,QAAQ2D,EAAkB/C,EAAU,OAAOZ,CAAK,GAC1D,EAAQ,QAAQ2D,EAAkB7C,EAAQ,OAAOd,CAAK;EACxD,GACM4D,MAAY,MAChBlE,EAAcsC,EAAU,OAAO,EAAE,OAAOV,EAAM,KAAK,SACjDsB,EAAOiB,GAAkB5D,EAAM,OAAOqB,CAAK,CAAC,CAC9C,GAEIwC,MAAU,MAAkB;GAChC,IAAMb,IAAOlC,EAAU;GAEvB,AADA,EAAU,QAAQ,MACdkC,MAAS,QAAQA,MAASjD,KAC9BN,EACEQ,EAAQ,SAAS,GACjB,EAAE,OAAO6D,GAAkB9D,EAAM,MAAM,QAAQgD,GAAMjD,CAAK,EAAE,SACtD+C,GAASE,GAAMjD,CAAK,CAC5B;EACF,GACMgE,MAAmB,MAAmB;GAC1C,EAAU,QAAQ,OAAO,YAAY/D,EAAM,MAAM,KAAK,GAAG,MAAU,CAACD,GAAOW,CAAK,CAAC,CAAC;EACpF,GAGMsD,KAAsB,MAAkB;GAC5C,IAAM7D,IACJN,EAAaE,CAAK,CAAC,EAAE,oBACrBqB,EAAQpB,EAAM,MAAMD,EAAM,EAAE,QAAQ,EAAE,CAAC,EAAE;GAE3C,OAAOI,IAAOhB,IAAU,QAAQ,mBAAmBgB,GAAM,IAAI,KAAA;EAC/D,GACM8D,MAAc,MAClBpF,EAAM,UAAU,kBAAkB,OACjCmF,EAAmBjE,CAAK,MAAM,KAAA,KAAa,EAAQF,EAAaE,CAAK,CAAC,EAAE,UACrEmE,MAAe,MAAkBD,GAAWlE,CAAK,KAAK,CAACc,EAAQ,MAAMd,IAErEoE,MAAiB,MAAkB;GACvC,IAAMC,IAAO,CAACvD,EAAQ,MAAMd;GAE5B,AADA,EAAQ,QAAQ;IAAE,GAAGc,EAAQ;KAAQd,IAAQqE;GAAK,GAC7CA,KAAM/E,IAAW,QAAQR,EAAM,UAAU,SAAS;EACzD,GACMwF,KAAc,MAAkB;GACpC,AAAK/D,EAAO,SAAO6D,GAAcpE,CAAK;EACxC,GACMuE,IAAkB/D,QAAe,CAAC1B,EAAM,UAAU,uBAAuB,CAACyB,EAAO,KAAK;;;GAMlF,OAAAtF,EAAA0J,EAAA,CAAqB,CAAC7F,EAAM,SAAS,KAD7C5D,EAAA,GAAAC,EAMiBC,IAAA;;IAHd,WAAW0D,EAAM;;IAElB,SAAAzD,GAAoC,EAAA,WAHf0J,QAAM,CAG3BzJ,EAAoCkJ,GAAA,EAArB,WAAWO,EAAM,GAAA,MAAA,GAAA,CAAA,WAAA,CAAA,CAAA,CAAA;;GAElC7J,GAAAA,GAAAA,CAAAA,WAAAA,CAAAA,MAAAA,EAAA,GAAAC,EAkNeI,IAAA;;IAlNO,OAAOuD,EAAM;IAAY,YAAU7D,EAAAgK,CAAA;IAAK,OAAOhK,EAAAiK,EAAA;;IACnE,SAAA7J,QAgNM,CAhNNG,EAgNM,OAAA;KAhNG,SAAA;KAAJ,KAAI+H;KAAgB,OAAM;KAAsB,aAAU;;KAC7D/H,EAES,QAFToC,IAESnC,EADPR,EAAAqI,EAAA,CAAgB,GAAA,CAAA;KAIVrD,EAAA,MAAM,WAAM,KAAUgC,GAAA,SAD9B/G,EAAA,GAAAQ,EAyBM,OAzBNmC,IAyBM,CApBJrC,EAaM,OAbNsC,IAaM,CAZJtC,EAEI,KAFJuC,IAEItC,EADCR,EAAA+D,CAAA,CAAC,CAAA,yBAAA,CAAA,GAAA,sBAAA,CAAA,GAAA,CAAA,GAENxD,EAQI,KARJwC,IAQIvC,EANAR,EAAA+D,CAAA,CAAC,CAAA,6BAAA,CAAA,GAAA,2EAAA,CAAA,GAAA,CAAA,CAAA,CAAA,GAQP1D,EAKEK,IAAA;MAJC,WAAWmD,EAAM;MACjB,OAAOmB,EAAA;MACR,OAAM;MACL,QAAM2D;KAIE,GAAA,MAAA,GAAA,CAAA,aAAA,OAAA,CAAA,CAAA,CAAA,KAAA3D,EAAA,MAAM,WAAM,KADzB/E,EAAA,GAAAQ,EAKI,KALJuC,IAKIxC,EADCR,EAAA+D,CAAA,CAAC,CAAA,iBAAA,CAAA,GAAA,gBAAA,CAAA,GAAA,CAAA,KAAAsG,EAAA,IAAA,EAAA;KAGExG,EAAM,UAAU,eAAemB,EAAA,MAAM,SAAM,KADnD/E,EAAA,GAAAC,EAOEU,IAAA;;MALC,SAASZ,EAAAuE,CAAA;MACT,gBAAcU,EAAO,aAAA;MACrB,cAAYA,EAAO,WAAA;MACnB,eAAY,AAAAqF,EAAA,QAAA,MAAEvB,GAAe,EAAA;MAC7B,aAAU,AAAAuB,EAAA,QAAA,MAAEvB,GAAe,EAAA;;;;;;KAE9BxI,EAmJK,MAAA;MAlJF,IAAIP,EAAAgK,CAAA;MACJ,KAAKhK,EAAA+F,CAAA,CAAO,CAAC;MACb,mBAAiBlC,EAAM,UAAU,QAAQ7D,EAAAuK,EAAA,CAAQ,CAACvK,EAAAgK,CAAA,CAAE,CAAA,CAAE,QAAQ,KAAA;MAC9D,oBAAkBhK,EAAAwK,EAAA,CAAI,CAAA;MACtB,OAAK3J,GAAEb,EAAA+F,CAAA,CAAO,CAAC,MAAM,KAAK;MAC1B,OAAKjF,EAAEd,EAAA2K,CAAA,CAAE,CAAA,cAAe3F,EAAA,MAAM,WAAM,KAAA,QAAA,CAAA;KAErC/E,GAAAA,EAAAA,EAAA,EAAA,GAAAQ,EA0IKM,IAAA,MAAAC,GAzIuBmF,GAAA,QAAY,EAA7B,SAAM,gBADjBlG,EAAA,GAAAQ,EA0IK,MAAA;MAxIF,KAAKsE;;MACL,KAAK/E,EAAA+F,CAAA,CAAO,CAAC;MACb,cAAYhB;MACZ,gBAAc/E,EAAA+F,CAAA,CAAO,CAAC,MAAM,MAAM,SAASf,EAAA,MAAM,SAAS,KAAA;MAC1D,iBAAehF,EAAA+F,CAAA,CAAO,CAAC,MAAM,MAAM,SAAShB,IAAK,IAAO,KAAA;MACxD,cAAY4B,EAAK;MACjB,WAAWE,EAAA;MACX,OAAK/F,EAAed,EAAA2K,CAAA,CAAE,CAAA,+CAAgD7E,EAAA,UAAcf,KAAK,YAAA,CAAA;MAGzF,cAAS,MAAE,EAAA,QAAYA;MACvB,YAAQ,AAAAuF,EAAA,QAAA,MAAEzD,EAAA,SAAaiE,EAAO,eAAc;MAC5C,SAAI,MAAEjC,GAAO9D,CAAK;MAClB,WAAO,AAAAuF,EAAA,QAAA,MAAE,EAAA,QAAS;KAEnBjK,GAAAA,CAAAA,EAwHcY,IAAA,EAxHA,MAAI,CAAGsF,EAAYxB,CAAK,EAAA,GAAA;MACpC,SAAA3E,QAiFM,CAjFNG,EAiFM,OAjFN4C,IAiFM;OA5EI0D,EAAA,SADR5G,EAAA,GAAAQ,EAMS,QAAA;;QAJP,OAAM;QACL,OAAOwE,EAAO,SAAA,CAAA,EAAa,SAAS,KAAA;QACrC,eAAY;OACX5E,GAAAA,CAAAA,EACDL,EAAAkB,CAAA,GAAA,EADQ,MAAM+D,EAAO,SAAA,CAAA,EAAa,QAAI,gBAAA,GAAA,MAAA,GAAA,CAAA,MAAA,CAAA,CAAA,GAAA,GAAA7B,EAAA,KAAAiH,EAAA,IAAA,EAAA;OAGbxG,EAAM,UAAU,eAAiCuC,EAAQO,EAAK,IAAI,CAAA,EAAG,QAAQ9C,EAAM,UAAU,MAAMkB,EAAK,EAAG,SADtI9E,EAAA,GAAAC,EAOEF,EAAAkB,CAAA,GAAA;;QAFC,MAAMkF,EAAQO,EAAK,IAAI,CAAA,EAAG,QAAQ9C,EAAM,UAAU,MAAMkB,EAAK,EAAG;QACjE,OAAM;;OAGAlB,EAAM,UAAU,gBADxB5D,EAAA,GAAAQ,EAIC,QAJD4C,IAIC7C,EADKR,EAAAiE,EAAA,CAAS,CAAC,OAAOc,IAAK,CAAA,CAAA,GAAA,CAAA,KAAAsF,EAAA,IAAA,EAAA;OAE5B9J,EAcO,QAdP+C,IAcO,CAZGO,EAAM,UAAU,eADxB5D,EAAA,GAAAQ,EAKC,QALD8C,IAKC/C,EADKR,EAAAoI,CAAA,CAAgB,CAACvE,EAAM,WAAWmB,EAAA,OAAOD,CAAK,CAAA,GAAA,CAAA,KAAAsF,EAAA,IAAA,EAAA,GAG5CxF,EAAaE,CAAK,CAAA,EAAG,WAD7B9E,EAAA,GAAAQ,EAMC,QAND+C,IAMChD,EADKqE,EAAaE,CAAK,CAAA,EAAG,OAAO,GAAA,CAAA,KAAAsF,EAAA,IAAA,EAAA,CAAA,CAAA;OAGpChK,EAyCoBc,IAAA;QAxCjB,SAASnB,EAAAuE,CAAA;QACT,YAAU,OAAOQ,CAAK;QACf;QACP,OAAOC,EAAA,MAAM;QACb,UAAU,MAAyBC,EAAQE,GAAMJ,CAAK;QACtD,iBAAekC,EAAA,SAAQ3B,EAAA,QAAS,KAAA,IAAYT,EAAaE,CAAK,CAAA,EAAG;QACjE,SAAS+B,EAAA,SAAO,CAAKG,EAAA;QACrB,aAAWP,EAASC,CAAI,KAAA,CAAMM,EAAA;QAC9B,cAAYR,EAAA,SAAS,CAAKQ,EAAA;QAC1B,aAAa,EAAQpD,EAAM,UAAU;QACrC,WAAW0C,EAAYxB,CAAK;QAC5B,QAAM/E,EAAAqH,CAAA,CAAK,CAAC;QACZ,UAAK,MAAEC,GAAUvC,CAAK;QACtB,WAAM,MAAEwC,GAAWxC,CAAK;QACxB,WAAM,MAAE,EAAA,QAAS;SAAA,GAAQY,EAAA;UAAYZ,IAAK,CAAIwB,EAAYxB,CAAK;QAAA;;QAEhE,SAAA3E,QAWW,CAVH6I,GAAWlE,CAAK,KAAA,CAAMO,EAAA,SAD9BrF,EAAA,GAAAC,EAWWkB,GAAA;;SATT,MAAK;SACL,SAAQ;SACR,MAAK;SACJ,cAAYpB,EAAA+D,CAAA,CAAC,CAAA,sBAAA,CAAA,GAAA,YAAA;SACb,gBAAc,EAAQ8B,EAAA,MAAQd;SAC9B,UAAK,MAAEoE,GAAcpE,CAAK;;SAC1B,SAAA3E,QAGD,CAHCC,EAGDL,EAAAkB,CAAA,GAAA;UAFE,MAAK;UACJ,UAAUlB,EAAA+K,EAAA;;;;;;;QAGP,CAAA,KAAAV,EAAA,IAAA,EAAA,GAAApD,EAAA,SADRhH,EAAA,GAAAC,EAWEmB,IAAA;;SATC,OAAOrB,EAAAoI,CAAA,CAAgB,CAACvE,EAAM,WAAWmB,EAAA,OAAOD,CAAK,KAAA;SACrD,UAAU,MAAyBE,EAAQE,GAAMJ,CAAK;SACtD,eAAa+B,EAAA,SAAW/B,IAAK;SAC7B,iBAAe+B,EAAA,SAAW/B,IAAQC,EAAA,MAAM,SAAM;SAC9C,aAAW0B,EAASC,CAAI;SACxB,cAAYF,EAAA;SACZ,iBAAe5B,EAAaE,CAAK,CAAA,EAAG;SACpC,WAAS,MAAyBqC,GAAajC,GAAMJ,CAAK;SAC1D,UAAQ,MAAyByC,GAAYC,GAAQ1C,CAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAIjE1E,CAAAA,GAAAA,EAoCqBiB,IAAA,MAAA;OAnCnB,SAAAlB,QAyBM,CAxBE8I,GAAYnE,CAAK,KADzB9E,EAAA,GAAAQ,EAyBM,OAAA;;QAvBJ,aAAU;QACT,OAAKK,EAAEd,EAAA2K,CAAA,CAAE,CAAA,OAAQrB,EAAA,SAAe,gBAAA,CAAA;QAChC,MAAMA,EAAA,QAAe,WAAc,KAAA;QACnC,UAAUA,EAAA,QAAe,IAAO,KAAA;QAChC,cAA+BA,EAAA,QAAkBtJ,EAAA+D,CAAA,CAAC,CAAA,sBAAA,CAAA,GAAA,YAAA,IAA2C,KAAA;QAG7F,UAAK,MAAEuF,EAAA,SAAmBD,EAAWtE,CAAK;QAC1C,WAAO,CAAgBxD,EAAAC,GAAA,MAAA8H,EAAA,SAAmBD,EAAWtE,CAAK,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,OAAA,CAAA,GACnCxD,EAAAC,GAAA,MAAA8H,EAAA,SAAmBD,EAAWtE,CAAK,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,OAAA,CAAA,CAAA;OAE3DxE,GAAAA,CAAAA,EAWM,OAAA,EAXA,OAAKO,EAAEd,EAAA2K,CAAA,CAAE,CAAA,CAAE9G,EAAM,UAAU,uBAAmB,qBAAA,CAAA,EAAA,GAAA,CAG1CmF,EAAmBjE,CAAK,KAFhC9E,EAAA,GAAAC,EAMEuB,GALKuH,EAAmBjE,CAAK,CAAA,GAAA;;QAE5B,MAAM4B,EAAK;QACX,OAAOA,EAAK;QACL;;;;;OAIV1G,CAAAA,MAAAA,EAAA,GAAAQ,EAA0D,OAAA;;QAA9C,WAAQoE,EAAaE,CAAK,CAAA,EAAG,WAAO;OAGpD9E,GAAAA,MAAAA,GAAAA,EAAAA,EAAAA,GAAAA,CAAAA,CAAAA,GAAAA,IAAAA,EAAAA,MAAAA,EAAA,GAAAQ,EAQM,OARNkD,IAQM,CAPJtD,EAMEqB,IAAA;QALC,YAAY1B,EAAAmL,EAAA,CAAiB,CAACtH,EAAM,WAAWmB,EAAA,OAAOD,CAAK;QAC3D,SAA8BqB,EAAQO,EAAK,IAAI,CAAA,EAAG,WAA+B9C,EAAM,UAAU,MAAMkB,EAAK,EAAG,WAAO,EAAA,SAAA,EAAA;;;;;;KAW3HyB,EAAA,SAAUO,EAAA,SAAalD,EAAM,UAAU,OAAO,SAAM,KAAQmB,EAAA,MAAM,SAAM,KADhF/E,EAAA,GAAAC,EAceyB,IAAA;;MAZZ,WAAWkC,EAAM,UAAU;;MAE5B,SAAAzD,QASqB,CATrBC,EASqBuB,IAAA;OATA,WAAWiC,EAAM;OAAY,OAAOmB,EAAA;OAAQ,QAAM2D;;OACrE,SAAAvI,QAOC,CAPDC,EAOCe,GAAA;QANC,MAAK;QACL,SAAQ;QACR,MAAK;QACJ,UAAU2F,EAAA,OAAW;QACtB,aAAU;;QACT,SAAA3G,QAAsD,CAA1C2G,EAAA,OAAW,QAAvB9G,EAAA,GAAAC,EAAsDF,EAAAkB,CAAA,GAAA;;SAAxB,MAAM6F,EAAA,MAAU;QAAQlF,GAAAA,MAAAA,GAAAA,CAAAA,MAAAA,CAAAA,KAAAA,EAAAA,IAAAA,EAAAA,GAAAA,GAAA,MAACrB,EAAGuG,EAAA,OAAW,KAAK,GAAA,CAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"BuilderField.vue_vue_type_script_setup_true_lang.js","names":["_resolveComponent","_unref","_openBlock","_createBlock","DeferredSchema","_withCtx","_createVNode","FieldWrapper","_createElementVNode","_toDisplayString","_createElementBlock","BuilderBlockPickerPanel","_createCommentVNode","CollapseAllActions","_normalizeStyle","_normalizeClass","_Fragment","_renderList","Collapsible","Icon","ItemHeaderActions","Button","BuilderBlockActionsMenu","CollapsibleContent","_withKeys","_withModifiers","_resolveDynamicComponent","SchemaRenderer","AddActionRow","BuilderBlockPicker","_createTextVNode","Icon_default","Button_default","SchemaRenderer_default","DeferredSchema_default","FieldWrapper_default","Collapsible_default","CollapsibleContent_default","AddActionRow_default","CollapseAllActions_default","ItemHeaderActions_default","BuilderBlockActionsMenu_default","BuilderBlockPickerPanel_default","BuilderBlockPicker_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","BuilderField_vue_vue_type_script_setup_true_lang_default","props","__props","t","useT","formatter","useFormatter","registry","useOptionalRegistry","form","useOptionalFormStore","context","useFieldActionContext","runItemAction","useRunItemAction","fallbacks","useItemActionFallbacks","serverItemOf","server","index","items","resolve","resolveItemAction","name","fieldDisabled","useField","locked","computed","readOnly","builderItems","value","collapsed","ref","editing","dragIndex","virtual","useVirtualList","virtualizeOptions","VIRTUAL_ESTIMATES","visibleItems","blockOf","block","type","isCollapsed","canAdd","canDelete","canClone","item","canAddBuilderBlock","draggable","buttons","addAction","showEmptyPicker","menu","runner","useActionRunner","onMenuAction","moves","cloneItem","removeItem","onMenuExtra","action","itemActionContext","update","setValue","next","moveItem","moveRepeaterItem","from","to","moveIndexedState","useItemMoves","builderItemLabel","moveAnnouncement","moveContainer","removeRepeaterItem","removeIndexedState","cloneRepeaterItem","cloneIndexedState","addBlock","insertBuilderItem","onDrop","reorderedItemKeys","setAllCollapsed","refreshSummaries","indexes","toggleCollapsed","collapse","previewComponentOf","hasPreview","showPreview","toggleEditing","open","openEditor","previewIsButton","_component_BuilderField","resolveComponent","unref","isDeferredPlaceholder","openBlock","createBlock","withCtx","loaded","createVNode","id","error","createElementVNode","toDisplayString","createElementBlock","createCommentVNode","_cache","fieldIds","aria","normalizeStyle","normalizeClass","cn","Fragment","renderList","$event","PencilIcon","withKeys","withModifiers","resolveDynamicComponent","builderItemSchema","createTextVNode"],"sources":["../../../src/components/fields/BuilderField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { BuilderBlockSchema, BuilderItemState, BuilderSchema } from '@alfakit/core'\nimport {\n isDeferredPlaceholder,\n VIRTUAL_ESTIMATES,\n builderItemLabel,\n builderItems,\n builderItemSchema,\n canAddBuilderBlock,\n cloneIndexedState,\n cloneRepeaterItem,\n fieldIds,\n insertBuilderItem,\n itemActionContext,\n moveIndexedState,\n moveRepeaterItem,\n removeIndexedState,\n removeRepeaterItem,\n reorderedItemKeys,\n resolveItemAction,\n virtualizeOptions,\n type ActionSchema,\n type ItemActionName,\n} from '@alfakit/core'\nimport { PencilIcon } from '@lucide/vue'\nimport { computed, ref } from 'vue'\nimport { useScrollToErroredItem, useVirtualList } from '../../composables/use-virtual-list'\nimport { useActionRunner, useFormatter, useOptionalRegistry, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport DeferredSchema from '../layout/DeferredSchema.vue'\nimport { useField, useOptionalFormStore } from '../form/form-context'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport SchemaRenderer from '../schema/SchemaRenderer.vue'\nimport Button from '../ui/Button.vue'\nimport Collapsible from '../ui/Collapsible.vue'\nimport CollapsibleContent from '../ui/CollapsibleContent.vue'\nimport AddActionRow from './AddActionRow.vue'\nimport CollapseAllActions from './CollapseAllActions.vue'\nimport ItemHeaderActions from './ItemHeaderActions.vue'\nimport { useItemActionFallbacks, useItemMoves, useRunItemAction } from './item-actions'\nimport BuilderBlockActionsMenu from './BuilderBlockActionsMenu.vue'\nimport BuilderBlockPicker from './BuilderBlockPicker.vue'\nimport BuilderBlockPickerPanel from './BuilderBlockPickerPanel.vue'\n\n/**\n * A list of blocks, each with its own schema. Items use the server's per-item schema\n * when it describes the same block, else the block's template (fresh, moved or cloned\n * items the server has not seen yet).\n */\nconst props = defineProps<{ component: BuilderSchema }>()\nconst t = useT()\nconst formatter = useFormatter()\nconst registry = useOptionalRegistry()\nconst form = useOptionalFormStore()\nconst context = useFieldActionContext(() => props.component)\nconst runItemAction = useRunItemAction(context)\nconst fallbacks = useItemActionFallbacks('builder')\nconst serverItemOf = (index: number) => {\n const server = props.component.items[index]\n\n return server && server.type === items.value[index]?.type ? server : undefined\n}\nconst resolve = (name: ItemActionName, index?: number) =>\n resolveItemAction(\n name,\n props.component,\n index === undefined ? undefined : serverItemOf(index),\n name === 'add' ? { ...fallbacks.add, label: props.component.addActionLabel } : fallbacks[name],\n )\n\nconst {\n value,\n setValue,\n error,\n id,\n disabled: fieldDisabled,\n readOnly,\n aria,\n} = useField<unknown>(() => props.component)\nconst locked = computed(() => fieldDisabled.value || readOnly.value)\nconst items = computed(() => builderItems(value.value))\nconst collapsed = ref<Record<number, boolean>>({})\nconst editing = ref<Record<number, boolean>>({})\nconst dragIndex = ref<number | null>(null)\n// Long lists render only the visible blocks (plus a few around them).\nconst virtual = useVirtualList(\n () => items.value.length,\n () => virtualizeOptions(props.component.virtualize, VIRTUAL_ESTIMATES.item, 12),\n)\nuseScrollToErroredItem(virtual, () => props.component.statePath)\nconst visibleItems = computed(() =>\n virtual.indexes.value.map((index) => ({ item: items.value[index] as BuilderItemState, index })),\n)\n\nconst blockOf = (type: string) => props.component.blocks.find((block) => block.name === type)\nconst isCollapsed = (index: number) => collapsed.value[index] ?? props.component.collapsed ?? false\nconst canAdd = computed(\n () =>\n props.component.addable &&\n !locked.value &&\n (props.component.maxItems == null || items.value.length < props.component.maxItems),\n)\nconst canDelete = computed(\n () =>\n props.component.deletable &&\n !locked.value &&\n (props.component.minItems == null || items.value.length > props.component.minItems),\n)\nconst canClone = (item: BuilderItemState) => {\n const block = blockOf(item.type)\n\n return Boolean(\n props.component.cloneable &&\n canAdd.value &&\n block &&\n canAddBuilderBlock(props.component, block, items.value),\n )\n}\n\nconst draggable = computed(\n () =>\n props.component.reorderable &&\n props.component.reorderableWithDragAndDrop !== false &&\n !locked.value &&\n resolve('reorder') !== null,\n)\nconst buttons = computed(\n () =>\n props.component.reorderable &&\n props.component.reorderableWithButtons !== false &&\n !locked.value,\n)\nconst addAction = computed(() => resolve('add'))\nconst showEmptyPicker = computed(\n () =>\n canAdd.value &&\n addAction.value !== null &&\n !addAction.value.disabled &&\n props.component.blocks.length > 0,\n)\nconst menu = computed(() => Boolean(props.component.blockActionsMenu) && !locked.value)\nconst runner = useActionRunner()\n// `blockActionsMenu()`: built-in actions run in the browser unless customised.\nconst onMenuAction = (name: ItemActionName, index: number) =>\n runItemAction(resolve(name, index), { item: String(index) }, () => {\n if (name === 'moveUp') moves.moveWithButton(index, index - 1)\n else if (name === 'moveDown') moves.moveWithButton(index, index + 1)\n else if (name === 'clone') cloneItem(index)\n else if (name === 'delete') removeItem(index)\n })\nconst onMenuExtra = (action: ActionSchema, index: number) =>\n void runner.trigger(action, itemActionContext(context.value, { item: String(index) }))\n\nconst update = (next: unknown[]) => setValue(next as BuilderItemState[])\nconst moveItem = (from: number, to: number) => {\n update(moveRepeaterItem(items.value, from, to))\n collapsed.value = moveIndexedState(collapsed.value, from, to)\n editing.value = moveIndexedState(editing.value, from, to)\n}\n// Move buttons: announced, one at a time, focus kept on the moved item.\nconst moves = useItemMoves(\n value,\n (from, to) => moveItem(from, to),\n (index) => builderItemLabel(props.component, items.value, index),\n () => items.value.length,\n)\nconst moveAnnouncement = moves.announcement\nconst moveContainer = moves.container\nconst removeItem = (index: number) => {\n update(removeRepeaterItem(items.value, index))\n collapsed.value = removeIndexedState(collapsed.value, index)\n editing.value = removeIndexedState(editing.value, index)\n}\nconst cloneItem = (index: number) => {\n update(cloneRepeaterItem(items.value, index))\n collapsed.value = cloneIndexedState(collapsed.value, index)\n editing.value = cloneIndexedState(editing.value, index)\n}\nconst addBlock = (block: BuilderBlockSchema) =>\n runItemAction(addAction.value, { block: block.name }, () =>\n update(insertBuilderItem(items.value, block)),\n )\n// A drop runs the reorder action: in the browser, or on the server when customised.\nconst onDrop = (index: number) => {\n const from = dragIndex.value\n dragIndex.value = null\n if (from === null || from === index) return\n runItemAction(\n resolve('reorder'),\n { items: reorderedItemKeys(items.value.length, from, index) },\n () => moveItem(from, index),\n )\n}\nconst setAllCollapsed = (value: boolean) => {\n collapsed.value = Object.fromEntries(items.value.map((_, index) => [index, value]))\n if (value) refreshSummaries(items.value.map((_, index) => index))\n}\n// Summaries are made on the server: collapsing items whose block has one brings them up\n// to date with the edits.\nconst refreshSummaries = (indexes: number[]) => {\n if (indexes.some((index) => blockOf(items.value[index]?.type ?? '')?.summary)) {\n void form?.refresh(props.component.statePath)\n }\n}\nconst toggleCollapsed = (index: number) => {\n const collapse = !isCollapsed(index)\n collapsed.value = { ...collapsed.value, [index]: collapse }\n if (collapse) refreshSummaries([index])\n}\n\n// Previews (`blockPreviews()`): server HTML or a registered `builder-preview:{name}`.\nconst previewComponentOf = (index: number) => {\n const name =\n serverItemOf(index)?.previewComponent ??\n blockOf(items.value[index]?.type ?? '')?.previewComponent\n\n return name ? registry?.resolve(`builder-preview:${name}`) : undefined\n}\nconst hasPreview = (index: number) =>\n props.component.blockPreviews === true &&\n (previewComponentOf(index) !== undefined || Boolean(serverItemOf(index)?.preview))\nconst showPreview = (index: number) => hasPreview(index) && !editing.value[index]\n// Closing the editor re-renders the previews from the server.\nconst toggleEditing = (index: number) => {\n const open = !editing.value[index]\n editing.value = { ...editing.value, [index]: open }\n if (!open) void form?.refresh(props.component.statePath)\n}\nconst openEditor = (index: number) => {\n if (!locked.value) toggleEditing(index)\n}\nconst previewIsButton = computed(() => !props.component.interactivePreviews && !locked.value)\n</script>\n\n<template>\n <!-- `deferLoading()`: a skeleton until the field scrolls into view. -->\n <DeferredSchema\n v-if=\"isDeferredPlaceholder(props.component)\"\n v-slot=\"{ component: loaded }\"\n :component=\"props.component\"\n >\n <BuilderField :component=\"loaded\" />\n </DeferredSchema>\n <FieldWrapper v-else :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div ref=\"moveContainer\" class=\"flex flex-col gap-3\" data-slot=\"builder\">\n <span aria-live=\"polite\" aria-atomic=\"true\" class=\"sr-only\" data-slot=\"item-move-status\">{{\n moveAnnouncement\n }}</span>\n <!-- No blocks yet: the picker is right there instead of behind a button. -->\n <div\n v-if=\"items.length === 0 && showEmptyPicker\"\n data-slot=\"builder-empty\"\n class=\"bg-muted/30 flex flex-col gap-3 rounded-lg border border-dashed p-3 sm:p-4\"\n >\n <div class=\"px-1\">\n <p class=\"text-sm font-medium\">\n {{ t('builder.empty_heading', {}, 'Add your first block') }}\n </p>\n <p class=\"text-muted-foreground text-sm\">\n {{\n t(\n 'builder.empty_description',\n {},\n 'Pick one to start. You can add more, reorder and remove them at any time.',\n )\n }}\n </p>\n </div>\n <BuilderBlockPickerPanel\n :component=\"props.component\"\n :items=\"items\"\n class=\"bg-card max-h-[28rem] rounded-md border\"\n @pick=\"addBlock\"\n />\n </div>\n <p\n v-else-if=\"items.length === 0\"\n class=\"text-muted-foreground rounded-lg border border-dashed p-4 text-center text-sm\"\n >\n {{ t('builder.empty', {}, 'No blocks yet.') }}\n </p>\n <CollapseAllActions\n v-if=\"props.component.collapsible && items.length > 0\"\n :context=\"context\"\n :collapse-all=\"resolve('collapseAll')\"\n :expand-all=\"resolve('expandAll')\"\n @collapse-all=\"setAllCollapsed(true)\"\n @expand-all=\"setAllCollapsed(false)\"\n />\n <ul\n :id=\"id\"\n :ref=\"virtual.attach\"\n :aria-labelledby=\"props.component.label ? fieldIds(id).label : undefined\"\n :aria-describedby=\"aria['aria-describedby']\"\n :style=\"virtual.style.value\"\n :class=\"cn('grid gap-3', items.length === 0 && 'hidden')\"\n >\n <li\n v-for=\"{ item, index } in visibleItems\"\n :key=\"index\"\n :ref=\"virtual.item\"\n :data-index=\"index\"\n :aria-setsize=\"virtual.range.value.active ? items.length : undefined\"\n :aria-posinset=\"virtual.range.value.active ? index + 1 : undefined\"\n :data-block=\"item.type\"\n :draggable=\"draggable\"\n :class=\"\n cn('bg-card min-w-0 rounded-lg border shadow-xs', dragIndex === index && 'opacity-50')\n \"\n @dragstart=\"dragIndex = index\"\n @dragover=\"draggable && $event.preventDefault()\"\n @drop=\"onDrop(index)\"\n @dragend=\"dragIndex = null\"\n >\n <Collapsible :open=\"!isCollapsed(index)\">\n <div\n class=\"flex flex-wrap items-center gap-1 border-b px-2 py-1.5\"\n data-slot=\"item-header\"\n >\n <span\n v-if=\"draggable\"\n class=\"text-muted-foreground cursor-grab\"\n :title=\"resolve('reorder')?.label ?? undefined\"\n aria-hidden=\"true\"\n ><Icon :name=\"resolve('reorder')?.icon ?? 'grip-vertical'\"\n /></span>\n <Icon\n v-if=\"\n props.component.blockIcons &&\n (blockOf(item.type)?.icon ?? props.component.items[index]?.icon)\n \"\n :name=\"blockOf(item.type)?.icon ?? props.component.items[index]?.icon\"\n class=\"text-muted-foreground size-4\"\n />\n <span\n v-if=\"props.component.blockNumbers\"\n class=\"text-muted-foreground text-xs tabular-nums\"\n >{{ formatter.number(index + 1) }}</span\n >\n <span class=\"flex min-w-0 flex-1 items-baseline gap-2\">\n <span\n v-if=\"props.component.blockLabels\"\n dir=\"auto\"\n class=\"min-w-0 shrink-0 truncate text-sm font-medium sm:max-w-[60%]\"\n >{{ builderItemLabel(props.component, items, index) }}</span\n >\n <span\n v-if=\"serverItemOf(index)?.summary\"\n dir=\"auto\"\n class=\"text-muted-foreground min-w-0 truncate text-sm\"\n data-slot=\"builder-item-summary\"\n >{{ serverItemOf(index)?.summary }}</span\n >\n </span>\n <ItemHeaderActions\n :context=\"context\"\n :item-key=\"String(index)\"\n :index=\"index\"\n :count=\"items.length\"\n :resolve=\"(name: ItemActionName) => resolve(name, index)\"\n :extra-actions=\"menu || locked ? undefined : serverItemOf(index)?.actions\"\n :buttons=\"buttons && !menu\"\n :can-clone=\"canClone(item) && !menu\"\n :can-delete=\"canDelete && !menu\"\n :collapsible=\"Boolean(props.component.collapsible)\"\n :collapsed=\"isCollapsed(index)\"\n @move=\"moves.moveWithButton\"\n @clone=\"cloneItem(index)\"\n @delete=\"removeItem(index)\"\n @toggle=\"toggleCollapsed(index)\"\n >\n <Button\n v-if=\"hasPreview(index) && !locked\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('builder.edit_block', {}, 'Edit block')\"\n :aria-pressed=\"Boolean(editing[index])\"\n @click=\"toggleEditing(index)\"\n ><Icon\n name=\"alfakit::forms.components.builder.actions.edit\"\n :fallback=\"PencilIcon\"\n /></Button>\n <BuilderBlockActionsMenu\n v-if=\"menu\"\n :label=\"builderItemLabel(props.component, items, index) ?? ''\"\n :resolve=\"(name: ItemActionName) => resolve(name, index)\"\n :can-move-up=\"buttons && index > 0\"\n :can-move-down=\"buttons && index < items.length - 1\"\n :can-clone=\"canClone(item)\"\n :can-delete=\"canDelete\"\n :extra-actions=\"serverItemOf(index)?.actions\"\n @action=\"(name: ItemActionName) => onMenuAction(name, index)\"\n @extra=\"(action: ActionSchema) => onMenuExtra(action, index)\"\n />\n </ItemHeaderActions>\n </div>\n <CollapsibleContent>\n <div\n v-if=\"showPreview(index)\"\n data-slot=\"builder-preview\"\n :class=\"cn('p-4', previewIsButton && 'cursor-pointer')\"\n :role=\"previewIsButton ? 'button' : undefined\"\n :tabindex=\"previewIsButton ? 0 : undefined\"\n :aria-label=\"\n previewIsButton ? t('builder.edit_block', {}, 'Edit block') : undefined\n \"\n @click=\"previewIsButton && openEditor(index)\"\n @keydown.enter.prevent=\"previewIsButton && openEditor(index)\"\n @keydown.space.prevent=\"previewIsButton && openEditor(index)\"\n >\n <div :class=\"cn(!props.component.interactivePreviews && 'pointer-events-none')\">\n <component\n :is=\"previewComponentOf(index)\"\n v-if=\"previewComponentOf(index)\"\n :data=\"item.data\"\n :block=\"item.type\"\n :index=\"index\"\n />\n <!-- Server-rendered `Block::preview()` HTML (Blade escapes the data). -->\n <!-- eslint-disable-next-line vue/no-v-html -->\n <div v-else v-html=\"serverItemOf(index)?.preview ?? ''\" />\n </div>\n </div>\n <div v-else class=\"p-4\">\n <SchemaRenderer\n :components=\"builderItemSchema(props.component, items, index)\"\n :columns=\"\n blockOf(item.type)?.columns ??\n props.component.items[index]?.columns ?? { default: 1 }\n \"\n />\n </div>\n </CollapsibleContent>\n </Collapsible>\n </li>\n </ul>\n <AddActionRow\n v-if=\"canAdd && addAction && props.component.blocks.length > 0 && items.length > 0\"\n :alignment=\"props.component.addActionAlignment\"\n >\n <BuilderBlockPicker :component=\"props.component\" :items=\"items\" @pick=\"addBlock\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n :disabled=\"addAction?.disabled\"\n data-slot=\"builder-add\"\n ><Icon v-if=\"addAction?.icon\" :name=\"addAction.icon\" /> {{ addAction?.label }}</Button\n >\n </BuilderBlockPicker>\n </AddActionRow>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorPickerField.js","names":["_sfc_main","ColorPickerField_vue_vue_type_script_setup_true_lang_default","ColorPickerField_default"],"sources":["../../../src/components/fields/ColorPickerField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ColorPickerSchema } from '@alfakit/core'\nimport { colorAlpha, colorToHex, formatColor, withColorAlpha } from '@alfakit/core'\nimport { computed } from 'vue'\nimport { useField } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Input from '../ui/Input.vue'\nimport { useFormatter, useT } from '../../context'\n\nconst props = defineProps<{ component: ColorPickerSchema }>()\nconst { value, setValue, blur, error, id, disabled, readOnly, aria } = useField<string | null>(\n () => props.component,\n)\nconst isHex = computed(() => props.component.format === 'hex')\nconst swatch = computed(() => colorToHex(value.value) ?? '#000000')\nconst t = useT()\nconst formatter = useFormatter()\n// `rgba()`: the native swatch has no alpha, so a slider sets the opacity.\nconst alpha = computed(() => Math.round(colorAlpha(value.value) * 100))\nconst onAlpha = (event: Event) =>\n setValue(withColorAlpha(value.value, Number((event.target as HTMLInputElement).value) / 100))\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div class=\"flex items-center gap-2\">\n <label\n class=\"border-input relative size-9 shrink-0 cursor-pointer overflow-hidden rounded-md border shadow-xs\"\n :style=\"{ backgroundColor: value ?? 'transparent' }\"\n :aria-label=\"t('form.pick_color', {}, 'Pick a colour')\"\n >\n <input\n type=\"color\"\n :value=\"swatch\"\n :disabled=\"disabled || readOnly\"\n class=\"absolute inset-0 size-full cursor-pointer opacity-0\"\n @input=\"\n setValue(\n formatColor(($event.target as HTMLInputElement).value, props.component.format, value),\n )\n \"\n @blur=\"blur\"\n />\n </label>\n <Input\n :id=\"id\"\n :name=\"props.component.name\"\n :model-value=\"value ?? ''\"\n :placeholder=\"props.component.placeholder ?? (isHex ? '#000000' : props.component.format)\"\n :disabled=\"disabled\"\n :readonly=\"readOnly\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n class=\"font-mono\"\n v-bind=\"props.component.extraInputAttributes\"\n @update:model-value=\"setValue($event)\"\n @blur=\"blur\"\n />\n </div>\n <div\n v-if=\"props.component.format === 'rgba'\"\n data-slot=\"color-picker-alpha\"\n class=\"mt-2 flex items-center gap-2\"\n >\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n :value=\"alpha\"\n :disabled=\"disabled || readOnly\"\n :aria-label=\"t('form.color_opacity', {}, 'Opacity')\"\n :aria-valuetext=\"`${alpha}%`\"\n class=\"accent-primary h-2 flex-1 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\"\n @input=\"onAlpha\"\n @blur=\"blur\"\n />\n <span class=\"text-muted-foreground w-10 text-end text-xs tabular-nums\">{{\n formatter.percent(alpha / 100)\n }}</span>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ColorPickerField.js","names":["_sfc_main","ColorPickerField_vue_vue_type_script_setup_true_lang_default","ColorPickerField_default"],"sources":["../../../src/components/fields/ColorPickerField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ColorPickerSchema } from '@alfakit/core'\nimport { colorAlpha, colorToHex, formatColor, withColorAlpha } from '@alfakit/core'\nimport { computed } from 'vue'\nimport { useField } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Input from '../ui/Input.vue'\nimport { useFormatter, useT } from '../../context'\n\nconst props = defineProps<{ component: ColorPickerSchema }>()\nconst { value, setValue, blur, error, id, disabled, readOnly, aria } = useField<string | null>(\n () => props.component,\n)\nconst isHex = computed(() => props.component.format === 'hex')\nconst swatch = computed(() => colorToHex(value.value) ?? '#000000')\nconst t = useT()\nconst formatter = useFormatter()\n// `rgba()`: the native swatch has no alpha, so a slider sets the opacity.\nconst alpha = computed(() => Math.round(colorAlpha(value.value) * 100))\nconst onAlpha = (event: Event) =>\n setValue(withColorAlpha(value.value, Number((event.target as HTMLInputElement).value) / 100))\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div class=\"flex items-center gap-2\">\n <label\n class=\"border-input relative size-9 shrink-0 cursor-pointer overflow-hidden rounded-md border shadow-xs\"\n :style=\"{ backgroundColor: value ?? 'transparent' }\"\n :aria-label=\"t('form.pick_color', {}, 'Pick a colour')\"\n >\n <input\n type=\"color\"\n :value=\"swatch\"\n :disabled=\"disabled || readOnly\"\n class=\"absolute inset-0 size-full cursor-pointer opacity-0\"\n @input=\"\n setValue(\n formatColor(($event.target as HTMLInputElement).value, props.component.format, value),\n )\n \"\n @blur=\"blur\"\n />\n </label>\n <Input\n :id=\"id\"\n :name=\"props.component.name\"\n dir=\"ltr\"\n :model-value=\"value ?? ''\"\n :placeholder=\"props.component.placeholder ?? (isHex ? '#000000' : props.component.format)\"\n :disabled=\"disabled\"\n :readonly=\"readOnly\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n class=\"font-mono\"\n v-bind=\"props.component.extraInputAttributes\"\n @update:model-value=\"setValue($event)\"\n @blur=\"blur\"\n />\n </div>\n <div\n v-if=\"props.component.format === 'rgba'\"\n data-slot=\"color-picker-alpha\"\n class=\"mt-2 flex items-center gap-2\"\n >\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n :value=\"alpha\"\n :disabled=\"disabled || readOnly\"\n :aria-label=\"t('form.color_opacity', {}, 'Opacity')\"\n :aria-valuetext=\"`${alpha}%`\"\n class=\"accent-primary h-2 flex-1 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\"\n @input=\"onAlpha\"\n @blur=\"blur\"\n />\n <span class=\"text-muted-foreground w-10 text-end text-xs tabular-nums\">{{\n formatter.percent(alpha / 100)\n }}</span>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorPickerField.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ColorPickerField.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ColorPickerField.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ColorPickerField.vue"],"names":[],"mappings":"AAuFA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAQtD,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,iBAAiB,CAAA;CAAE,CAAC;;AAqMpD,wBAMG"}
|
|
@@ -38,6 +38,7 @@ var S = { class: "flex items-center gap-2" }, C = ["aria-label"], w = ["value",
|
|
|
38
38
|
}, null, 40, w)], 12, C), m(r, g({
|
|
39
39
|
id: b(N),
|
|
40
40
|
name: O.component.name,
|
|
41
|
+
dir: "ltr",
|
|
41
42
|
"model-value": b(k) ?? "",
|
|
42
43
|
placeholder: O.component.placeholder ?? (L.value ? "#000000" : O.component.format),
|
|
43
44
|
disabled: b(P),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorPickerField.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","FieldWrapper","_unref","_withCtx","_createElementVNode","_normalizeStyle","_createVNode","Input","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","Input_default","FieldWrapper_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","ColorPickerField_vue_vue_type_script_setup_true_lang_default","props","__props","useField","isHex","computed","swatch","colorToHex","value","t","useT","formatter","useFormatter","alpha","colorAlpha","onAlpha","setValue","withColorAlpha","event","openBlock","createBlock","unref","id","error","withCtx","createElementVNode","normalizeStyle","disabled","readOnly","_cache","formatColor","$event","args","blur","createVNode","mergeProps","aria","createElementBlock","toDisplayString","createCommentVNode"],"sources":["../../../src/components/fields/ColorPickerField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ColorPickerSchema } from '@alfakit/core'\nimport { colorAlpha, colorToHex, formatColor, withColorAlpha } from '@alfakit/core'\nimport { computed } from 'vue'\nimport { useField } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Input from '../ui/Input.vue'\nimport { useFormatter, useT } from '../../context'\n\nconst props = defineProps<{ component: ColorPickerSchema }>()\nconst { value, setValue, blur, error, id, disabled, readOnly, aria } = useField<string | null>(\n () => props.component,\n)\nconst isHex = computed(() => props.component.format === 'hex')\nconst swatch = computed(() => colorToHex(value.value) ?? '#000000')\nconst t = useT()\nconst formatter = useFormatter()\n// `rgba()`: the native swatch has no alpha, so a slider sets the opacity.\nconst alpha = computed(() => Math.round(colorAlpha(value.value) * 100))\nconst onAlpha = (event: Event) =>\n setValue(withColorAlpha(value.value, Number((event.target as HTMLInputElement).value) / 100))\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div class=\"flex items-center gap-2\">\n <label\n class=\"border-input relative size-9 shrink-0 cursor-pointer overflow-hidden rounded-md border shadow-xs\"\n :style=\"{ backgroundColor: value ?? 'transparent' }\"\n :aria-label=\"t('form.pick_color', {}, 'Pick a colour')\"\n >\n <input\n type=\"color\"\n :value=\"swatch\"\n :disabled=\"disabled || readOnly\"\n class=\"absolute inset-0 size-full cursor-pointer opacity-0\"\n @input=\"\n setValue(\n formatColor(($event.target as HTMLInputElement).value, props.component.format, value),\n )\n \"\n @blur=\"blur\"\n />\n </label>\n <Input\n :id=\"id\"\n :name=\"props.component.name\"\n :model-value=\"value ?? ''\"\n :placeholder=\"props.component.placeholder ?? (isHex ? '#000000' : props.component.format)\"\n :disabled=\"disabled\"\n :readonly=\"readOnly\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n class=\"font-mono\"\n v-bind=\"props.component.extraInputAttributes\"\n @update:model-value=\"setValue($event)\"\n @blur=\"blur\"\n />\n </div>\n <div\n v-if=\"props.component.format === 'rgba'\"\n data-slot=\"color-picker-alpha\"\n class=\"mt-2 flex items-center gap-2\"\n >\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n :value=\"alpha\"\n :disabled=\"disabled || readOnly\"\n :aria-label=\"t('form.color_opacity', {}, 'Opacity')\"\n :aria-valuetext=\"`${alpha}%`\"\n class=\"accent-primary h-2 flex-1 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\"\n @input=\"onAlpha\"\n @blur=\"blur\"\n />\n <span class=\"text-muted-foreground w-10 text-end text-xs tabular-nums\">{{\n formatter.percent(alpha / 100)\n }}</span>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EASA,IAAMsB,IAAQC,GACR,EAAE,UAAO,aAAU,SAAM,UAAO,OAAI,aAAU,aAAU,YAASC,QAC/DF,EAAM,SACd,GACMG,IAAQC,QAAeJ,EAAM,UAAU,WAAW,KAAK,GACvDK,IAASD,QAAeE,EAAWC,EAAM,KAAK,KAAK,SAAS,GAC5DC,IAAIC,EAAK,GACTC,IAAYC,EAAa,GAEzBC,IAAQR,QAAe,KAAK,MAAMS,EAAWN,EAAM,KAAK,IAAI,GAAG,CAAC,GAChEO,KAAW,MACfC,EAASC,EAAeT,EAAM,OAAO,OAAQU,EAAM,OAA4B,KAAK,IAAI,GAAG,CAAC;oBAI5FvC,EAAA,GAAAC,
|
|
1
|
+
{"version":3,"file":"ColorPickerField.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","FieldWrapper","_unref","_withCtx","_createElementVNode","_normalizeStyle","_createVNode","Input","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","Input_default","FieldWrapper_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","ColorPickerField_vue_vue_type_script_setup_true_lang_default","props","__props","useField","isHex","computed","swatch","colorToHex","value","t","useT","formatter","useFormatter","alpha","colorAlpha","onAlpha","setValue","withColorAlpha","event","openBlock","createBlock","unref","id","error","withCtx","createElementVNode","normalizeStyle","disabled","readOnly","_cache","formatColor","$event","args","blur","createVNode","mergeProps","aria","createElementBlock","toDisplayString","createCommentVNode"],"sources":["../../../src/components/fields/ColorPickerField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ColorPickerSchema } from '@alfakit/core'\nimport { colorAlpha, colorToHex, formatColor, withColorAlpha } from '@alfakit/core'\nimport { computed } from 'vue'\nimport { useField } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Input from '../ui/Input.vue'\nimport { useFormatter, useT } from '../../context'\n\nconst props = defineProps<{ component: ColorPickerSchema }>()\nconst { value, setValue, blur, error, id, disabled, readOnly, aria } = useField<string | null>(\n () => props.component,\n)\nconst isHex = computed(() => props.component.format === 'hex')\nconst swatch = computed(() => colorToHex(value.value) ?? '#000000')\nconst t = useT()\nconst formatter = useFormatter()\n// `rgba()`: the native swatch has no alpha, so a slider sets the opacity.\nconst alpha = computed(() => Math.round(colorAlpha(value.value) * 100))\nconst onAlpha = (event: Event) =>\n setValue(withColorAlpha(value.value, Number((event.target as HTMLInputElement).value) / 100))\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div class=\"flex items-center gap-2\">\n <label\n class=\"border-input relative size-9 shrink-0 cursor-pointer overflow-hidden rounded-md border shadow-xs\"\n :style=\"{ backgroundColor: value ?? 'transparent' }\"\n :aria-label=\"t('form.pick_color', {}, 'Pick a colour')\"\n >\n <input\n type=\"color\"\n :value=\"swatch\"\n :disabled=\"disabled || readOnly\"\n class=\"absolute inset-0 size-full cursor-pointer opacity-0\"\n @input=\"\n setValue(\n formatColor(($event.target as HTMLInputElement).value, props.component.format, value),\n )\n \"\n @blur=\"blur\"\n />\n </label>\n <Input\n :id=\"id\"\n :name=\"props.component.name\"\n dir=\"ltr\"\n :model-value=\"value ?? ''\"\n :placeholder=\"props.component.placeholder ?? (isHex ? '#000000' : props.component.format)\"\n :disabled=\"disabled\"\n :readonly=\"readOnly\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n class=\"font-mono\"\n v-bind=\"props.component.extraInputAttributes\"\n @update:model-value=\"setValue($event)\"\n @blur=\"blur\"\n />\n </div>\n <div\n v-if=\"props.component.format === 'rgba'\"\n data-slot=\"color-picker-alpha\"\n class=\"mt-2 flex items-center gap-2\"\n >\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n :value=\"alpha\"\n :disabled=\"disabled || readOnly\"\n :aria-label=\"t('form.color_opacity', {}, 'Opacity')\"\n :aria-valuetext=\"`${alpha}%`\"\n class=\"accent-primary h-2 flex-1 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\"\n @input=\"onAlpha\"\n @blur=\"blur\"\n />\n <span class=\"text-muted-foreground w-10 text-end text-xs tabular-nums\">{{\n formatter.percent(alpha / 100)\n }}</span>\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboboxSelect.js","names":["_sfc_main","ComboboxSelect_vue_vue_type_script_setup_true_lang_default","ComboboxSelect_default"],"sources":["../../../src/components/fields/ComboboxSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FieldAriaAttributes, Option, OptionOrGroup, SelectSchema } from '@alfakit/core'\nimport { debounce, htmlLabelToText, isOptionGroup } from '@alfakit/core'\nimport { CheckIcon, ChevronsUpDownIcon, XIcon } from '@lucide/vue'\nimport { computed, ref, watch } from 'vue'\nimport { useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useFormStore } from '../form/form-context'\nimport Badge from '../ui/Badge.vue'\nimport Button from '../ui/Button.vue'\nimport Command from '../ui/Command.vue'\nimport CommandEmpty from '../ui/CommandEmpty.vue'\nimport CommandGroup from '../ui/CommandGroup.vue'\nimport CommandInput from '../ui/CommandInput.vue'\nimport CommandItem from '../ui/CommandItem.vue'\nimport CommandList from '../ui/CommandList.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport OptionLabel from './OptionLabel.vue'\nimport SelectReorderList from './SelectReorderList.vue'\n\nfunction flatten(options: OptionOrGroup[]): Option[] {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nconst props = defineProps<{\n component: SelectSchema\n value: string | number | Array<string | number> | null | undefined\n setValue: (value: string | number | Array<string | number> | null) => void\n id: string\n disabled: boolean\n readOnly?: boolean\n error?: string\n aria?: FieldAriaAttributes\n}>()\n\nconst t = useT()\nconst store = useFormStore()\nconst open = ref(false)\nconst search = ref('')\nconst loading = ref(false)\nconst remoteOptions = ref<Option[] | null>(null)\nconst labels = ref<Record<string, string>>({ ...(props.component.selectedLabels ?? {}) })\nlet requestId = 0\n\n// Labels the server sends with a refresh win (e.g. after the edit option modal renamed\n// the selected record).\nwatch(\n () => props.component.selectedLabels,\n (incoming) => {\n if (incoming && Object.keys(incoming).length > 0) {\n labels.value = { ...labels.value, ...incoming }\n }\n },\n)\n\nconst selected = computed<string[]>(() =>\n Array.isArray(props.value)\n ? props.value.map(String)\n : props.value === null || props.value === undefined || props.value === ''\n ? []\n : [String(props.value)],\n)\nconst staticOptions = computed(() => flatten(props.component.options))\nconst options = computed(() => remoteOptions.value ?? staticOptions.value)\nconst isRemote = computed(() => Boolean(props.component.searchable))\n\nconst runSearch = debounce((term: string) => {\n if (!isRemote.value) {\n return\n }\n\n const current = ++requestId\n loading.value = true\n\n void store\n .searchOptions(props.component.statePath, term)\n .then((results) => {\n if (current === requestId) {\n remoteOptions.value = results\n }\n })\n .finally(() => {\n if (current === requestId) {\n loading.value = false\n }\n })\n}, props.component.searchDebounce ?? 300)\n\nwatch([open, search], ([isOpen, term]) => {\n if (isOpen && isRemote.value) {\n runSearch(term)\n }\n})\n\n// Resolve labels for values we have not seen yet (e.g. after a server refresh).\nwatch(\n selected,\n (keys) => {\n const missing = keys.filter(\n (key) => !labels.value[key] && !staticOptions.value.some((option) => option.value === key),\n )\n\n // Labels are fetched from the browser only, never during a server render.\n if (missing.length === 0 || !isRemote.value || typeof window === 'undefined') {\n return\n }\n\n void store.optionLabels(props.component.statePath, missing).then((resolved) => {\n labels.value = { ...labels.value, ...resolved }\n })\n },\n { immediate: true },\n)\n\nconst labelFor = (key: string) =>\n labels.value[key] ??\n options.value.find((option) => option.value === key)?.label ??\n staticOptions.value.find((option) => option.value === key)?.label ??\n key\n\nconst canRemove = computed(\n () => !props.component.minItems || selected.value.length > props.component.minItems,\n)\n\nconst toggle = (key: string, label: string) => {\n labels.value = { ...labels.value, [key]: label }\n\n if (props.component.multiple) {\n const next = selected.value.includes(key)\n ? selected.value.filter((item) => item !== key)\n : [...selected.value, key]\n\n if (props.component.maxItems && next.length > props.component.maxItems) {\n return\n }\n\n // `minItems()`: the last required selections cannot be removed.\n if (\n props.component.minItems &&\n next.length < selected.value.length &&\n next.length < props.component.minItems\n ) {\n return\n }\n\n props.setValue(next)\n } else {\n props.setValue(\n selected.value.includes(key) && props.component.selectablePlaceholder ? null : key,\n )\n open.value = false\n }\n}\n\nconst filtered = computed(() =>\n isRemote.value\n ? options.value\n : options.value.filter((option) =>\n (props.component.allowHtml ? htmlLabelToText(option.label) : option.label)\n .toLowerCase()\n .includes(search.value.toLowerCase()),\n ),\n)\n// Nothing to choose from at all (not a search without matches).\nconst isEmpty = computed(() => search.value === '' && options.value.length === 0)\n\nconst onOpenChange = (next: boolean) => {\n open.value = next\n\n if (!next) {\n search.value = ''\n store.blur(props.component.statePath)\n }\n}\n\n// `reorderable()`: chosen options are listed under the trigger, where they can be dragged\n// or moved with Alt + arrow keys (chips inside the trigger button can't hold controls).\nconst reorderable = computed(() => Boolean(props.component.multiple && props.component.reorderable))\nconst plainLabelFor = (key: string) =>\n props.component.allowHtml ? htmlLabelToText(labelFor(key)) : labelFor(key)\n\nconst removeTag = (event: Event, key: string) => {\n event.stopPropagation()\n props.setValue(selected.value.filter((item) => item !== key))\n}\n</script>\n\n<template>\n <Popover :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverTrigger as-child>\n <Button\n :id=\"props.id\"\n type=\"button\"\n variant=\"outline\"\n data-input=\"\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-invalid=\"props.error ? true : undefined\"\n :aria-describedby=\"props.aria?.['aria-describedby']\"\n :aria-required=\"props.aria?.['aria-required']\"\n :aria-readonly=\"props.readOnly || undefined\"\n :disabled=\"props.disabled || props.readOnly\"\n :class=\"\n cn(\n 'h-auto min-h-9 w-full justify-between px-3 py-1.5 font-normal hover:bg-transparent',\n props.error && 'border-destructive',\n )\n \"\n >\n <span class=\"flex flex-1 flex-wrap items-center gap-1 text-start\">\n <span v-if=\"selected.length === 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <span v-if=\"reorderable && selected.length > 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <template v-if=\"props.component.multiple && !reorderable\">\n <Badge v-for=\"key in selected\" :key=\"key\" variant=\"secondary\" class=\"gap-1 pe-1\">\n <span v-if=\"props.component.allowHtml\" v-html=\"labelFor(key)\" />\n <span v-else>{{ labelFor(key) }}</span>\n <span\n v-if=\"!props.disabled && !props.readOnly && canRemove\"\n role=\"button\"\n tabindex=\"-1\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n class=\"hover:bg-foreground/10 rounded-sm\"\n @click=\"removeTag($event, key)\"\n >\n <XIcon class=\"size-3\" />\n </span>\n </Badge>\n </template>\n <template v-else-if=\"!props.component.multiple && selected[0] !== undefined\">\n <span\n v-if=\"props.component.allowHtml\"\n class=\"truncate\"\n v-html=\"labelFor(selected[0])\"\n />\n <span v-else class=\"truncate\">{{ labelFor(selected[0]) }}</span>\n </template>\n </span>\n <Spinner v-if=\"loading\" class=\"opacity-50\" />\n <ChevronsUpDownIcon v-else class=\"size-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-(--reka-popover-trigger-width) p-0\" align=\"start\">\n <Command>\n <CommandInput\n :model-value=\"search\"\n :placeholder=\"\n props.component.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n \"\n @update:model-value=\"search = $event\"\n />\n <CommandList>\n <div\n v-if=\"loading && filtered.length === 0\"\n data-slot=\"select-loading\"\n class=\"text-muted-foreground flex items-center justify-center gap-2 py-6 text-sm\"\n >\n <Spinner />\n {{\n search !== ''\n ? (props.component.searchingMessage ?? t('form.searching', {}, 'Searching…'))\n : (props.component.loadingMessage ?? t('loading', {}, 'Loading…'))\n }}\n </div>\n <CommandEmpty v-else-if=\"filtered.length === 0\">{{\n isEmpty\n ? (props.component.noOptionsMessage ??\n t('form.no_options', {}, 'No options available.'))\n : (props.component.noSearchResultsMessage ?? t('no_results', {}, 'No results.'))\n }}</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filtered\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n @select=\"toggle(option.value, option.label)\"\n >\n <CheckIcon\n :class=\"cn('size-4', selected.includes(option.value) ? 'opacity-100' : 'opacity-0')\"\n />\n <OptionLabel\n :option=\"option\"\n :allow-html=\"props.component.allowHtml\"\n :wrap=\"props.component.wrapOptionLabels !== false\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <SelectReorderList\n v-if=\"reorderable\"\n :id=\"props.id\"\n :values=\"Array.isArray(props.value) ? props.value : []\"\n :label-for=\"plainLabelFor\"\n :locked=\"props.disabled || Boolean(props.readOnly)\"\n :can-remove=\"canRemove\"\n :on-change=\"props.setValue\"\n />\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ComboboxSelect.js","names":["_sfc_main","ComboboxSelect_vue_vue_type_script_setup_true_lang_default","ComboboxSelect_default"],"sources":["../../../src/components/fields/ComboboxSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FieldAriaAttributes, Option, OptionOrGroup, SelectSchema } from '@alfakit/core'\nimport { debounce, htmlLabelToText, isOptionGroup } from '@alfakit/core'\nimport { CheckIcon, ChevronsUpDownIcon, XIcon } from '@lucide/vue'\nimport { computed, ref, watch } from 'vue'\nimport { useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useFormStore } from '../form/form-context'\nimport Badge from '../ui/Badge.vue'\nimport Button from '../ui/Button.vue'\nimport Command from '../ui/Command.vue'\nimport CommandEmpty from '../ui/CommandEmpty.vue'\nimport CommandGroup from '../ui/CommandGroup.vue'\nimport CommandInput from '../ui/CommandInput.vue'\nimport CommandItem from '../ui/CommandItem.vue'\nimport CommandList from '../ui/CommandList.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport OptionLabel from './OptionLabel.vue'\nimport SelectReorderList from './SelectReorderList.vue'\n\nfunction flatten(options: OptionOrGroup[]): Option[] {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nconst props = defineProps<{\n component: SelectSchema\n value: string | number | Array<string | number> | null | undefined\n setValue: (value: string | number | Array<string | number> | null) => void\n id: string\n disabled: boolean\n readOnly?: boolean\n error?: string\n aria?: FieldAriaAttributes\n}>()\n\nconst t = useT()\nconst store = useFormStore()\nconst open = ref(false)\nconst search = ref('')\nconst loading = ref(false)\nconst remoteOptions = ref<Option[] | null>(null)\nconst labels = ref<Record<string, string>>({ ...(props.component.selectedLabels ?? {}) })\nlet requestId = 0\n\n// Labels the server sends with a refresh win (e.g. after the edit option modal renamed\n// the selected record).\nwatch(\n () => props.component.selectedLabels,\n (incoming) => {\n if (incoming && Object.keys(incoming).length > 0) {\n labels.value = { ...labels.value, ...incoming }\n }\n },\n)\n\nconst selected = computed<string[]>(() =>\n Array.isArray(props.value)\n ? props.value.map(String)\n : props.value === null || props.value === undefined || props.value === ''\n ? []\n : [String(props.value)],\n)\nconst staticOptions = computed(() => flatten(props.component.options))\nconst options = computed(() => remoteOptions.value ?? staticOptions.value)\nconst isRemote = computed(() => Boolean(props.component.searchable))\n\nconst runSearch = debounce((term: string) => {\n if (!isRemote.value) {\n return\n }\n\n const current = ++requestId\n loading.value = true\n\n void store\n .searchOptions(props.component.statePath, term)\n .then((results) => {\n if (current === requestId) {\n remoteOptions.value = results\n }\n })\n .finally(() => {\n if (current === requestId) {\n loading.value = false\n }\n })\n}, props.component.searchDebounce ?? 300)\n\nwatch([open, search], ([isOpen, term]) => {\n if (isOpen && isRemote.value) {\n runSearch(term)\n }\n})\n\n// Resolve labels for values we have not seen yet (e.g. after a server refresh).\nwatch(\n selected,\n (keys) => {\n const missing = keys.filter(\n (key) => !labels.value[key] && !staticOptions.value.some((option) => option.value === key),\n )\n\n // Labels are fetched from the browser only, never during a server render.\n if (missing.length === 0 || !isRemote.value || typeof window === 'undefined') {\n return\n }\n\n void store.optionLabels(props.component.statePath, missing).then((resolved) => {\n labels.value = { ...labels.value, ...resolved }\n })\n },\n { immediate: true },\n)\n\nconst labelFor = (key: string) =>\n labels.value[key] ??\n options.value.find((option) => option.value === key)?.label ??\n staticOptions.value.find((option) => option.value === key)?.label ??\n key\n\nconst canRemove = computed(\n () => !props.component.minItems || selected.value.length > props.component.minItems,\n)\n\nconst toggle = (key: string, label: string) => {\n labels.value = { ...labels.value, [key]: label }\n\n if (props.component.multiple) {\n const next = selected.value.includes(key)\n ? selected.value.filter((item) => item !== key)\n : [...selected.value, key]\n\n if (props.component.maxItems && next.length > props.component.maxItems) {\n return\n }\n\n // `minItems()`: the last required selections cannot be removed.\n if (\n props.component.minItems &&\n next.length < selected.value.length &&\n next.length < props.component.minItems\n ) {\n return\n }\n\n props.setValue(next)\n } else {\n props.setValue(\n selected.value.includes(key) && props.component.selectablePlaceholder ? null : key,\n )\n open.value = false\n }\n}\n\nconst filtered = computed(() =>\n isRemote.value\n ? options.value\n : options.value.filter((option) =>\n (props.component.allowHtml ? htmlLabelToText(option.label) : option.label)\n .toLowerCase()\n .includes(search.value.toLowerCase()),\n ),\n)\n// Nothing to choose from at all (not a search without matches).\nconst isEmpty = computed(() => search.value === '' && options.value.length === 0)\n\nconst onOpenChange = (next: boolean) => {\n open.value = next\n\n if (!next) {\n search.value = ''\n store.blur(props.component.statePath)\n }\n}\n\n// `reorderable()`: chosen options are listed under the trigger, where they can be dragged\n// or moved with Alt + arrow keys (chips inside the trigger button can't hold controls).\nconst reorderable = computed(() => Boolean(props.component.multiple && props.component.reorderable))\nconst plainLabelFor = (key: string) =>\n props.component.allowHtml ? htmlLabelToText(labelFor(key)) : labelFor(key)\n\nconst removeTag = (event: Event, key: string) => {\n event.stopPropagation()\n props.setValue(selected.value.filter((item) => item !== key))\n}\n</script>\n\n<template>\n <Popover :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverTrigger as-child>\n <Button\n :id=\"props.id\"\n type=\"button\"\n variant=\"outline\"\n data-input=\"\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-invalid=\"props.error ? true : undefined\"\n :aria-describedby=\"props.aria?.['aria-describedby']\"\n :aria-required=\"props.aria?.['aria-required']\"\n :aria-readonly=\"props.readOnly || undefined\"\n :disabled=\"props.disabled || props.readOnly\"\n :class=\"\n cn(\n 'h-auto min-h-9 w-full justify-between px-3 py-1.5 font-normal hover:bg-transparent',\n props.error && 'border-destructive',\n )\n \"\n >\n <span class=\"flex min-w-0 flex-1 flex-wrap items-center gap-1 text-start\">\n <span v-if=\"selected.length === 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <span v-if=\"reorderable && selected.length > 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <template v-if=\"props.component.multiple && !reorderable\">\n <Badge v-for=\"key in selected\" :key=\"key\" variant=\"secondary\" class=\"gap-1 pe-1\">\n <span v-if=\"props.component.allowHtml\" v-html=\"labelFor(key)\" />\n <span v-else>{{ labelFor(key) }}</span>\n <span\n v-if=\"!props.disabled && !props.readOnly && canRemove\"\n role=\"button\"\n tabindex=\"-1\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n class=\"hover:bg-foreground/10 rounded-sm\"\n @click=\"removeTag($event, key)\"\n >\n <XIcon class=\"size-3\" />\n </span>\n </Badge>\n </template>\n <template v-else-if=\"!props.component.multiple && selected[0] !== undefined\">\n <span\n v-if=\"props.component.allowHtml\"\n class=\"truncate\"\n v-html=\"labelFor(selected[0])\"\n />\n <span v-else class=\"truncate\">{{ labelFor(selected[0]) }}</span>\n </template>\n </span>\n <Spinner v-if=\"loading\" class=\"opacity-50\" />\n <ChevronsUpDownIcon v-else class=\"size-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-(--reka-popover-trigger-width) p-0\" align=\"start\">\n <Command>\n <CommandInput\n :model-value=\"search\"\n :placeholder=\"\n props.component.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n \"\n @update:model-value=\"search = $event\"\n />\n <CommandList>\n <div\n v-if=\"loading && filtered.length === 0\"\n data-slot=\"select-loading\"\n class=\"text-muted-foreground flex items-center justify-center gap-2 py-6 text-sm\"\n >\n <Spinner />\n {{\n search !== ''\n ? (props.component.searchingMessage ?? t('form.searching', {}, 'Searching…'))\n : (props.component.loadingMessage ?? t('loading', {}, 'Loading…'))\n }}\n </div>\n <CommandEmpty v-else-if=\"filtered.length === 0\">{{\n isEmpty\n ? (props.component.noOptionsMessage ??\n t('form.no_options', {}, 'No options available.'))\n : (props.component.noSearchResultsMessage ?? t('no_results', {}, 'No results.'))\n }}</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filtered\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n @select=\"toggle(option.value, option.label)\"\n >\n <CheckIcon\n :class=\"cn('size-4', selected.includes(option.value) ? 'opacity-100' : 'opacity-0')\"\n />\n <OptionLabel\n :option=\"option\"\n :allow-html=\"props.component.allowHtml\"\n :wrap=\"props.component.wrapOptionLabels !== false\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <SelectReorderList\n v-if=\"reorderable\"\n :id=\"props.id\"\n :values=\"Array.isArray(props.value) ? props.value : []\"\n :label-for=\"plainLabelFor\"\n :locked=\"props.disabled || Boolean(props.readOnly)\"\n :can-remove=\"canRemove\"\n :on-change=\"props.setValue\"\n />\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboboxSelect.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ComboboxSelect.vue"],"names":[],"mappings":"AAsTA,OAAO,KAAK,EAAE,mBAAmB,EAAyB,YAAY,EAAE,MAAM,eAAe,CAAA;AA0B7F,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,YAAY,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IAClE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1E,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAC3B,CAAC;;
|
|
1
|
+
{"version":3,"file":"ComboboxSelect.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ComboboxSelect.vue"],"names":[],"mappings":"AAsTA,OAAO,KAAK,EAAE,mBAAmB,EAAyB,YAAY,EAAE,MAAM,eAAe,CAAA;AA0B7F,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,YAAY,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IAClE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1E,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAC3B,CAAC;;AA6jBF,wBAMG"}
|
|
@@ -19,7 +19,7 @@ import { debounce as l, htmlLabelToText as u, isOptionGroup as d } from "@alfaki
|
|
|
19
19
|
import { Fragment as f, computed as p, createBlock as m, createCommentVNode as h, createElementBlock as g, createElementVNode as ce, createTextVNode as _, createVNode as v, defineComponent as y, normalizeClass as b, openBlock as x, ref as S, renderList as C, toDisplayString as w, unref as T, watch as E, withCtx as D } from "vue";
|
|
20
20
|
import { CheckIcon as le, ChevronsUpDownIcon as ue, XIcon as de } from "@lucide/vue";
|
|
21
21
|
//#region src/components/fields/ComboboxSelect.vue?vue&type=script&setup=true&lang.ts
|
|
22
|
-
var fe = { class: "flex flex-1 flex-wrap items-center gap-1 text-start" }, O = {
|
|
22
|
+
var fe = { class: "flex min-w-0 flex-1 flex-wrap items-center gap-1 text-start" }, O = {
|
|
23
23
|
key: 0,
|
|
24
24
|
class: "text-muted-foreground"
|
|
25
25
|
}, k = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboboxSelect.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","Popover","_withCtx","PopoverTrigger","Button","_normalizeClass","_unref","_createElementVNode","_toDisplayString","_createCommentVNode","_renderList","_createBlock","Badge","Spinner","PopoverContent","Command","CommandInput","CommandList","_createTextVNode","CommandEmpty","CommandGroup","CommandItem","OptionLabel","SelectReorderList","Badge_default","Button_default","Spinner_default","Popover_default","PopoverContent_default","PopoverTrigger_default","Command_default","CommandEmpty_default","CommandGroup_default","CommandInput_default","CommandItem_default","CommandList_default","OptionLabel_default","SelectReorderList_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","ComboboxSelect_vue_vue_type_script_setup_true_lang_default","flatten","options","isOptionGroup","option","props","__props","t","useT","store","useFormStore","open","ref","search","loading","remoteOptions","labels","requestId","incoming","selected","computed","staticOptions","isRemote","runSearch","debounce","current","term","results","watch","isOpen","missing","keys","key","resolved","labelFor","canRemove","toggle","label","next","item","filtered","htmlLabelToText","isEmpty","onOpenChange","reorderable","plainLabelFor","removeTag","event","openBlock","createElementBlock","Fragment","createVNode","withCtx","normalizeClass","unref","cn","createElementVNode","toDisplayString","createCommentVNode","renderList","createBlock","$event","XIcon","ChevronsUpDownIcon","_cache","createTextVNode","CheckIcon"],"sources":["../../../src/components/fields/ComboboxSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FieldAriaAttributes, Option, OptionOrGroup, SelectSchema } from '@alfakit/core'\nimport { debounce, htmlLabelToText, isOptionGroup } from '@alfakit/core'\nimport { CheckIcon, ChevronsUpDownIcon, XIcon } from '@lucide/vue'\nimport { computed, ref, watch } from 'vue'\nimport { useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useFormStore } from '../form/form-context'\nimport Badge from '../ui/Badge.vue'\nimport Button from '../ui/Button.vue'\nimport Command from '../ui/Command.vue'\nimport CommandEmpty from '../ui/CommandEmpty.vue'\nimport CommandGroup from '../ui/CommandGroup.vue'\nimport CommandInput from '../ui/CommandInput.vue'\nimport CommandItem from '../ui/CommandItem.vue'\nimport CommandList from '../ui/CommandList.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport OptionLabel from './OptionLabel.vue'\nimport SelectReorderList from './SelectReorderList.vue'\n\nfunction flatten(options: OptionOrGroup[]): Option[] {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nconst props = defineProps<{\n component: SelectSchema\n value: string | number | Array<string | number> | null | undefined\n setValue: (value: string | number | Array<string | number> | null) => void\n id: string\n disabled: boolean\n readOnly?: boolean\n error?: string\n aria?: FieldAriaAttributes\n}>()\n\nconst t = useT()\nconst store = useFormStore()\nconst open = ref(false)\nconst search = ref('')\nconst loading = ref(false)\nconst remoteOptions = ref<Option[] | null>(null)\nconst labels = ref<Record<string, string>>({ ...(props.component.selectedLabels ?? {}) })\nlet requestId = 0\n\n// Labels the server sends with a refresh win (e.g. after the edit option modal renamed\n// the selected record).\nwatch(\n () => props.component.selectedLabels,\n (incoming) => {\n if (incoming && Object.keys(incoming).length > 0) {\n labels.value = { ...labels.value, ...incoming }\n }\n },\n)\n\nconst selected = computed<string[]>(() =>\n Array.isArray(props.value)\n ? props.value.map(String)\n : props.value === null || props.value === undefined || props.value === ''\n ? []\n : [String(props.value)],\n)\nconst staticOptions = computed(() => flatten(props.component.options))\nconst options = computed(() => remoteOptions.value ?? staticOptions.value)\nconst isRemote = computed(() => Boolean(props.component.searchable))\n\nconst runSearch = debounce((term: string) => {\n if (!isRemote.value) {\n return\n }\n\n const current = ++requestId\n loading.value = true\n\n void store\n .searchOptions(props.component.statePath, term)\n .then((results) => {\n if (current === requestId) {\n remoteOptions.value = results\n }\n })\n .finally(() => {\n if (current === requestId) {\n loading.value = false\n }\n })\n}, props.component.searchDebounce ?? 300)\n\nwatch([open, search], ([isOpen, term]) => {\n if (isOpen && isRemote.value) {\n runSearch(term)\n }\n})\n\n// Resolve labels for values we have not seen yet (e.g. after a server refresh).\nwatch(\n selected,\n (keys) => {\n const missing = keys.filter(\n (key) => !labels.value[key] && !staticOptions.value.some((option) => option.value === key),\n )\n\n // Labels are fetched from the browser only, never during a server render.\n if (missing.length === 0 || !isRemote.value || typeof window === 'undefined') {\n return\n }\n\n void store.optionLabels(props.component.statePath, missing).then((resolved) => {\n labels.value = { ...labels.value, ...resolved }\n })\n },\n { immediate: true },\n)\n\nconst labelFor = (key: string) =>\n labels.value[key] ??\n options.value.find((option) => option.value === key)?.label ??\n staticOptions.value.find((option) => option.value === key)?.label ??\n key\n\nconst canRemove = computed(\n () => !props.component.minItems || selected.value.length > props.component.minItems,\n)\n\nconst toggle = (key: string, label: string) => {\n labels.value = { ...labels.value, [key]: label }\n\n if (props.component.multiple) {\n const next = selected.value.includes(key)\n ? selected.value.filter((item) => item !== key)\n : [...selected.value, key]\n\n if (props.component.maxItems && next.length > props.component.maxItems) {\n return\n }\n\n // `minItems()`: the last required selections cannot be removed.\n if (\n props.component.minItems &&\n next.length < selected.value.length &&\n next.length < props.component.minItems\n ) {\n return\n }\n\n props.setValue(next)\n } else {\n props.setValue(\n selected.value.includes(key) && props.component.selectablePlaceholder ? null : key,\n )\n open.value = false\n }\n}\n\nconst filtered = computed(() =>\n isRemote.value\n ? options.value\n : options.value.filter((option) =>\n (props.component.allowHtml ? htmlLabelToText(option.label) : option.label)\n .toLowerCase()\n .includes(search.value.toLowerCase()),\n ),\n)\n// Nothing to choose from at all (not a search without matches).\nconst isEmpty = computed(() => search.value === '' && options.value.length === 0)\n\nconst onOpenChange = (next: boolean) => {\n open.value = next\n\n if (!next) {\n search.value = ''\n store.blur(props.component.statePath)\n }\n}\n\n// `reorderable()`: chosen options are listed under the trigger, where they can be dragged\n// or moved with Alt + arrow keys (chips inside the trigger button can't hold controls).\nconst reorderable = computed(() => Boolean(props.component.multiple && props.component.reorderable))\nconst plainLabelFor = (key: string) =>\n props.component.allowHtml ? htmlLabelToText(labelFor(key)) : labelFor(key)\n\nconst removeTag = (event: Event, key: string) => {\n event.stopPropagation()\n props.setValue(selected.value.filter((item) => item !== key))\n}\n</script>\n\n<template>\n <Popover :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverTrigger as-child>\n <Button\n :id=\"props.id\"\n type=\"button\"\n variant=\"outline\"\n data-input=\"\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-invalid=\"props.error ? true : undefined\"\n :aria-describedby=\"props.aria?.['aria-describedby']\"\n :aria-required=\"props.aria?.['aria-required']\"\n :aria-readonly=\"props.readOnly || undefined\"\n :disabled=\"props.disabled || props.readOnly\"\n :class=\"\n cn(\n 'h-auto min-h-9 w-full justify-between px-3 py-1.5 font-normal hover:bg-transparent',\n props.error && 'border-destructive',\n )\n \"\n >\n <span class=\"flex flex-1 flex-wrap items-center gap-1 text-start\">\n <span v-if=\"selected.length === 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <span v-if=\"reorderable && selected.length > 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <template v-if=\"props.component.multiple && !reorderable\">\n <Badge v-for=\"key in selected\" :key=\"key\" variant=\"secondary\" class=\"gap-1 pe-1\">\n <span v-if=\"props.component.allowHtml\" v-html=\"labelFor(key)\" />\n <span v-else>{{ labelFor(key) }}</span>\n <span\n v-if=\"!props.disabled && !props.readOnly && canRemove\"\n role=\"button\"\n tabindex=\"-1\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n class=\"hover:bg-foreground/10 rounded-sm\"\n @click=\"removeTag($event, key)\"\n >\n <XIcon class=\"size-3\" />\n </span>\n </Badge>\n </template>\n <template v-else-if=\"!props.component.multiple && selected[0] !== undefined\">\n <span\n v-if=\"props.component.allowHtml\"\n class=\"truncate\"\n v-html=\"labelFor(selected[0])\"\n />\n <span v-else class=\"truncate\">{{ labelFor(selected[0]) }}</span>\n </template>\n </span>\n <Spinner v-if=\"loading\" class=\"opacity-50\" />\n <ChevronsUpDownIcon v-else class=\"size-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-(--reka-popover-trigger-width) p-0\" align=\"start\">\n <Command>\n <CommandInput\n :model-value=\"search\"\n :placeholder=\"\n props.component.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n \"\n @update:model-value=\"search = $event\"\n />\n <CommandList>\n <div\n v-if=\"loading && filtered.length === 0\"\n data-slot=\"select-loading\"\n class=\"text-muted-foreground flex items-center justify-center gap-2 py-6 text-sm\"\n >\n <Spinner />\n {{\n search !== ''\n ? (props.component.searchingMessage ?? t('form.searching', {}, 'Searching…'))\n : (props.component.loadingMessage ?? t('loading', {}, 'Loading…'))\n }}\n </div>\n <CommandEmpty v-else-if=\"filtered.length === 0\">{{\n isEmpty\n ? (props.component.noOptionsMessage ??\n t('form.no_options', {}, 'No options available.'))\n : (props.component.noSearchResultsMessage ?? t('no_results', {}, 'No results.'))\n }}</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filtered\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n @select=\"toggle(option.value, option.label)\"\n >\n <CheckIcon\n :class=\"cn('size-4', selected.includes(option.value) ? 'opacity-100' : 'opacity-0')\"\n />\n <OptionLabel\n :option=\"option\"\n :allow-html=\"props.component.allowHtml\"\n :wrap=\"props.component.wrapOptionLabels !== false\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <SelectReorderList\n v-if=\"reorderable\"\n :id=\"props.id\"\n :values=\"Array.isArray(props.value) ? props.value : []\"\n :label-for=\"plainLabelFor\"\n :locked=\"props.disabled || Boolean(props.readOnly)\"\n :can-remove=\"canRemove\"\n :on-change=\"props.setValue\"\n />\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComboboxSelect.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","Popover","_withCtx","PopoverTrigger","Button","_normalizeClass","_unref","_createElementVNode","_toDisplayString","_createCommentVNode","_renderList","_createBlock","Badge","Spinner","PopoverContent","Command","CommandInput","CommandList","_createTextVNode","CommandEmpty","CommandGroup","CommandItem","OptionLabel","SelectReorderList","Badge_default","Button_default","Spinner_default","Popover_default","PopoverContent_default","PopoverTrigger_default","Command_default","CommandEmpty_default","CommandGroup_default","CommandInput_default","CommandItem_default","CommandList_default","OptionLabel_default","SelectReorderList_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","ComboboxSelect_vue_vue_type_script_setup_true_lang_default","flatten","options","isOptionGroup","option","props","__props","t","useT","store","useFormStore","open","ref","search","loading","remoteOptions","labels","requestId","incoming","selected","computed","staticOptions","isRemote","runSearch","debounce","current","term","results","watch","isOpen","missing","keys","key","resolved","labelFor","canRemove","toggle","label","next","item","filtered","htmlLabelToText","isEmpty","onOpenChange","reorderable","plainLabelFor","removeTag","event","openBlock","createElementBlock","Fragment","createVNode","withCtx","normalizeClass","unref","cn","createElementVNode","toDisplayString","createCommentVNode","renderList","createBlock","$event","XIcon","ChevronsUpDownIcon","_cache","createTextVNode","CheckIcon"],"sources":["../../../src/components/fields/ComboboxSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { FieldAriaAttributes, Option, OptionOrGroup, SelectSchema } from '@alfakit/core'\nimport { debounce, htmlLabelToText, isOptionGroup } from '@alfakit/core'\nimport { CheckIcon, ChevronsUpDownIcon, XIcon } from '@lucide/vue'\nimport { computed, ref, watch } from 'vue'\nimport { useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useFormStore } from '../form/form-context'\nimport Badge from '../ui/Badge.vue'\nimport Button from '../ui/Button.vue'\nimport Command from '../ui/Command.vue'\nimport CommandEmpty from '../ui/CommandEmpty.vue'\nimport CommandGroup from '../ui/CommandGroup.vue'\nimport CommandInput from '../ui/CommandInput.vue'\nimport CommandItem from '../ui/CommandItem.vue'\nimport CommandList from '../ui/CommandList.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport OptionLabel from './OptionLabel.vue'\nimport SelectReorderList from './SelectReorderList.vue'\n\nfunction flatten(options: OptionOrGroup[]): Option[] {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nconst props = defineProps<{\n component: SelectSchema\n value: string | number | Array<string | number> | null | undefined\n setValue: (value: string | number | Array<string | number> | null) => void\n id: string\n disabled: boolean\n readOnly?: boolean\n error?: string\n aria?: FieldAriaAttributes\n}>()\n\nconst t = useT()\nconst store = useFormStore()\nconst open = ref(false)\nconst search = ref('')\nconst loading = ref(false)\nconst remoteOptions = ref<Option[] | null>(null)\nconst labels = ref<Record<string, string>>({ ...(props.component.selectedLabels ?? {}) })\nlet requestId = 0\n\n// Labels the server sends with a refresh win (e.g. after the edit option modal renamed\n// the selected record).\nwatch(\n () => props.component.selectedLabels,\n (incoming) => {\n if (incoming && Object.keys(incoming).length > 0) {\n labels.value = { ...labels.value, ...incoming }\n }\n },\n)\n\nconst selected = computed<string[]>(() =>\n Array.isArray(props.value)\n ? props.value.map(String)\n : props.value === null || props.value === undefined || props.value === ''\n ? []\n : [String(props.value)],\n)\nconst staticOptions = computed(() => flatten(props.component.options))\nconst options = computed(() => remoteOptions.value ?? staticOptions.value)\nconst isRemote = computed(() => Boolean(props.component.searchable))\n\nconst runSearch = debounce((term: string) => {\n if (!isRemote.value) {\n return\n }\n\n const current = ++requestId\n loading.value = true\n\n void store\n .searchOptions(props.component.statePath, term)\n .then((results) => {\n if (current === requestId) {\n remoteOptions.value = results\n }\n })\n .finally(() => {\n if (current === requestId) {\n loading.value = false\n }\n })\n}, props.component.searchDebounce ?? 300)\n\nwatch([open, search], ([isOpen, term]) => {\n if (isOpen && isRemote.value) {\n runSearch(term)\n }\n})\n\n// Resolve labels for values we have not seen yet (e.g. after a server refresh).\nwatch(\n selected,\n (keys) => {\n const missing = keys.filter(\n (key) => !labels.value[key] && !staticOptions.value.some((option) => option.value === key),\n )\n\n // Labels are fetched from the browser only, never during a server render.\n if (missing.length === 0 || !isRemote.value || typeof window === 'undefined') {\n return\n }\n\n void store.optionLabels(props.component.statePath, missing).then((resolved) => {\n labels.value = { ...labels.value, ...resolved }\n })\n },\n { immediate: true },\n)\n\nconst labelFor = (key: string) =>\n labels.value[key] ??\n options.value.find((option) => option.value === key)?.label ??\n staticOptions.value.find((option) => option.value === key)?.label ??\n key\n\nconst canRemove = computed(\n () => !props.component.minItems || selected.value.length > props.component.minItems,\n)\n\nconst toggle = (key: string, label: string) => {\n labels.value = { ...labels.value, [key]: label }\n\n if (props.component.multiple) {\n const next = selected.value.includes(key)\n ? selected.value.filter((item) => item !== key)\n : [...selected.value, key]\n\n if (props.component.maxItems && next.length > props.component.maxItems) {\n return\n }\n\n // `minItems()`: the last required selections cannot be removed.\n if (\n props.component.minItems &&\n next.length < selected.value.length &&\n next.length < props.component.minItems\n ) {\n return\n }\n\n props.setValue(next)\n } else {\n props.setValue(\n selected.value.includes(key) && props.component.selectablePlaceholder ? null : key,\n )\n open.value = false\n }\n}\n\nconst filtered = computed(() =>\n isRemote.value\n ? options.value\n : options.value.filter((option) =>\n (props.component.allowHtml ? htmlLabelToText(option.label) : option.label)\n .toLowerCase()\n .includes(search.value.toLowerCase()),\n ),\n)\n// Nothing to choose from at all (not a search without matches).\nconst isEmpty = computed(() => search.value === '' && options.value.length === 0)\n\nconst onOpenChange = (next: boolean) => {\n open.value = next\n\n if (!next) {\n search.value = ''\n store.blur(props.component.statePath)\n }\n}\n\n// `reorderable()`: chosen options are listed under the trigger, where they can be dragged\n// or moved with Alt + arrow keys (chips inside the trigger button can't hold controls).\nconst reorderable = computed(() => Boolean(props.component.multiple && props.component.reorderable))\nconst plainLabelFor = (key: string) =>\n props.component.allowHtml ? htmlLabelToText(labelFor(key)) : labelFor(key)\n\nconst removeTag = (event: Event, key: string) => {\n event.stopPropagation()\n props.setValue(selected.value.filter((item) => item !== key))\n}\n</script>\n\n<template>\n <Popover :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverTrigger as-child>\n <Button\n :id=\"props.id\"\n type=\"button\"\n variant=\"outline\"\n data-input=\"\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-invalid=\"props.error ? true : undefined\"\n :aria-describedby=\"props.aria?.['aria-describedby']\"\n :aria-required=\"props.aria?.['aria-required']\"\n :aria-readonly=\"props.readOnly || undefined\"\n :disabled=\"props.disabled || props.readOnly\"\n :class=\"\n cn(\n 'h-auto min-h-9 w-full justify-between px-3 py-1.5 font-normal hover:bg-transparent',\n props.error && 'border-destructive',\n )\n \"\n >\n <span class=\"flex min-w-0 flex-1 flex-wrap items-center gap-1 text-start\">\n <span v-if=\"selected.length === 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <span v-if=\"reorderable && selected.length > 0\" class=\"text-muted-foreground\">{{\n props.component.placeholder ?? t('select_option', {}, 'Select an option')\n }}</span>\n <template v-if=\"props.component.multiple && !reorderable\">\n <Badge v-for=\"key in selected\" :key=\"key\" variant=\"secondary\" class=\"gap-1 pe-1\">\n <span v-if=\"props.component.allowHtml\" v-html=\"labelFor(key)\" />\n <span v-else>{{ labelFor(key) }}</span>\n <span\n v-if=\"!props.disabled && !props.readOnly && canRemove\"\n role=\"button\"\n tabindex=\"-1\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n class=\"hover:bg-foreground/10 rounded-sm\"\n @click=\"removeTag($event, key)\"\n >\n <XIcon class=\"size-3\" />\n </span>\n </Badge>\n </template>\n <template v-else-if=\"!props.component.multiple && selected[0] !== undefined\">\n <span\n v-if=\"props.component.allowHtml\"\n class=\"truncate\"\n v-html=\"labelFor(selected[0])\"\n />\n <span v-else class=\"truncate\">{{ labelFor(selected[0]) }}</span>\n </template>\n </span>\n <Spinner v-if=\"loading\" class=\"opacity-50\" />\n <ChevronsUpDownIcon v-else class=\"size-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-(--reka-popover-trigger-width) p-0\" align=\"start\">\n <Command>\n <CommandInput\n :model-value=\"search\"\n :placeholder=\"\n props.component.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n \"\n @update:model-value=\"search = $event\"\n />\n <CommandList>\n <div\n v-if=\"loading && filtered.length === 0\"\n data-slot=\"select-loading\"\n class=\"text-muted-foreground flex items-center justify-center gap-2 py-6 text-sm\"\n >\n <Spinner />\n {{\n search !== ''\n ? (props.component.searchingMessage ?? t('form.searching', {}, 'Searching…'))\n : (props.component.loadingMessage ?? t('loading', {}, 'Loading…'))\n }}\n </div>\n <CommandEmpty v-else-if=\"filtered.length === 0\">{{\n isEmpty\n ? (props.component.noOptionsMessage ??\n t('form.no_options', {}, 'No options available.'))\n : (props.component.noSearchResultsMessage ?? t('no_results', {}, 'No results.'))\n }}</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"option in filtered\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n @select=\"toggle(option.value, option.label)\"\n >\n <CheckIcon\n :class=\"cn('size-4', selected.includes(option.value) ? 'opacity-100' : 'opacity-0')\"\n />\n <OptionLabel\n :option=\"option\"\n :allow-html=\"props.component.allowHtml\"\n :wrap=\"props.component.wrapOptionLabels !== false\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <SelectReorderList\n v-if=\"reorderable\"\n :id=\"props.id\"\n :values=\"Array.isArray(props.value) ? props.value : []\"\n :label-for=\"plainLabelFor\"\n :locked=\"props.disabled || Boolean(props.readOnly)\"\n :can-remove=\"canRemove\"\n :on-change=\"props.setValue\"\n />\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileUploadField.js","names":["_sfc_main","FileUploadField_vue_vue_type_script_setup_true_lang_default","FileUploadField_default"],"sources":["../../../src/components/fields/FileUploadField.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { defineComponent } from 'vue'\nimport { lazyComponent } from '../../lib/lazy'\n\n// The image editor's code loads with the first image to edit, not with the form.\nconst ImageEditorDialog = lazyComponent(\n 'ImageEditorDialog',\n () => import('./ImageEditorDialog.vue').then((module) => module.default),\n defineComponent({ name: 'ImageEditorDialogLoading', render: () => null }),\n)\n</script>\n\n<script setup lang=\"ts\">\nimport type { FileUploadSchema, UploadedFile } from '@alfakit/core'\nimport {\n canEditImage,\n concurrencyLimiter,\n cssAspectRatio,\n editableImageName,\n fetchEditableImage,\n fieldMemory,\n fileUploadAccept,\n filesFromClipboard,\n fileUploadPaths,\n formatBytes,\n insertUploadedFile,\n moveRepeaterItem,\n requestJson,\n transformImage,\n uploadedFileLinks,\n panelUploadOptions,\n uploadTemporaryFile,\n} from '@alfakit/core'\nimport {\n ChevronDownIcon,\n ChevronUpIcon,\n DownloadIcon,\n ExternalLinkIcon,\n FileIcon,\n PencilIcon,\n UploadCloudIcon,\n XIcon,\n} from '@lucide/vue'\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { useStore } from '../../composables/use-store'\nimport { useChoice, usePanel, useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useField, useFormStore } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Button from '../ui/Button.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport type { ImageEditRequest } from './ImageEditorDialog.vue'\n\ninterface LocalFile extends UploadedFile {\n progress?: number\n error?: string\n preview?: string\n}\n\nconst props = defineProps<{ component: FileUploadSchema }>()\nconst t = useT()\nconst choice = useChoice()\nconst panel = usePanel()\nconst {\n value,\n setValue,\n error,\n id,\n disabled: fieldDisabled,\n readOnly,\n aria,\n} = useField<string | string[] | null>(() => props.component)\n// Read-only keeps open/download links but hides upload and remove controls.\nconst disabled = computed(() => fieldDisabled.value || readOnly.value)\nconst store = useFormStore()\n// In-flight uploads and the metadata of files uploaded in this session (the server schema\n// only knows about saved files) live in the form's field memory, not in component state:\n// a virtualised Repeater item unmounts when scrolled out of view and finds them again,\n// local image previews included, when it mounts back.\nconst uploadingStore = fieldMemory<LocalFile[]>(\n store,\n `${props.component.statePath}:uploading`,\n () => [],\n)\nconst uploadedStore = fieldMemory<FileUploadSchema['files']>(\n store,\n `${props.component.statePath}:uploaded`,\n () => [],\n)\nconst uploadingState = useStore(uploadingStore)\nconst uploadedState = useStore(uploadedStore)\nconst uploading = computed({\n get: () => uploadingState.value,\n set: (next: LocalFile[]) => uploadingStore.set(next),\n})\nconst uploaded = computed({\n get: () => uploadedState.value,\n set: (next: FileUploadSchema['files']) => uploadedStore.set(next),\n})\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst dragPath = ref<string | null>(null)\n// The image open in the editor (`imageEditor()`), one at a time.\nconst editing = shallowRef<ImageEditRequest | null>(null)\nconst editorRequested = ref(false)\n\nwatch(editing, (request) => {\n if (request) editorRequested.value = true\n})\nconst editImage = (file: File) =>\n new Promise<File | null>((resolve) => {\n editing.value = {\n file,\n resolve: (edited) => {\n editing.value = null\n resolve(edited)\n },\n }\n })\n// Object URLs made for local image previews; released when the file is removed.\nconst objectUrls = fieldMemory<Set<string>>(\n store,\n `${props.component.statePath}:previews`,\n () => new Set(),\n).get()\n\nconst releasePreview = (url: string | undefined) => {\n if (url && objectUrls.delete(url)) URL.revokeObjectURL(url)\n}\n\nconst paths = computed(() => fileUploadPaths(value.value))\nconst files = computed<LocalFile[]>(() =>\n paths.value.map(\n (path) =>\n [...props.component.files, ...uploaded.value].find((file) => file.path === path) ?? {\n path,\n name: path.replace(/^tmp:/, ''),\n },\n ),\n)\nconst maxFiles = computed(() =>\n props.component.multiple ? (props.component.maxFiles ?? Infinity) : 1,\n)\nconst canAddMore = computed(() => files.value.length + uploading.value.length < maxFiles.value)\nconst circle = computed(() => props.component.panelLayout === 'circle' || props.component.avatar)\nconst integrated = computed(() => props.component.panelLayout === 'integrated' && !circle.value)\nconst removeAtStart = computed(() => props.component.positions?.removeButton === 'start')\nconst progressAtStart = computed(() => props.component.positions?.progressIndicator === 'start')\nconst uploadAlign = computed(() => props.component.positions?.uploadButton ?? 'center')\n\nconst commit = (next: string[]) => setValue(props.component.multiple ? next : (next[0] ?? null))\n\nconst upload = async (list: FileList | File[]) => {\n const endpoint = panel.value.endpoints.upload\n\n if (!endpoint) {\n return\n }\n\n const accepted = Array.from(list).slice(\n 0,\n Math.max(0, maxFiles.value - files.value.length - uploading.value.length),\n )\n\n // `maxParallelUploads()`: files are prepared (edited, resized) one by one and then\n // queued, so at most that many upload at once.\n const limit = concurrencyLimiter(props.component.maxParallelUploads ?? 1)\n const queued: Promise<void>[] = []\n\n for (const [position, picked] of accepted.entries()) {\n let original = picked\n const edit = Boolean(props.component.imageEditor && canEditImage(picked))\n\n if (edit) {\n const edited = await editImage(picked)\n\n // Cancelled: the file is not uploaded.\n if (!edited) continue\n\n original = edited\n }\n\n const file = await prepare(original, edit)\n\n if (!file) continue\n\n const local = pendingFile(file, position)\n uploading.value = [...uploading.value, local]\n\n queued.push(limit(() => send(endpoint, file, local)))\n }\n\n await Promise.all(queued)\n}\n\n/** Resize and orient a picked (or edited) file, and check its size. Null when rejected. */\nconst prepare = async (original: File, edited: boolean): Promise<File | null> => {\n const file =\n props.component.imageCropAspectRatio ||\n props.component.imageResizeTargetWidth ||\n props.component.imageResizeTargetHeight ||\n props.component.orientImagesFromExif\n ? await transformImage(original, {\n // The editor already cropped the image.\n aspectRatio: edited ? null : props.component.imageCropAspectRatio,\n mode: props.component.imageResizeMode,\n width: props.component.imageResizeTargetWidth,\n height: props.component.imageResizeTargetHeight,\n orient: props.component.orientImagesFromExif,\n })\n : original\n\n if (props.component.minSize && file.size < props.component.minSize * 1024) {\n uploading.value = [\n ...uploading.value,\n {\n path: file.name,\n name: file.name,\n size: file.size,\n error: t('form.file_too_small', {}, 'This file is too small.'),\n },\n ]\n\n return null\n }\n\n if (props.component.maxSize && file.size > props.component.maxSize * 1024) {\n uploading.value = [\n ...uploading.value,\n {\n path: file.name,\n name: file.name,\n size: file.size,\n error: t('form.file_too_large', {}, 'This file is too large.'),\n },\n ]\n\n return null\n }\n\n return file\n}\n\n/** The in-progress entry shown while `file` uploads. */\nconst pendingFile = (file: File, position: number | string): LocalFile => {\n const local: LocalFile = {\n // Unique within the batch and across re-picks of a changed file.\n path: `pending:${position}:${file.lastModified}:${file.size}:${file.name}`,\n name: file.name,\n size: file.size,\n progress: 0,\n preview: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }\n if (local.preview) objectUrls.add(local.preview)\n\n return local\n}\n\n// `imageEditor()`: uploaded JPEG, PNG and WebP images can be edited again.\nconst canEditExisting = (file: LocalFile, index: number) =>\n Boolean(props.component.imageEditor) &&\n !disabled.value &&\n index < files.value.length &&\n !file.missing &&\n Boolean(file.url ?? file.preview) &&\n editableImageName(file) !== null\n\n/**\n * The \"Edit\" button on an uploaded image: open it in the editor and upload the result\n * in its place (the old file goes like any removed one: a temporary upload is deleted\n * now, a stored file when the form is saved with `deleteRemovedFiles()`).\n */\nconst editExisting = async (existing: LocalFile) => {\n const endpoint = panel.value.endpoints.upload\n const url = existing.url ?? existing.preview\n const name = editableImageName(existing)\n\n if (!endpoint || !url || !name) return\n\n const source = await fetchEditableImage(url, name)\n\n if (!source) {\n uploading.value = [\n ...uploading.value,\n {\n path: `failed:${existing.path}`,\n name: existing.name,\n error: t('image_editor.load_failed', {}, 'The image could not be opened.'),\n },\n ]\n\n return\n }\n\n const edited = await editImage(source)\n\n // Cancelled, or the editor couldn't decode it: keep the file as it is.\n if (!edited || edited === source) return\n\n const file = await prepare(edited, true)\n\n if (!file) return\n\n const local = pendingFile(file, `edit:${existing.path}`)\n uploading.value = [...uploading.value, local]\n await send(endpoint, file, local, existing.path)\n}\n\nconst send = async (endpoint: string, file: File, local: LocalFile, replacing?: string) => {\n try {\n // Large files go in resumable chunks; progress covers the whole file.\n const response = await uploadTemporaryFile(endpoint, file, {\n ...panelUploadOptions(panel.value, props.component.uploadPolicy),\n onProgress: (progress) => {\n uploading.value = uploading.value.map((item) =>\n item.path === local.path ? { ...item, progress } : item,\n )\n },\n })\n\n uploaded.value = [\n ...uploaded.value,\n {\n path: response.key,\n name: response.name,\n size: response.size,\n temporary: true,\n url: local.preview,\n },\n ]\n uploading.value = uploading.value.filter((item) => item.path !== local.path)\n // Read the store, not `value`: it may lag behind within a batch.\n const current = fileUploadPaths(\n store.getValue<string | string[] | null>(props.component.statePath, null),\n )\n\n if (replacing !== undefined && current.includes(replacing)) {\n // An edited image takes the old one's place.\n commit(current.map((path) => (path === replacing ? response.key : path)))\n discard(replacing)\n } else {\n commit(insertUploadedFile(current, response.key, props.component, maxFiles.value))\n }\n } catch (uploadError) {\n const message =\n (uploadError as { errors?: Record<string, string[]> }).errors?.file?.[0] ??\n (uploadError as Error).message\n uploading.value = uploading.value.map((item) =>\n item.path === local.path ? { ...item, error: message, progress: undefined } : item,\n )\n }\n}\n\nconst remove = (path: string) => {\n commit(paths.value.filter((item) => item !== path))\n discard(path)\n}\n\n/** Release a file that left the field: its preview, and a temporary upload's file. */\nconst discard = (path: string) => {\n releasePreview(uploadedStore.get().find((file) => file.path === path)?.url)\n\n if (path.startsWith('tmp:') && panel.value.endpoints.uploadDestroy) {\n void requestJson(panel.value.endpoints.uploadDestroy, {\n method: 'DELETE',\n body: { key: path },\n }).catch(() => undefined)\n }\n}\n\nconst removeEntry = (file: LocalFile) => {\n if (file.error || file.progress !== undefined) {\n releasePreview(file.preview)\n uploading.value = uploading.value.filter((item) => item.path !== file.path)\n } else {\n remove(file.path)\n }\n}\n\nconst onDrop = (event: DragEvent) => {\n event.preventDefault()\n dragging.value = false\n\n if (!disabled.value && event.dataTransfer && event.dataTransfer.files.length > 0) {\n void upload(event.dataTransfer.files)\n }\n}\n\n// `pasteable()`: images and files pasted while the drop zone has focus.\nconst onPaste = (event: ClipboardEvent) => {\n if (disabled.value || props.component.pasteable === false) return\n\n const pasted = filesFromClipboard(event.clipboardData)\n\n if (pasted.length > 0) {\n event.preventDefault()\n void upload(pasted)\n }\n}\n\nconst onPick = (event: Event) => {\n const input = event.target as HTMLInputElement\n // Copy the list: clearing the input empties it while the editor is open.\n const picked = input.files ? Array.from(input.files) : []\n input.value = ''\n void upload(picked)\n}\n\nconst move = (from: number, to: number) =>\n commit(moveRepeaterItem(paths.value, from, to) as string[])\nconst reorderable = computed(() =>\n Boolean(props.component.reorderable && props.component.multiple && !disabled.value),\n)\n\nconst onItemDrop = (event: DragEvent, index: number) => {\n if (!reorderable.value || dragPath.value === null) return\n event.preventDefault()\n event.stopPropagation()\n const from = paths.value.indexOf(dragPath.value)\n if (from !== -1 && index < files.value.length) move(from, index)\n dragPath.value = null\n}\n\nconst hints = computed(() =>\n [\n props.component.acceptedFileTypes?.join(', '),\n props.component.maxSize ? `≤ ${formatBytes(props.component.maxSize * 1024, 0)}` : null,\n props.component.minFiles\n ? choice(\n 'form.min_files',\n props.component.minFiles,\n {},\n 'At least :count file|At least :count files',\n )\n : null,\n ]\n .filter(Boolean)\n .join(' · '),\n)\n\n// `previewable(false)` shows every file as a name and icon, without the image.\nconst isImage = (file: LocalFile) =>\n props.component.previewable !== false &&\n (props.component.image || /\\.(png|jpe?g|gif|webp|svg|avif)$/i.test(file.name))\n\n// Previews whose image failed to load (missing or unreachable file) show the file icon.\nconst brokenPreviews = ref<ReadonlySet<string>>(new Set())\n\n/** `imagePreviewHeight()`: numbers are pixels, strings are CSS lengths (`'250px'`, `'10rem'`). */\nfunction cssLength(value: number | string): string {\n return typeof value === 'number' || /^\\d+(\\.\\d+)?$/.test(value) ? `${value}px` : value\n}\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div\n data-slot=\"file-upload\"\n :data-layout=\"props.component.panelLayout\"\n :class=\"cn('flex flex-col gap-3', integrated && 'rounded-md border p-2')\"\n >\n <ul\n v-if=\"files.length > 0 || uploading.length > 0\"\n :class=\"\n cn(\n 'grid gap-2',\n props.component.panelLayout === 'grid' && 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n circle && 'justify-items-center',\n )\n \"\n >\n <li\n v-for=\"(file, index) in [...files, ...uploading]\"\n :key=\"file.path\"\n :draggable=\"reorderable && index < files.length\"\n :class=\"\n cn(\n dragPath === file.path && 'opacity-50',\n 'bg-muted/30 relative flex min-w-0 items-center gap-3 overflow-hidden rounded-md border p-2 text-sm',\n circle && 'size-28 justify-center rounded-full border-0 p-0',\n file.error && 'border-destructive',\n )\n \"\n @dragstart=\"dragPath = file.path\"\n @dragover=\"reorderable && $event.preventDefault()\"\n @drop=\"onItemDrop($event, index)\"\n @dragend=\"dragPath = null\"\n >\n <Button\n v-if=\"!circle && removeAtStart && props.component.deletable && !disabled\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n @click=\"removeEntry(file)\"\n ><XIcon\n /></Button>\n <Spinner\n v-if=\"!circle && progressAtStart && file.progress !== undefined && !file.error\"\n />\n <img\n v-if=\"isImage(file) && (file.url || file.preview) && !brokenPreviews.has(file.path)\"\n :src=\"file.url ?? file.preview\"\n alt=\"\"\n :class=\"cn('size-12 shrink-0 rounded object-cover', circle && 'size-28 rounded-full')\"\n :style=\"\n !circle && props.component.imagePreviewHeight\n ? {\n height: cssLength(props.component.imagePreviewHeight),\n width: 'auto',\n maxWidth: '50%',\n }\n : undefined\n \"\n @error=\"brokenPreviews = new Set(brokenPreviews).add(file.path)\"\n />\n <FileIcon v-else-if=\"!circle\" class=\"text-muted-foreground size-6 shrink-0\" />\n <div v-if=\"!circle\" class=\"min-w-0 flex-1\">\n <p class=\"truncate font-medium\">{{ file.name }}</p>\n <p :class=\"cn('text-muted-foreground text-xs', file.missing && 'text-destructive')\">\n {{\n file.error ??\n (file.missing\n ? t('form.file_missing', {}, 'This file no longer exists.')\n : file.progress !== undefined && !file.error\n ? (props.component.uploadingMessage ?? t('form.uploading', {}, 'Uploading…'))\n : file.size\n ? formatBytes(file.size)\n : file.temporary\n ? t('form.uploading', {}, 'Pending')\n : '')\n }}\n </p>\n </div>\n <div :class=\"cn('flex items-center gap-1', circle && 'absolute -end-1 -bottom-1')\">\n <Spinner\n v-if=\"(circle || !progressAtStart) && file.progress !== undefined && !file.error\"\n />\n <template v-if=\"reorderable && index < files.length\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('form.move_up', {}, 'Move up')\"\n :disabled=\"index === 0\"\n @click=\"move(index, index - 1)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('form.move_down', {}, 'Move down')\"\n :disabled=\"index === files.length - 1\"\n @click=\"move(index, index + 1)\"\n ><ChevronDownIcon\n /></Button>\n </template>\n <Button\n v-if=\"canEditExisting(file, index)\"\n type=\"button\"\n :variant=\"circle ? 'secondary' : 'ghost'\"\n size=\"icon-xs\"\n :class=\"cn(circle && 'rounded-full shadow')\"\n :aria-label=\"t('image_editor.title', {}, 'Edit image')\"\n :title=\"t('image_editor.title', {}, 'Edit image')\"\n data-action=\"edit-image\"\n @click=\"void editExisting(file)\"\n ><PencilIcon\n /></Button>\n <Button\n v-if=\"props.component.openable && uploadedFileLinks(file).open\"\n as-child\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n >\n <a\n :href=\"uploadedFileLinks(file).open\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n :aria-label=\"`${t('open', {}, 'Open')} ${t('opens_in_new_tab', {}, '(opens in a new tab)')}`\"\n ><ExternalLinkIcon\n /></a>\n </Button>\n <Button\n v-if=\"props.component.downloadable && uploadedFileLinks(file).download\"\n as-child\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n >\n <a\n :href=\"uploadedFileLinks(file).download\"\n download\n :aria-label=\"t('download', {}, 'Download')\"\n ><DownloadIcon\n /></a>\n </Button>\n <Button\n v-if=\"props.component.deletable && !disabled && (circle || !removeAtStart)\"\n type=\"button\"\n :variant=\"circle ? 'secondary' : 'ghost'\"\n size=\"icon-xs\"\n :class=\"cn(circle && 'rounded-full shadow')\"\n :aria-label=\"t('remove', {}, 'Remove')\"\n @click=\"removeEntry(file)\"\n ><XIcon\n /></Button>\n </div>\n </li>\n </ul>\n <div\n v-if=\"canAddMore && !disabled\"\n role=\"button\"\n tabindex=\"0\"\n :data-align=\"uploadAlign\"\n :style=\"\n !circle && props.component.panelAspectRatio\n ? { aspectRatio: cssAspectRatio(props.component.panelAspectRatio) }\n : undefined\n \"\n :class=\"\n cn(\n 'text-muted-foreground hover:border-ring flex cursor-pointer flex-col justify-center gap-1 rounded-md border border-dashed px-4 py-6 text-sm transition-colors',\n uploadAlign === 'start'\n ? 'items-start text-start'\n : uploadAlign === 'end'\n ? 'items-end text-end'\n : 'items-center text-center',\n integrated && 'border-transparent',\n dragging && 'border-ring bg-accent',\n circle && 'size-28 items-center rounded-full py-0',\n )\n \"\n :aria-describedby=\"aria['aria-describedby']\"\n @click=\"inputRef?.click()\"\n @keydown.enter.prevent=\"inputRef?.click()\"\n @keydown.space.prevent=\"inputRef?.click()\"\n @dragover.prevent=\"dragging = true\"\n @dragleave=\"dragging = false\"\n @drop=\"onDrop\"\n @paste=\"onPaste\"\n >\n <UploadCloudIcon class=\"size-5\" />\n <span v-if=\"!circle\">{{\n props.component.placeholder ??\n t('form.drop_files', {}, 'Drop files here or click to browse')\n }}</span>\n <span v-if=\"!circle && hints\" class=\"text-muted-foreground text-xs\">{{ hints }}</span>\n </div>\n <input\n :id=\"id\"\n ref=\"inputRef\"\n type=\"file\"\n class=\"sr-only\"\n tabindex=\"-1\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n :multiple=\"props.component.multiple\"\n :accept=\"fileUploadAccept(props.component)\"\n :disabled=\"disabled\"\n @change=\"onPick\"\n />\n <!-- Mounted from the first edit on (it stays for its closing animation). -->\n <ImageEditorDialog\n v-if=\"props.component.imageEditor && (editing || editorRequested)\"\n :request=\"editing\"\n :options=\"props.component.imageEditor\"\n />\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"FileUploadField.js","names":["_sfc_main","FileUploadField_vue_vue_type_script_setup_true_lang_default","FileUploadField_default"],"sources":["../../../src/components/fields/FileUploadField.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { defineComponent } from 'vue'\nimport { lazyComponent } from '../../lib/lazy'\n\n// The image editor's code loads with the first image to edit, not with the form.\nconst ImageEditorDialog = lazyComponent(\n 'ImageEditorDialog',\n () => import('./ImageEditorDialog.vue').then((module) => module.default),\n defineComponent({ name: 'ImageEditorDialogLoading', render: () => null }),\n)\n</script>\n\n<script setup lang=\"ts\">\nimport type { FileUploadSchema, UploadedFile } from '@alfakit/core'\nimport {\n canEditImage,\n concurrencyLimiter,\n cssAspectRatio,\n editableImageName,\n fetchEditableImage,\n fieldMemory,\n fileUploadAccept,\n filesFromClipboard,\n fileUploadPaths,\n fileManagerSize,\n insertUploadedFile,\n moveRepeaterItem,\n requestJson,\n transformImage,\n uploadedFileLinks,\n panelUploadOptions,\n uploadTemporaryFile,\n} from '@alfakit/core'\nimport {\n ChevronDownIcon,\n ChevronUpIcon,\n DownloadIcon,\n ExternalLinkIcon,\n FileIcon,\n PencilIcon,\n UploadCloudIcon,\n XIcon,\n} from '@lucide/vue'\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { useStore } from '../../composables/use-store'\nimport { useChoice, useFormatter, usePanel, useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { useField, useFormStore } from '../form/form-context'\nimport FieldWrapper from '../schema/FieldWrapper.vue'\nimport Button from '../ui/Button.vue'\nimport Spinner from '../ui/Spinner.vue'\nimport type { ImageEditRequest } from './ImageEditorDialog.vue'\n\ninterface LocalFile extends UploadedFile {\n progress?: number\n error?: string\n preview?: string\n}\n\nconst props = defineProps<{ component: FileUploadSchema }>()\nconst t = useT()\nconst formatter = useFormatter()\nconst choice = useChoice()\nconst panel = usePanel()\nconst {\n value,\n setValue,\n error,\n id,\n disabled: fieldDisabled,\n readOnly,\n aria,\n} = useField<string | string[] | null>(() => props.component)\n// Read-only keeps open/download links but hides upload and remove controls.\nconst disabled = computed(() => fieldDisabled.value || readOnly.value)\nconst store = useFormStore()\n// In-flight uploads and the metadata of files uploaded in this session (the server schema\n// only knows about saved files) live in the form's field memory, not in component state:\n// a virtualised Repeater item unmounts when scrolled out of view and finds them again,\n// local image previews included, when it mounts back.\nconst uploadingStore = fieldMemory<LocalFile[]>(\n store,\n `${props.component.statePath}:uploading`,\n () => [],\n)\nconst uploadedStore = fieldMemory<FileUploadSchema['files']>(\n store,\n `${props.component.statePath}:uploaded`,\n () => [],\n)\nconst uploadingState = useStore(uploadingStore)\nconst uploadedState = useStore(uploadedStore)\nconst uploading = computed({\n get: () => uploadingState.value,\n set: (next: LocalFile[]) => uploadingStore.set(next),\n})\nconst uploaded = computed({\n get: () => uploadedState.value,\n set: (next: FileUploadSchema['files']) => uploadedStore.set(next),\n})\nconst dragging = ref(false)\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst dragPath = ref<string | null>(null)\n// The image open in the editor (`imageEditor()`), one at a time.\nconst editing = shallowRef<ImageEditRequest | null>(null)\nconst editorRequested = ref(false)\n\nwatch(editing, (request) => {\n if (request) editorRequested.value = true\n})\nconst editImage = (file: File) =>\n new Promise<File | null>((resolve) => {\n editing.value = {\n file,\n resolve: (edited) => {\n editing.value = null\n resolve(edited)\n },\n }\n })\n// Object URLs made for local image previews; released when the file is removed.\nconst objectUrls = fieldMemory<Set<string>>(\n store,\n `${props.component.statePath}:previews`,\n () => new Set(),\n).get()\n\nconst releasePreview = (url: string | undefined) => {\n if (url && objectUrls.delete(url)) URL.revokeObjectURL(url)\n}\n\nconst paths = computed(() => fileUploadPaths(value.value))\nconst files = computed<LocalFile[]>(() =>\n paths.value.map(\n (path) =>\n [...props.component.files, ...uploaded.value].find((file) => file.path === path) ?? {\n path,\n name: path.replace(/^tmp:/, ''),\n },\n ),\n)\nconst maxFiles = computed(() =>\n props.component.multiple ? (props.component.maxFiles ?? Infinity) : 1,\n)\nconst canAddMore = computed(() => files.value.length + uploading.value.length < maxFiles.value)\nconst circle = computed(() => props.component.panelLayout === 'circle' || props.component.avatar)\nconst integrated = computed(() => props.component.panelLayout === 'integrated' && !circle.value)\nconst removeAtStart = computed(() => props.component.positions?.removeButton === 'start')\nconst progressAtStart = computed(() => props.component.positions?.progressIndicator === 'start')\nconst uploadAlign = computed(() => props.component.positions?.uploadButton ?? 'center')\n\nconst commit = (next: string[]) => setValue(props.component.multiple ? next : (next[0] ?? null))\n\nconst upload = async (list: FileList | File[]) => {\n const endpoint = panel.value.endpoints.upload\n\n if (!endpoint) {\n return\n }\n\n const accepted = Array.from(list).slice(\n 0,\n Math.max(0, maxFiles.value - files.value.length - uploading.value.length),\n )\n\n // `maxParallelUploads()`: files are prepared (edited, resized) one by one and then\n // queued, so at most that many upload at once.\n const limit = concurrencyLimiter(props.component.maxParallelUploads ?? 1)\n const queued: Promise<void>[] = []\n\n for (const [position, picked] of accepted.entries()) {\n let original = picked\n const edit = Boolean(props.component.imageEditor && canEditImage(picked))\n\n if (edit) {\n const edited = await editImage(picked)\n\n // Cancelled: the file is not uploaded.\n if (!edited) continue\n\n original = edited\n }\n\n const file = await prepare(original, edit)\n\n if (!file) continue\n\n const local = pendingFile(file, position)\n uploading.value = [...uploading.value, local]\n\n queued.push(limit(() => send(endpoint, file, local)))\n }\n\n await Promise.all(queued)\n}\n\n/** Resize and orient a picked (or edited) file, and check its size. Null when rejected. */\nconst prepare = async (original: File, edited: boolean): Promise<File | null> => {\n const file =\n props.component.imageCropAspectRatio ||\n props.component.imageResizeTargetWidth ||\n props.component.imageResizeTargetHeight ||\n props.component.orientImagesFromExif\n ? await transformImage(original, {\n // The editor already cropped the image.\n aspectRatio: edited ? null : props.component.imageCropAspectRatio,\n mode: props.component.imageResizeMode,\n width: props.component.imageResizeTargetWidth,\n height: props.component.imageResizeTargetHeight,\n orient: props.component.orientImagesFromExif,\n })\n : original\n\n if (props.component.minSize && file.size < props.component.minSize * 1024) {\n uploading.value = [\n ...uploading.value,\n {\n path: file.name,\n name: file.name,\n size: file.size,\n error: t('form.file_too_small', {}, 'This file is too small.'),\n },\n ]\n\n return null\n }\n\n if (props.component.maxSize && file.size > props.component.maxSize * 1024) {\n uploading.value = [\n ...uploading.value,\n {\n path: file.name,\n name: file.name,\n size: file.size,\n error: t('form.file_too_large', {}, 'This file is too large.'),\n },\n ]\n\n return null\n }\n\n return file\n}\n\n/** The in-progress entry shown while `file` uploads. */\nconst pendingFile = (file: File, position: number | string): LocalFile => {\n const local: LocalFile = {\n // Unique within the batch and across re-picks of a changed file.\n path: `pending:${position}:${file.lastModified}:${file.size}:${file.name}`,\n name: file.name,\n size: file.size,\n progress: 0,\n preview: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }\n if (local.preview) objectUrls.add(local.preview)\n\n return local\n}\n\n// `imageEditor()`: uploaded JPEG, PNG and WebP images can be edited again.\nconst canEditExisting = (file: LocalFile, index: number) =>\n Boolean(props.component.imageEditor) &&\n !disabled.value &&\n index < files.value.length &&\n !file.missing &&\n Boolean(file.url ?? file.preview) &&\n editableImageName(file) !== null\n\n/**\n * The \"Edit\" button on an uploaded image: open it in the editor and upload the result\n * in its place (the old file goes like any removed one: a temporary upload is deleted\n * now, a stored file when the form is saved with `deleteRemovedFiles()`).\n */\nconst editExisting = async (existing: LocalFile) => {\n const endpoint = panel.value.endpoints.upload\n const url = existing.url ?? existing.preview\n const name = editableImageName(existing)\n\n if (!endpoint || !url || !name) return\n\n const source = await fetchEditableImage(url, name)\n\n if (!source) {\n uploading.value = [\n ...uploading.value,\n {\n path: `failed:${existing.path}`,\n name: existing.name,\n error: t('image_editor.load_failed', {}, 'The image could not be opened.'),\n },\n ]\n\n return\n }\n\n const edited = await editImage(source)\n\n // Cancelled, or the editor couldn't decode it: keep the file as it is.\n if (!edited || edited === source) return\n\n const file = await prepare(edited, true)\n\n if (!file) return\n\n const local = pendingFile(file, `edit:${existing.path}`)\n uploading.value = [...uploading.value, local]\n await send(endpoint, file, local, existing.path)\n}\n\nconst send = async (endpoint: string, file: File, local: LocalFile, replacing?: string) => {\n try {\n // Large files go in resumable chunks; progress covers the whole file.\n const response = await uploadTemporaryFile(endpoint, file, {\n ...panelUploadOptions(panel.value, props.component.uploadPolicy),\n onProgress: (progress) => {\n uploading.value = uploading.value.map((item) =>\n item.path === local.path ? { ...item, progress } : item,\n )\n },\n })\n\n uploaded.value = [\n ...uploaded.value,\n {\n path: response.key,\n name: response.name,\n size: response.size,\n temporary: true,\n url: local.preview,\n },\n ]\n uploading.value = uploading.value.filter((item) => item.path !== local.path)\n // Read the store, not `value`: it may lag behind within a batch.\n const current = fileUploadPaths(\n store.getValue<string | string[] | null>(props.component.statePath, null),\n )\n\n if (replacing !== undefined && current.includes(replacing)) {\n // An edited image takes the old one's place.\n commit(current.map((path) => (path === replacing ? response.key : path)))\n discard(replacing)\n } else {\n commit(insertUploadedFile(current, response.key, props.component, maxFiles.value))\n }\n } catch (uploadError) {\n const message =\n (uploadError as { errors?: Record<string, string[]> }).errors?.file?.[0] ??\n (uploadError as Error).message\n uploading.value = uploading.value.map((item) =>\n item.path === local.path ? { ...item, error: message, progress: undefined } : item,\n )\n }\n}\n\nconst remove = (path: string) => {\n commit(paths.value.filter((item) => item !== path))\n discard(path)\n}\n\n/** Release a file that left the field: its preview, and a temporary upload's file. */\nconst discard = (path: string) => {\n releasePreview(uploadedStore.get().find((file) => file.path === path)?.url)\n\n if (path.startsWith('tmp:') && panel.value.endpoints.uploadDestroy) {\n void requestJson(panel.value.endpoints.uploadDestroy, {\n method: 'DELETE',\n body: { key: path },\n }).catch(() => undefined)\n }\n}\n\nconst removeEntry = (file: LocalFile) => {\n if (file.error || file.progress !== undefined) {\n releasePreview(file.preview)\n uploading.value = uploading.value.filter((item) => item.path !== file.path)\n } else {\n remove(file.path)\n }\n}\n\nconst onDrop = (event: DragEvent) => {\n event.preventDefault()\n dragging.value = false\n\n if (!disabled.value && event.dataTransfer && event.dataTransfer.files.length > 0) {\n void upload(event.dataTransfer.files)\n }\n}\n\n// `pasteable()`: images and files pasted while the drop zone has focus.\nconst onPaste = (event: ClipboardEvent) => {\n if (disabled.value || props.component.pasteable === false) return\n\n const pasted = filesFromClipboard(event.clipboardData)\n\n if (pasted.length > 0) {\n event.preventDefault()\n void upload(pasted)\n }\n}\n\nconst onPick = (event: Event) => {\n const input = event.target as HTMLInputElement\n // Copy the list: clearing the input empties it while the editor is open.\n const picked = input.files ? Array.from(input.files) : []\n input.value = ''\n void upload(picked)\n}\n\nconst move = (from: number, to: number) =>\n commit(moveRepeaterItem(paths.value, from, to) as string[])\nconst reorderable = computed(() =>\n Boolean(props.component.reorderable && props.component.multiple && !disabled.value),\n)\n\nconst onItemDrop = (event: DragEvent, index: number) => {\n if (!reorderable.value || dragPath.value === null) return\n event.preventDefault()\n event.stopPropagation()\n const from = paths.value.indexOf(dragPath.value)\n if (from !== -1 && index < files.value.length) move(from, index)\n dragPath.value = null\n}\n\nconst hints = computed(() =>\n [\n props.component.acceptedFileTypes?.join(', '),\n props.component.maxSize\n ? `≤ ${fileManagerSize(props.component.maxSize * 1024, formatter.value)}`\n : null,\n props.component.minFiles\n ? choice(\n 'form.min_files',\n props.component.minFiles,\n {},\n 'At least :count file|At least :count files',\n )\n : null,\n ]\n .filter(Boolean)\n .join(' · '),\n)\n\n// `previewable(false)` shows every file as a name and icon, without the image.\nconst isImage = (file: LocalFile) =>\n props.component.previewable !== false &&\n (props.component.image || /\\.(png|jpe?g|gif|webp|svg|avif)$/i.test(file.name))\n\n// Previews whose image failed to load (missing or unreachable file) show the file icon.\nconst brokenPreviews = ref<ReadonlySet<string>>(new Set())\n\n/** `imagePreviewHeight()`: numbers are pixels, strings are CSS lengths (`'250px'`, `'10rem'`). */\nfunction cssLength(value: number | string): string {\n return typeof value === 'number' || /^\\d+(\\.\\d+)?$/.test(value) ? `${value}px` : value\n}\n</script>\n\n<template>\n <FieldWrapper :field=\"props.component\" :html-for=\"id\" :error=\"error\">\n <div\n data-slot=\"file-upload\"\n :data-layout=\"props.component.panelLayout\"\n :class=\"cn('flex flex-col gap-3', integrated && 'rounded-md border p-2')\"\n >\n <ul\n v-if=\"files.length > 0 || uploading.length > 0\"\n :class=\"\n cn(\n 'grid gap-2',\n props.component.panelLayout === 'grid' && 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n circle && 'justify-items-center',\n )\n \"\n >\n <li\n v-for=\"(file, index) in [...files, ...uploading]\"\n :key=\"file.path\"\n :draggable=\"reorderable && index < files.length\"\n :class=\"\n cn(\n dragPath === file.path && 'opacity-50',\n 'bg-muted/30 relative flex min-w-0 items-center gap-3 overflow-hidden rounded-md border p-2 text-sm',\n circle && 'size-28 justify-center rounded-full border-0 p-0',\n file.error && 'border-destructive',\n )\n \"\n @dragstart=\"dragPath = file.path\"\n @dragover=\"reorderable && $event.preventDefault()\"\n @drop=\"onItemDrop($event, index)\"\n @dragend=\"dragPath = null\"\n >\n <Button\n v-if=\"!circle && removeAtStart && props.component.deletable && !disabled\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('file_upload.remove', { name: file.name }, 'Remove :name')\"\n @click=\"removeEntry(file)\"\n ><XIcon\n /></Button>\n <Spinner\n v-if=\"!circle && progressAtStart && file.progress !== undefined && !file.error\"\n />\n <img\n v-if=\"isImage(file) && (file.url || file.preview) && !brokenPreviews.has(file.path)\"\n :src=\"file.url ?? file.preview\"\n alt=\"\"\n :class=\"cn('size-12 shrink-0 rounded object-cover', circle && 'size-28 rounded-full')\"\n :style=\"\n !circle && props.component.imagePreviewHeight\n ? {\n height: cssLength(props.component.imagePreviewHeight),\n width: 'auto',\n maxWidth: '50%',\n }\n : undefined\n \"\n @error=\"brokenPreviews = new Set(brokenPreviews).add(file.path)\"\n />\n <FileIcon v-else-if=\"!circle\" class=\"text-muted-foreground size-6 shrink-0\" />\n <div v-if=\"!circle\" class=\"min-w-0 flex-1\">\n <p class=\"truncate font-medium\">{{ file.name }}</p>\n <p :class=\"cn('text-muted-foreground text-xs', file.missing && 'text-destructive')\">\n {{\n file.error ??\n (file.missing\n ? t('form.file_missing', {}, 'This file no longer exists.')\n : file.progress !== undefined && !file.error\n ? (props.component.uploadingMessage ?? t('form.uploading', {}, 'Uploading…'))\n : file.size\n ? fileManagerSize(file.size, formatter)\n : file.temporary\n ? t('form.uploading', {}, 'Pending')\n : '')\n }}\n </p>\n </div>\n <div :class=\"cn('flex items-center gap-1', circle && 'absolute -end-1 -bottom-1')\">\n <Spinner\n v-if=\"(circle || !progressAtStart) && file.progress !== undefined && !file.error\"\n />\n <template v-if=\"reorderable && index < files.length\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('file_upload.move_up', { name: file.name }, 'Move :name up')\"\n :disabled=\"index === 0\"\n @click=\"move(index, index - 1)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :aria-label=\"t('file_upload.move_down', { name: file.name }, 'Move :name down')\"\n :disabled=\"index === files.length - 1\"\n @click=\"move(index, index + 1)\"\n ><ChevronDownIcon\n /></Button>\n </template>\n <Button\n v-if=\"canEditExisting(file, index)\"\n type=\"button\"\n :variant=\"circle ? 'secondary' : 'ghost'\"\n size=\"icon-xs\"\n :class=\"cn(circle && 'rounded-full shadow')\"\n :aria-label=\"t('file_upload.edit', { name: file.name }, 'Edit :name')\"\n :title=\"t('image_editor.title', {}, 'Edit image')\"\n data-action=\"edit-image\"\n @click=\"void editExisting(file)\"\n ><PencilIcon\n /></Button>\n <Button\n v-if=\"props.component.openable && uploadedFileLinks(file).open\"\n as-child\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n >\n <a\n :href=\"uploadedFileLinks(file).open\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n :aria-label=\"`${t('file_upload.open', { name: file.name }, 'Open :name')} ${t('opens_in_new_tab', {}, '(opens in a new tab)')}`\"\n ><ExternalLinkIcon\n /></a>\n </Button>\n <Button\n v-if=\"props.component.downloadable && uploadedFileLinks(file).download\"\n as-child\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n >\n <a\n :href=\"uploadedFileLinks(file).download\"\n download\n :aria-label=\"t('file_upload.download', { name: file.name }, 'Download :name')\"\n ><DownloadIcon\n /></a>\n </Button>\n <Button\n v-if=\"props.component.deletable && !disabled && (circle || !removeAtStart)\"\n type=\"button\"\n :variant=\"circle ? 'secondary' : 'ghost'\"\n size=\"icon-xs\"\n :class=\"cn(circle && 'rounded-full shadow')\"\n :aria-label=\"t('file_upload.remove', { name: file.name }, 'Remove :name')\"\n @click=\"removeEntry(file)\"\n ><XIcon\n /></Button>\n </div>\n </li>\n </ul>\n <div\n v-if=\"canAddMore && !disabled\"\n role=\"button\"\n tabindex=\"0\"\n :data-align=\"uploadAlign\"\n :style=\"\n !circle && props.component.panelAspectRatio\n ? { aspectRatio: cssAspectRatio(props.component.panelAspectRatio) }\n : undefined\n \"\n :class=\"\n cn(\n 'text-muted-foreground hover:border-ring flex cursor-pointer flex-col justify-center gap-1 rounded-md border border-dashed px-4 py-6 text-sm transition-colors',\n uploadAlign === 'start'\n ? 'items-start text-start'\n : uploadAlign === 'end'\n ? 'items-end text-end'\n : 'items-center text-center',\n integrated && 'border-transparent',\n dragging && 'border-ring bg-accent',\n circle && 'size-28 items-center rounded-full py-0',\n )\n \"\n :aria-describedby=\"aria['aria-describedby']\"\n @click=\"inputRef?.click()\"\n @keydown.enter.prevent=\"inputRef?.click()\"\n @keydown.space.prevent=\"inputRef?.click()\"\n @dragover.prevent=\"dragging = true\"\n @dragleave=\"dragging = false\"\n @drop=\"onDrop\"\n @paste=\"onPaste\"\n >\n <UploadCloudIcon class=\"size-5\" />\n <span v-if=\"!circle\">{{\n props.component.placeholder ??\n t('form.drop_files', {}, 'Drop files here or click to browse')\n }}</span>\n <span v-if=\"!circle && hints\" class=\"text-muted-foreground text-xs\">{{ hints }}</span>\n </div>\n <input\n :id=\"id\"\n ref=\"inputRef\"\n type=\"file\"\n class=\"sr-only\"\n tabindex=\"-1\"\n :aria-invalid=\"aria['aria-invalid']\"\n :aria-describedby=\"aria['aria-describedby']\"\n :aria-required=\"aria['aria-required']\"\n :multiple=\"props.component.multiple\"\n :accept=\"fileUploadAccept(props.component)\"\n :disabled=\"disabled\"\n @change=\"onPick\"\n />\n <!-- Mounted from the first edit on (it stays for its closing animation). -->\n <ImageEditorDialog\n v-if=\"props.component.imageEditor && (editing || editorRequested)\"\n :request=\"editing\"\n :options=\"props.component.imageEditor\"\n />\n </div>\n </FieldWrapper>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileUploadField.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/FileUploadField.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FileUploadField.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fields/FileUploadField.vue"],"names":[],"mappings":"AAuqBA,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,eAAe,CAAA;;eA0DnC,gBAAgB;;eAAhB,gBAAgB;;AAPhD,wBA09BK"}
|