@alfakit/vue 0.1.8 → 0.1.10
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 +14 -0
- package/dist/app/components.d.ts.map +1 -1
- package/dist/app/components.js +1 -0
- package/dist/app/components.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 +1 -1
- package/dist/components/actions/ActionModal.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.js.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue.d.ts.map +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/dev-tools/DevToolsInspector.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/CheckboxListField.js.map +1 -1
- package/dist/components/fields/CheckboxListField.vue.d.ts.map +1 -1
- package/dist/components/fields/CheckboxListField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/fields/CheckboxListField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/DatePickerField.js.map +1 -1
- package/dist/components/fields/DatePickerField.vue.d.ts.map +1 -1
- package/dist/components/fields/DatePickerField.vue_vue_type_script_setup_true_lang.js +34 -32
- package/dist/components/fields/DatePickerField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.js.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue.d.ts +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue.d.ts.map +1 -1
- package/dist/components/fields/RichEditorSuggestions.vue_vue_type_script_setup_true_lang.js +29 -23
- package/dist/components/fields/RichEditorSuggestions.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.js.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.vue.d.ts.map +1 -1
- package/dist/components/fields/RichEditorToolbarGroup.vue_vue_type_script_setup_true_lang.js +9 -4
- package/dist/components/fields/RichEditorToolbarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.js.map +1 -1
- package/dist/components/file-manager/FileManagerView.vue.d.ts.map +1 -1
- package/dist/components/file-manager/FileManagerView.vue_vue_type_script_setup_true_lang.js +5 -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 +79 -78
- package/dist/components/form/FormBlock.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/CalloutComponent.js.map +1 -1
- package/dist/components/layout/CalloutComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/CalloutComponent.vue_vue_type_script_setup_true_lang.js +43 -39
- package/dist/components/layout/CalloutComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/EmptyStateComponent.js.map +1 -1
- package/dist/components/layout/EmptyStateComponent.vue.d.ts.map +1 -1
- package/dist/components/layout/EmptyStateComponent.vue_vue_type_script_setup_true_lang.js +29 -26
- package/dist/components/layout/EmptyStateComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/MixedContent.js.map +1 -1
- package/dist/components/layout/MixedContent.vue.d.ts +5 -3
- package/dist/components/layout/MixedContent.vue.d.ts.map +1 -1
- package/dist/components/layout/MixedContent.vue_vue_type_script_setup_true_lang.js +5 -4
- package/dist/components/layout/MixedContent.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 +12 -8
- package/dist/components/layout/SectionComponent.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 +133 -104
- package/dist/components/notifications/NotificationsMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/BottomNav.js.map +1 -1
- package/dist/components/panel/BottomNav.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/panel/BottomNav.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/KeyboardShortcuts.js.map +1 -1
- package/dist/components/panel/KeyboardShortcuts.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/KeyboardShortcuts.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavGroup.js.map +1 -1
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/panel/NavGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavItem.js.map +1 -1
- package/dist/components/panel/NavItem.vue.d.ts.map +1 -1
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/NavItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/NavSubItem.js.map +1 -1
- package/dist/components/panel/NavSubItem.vue.d.ts.map +1 -1
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/panel/NavSubItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.js.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.vue.d.ts.map +1 -1
- package/dist/components/panel/ShortcutsOverlay.vue_vue_type_script_setup_true_lang.js +20 -19
- package/dist/components/panel/ShortcutsOverlay.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 +6 -6
- package/dist/components/table/ColumnManager.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/table/Spreadsheet.js.map +1 -1
- package/dist/components/table/Spreadsheet.vue.d.ts.map +1 -1
- package/dist/components/table/Spreadsheet.vue_vue_type_script_setup_true_lang.js +37 -33
- package/dist/components/table/Spreadsheet.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.js.map +1 -1
- package/dist/components/translation-manager/TranslationManagerView.vue.d.ts.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/SidebarMenuButton.js.map +1 -1
- package/dist/components/ui/SidebarMenuButton.vue.d.ts.map +1 -1
- package/dist/components/ui/SidebarMenuButton.vue_vue_type_script_setup_true_lang.js +12 -12
- package/dist/components/ui/SidebarMenuButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ui/TooltipTrigger.js.map +1 -1
- package/dist/components/ui/TooltipTrigger.vue.d.ts +2 -2
- package/dist/components/ui/TooltipTrigger.vue.d.ts.map +1 -1
- package/dist/components/ui/TooltipTrigger.vue_vue_type_script_setup_true_lang.js +10 -7
- package/dist/components/ui/TooltipTrigger.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/widgets/StatCard.js.map +1 -1
- package/dist/components/widgets/StatCard.vue.d.ts.map +1 -1
- package/dist/components/widgets/StatCard.vue_vue_type_script_setup_true_lang.js +30 -30
- package/dist/components/widgets/StatCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/eject-manifest.json +2 -2
- package/dist/layouts/TopNavLayout.js.map +1 -1
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/layouts/TopNavLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/layouts/TopNavMenu.js.map +1 -1
- package/dist/layouts/TopNavMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/layouts/TopNavMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/pages/AlfakitPage.js.map +1 -1
- package/dist/pages/AlfakitPage.vue.d.ts.map +1 -1
- package/dist/pages/AlfakitPage.vue_vue_type_script_setup_true_lang.js +16 -19
- package/dist/pages/AlfakitPage.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 +29 -24
- package/dist/standalone/AlfakitComponent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/standalone/runtime.d.ts.map +1 -1
- package/dist/standalone/runtime.js +25 -25
- package/dist/standalone/runtime.js.map +1 -1
- package/dist/standalone.css +12 -7
- package/dist/starter-kit/AlfakitStarterKitRoot.js.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue.d.ts.map +1 -1
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js +25 -21
- package/dist/starter-kit/AlfakitStarterKitRoot.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/starter-kit/navigation.d.ts +6 -1
- package/dist/starter-kit/navigation.d.ts.map +1 -1
- package/dist/starter-kit/navigation.js +11 -10
- package/dist/starter-kit/navigation.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/themes.css +4 -0
- package/package.json +2 -2
- package/src/app/components.ts +3 -0
- package/src/components/actions/ActionModal.vue +2 -0
- package/src/components/dev-tools/DevToolsInspector.vue +1 -1
- package/src/components/fields/CheckboxListField.vue +2 -2
- package/src/components/fields/DatePickerField.vue +1 -0
- package/src/components/fields/RichEditorSuggestions.vue +13 -6
- package/src/components/fields/RichEditorToolbarGroup.vue +16 -1
- package/src/components/file-manager/FileManagerView.vue +3 -1
- package/src/components/form/FormBlock.vue +15 -2
- package/src/components/layout/CalloutComponent.vue +15 -2
- package/src/components/layout/EmptyStateComponent.vue +10 -1
- package/src/components/layout/MixedContent.vue +12 -4
- package/src/components/layout/SectionComponent.vue +20 -4
- package/src/components/notifications/NotificationsMenu.vue +45 -1
- package/src/components/panel/BottomNav.vue +2 -2
- package/src/components/panel/KeyboardShortcuts.vue +1 -1
- package/src/components/panel/NavGroup.vue +3 -3
- package/src/components/panel/NavItem.vue +5 -1
- package/src/components/panel/NavSubItem.vue +5 -1
- package/src/components/panel/ShortcutsOverlay.vue +27 -22
- package/src/components/table/ColumnManager.vue +6 -4
- package/src/components/table/Spreadsheet.vue +9 -4
- package/src/components/translation-manager/TranslationManagerView.vue +1 -1
- package/src/components/ui/SidebarMenuButton.vue +6 -0
- package/src/components/ui/TooltipTrigger.vue +4 -1
- package/src/components/widgets/StatCard.vue +6 -3
- package/src/layouts/TopNavLayout.vue +2 -2
- package/src/layouts/TopNavMenu.vue +1 -1
- package/src/pages/AlfakitPage.vue +13 -3
- package/src/standalone/AlfakitComponent.vue +16 -5
- package/src/standalone/runtime.ts +3 -0
- package/src/starter-kit/AlfakitStarterKitRoot.vue +7 -0
- package/src/starter-kit/navigation.ts +12 -1
- package/src/styles.css +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnManager.js","names":["_sfc_main","ColumnManager_vue_vue_type_script_setup_true_lang_default","ColumnManager_default"],"sources":["../../../src/components/table/ColumnManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TableSchema } from '@alfakit/core'\nimport {\n columnDraftChanged,\n columnManagerPosition,\n columnOrderChanged,\n defaultVisibleColumns,\n FILTERS_MODAL_WIDTH_CLASSES,\n gridColumnsClasses,\n keepMoveButtonFocus,\n moveColumnInManager,\n moveColumnTo,\n orderColumnsBy,\n saveColumnOrder,\n slideOverSide,\n toggleVisibleColumn,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronUpIcon, Columns3Icon, GripVerticalIcon } from '@lucide/vue'\nimport { createReusableTemplate } from '@vueuse/core'\nimport { computed, ref, useId, watch } from 'vue'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { showToast } from '../notifications/flash-toasts'\nimport Button from '../ui/Button.vue'\nimport Checkbox from '../ui/Checkbox.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport DialogTrigger from '../ui/DialogTrigger.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Sheet from '../ui/Sheet.vue'\nimport SheetContent from '../ui/SheetContent.vue'\nimport SheetDescription from '../ui/SheetDescription.vue'\nimport SheetHeader from '../ui/SheetHeader.vue'\nimport SheetTitle from '../ui/SheetTitle.vue'\nimport SheetTrigger from '../ui/SheetTrigger.vue'\nimport TableTriggerButton from './TableTriggerButton.vue'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and, with\n * `reorderableColumns()`, move columns (stored per user on the server) by dragging a row\n * onto another or with the move up / down buttons (the keyboard path).\n *\n * `deferColumnManager()` keeps toggles in a draft until \"Apply\"; `columnManagerLayout()`\n * opens it in a dialog; `columnManagerColumns()` lays the list out in a grid.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst t = useT()\nconst panel = usePanel()\nconst direction = useDirection()\nconst open = ref(false)\n// `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\nconst modal = computed(() => props.table.triggerActions?.columnManager?.modal)\nconst modalHeading = computed(() => modal.value?.heading ?? t('table.columns', {}, 'Columns'))\nconst modalDescription = computed(() => modal.value?.description ?? props.table.pluralModelLabel)\nconst modalWidthClass = computed(() =>\n modal.value?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.value.width] : undefined,\n)\nconst saving = ref(false)\nconst dragging = ref<string | null>(null)\nconst dropTarget = ref<string | null>(null)\n// Toggles of a deferred column manager wait here until \"Apply\".\nconst draft = ref<string[] | null>(null)\nconst reorderable = computed(() =>\n Boolean(props.table.reorderableColumns && panel.value.endpoints.tableColumns),\n)\nconst toggleable = computed(() => Boolean(props.table.toggleableColumns))\nconst deferred = computed(() => Boolean(props.table.deferColumnManager))\n// A moved column order: the draft of a deferred column manager, or shown at once while\n// it is saved.\nconst orderDraft = ref<string[] | null>(null)\n// Screen-reader announcement of the last move.\nconst announcement = ref('')\nconst idBase = useId()\nconst columns = computed(() =>\n orderColumnsBy(\n reorderable.value\n ? props.table.columns\n : props.table.columns.filter((column) => column.toggleable),\n orderDraft.value,\n ),\n)\nconst appliedNames = computed(() => props.table.columns.map((column) => column.name))\nconst names = computed(() => orderDraft.value ?? appliedNames.value)\nconst orderChanged = computed(\n () => orderDraft.value !== null && columnOrderChanged(appliedNames.value, orderDraft.value),\n)\nconst positions = computed(() =>\n Object.fromEntries(\n columns.value.map((column) => [\n column.name,\n columnManagerPosition(names.value, column.name, props.table.columnGroups),\n ]),\n ),\n)\n\n// The server's order replaces the one shown while saving.\nwatch(\n () => props.table.columns,\n () => {\n if (!deferred.value) orderDraft.value = null\n },\n)\nconst visible = computed(() => draft.value ?? props.table.visibleColumns)\n\nconst trigger = computed(() =>\n triggerButtonView(props.table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n }),\n)\nconst resetView = computed(() =>\n triggerButtonView(props.table, 'columnManagerReset', {\n label: toggleable.value\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n }),\n)\nconst applyView = computed(() =>\n deferred.value\n ? triggerButtonView(props.table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null,\n)\nconst inHeader = computed(() => props.table.columnManagerResetActionPosition === 'header')\nconst draftChanged = computed(\n () => draft.value !== null && columnDraftChanged(props.table.visibleColumns, draft.value),\n)\n\nconst toggle = (name: string) => {\n const next = toggleVisibleColumn(visible.value, name)\n\n if (deferred.value) {\n draft.value = next\n } else {\n visitTable(props.table.identifier, { columns: next }, ['page'])\n }\n}\n\nconst dragStart = (event: DragEvent, name: string) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n dragging.value = name\n}\n\nconst dragOver = (event: DragEvent, name: string) => {\n if (dragging.value === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = name\n}\n\nconst drop = (name: string) => {\n if (dragging.value !== null && dragging.value !== name) {\n reorder(dragging.value, moveColumnTo(names.value, dragging.value, name))\n }\n\n dragging.value = null\n dropTarget.value = null\n}\n\nconst save = async (order: string[]): Promise<boolean> => {\n const endpoint = panel.value.endpoints.tableColumns\n\n if (!endpoint) return false\n\n saving.value = true\n\n try {\n await saveColumnOrder(endpoint, props.token, props.component, order)\n\n return true\n } catch (error) {\n showToast({\n id: 'alfakit-column-order-failed',\n title: t('table.column_order_failed', {}, \"Couldn't save the column order\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n\n return false\n } finally {\n saving.value = false\n }\n}\n\nconst positionText = (name: string) => {\n const { position, count, group } = positions.value[name] ?? { position: 0, count: 0 }\n\n return group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t('table.column_manager.position', { position, count }, `Position ${position} of ${count}.`)\n}\n\nconst announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, props.table.columnGroups)\n const label = props.table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n announcement.value = group\n ? deferred.value\n ? t(\n 'table.column_manager.reordered_in_group_deferred',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered_in_group',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}.`,\n )\n : deferred.value\n ? t(\n 'table.column_manager.reordered_deferred',\n replace,\n `${label} moved to position ${position} of ${count}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered',\n replace,\n `${label} moved to position ${position} of ${count}.`,\n )\n}\n\n// A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\nconst reorder = (name: string, order: string[]) => {\n if (saving.value || !columnOrderChanged(names.value, order)) return\n\n orderDraft.value = order\n announcement.value = ''\n\n if (deferred.value) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => {\n if (saved) announce(name, order)\n else orderDraft.value = null\n })\n}\n\nconst move = (name: string, offset: -1 | 1, event: MouseEvent) => {\n keepMoveButtonFocus(event.currentTarget as HTMLElement | null)\n reorder(name, moveColumnInManager(names.value, name, offset, props.table.columnGroups))\n}\n\n// The order is saved before the visibility visit: both requests share the session, and a\n// visit running alongside the save would write the old order back when it finishes.\nconst apply = async () => {\n const order = orderDraft.value && orderChanged.value ? orderDraft.value : null\n const columns =\n draft.value && columnDraftChanged(props.table.visibleColumns, draft.value) ? draft.value : null\n\n draft.value = null\n orderDraft.value = null\n announcement.value = ''\n open.value = false\n\n if (order) await save(order)\n\n if (columns) visitTable(props.table.identifier, { columns }, ['page'])\n}\n\n// Reset: the defined column order and, for toggleable columns, the default visibility.\nconst reset = async () => {\n if (reorderable.value) {\n orderDraft.value = null\n await save([])\n }\n\n if (toggleable.value) {\n draft.value = null\n visitTable(props.table.identifier, { columns: defaultVisibleColumns(props.table) }, ['page'])\n }\n}\n\nconst setOpen = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n draft.value = null\n if (deferred.value) orderDraft.value = null\n announcement.value = ''\n }\n open.value = next\n}\n\nconst [DefineBody, ManagerBody] = createReusableTemplate()\nconst [DefineReset, ResetButton] = createReusableTemplate()\n</script>\n\n<template>\n <template v-if=\"(toggleable || reorderable) && trigger\">\n <DefineReset>\n <TableTriggerButton\n v-if=\"resetView\"\n :view=\"resetView\"\n :disabled=\"saving\"\n data-slot=\"column-manager-reset\"\n :class=\"cn(!applyView && 'w-full')\"\n @click=\"reset\"\n />\n </DefineReset>\n <DefineBody>\n <div class=\"flex flex-col gap-1\">\n <div v-if=\"inHeader && resetView\" class=\"flex justify-end\"><ResetButton /></div>\n <ul\n :class=\"\n cn(\n props.table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(props.table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )\n \"\n :aria-label=\"t('table.columns', {}, 'Columns')\"\n >\n <li\n v-for=\"column in columns\"\n :key=\"column.name\"\n :data-column=\"column.name\"\n :draggable=\"reorderable && !saving\"\n :class=\"\n cn(\n 'flex items-center gap-2 rounded-md px-2 py-1 text-sm',\n reorderable && 'cursor-grab',\n dragging === column.name && 'opacity-50',\n dropTarget === column.name && dragging !== column.name && 'bg-muted',\n )\n \"\n @dragstart=\"dragStart($event, column.name)\"\n @dragover=\"dragOver($event, column.name)\"\n @dragleave=\"dropTarget === column.name && (dropTarget = null)\"\n @dragend=\"((dragging = null), (dropTarget = null))\"\n @drop.prevent=\"drop(column.name)\"\n >\n <GripVerticalIcon\n v-if=\"reorderable\"\n class=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n <label v-if=\"column.toggleable\" class=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n :model-value=\"visible.includes(column.name)\"\n @update:model-value=\"toggle(column.name)\"\n />\n <span class=\"truncate\">{{ column.label }}</span>\n </label>\n <span v-else class=\"min-w-0 flex-1 truncate\">{{ column.label }}</span>\n <span v-if=\"reorderable\" class=\"flex shrink-0 items-center\">\n <span\n :id=\"`${idBase}-${column.name}-position`\"\n class=\"sr-only\"\n data-slot=\"column-manager-position\"\n >{{ positionText(column.name) }}</span\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"(positions[column.name]?.position ?? 0) <= 1\"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_up', { column: column.label }, `Move ${column.label} up`)\n \"\n @click=\"move(column.name, -1, $event)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"\n (positions[column.name]?.position ?? 0) >= (positions[column.name]?.count ?? 0)\n \"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_down', { column: column.label }, `Move ${column.label} down`)\n \"\n @click=\"move(column.name, 1, $event)\"\n ><ChevronDownIcon\n /></Button>\n </span>\n </li>\n </ul>\n <div\n v-if=\"reorderable\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {{ announcement }}\n </div>\n <div\n v-if=\"(!inHeader && resetView) || applyView\"\n class=\"mt-1 flex items-center justify-end gap-2\"\n >\n <ResetButton v-if=\"!inHeader\" />\n <TableTriggerButton\n v-if=\"applyView\"\n :view=\"applyView\"\n data-slot=\"column-manager-apply\"\n :disabled=\"!draftChanged && !orderChanged\"\n @click=\"apply\"\n />\n </div>\n </div>\n </DefineBody>\n <Sheet\n v-if=\"props.table.columnManagerLayout === 'modal' && modal?.slideOver\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <SheetTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </SheetTrigger>\n <SheetContent\n :side=\"slideOverSide(modal.slideOverPosition, direction)\"\n :class=\"cn('flex w-full flex-col gap-0 p-0', modalWidthClass ?? 'sm:max-w-md')\"\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader class=\"border-b\">\n <SheetTitle>{{ modalHeading }}</SheetTitle>\n <SheetDescription :class=\"modal.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</SheetDescription>\n </SheetHeader>\n <div class=\"flex-1 overflow-y-auto p-4\"><ManagerBody /></div>\n </SheetContent>\n </Sheet>\n <Dialog\n v-else-if=\"props.table.columnManagerLayout === 'modal'\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <DialogTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </DialogTrigger>\n <DialogContent :class=\"modalWidthClass ?? 'sm:max-w-lg'\" data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{{ modalHeading }}</DialogTitle>\n <DialogDescription :class=\"modal?.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</DialogDescription>\n </DialogHeader>\n <div class=\"max-h-[60vh] overflow-y-auto\"><ManagerBody /></div>\n </DialogContent>\n </Dialog>\n <Popover v-else :open=\"open\" @update:open=\"setOpen\">\n <PopoverTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n :class=\"cn('p-2', props.table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')\"\n data-slot=\"column-manager\"\n >\n <ManagerBody />\n </PopoverContent>\n </Popover>\n </template>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ColumnManager.js","names":["_sfc_main","ColumnManager_vue_vue_type_script_setup_true_lang_default","ColumnManager_default"],"sources":["../../../src/components/table/ColumnManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TableSchema } from '@alfakit/core'\nimport {\n columnDraftChanged,\n columnManagerPosition,\n columnOrderChanged,\n defaultVisibleColumns,\n FILTERS_MODAL_WIDTH_CLASSES,\n gridColumnsClasses,\n keepMoveButtonFocus,\n moveColumnInManager,\n moveColumnTo,\n orderColumnsBy,\n saveColumnOrder,\n slideOverSide,\n toggleVisibleColumn,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronUpIcon, Columns3Icon, GripVerticalIcon } from '@lucide/vue'\nimport { createReusableTemplate } from '@vueuse/core'\nimport { computed, ref, useId, watch } from 'vue'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { showToast } from '../notifications/flash-toasts'\nimport Button from '../ui/Button.vue'\nimport Checkbox from '../ui/Checkbox.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport DialogTrigger from '../ui/DialogTrigger.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Sheet from '../ui/Sheet.vue'\nimport SheetContent from '../ui/SheetContent.vue'\nimport SheetDescription from '../ui/SheetDescription.vue'\nimport SheetHeader from '../ui/SheetHeader.vue'\nimport SheetTitle from '../ui/SheetTitle.vue'\nimport SheetTrigger from '../ui/SheetTrigger.vue'\nimport TableTriggerButton from './TableTriggerButton.vue'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and, with\n * `reorderableColumns()`, move columns (stored per user on the server) by dragging a row\n * onto another or with the move up / down buttons (the keyboard path).\n *\n * `deferColumnManager()` keeps toggles in a draft until \"Apply\"; `columnManagerLayout()`\n * opens it in a dialog; `columnManagerColumns()` lays the list out in a grid.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst t = useT()\nconst panel = usePanel()\nconst direction = useDirection()\nconst open = ref(false)\n// `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\nconst modal = computed(() => props.table.triggerActions?.columnManager?.modal)\nconst modalHeading = computed(() => modal.value?.heading ?? t('table.columns', {}, 'Columns'))\nconst modalDescription = computed(() => modal.value?.description ?? props.table.pluralModelLabel)\nconst modalWidthClass = computed(() =>\n modal.value?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.value.width] : undefined,\n)\nconst saving = ref(false)\nconst dragging = ref<string | null>(null)\nconst dropTarget = ref<string | null>(null)\n// Toggles of a deferred column manager wait here until \"Apply\".\nconst draft = ref<string[] | null>(null)\nconst reorderable = computed(() =>\n Boolean(props.table.reorderableColumns && panel.value.endpoints.tableColumns),\n)\nconst toggleable = computed(() => Boolean(props.table.toggleableColumns))\nconst deferred = computed(() => Boolean(props.table.deferColumnManager))\n// A moved column order: the draft of a deferred column manager, or shown at once while\n// it is saved.\nconst orderDraft = ref<string[] | null>(null)\n// Screen-reader announcement of the last move.\nconst announcement = ref('')\nconst idBase = useId()\nconst columns = computed(() =>\n orderColumnsBy(\n reorderable.value\n ? props.table.columns\n : props.table.columns.filter((column) => column.toggleable),\n orderDraft.value,\n ),\n)\nconst appliedNames = computed(() => props.table.columns.map((column) => column.name))\nconst names = computed(() => orderDraft.value ?? appliedNames.value)\nconst orderChanged = computed(\n () => orderDraft.value !== null && columnOrderChanged(appliedNames.value, orderDraft.value),\n)\nconst positions = computed(() =>\n Object.fromEntries(\n columns.value.map((column) => [\n column.name,\n columnManagerPosition(names.value, column.name, props.table.columnGroups),\n ]),\n ),\n)\n\n// The server's order replaces the one shown while saving.\nwatch(\n () => props.table.columns,\n () => {\n if (!deferred.value) orderDraft.value = null\n },\n)\nconst visible = computed(() => draft.value ?? props.table.visibleColumns)\n\nconst trigger = computed(() =>\n triggerButtonView(props.table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n }),\n)\nconst resetView = computed(() =>\n triggerButtonView(props.table, 'columnManagerReset', {\n label: toggleable.value\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n }),\n)\nconst applyView = computed(() =>\n deferred.value\n ? triggerButtonView(props.table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null,\n)\nconst inHeader = computed(() => props.table.columnManagerResetActionPosition === 'header')\nconst draftChanged = computed(\n () => draft.value !== null && columnDraftChanged(props.table.visibleColumns, draft.value),\n)\n\nconst toggle = (name: string) => {\n const next = toggleVisibleColumn(visible.value, name)\n\n if (deferred.value) {\n draft.value = next\n } else {\n visitTable(props.table.identifier, { columns: next }, ['page'])\n }\n}\n\nconst dragStart = (event: DragEvent, name: string) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n dragging.value = name\n}\n\nconst dragOver = (event: DragEvent, name: string) => {\n if (dragging.value === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = name\n}\n\nconst drop = (name: string) => {\n if (dragging.value !== null && dragging.value !== name) {\n reorder(dragging.value, moveColumnTo(names.value, dragging.value, name))\n }\n\n dragging.value = null\n dropTarget.value = null\n}\n\nconst save = async (order: string[], options: { reload?: boolean } = {}): Promise<boolean> => {\n const endpoint = panel.value.endpoints.tableColumns\n\n if (!endpoint) return false\n\n saving.value = true\n\n try {\n await saveColumnOrder(endpoint, props.token, props.component, order, options)\n\n return true\n } catch (error) {\n showToast({\n id: 'alfakit-column-order-failed',\n title: t('table.column_order_failed', {}, \"Couldn't save the column order\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n\n return false\n } finally {\n saving.value = false\n }\n}\n\nconst positionText = (name: string) => {\n const { position, count, group } = positions.value[name] ?? { position: 0, count: 0 }\n\n return group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t('table.column_manager.position', { position, count }, `Position ${position} of ${count}.`)\n}\n\nconst announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, props.table.columnGroups)\n const label = props.table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n announcement.value = group\n ? deferred.value\n ? t(\n 'table.column_manager.reordered_in_group_deferred',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered_in_group',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}.`,\n )\n : deferred.value\n ? t(\n 'table.column_manager.reordered_deferred',\n replace,\n `${label} moved to position ${position} of ${count}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered',\n replace,\n `${label} moved to position ${position} of ${count}.`,\n )\n}\n\n// A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\nconst reorder = (name: string, order: string[]) => {\n if (saving.value || !columnOrderChanged(names.value, order)) return\n\n orderDraft.value = order\n announcement.value = ''\n\n if (deferred.value) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => {\n if (saved) announce(name, order)\n else orderDraft.value = null\n })\n}\n\nconst move = (name: string, offset: -1 | 1, event: MouseEvent) => {\n keepMoveButtonFocus(event.currentTarget as HTMLElement | null)\n reorder(name, moveColumnInManager(names.value, name, offset, props.table.columnGroups))\n}\n\n// The order is saved before the visibility visit: both requests share the session, and a\n// visit running alongside the save would write the old order back when it finishes.\nconst apply = async () => {\n const order = orderDraft.value && orderChanged.value ? orderDraft.value : null\n const columns =\n draft.value && columnDraftChanged(props.table.visibleColumns, draft.value) ? draft.value : null\n\n draft.value = null\n orderDraft.value = null\n announcement.value = ''\n open.value = false\n\n // The visibility visit renders the new order too: no second reload racing it.\n if (order) await save(order, { reload: !columns })\n\n if (columns) visitTable(props.table.identifier, { columns }, ['page'])\n}\n\n// Reset: the defined column order and, for toggleable columns, the default visibility.\nconst reset = async () => {\n if (reorderable.value) {\n orderDraft.value = null\n // The visibility visit below renders the defined order too.\n await save([], { reload: !toggleable.value })\n }\n\n if (toggleable.value) {\n draft.value = null\n visitTable(props.table.identifier, { columns: defaultVisibleColumns(props.table) }, ['page'])\n }\n}\n\nconst setOpen = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n draft.value = null\n if (deferred.value) orderDraft.value = null\n announcement.value = ''\n }\n open.value = next\n}\n\nconst [DefineBody, ManagerBody] = createReusableTemplate()\nconst [DefineReset, ResetButton] = createReusableTemplate()\n</script>\n\n<template>\n <template v-if=\"(toggleable || reorderable) && trigger\">\n <DefineReset>\n <TableTriggerButton\n v-if=\"resetView\"\n :view=\"resetView\"\n :disabled=\"saving\"\n data-slot=\"column-manager-reset\"\n :class=\"cn(!applyView && 'w-full')\"\n @click=\"reset\"\n />\n </DefineReset>\n <DefineBody>\n <div class=\"flex flex-col gap-1\">\n <div v-if=\"inHeader && resetView\" class=\"flex justify-end\"><ResetButton /></div>\n <ul\n :class=\"\n cn(\n props.table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(props.table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )\n \"\n :aria-label=\"t('table.columns', {}, 'Columns')\"\n >\n <li\n v-for=\"column in columns\"\n :key=\"column.name\"\n :data-column=\"column.name\"\n :draggable=\"reorderable && !saving\"\n :class=\"\n cn(\n 'flex items-center gap-2 rounded-md px-2 py-1 text-sm',\n reorderable && 'cursor-grab',\n dragging === column.name && 'opacity-50',\n dropTarget === column.name && dragging !== column.name && 'bg-muted',\n )\n \"\n @dragstart=\"dragStart($event, column.name)\"\n @dragover=\"dragOver($event, column.name)\"\n @dragleave=\"dropTarget === column.name && (dropTarget = null)\"\n @dragend=\"((dragging = null), (dropTarget = null))\"\n @drop.prevent=\"drop(column.name)\"\n >\n <GripVerticalIcon\n v-if=\"reorderable\"\n class=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n <label v-if=\"column.toggleable\" class=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n :model-value=\"visible.includes(column.name)\"\n @update:model-value=\"toggle(column.name)\"\n />\n <span class=\"truncate\">{{ column.label }}</span>\n </label>\n <span v-else class=\"min-w-0 flex-1 truncate\">{{ column.label }}</span>\n <span v-if=\"reorderable\" class=\"flex shrink-0 items-center\">\n <span\n :id=\"`${idBase}-${column.name}-position`\"\n class=\"sr-only\"\n data-slot=\"column-manager-position\"\n >{{ positionText(column.name) }}</span\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"(positions[column.name]?.position ?? 0) <= 1\"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_up', { column: column.label }, `Move ${column.label} up`)\n \"\n @click=\"move(column.name, -1, $event)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"\n (positions[column.name]?.position ?? 0) >= (positions[column.name]?.count ?? 0)\n \"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_down', { column: column.label }, `Move ${column.label} down`)\n \"\n @click=\"move(column.name, 1, $event)\"\n ><ChevronDownIcon\n /></Button>\n </span>\n </li>\n </ul>\n <div\n v-if=\"reorderable\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {{ announcement }}\n </div>\n <div\n v-if=\"(!inHeader && resetView) || applyView\"\n class=\"mt-1 flex items-center justify-end gap-2\"\n >\n <ResetButton v-if=\"!inHeader\" />\n <TableTriggerButton\n v-if=\"applyView\"\n :view=\"applyView\"\n data-slot=\"column-manager-apply\"\n :disabled=\"!draftChanged && !orderChanged\"\n @click=\"apply\"\n />\n </div>\n </div>\n </DefineBody>\n <Sheet\n v-if=\"props.table.columnManagerLayout === 'modal' && modal?.slideOver\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <SheetTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </SheetTrigger>\n <SheetContent\n :side=\"slideOverSide(modal.slideOverPosition, direction)\"\n :class=\"cn('flex w-full flex-col gap-0 p-0', modalWidthClass ?? 'sm:max-w-md')\"\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader class=\"border-b\">\n <SheetTitle>{{ modalHeading }}</SheetTitle>\n <SheetDescription :class=\"modal.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</SheetDescription>\n </SheetHeader>\n <div class=\"flex-1 overflow-y-auto p-4\"><ManagerBody /></div>\n </SheetContent>\n </Sheet>\n <Dialog\n v-else-if=\"props.table.columnManagerLayout === 'modal'\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <DialogTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </DialogTrigger>\n <DialogContent :class=\"modalWidthClass ?? 'sm:max-w-lg'\" data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{{ modalHeading }}</DialogTitle>\n <DialogDescription :class=\"modal?.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</DialogDescription>\n </DialogHeader>\n <div class=\"max-h-[60vh] overflow-y-auto\"><ManagerBody /></div>\n </DialogContent>\n </Dialog>\n <Popover v-else :open=\"open\" @update:open=\"setOpen\">\n <PopoverTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n :class=\"cn('p-2', props.table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')\"\n data-slot=\"column-manager\"\n >\n <ManagerBody />\n </PopoverContent>\n </Popover>\n </template>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnManager.vue.d.ts","sourceRoot":"","sources":["../../../src/components/table/ColumnManager.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ColumnManager.vue.d.ts","sourceRoot":"","sources":["../../../src/components/table/ColumnManager.vue"],"names":[],"mappings":"AAkfA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AA4ChD;;;;;;;GAOG;AACH,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;;AAw8B5E,wBAMG"}
|
|
@@ -84,12 +84,12 @@ var Ie = { class: "flex flex-col gap-1" }, Le = {
|
|
|
84
84
|
N.value !== null && (e.preventDefault(), e.dataTransfer && (e.dataTransfer.dropEffect = "move"), P.value = t);
|
|
85
85
|
}, it = (e) => {
|
|
86
86
|
N.value !== null && N.value !== e && st(N.value, Ce(U.value, N.value, e)), N.value = null, P.value = null;
|
|
87
|
-
}, Z = async (e) => {
|
|
88
|
-
let
|
|
89
|
-
if (!
|
|
87
|
+
}, Z = async (e, t = {}) => {
|
|
88
|
+
let n = Ye.value.endpoints.tableColumns;
|
|
89
|
+
if (!n) return !1;
|
|
90
90
|
M.value = !0;
|
|
91
91
|
try {
|
|
92
|
-
return await Te(
|
|
92
|
+
return await Te(n, D.token, D.component, e, t), !0;
|
|
93
93
|
} catch (e) {
|
|
94
94
|
return o({
|
|
95
95
|
id: "alfakit-column-order-failed",
|
|
@@ -136,9 +136,9 @@ var Ie = { class: "flex flex-col gap-1" }, Le = {
|
|
|
136
136
|
xe(n.currentTarget), st(e, Se(U.value, e, t, D.table.columnGroups));
|
|
137
137
|
}, lt = async () => {
|
|
138
138
|
let e = z.value && W.value ? z.value : null, t = F.value && c(D.table.visibleColumns, F.value) ? F.value : null;
|
|
139
|
-
F.value = null, z.value = null, B.value = "", k.value = !1, e && await Z(e), t && u(D.table.identifier, { columns: t }, ["page"]);
|
|
139
|
+
F.value = null, z.value = null, B.value = "", k.value = !1, e && await Z(e, { reload: !t }), t && u(D.table.identifier, { columns: t }, ["page"]);
|
|
140
140
|
}, ut = async () => {
|
|
141
|
-
I.value && (z.value = null, await Z([])), L.value && (F.value = null, u(D.table.identifier, { columns: ye(D.table) }, ["page"]));
|
|
141
|
+
I.value && (z.value = null, await Z([], { reload: !L.value })), L.value && (F.value = null, u(D.table.identifier, { columns: ye(D.table) }, ["page"]));
|
|
142
142
|
}, Q = (e) => {
|
|
143
143
|
e || (F.value = null, R.value && (z.value = null), B.value = ""), k.value = e;
|
|
144
144
|
}, [dt, $] = E(), [ft, pt] = E();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnManager.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_withCtx","_createBlock","TableTriggerButton","_normalizeClass","_createCommentVNode","_createElementVNode","_renderList","_withModifiers","Checkbox","_toDisplayString","Button","Sheet","SheetTrigger","Icon","SheetContent","SheetHeader","SheetTitle","_createTextVNode","SheetDescription","Dialog","DialogTrigger","DialogContent","DialogHeader","DialogTitle","DialogDescription","Popover","PopoverTrigger","PopoverContent","Icon_default","Button_default","Checkbox_default","Dialog_default","DialogContent_default","DialogDescription_default","DialogHeader_default","DialogTitle_default","DialogTrigger_default","Popover_default","PopoverContent_default","PopoverTrigger_default","Sheet_default","SheetContent_default","SheetDescription_default","SheetHeader_default","SheetTitle_default","SheetTrigger_default","TableTriggerButton_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","ColumnManager_vue_vue_type_script_setup_true_lang_default","props","__props","t","useT","panel","usePanel","direction","useDirection","open","ref","modal","computed","modalHeading","modalDescription","modalWidthClass","FILTERS_MODAL_WIDTH_CLASSES","saving","dragging","dropTarget","draft","reorderable","toggleable","deferred","orderDraft","announcement","idBase","useId","columns","orderColumnsBy","column","appliedNames","names","orderChanged","columnOrderChanged","positions","columnManagerPosition","visible","trigger","triggerButtonView","resetView","applyView","inHeader","draftChanged","columnDraftChanged","toggle","next","toggleVisibleColumn","name","visitTable","dragStart","event","dragOver","drop","reorder","moveColumnTo","save","endpoint","saveColumnOrder","order","error","showToast","positionText","group","position","count","announce","label","replace","saved","move","keepMoveButtonFocus","moveColumnInManager","offset","apply","reset","defaultVisibleColumns","setOpen","DefineBody","ManagerBody","createReusableTemplate","DefineReset","ResetButton","openBlock","createElementBlock","Fragment","createVNode","unref","withCtx","createBlock","normalizeClass","cn","createCommentVNode","createElementVNode","gridColumnsClasses","renderList","$event","_cache","withModifiers","GripVerticalIcon","toDisplayString","ChevronUpIcon","ChevronDownIcon","Columns3Icon","slideOverSide","createTextVNode"],"sources":["../../../src/components/table/ColumnManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TableSchema } from '@alfakit/core'\nimport {\n columnDraftChanged,\n columnManagerPosition,\n columnOrderChanged,\n defaultVisibleColumns,\n FILTERS_MODAL_WIDTH_CLASSES,\n gridColumnsClasses,\n keepMoveButtonFocus,\n moveColumnInManager,\n moveColumnTo,\n orderColumnsBy,\n saveColumnOrder,\n slideOverSide,\n toggleVisibleColumn,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronUpIcon, Columns3Icon, GripVerticalIcon } from '@lucide/vue'\nimport { createReusableTemplate } from '@vueuse/core'\nimport { computed, ref, useId, watch } from 'vue'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { showToast } from '../notifications/flash-toasts'\nimport Button from '../ui/Button.vue'\nimport Checkbox from '../ui/Checkbox.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport DialogTrigger from '../ui/DialogTrigger.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Sheet from '../ui/Sheet.vue'\nimport SheetContent from '../ui/SheetContent.vue'\nimport SheetDescription from '../ui/SheetDescription.vue'\nimport SheetHeader from '../ui/SheetHeader.vue'\nimport SheetTitle from '../ui/SheetTitle.vue'\nimport SheetTrigger from '../ui/SheetTrigger.vue'\nimport TableTriggerButton from './TableTriggerButton.vue'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and, with\n * `reorderableColumns()`, move columns (stored per user on the server) by dragging a row\n * onto another or with the move up / down buttons (the keyboard path).\n *\n * `deferColumnManager()` keeps toggles in a draft until \"Apply\"; `columnManagerLayout()`\n * opens it in a dialog; `columnManagerColumns()` lays the list out in a grid.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst t = useT()\nconst panel = usePanel()\nconst direction = useDirection()\nconst open = ref(false)\n// `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\nconst modal = computed(() => props.table.triggerActions?.columnManager?.modal)\nconst modalHeading = computed(() => modal.value?.heading ?? t('table.columns', {}, 'Columns'))\nconst modalDescription = computed(() => modal.value?.description ?? props.table.pluralModelLabel)\nconst modalWidthClass = computed(() =>\n modal.value?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.value.width] : undefined,\n)\nconst saving = ref(false)\nconst dragging = ref<string | null>(null)\nconst dropTarget = ref<string | null>(null)\n// Toggles of a deferred column manager wait here until \"Apply\".\nconst draft = ref<string[] | null>(null)\nconst reorderable = computed(() =>\n Boolean(props.table.reorderableColumns && panel.value.endpoints.tableColumns),\n)\nconst toggleable = computed(() => Boolean(props.table.toggleableColumns))\nconst deferred = computed(() => Boolean(props.table.deferColumnManager))\n// A moved column order: the draft of a deferred column manager, or shown at once while\n// it is saved.\nconst orderDraft = ref<string[] | null>(null)\n// Screen-reader announcement of the last move.\nconst announcement = ref('')\nconst idBase = useId()\nconst columns = computed(() =>\n orderColumnsBy(\n reorderable.value\n ? props.table.columns\n : props.table.columns.filter((column) => column.toggleable),\n orderDraft.value,\n ),\n)\nconst appliedNames = computed(() => props.table.columns.map((column) => column.name))\nconst names = computed(() => orderDraft.value ?? appliedNames.value)\nconst orderChanged = computed(\n () => orderDraft.value !== null && columnOrderChanged(appliedNames.value, orderDraft.value),\n)\nconst positions = computed(() =>\n Object.fromEntries(\n columns.value.map((column) => [\n column.name,\n columnManagerPosition(names.value, column.name, props.table.columnGroups),\n ]),\n ),\n)\n\n// The server's order replaces the one shown while saving.\nwatch(\n () => props.table.columns,\n () => {\n if (!deferred.value) orderDraft.value = null\n },\n)\nconst visible = computed(() => draft.value ?? props.table.visibleColumns)\n\nconst trigger = computed(() =>\n triggerButtonView(props.table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n }),\n)\nconst resetView = computed(() =>\n triggerButtonView(props.table, 'columnManagerReset', {\n label: toggleable.value\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n }),\n)\nconst applyView = computed(() =>\n deferred.value\n ? triggerButtonView(props.table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null,\n)\nconst inHeader = computed(() => props.table.columnManagerResetActionPosition === 'header')\nconst draftChanged = computed(\n () => draft.value !== null && columnDraftChanged(props.table.visibleColumns, draft.value),\n)\n\nconst toggle = (name: string) => {\n const next = toggleVisibleColumn(visible.value, name)\n\n if (deferred.value) {\n draft.value = next\n } else {\n visitTable(props.table.identifier, { columns: next }, ['page'])\n }\n}\n\nconst dragStart = (event: DragEvent, name: string) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n dragging.value = name\n}\n\nconst dragOver = (event: DragEvent, name: string) => {\n if (dragging.value === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = name\n}\n\nconst drop = (name: string) => {\n if (dragging.value !== null && dragging.value !== name) {\n reorder(dragging.value, moveColumnTo(names.value, dragging.value, name))\n }\n\n dragging.value = null\n dropTarget.value = null\n}\n\nconst save = async (order: string[]): Promise<boolean> => {\n const endpoint = panel.value.endpoints.tableColumns\n\n if (!endpoint) return false\n\n saving.value = true\n\n try {\n await saveColumnOrder(endpoint, props.token, props.component, order)\n\n return true\n } catch (error) {\n showToast({\n id: 'alfakit-column-order-failed',\n title: t('table.column_order_failed', {}, \"Couldn't save the column order\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n\n return false\n } finally {\n saving.value = false\n }\n}\n\nconst positionText = (name: string) => {\n const { position, count, group } = positions.value[name] ?? { position: 0, count: 0 }\n\n return group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t('table.column_manager.position', { position, count }, `Position ${position} of ${count}.`)\n}\n\nconst announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, props.table.columnGroups)\n const label = props.table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n announcement.value = group\n ? deferred.value\n ? t(\n 'table.column_manager.reordered_in_group_deferred',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered_in_group',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}.`,\n )\n : deferred.value\n ? t(\n 'table.column_manager.reordered_deferred',\n replace,\n `${label} moved to position ${position} of ${count}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered',\n replace,\n `${label} moved to position ${position} of ${count}.`,\n )\n}\n\n// A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\nconst reorder = (name: string, order: string[]) => {\n if (saving.value || !columnOrderChanged(names.value, order)) return\n\n orderDraft.value = order\n announcement.value = ''\n\n if (deferred.value) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => {\n if (saved) announce(name, order)\n else orderDraft.value = null\n })\n}\n\nconst move = (name: string, offset: -1 | 1, event: MouseEvent) => {\n keepMoveButtonFocus(event.currentTarget as HTMLElement | null)\n reorder(name, moveColumnInManager(names.value, name, offset, props.table.columnGroups))\n}\n\n// The order is saved before the visibility visit: both requests share the session, and a\n// visit running alongside the save would write the old order back when it finishes.\nconst apply = async () => {\n const order = orderDraft.value && orderChanged.value ? orderDraft.value : null\n const columns =\n draft.value && columnDraftChanged(props.table.visibleColumns, draft.value) ? draft.value : null\n\n draft.value = null\n orderDraft.value = null\n announcement.value = ''\n open.value = false\n\n if (order) await save(order)\n\n if (columns) visitTable(props.table.identifier, { columns }, ['page'])\n}\n\n// Reset: the defined column order and, for toggleable columns, the default visibility.\nconst reset = async () => {\n if (reorderable.value) {\n orderDraft.value = null\n await save([])\n }\n\n if (toggleable.value) {\n draft.value = null\n visitTable(props.table.identifier, { columns: defaultVisibleColumns(props.table) }, ['page'])\n }\n}\n\nconst setOpen = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n draft.value = null\n if (deferred.value) orderDraft.value = null\n announcement.value = ''\n }\n open.value = next\n}\n\nconst [DefineBody, ManagerBody] = createReusableTemplate()\nconst [DefineReset, ResetButton] = createReusableTemplate()\n</script>\n\n<template>\n <template v-if=\"(toggleable || reorderable) && trigger\">\n <DefineReset>\n <TableTriggerButton\n v-if=\"resetView\"\n :view=\"resetView\"\n :disabled=\"saving\"\n data-slot=\"column-manager-reset\"\n :class=\"cn(!applyView && 'w-full')\"\n @click=\"reset\"\n />\n </DefineReset>\n <DefineBody>\n <div class=\"flex flex-col gap-1\">\n <div v-if=\"inHeader && resetView\" class=\"flex justify-end\"><ResetButton /></div>\n <ul\n :class=\"\n cn(\n props.table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(props.table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )\n \"\n :aria-label=\"t('table.columns', {}, 'Columns')\"\n >\n <li\n v-for=\"column in columns\"\n :key=\"column.name\"\n :data-column=\"column.name\"\n :draggable=\"reorderable && !saving\"\n :class=\"\n cn(\n 'flex items-center gap-2 rounded-md px-2 py-1 text-sm',\n reorderable && 'cursor-grab',\n dragging === column.name && 'opacity-50',\n dropTarget === column.name && dragging !== column.name && 'bg-muted',\n )\n \"\n @dragstart=\"dragStart($event, column.name)\"\n @dragover=\"dragOver($event, column.name)\"\n @dragleave=\"dropTarget === column.name && (dropTarget = null)\"\n @dragend=\"((dragging = null), (dropTarget = null))\"\n @drop.prevent=\"drop(column.name)\"\n >\n <GripVerticalIcon\n v-if=\"reorderable\"\n class=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n <label v-if=\"column.toggleable\" class=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n :model-value=\"visible.includes(column.name)\"\n @update:model-value=\"toggle(column.name)\"\n />\n <span class=\"truncate\">{{ column.label }}</span>\n </label>\n <span v-else class=\"min-w-0 flex-1 truncate\">{{ column.label }}</span>\n <span v-if=\"reorderable\" class=\"flex shrink-0 items-center\">\n <span\n :id=\"`${idBase}-${column.name}-position`\"\n class=\"sr-only\"\n data-slot=\"column-manager-position\"\n >{{ positionText(column.name) }}</span\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"(positions[column.name]?.position ?? 0) <= 1\"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_up', { column: column.label }, `Move ${column.label} up`)\n \"\n @click=\"move(column.name, -1, $event)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"\n (positions[column.name]?.position ?? 0) >= (positions[column.name]?.count ?? 0)\n \"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_down', { column: column.label }, `Move ${column.label} down`)\n \"\n @click=\"move(column.name, 1, $event)\"\n ><ChevronDownIcon\n /></Button>\n </span>\n </li>\n </ul>\n <div\n v-if=\"reorderable\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {{ announcement }}\n </div>\n <div\n v-if=\"(!inHeader && resetView) || applyView\"\n class=\"mt-1 flex items-center justify-end gap-2\"\n >\n <ResetButton v-if=\"!inHeader\" />\n <TableTriggerButton\n v-if=\"applyView\"\n :view=\"applyView\"\n data-slot=\"column-manager-apply\"\n :disabled=\"!draftChanged && !orderChanged\"\n @click=\"apply\"\n />\n </div>\n </div>\n </DefineBody>\n <Sheet\n v-if=\"props.table.columnManagerLayout === 'modal' && modal?.slideOver\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <SheetTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </SheetTrigger>\n <SheetContent\n :side=\"slideOverSide(modal.slideOverPosition, direction)\"\n :class=\"cn('flex w-full flex-col gap-0 p-0', modalWidthClass ?? 'sm:max-w-md')\"\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader class=\"border-b\">\n <SheetTitle>{{ modalHeading }}</SheetTitle>\n <SheetDescription :class=\"modal.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</SheetDescription>\n </SheetHeader>\n <div class=\"flex-1 overflow-y-auto p-4\"><ManagerBody /></div>\n </SheetContent>\n </Sheet>\n <Dialog\n v-else-if=\"props.table.columnManagerLayout === 'modal'\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <DialogTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </DialogTrigger>\n <DialogContent :class=\"modalWidthClass ?? 'sm:max-w-lg'\" data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{{ modalHeading }}</DialogTitle>\n <DialogDescription :class=\"modal?.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</DialogDescription>\n </DialogHeader>\n <div class=\"max-h-[60vh] overflow-y-auto\"><ManagerBody /></div>\n </DialogContent>\n </Dialog>\n <Popover v-else :open=\"open\" @update:open=\"setOpen\">\n <PopoverTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n :class=\"cn('p-2', props.table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')\"\n data-slot=\"column-manager\"\n >\n <ManagerBody />\n </PopoverContent>\n </Popover>\n </template>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ColumnManager.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_withCtx","_createBlock","TableTriggerButton","_normalizeClass","_createCommentVNode","_createElementVNode","_renderList","_withModifiers","Checkbox","_toDisplayString","Button","Sheet","SheetTrigger","Icon","SheetContent","SheetHeader","SheetTitle","_createTextVNode","SheetDescription","Dialog","DialogTrigger","DialogContent","DialogHeader","DialogTitle","DialogDescription","Popover","PopoverTrigger","PopoverContent","Icon_default","Button_default","Checkbox_default","Dialog_default","DialogContent_default","DialogDescription_default","DialogHeader_default","DialogTitle_default","DialogTrigger_default","Popover_default","PopoverContent_default","PopoverTrigger_default","Sheet_default","SheetContent_default","SheetDescription_default","SheetHeader_default","SheetTitle_default","SheetTrigger_default","TableTriggerButton_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","ColumnManager_vue_vue_type_script_setup_true_lang_default","props","__props","t","useT","panel","usePanel","direction","useDirection","open","ref","modal","computed","modalHeading","modalDescription","modalWidthClass","FILTERS_MODAL_WIDTH_CLASSES","saving","dragging","dropTarget","draft","reorderable","toggleable","deferred","orderDraft","announcement","idBase","useId","columns","orderColumnsBy","column","appliedNames","names","orderChanged","columnOrderChanged","positions","columnManagerPosition","visible","trigger","triggerButtonView","resetView","applyView","inHeader","draftChanged","columnDraftChanged","toggle","next","toggleVisibleColumn","name","visitTable","dragStart","event","dragOver","drop","reorder","moveColumnTo","save","endpoint","saveColumnOrder","order","options","error","showToast","positionText","group","position","count","announce","label","replace","saved","move","keepMoveButtonFocus","moveColumnInManager","offset","apply","reset","defaultVisibleColumns","setOpen","DefineBody","ManagerBody","createReusableTemplate","DefineReset","ResetButton","openBlock","createElementBlock","Fragment","createVNode","unref","withCtx","createBlock","normalizeClass","cn","createCommentVNode","createElementVNode","gridColumnsClasses","renderList","$event","_cache","withModifiers","GripVerticalIcon","toDisplayString","ChevronUpIcon","ChevronDownIcon","Columns3Icon","slideOverSide","createTextVNode"],"sources":["../../../src/components/table/ColumnManager.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { TableSchema } from '@alfakit/core'\nimport {\n columnDraftChanged,\n columnManagerPosition,\n columnOrderChanged,\n defaultVisibleColumns,\n FILTERS_MODAL_WIDTH_CLASSES,\n gridColumnsClasses,\n keepMoveButtonFocus,\n moveColumnInManager,\n moveColumnTo,\n orderColumnsBy,\n saveColumnOrder,\n slideOverSide,\n toggleVisibleColumn,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronUpIcon, Columns3Icon, GripVerticalIcon } from '@lucide/vue'\nimport { createReusableTemplate } from '@vueuse/core'\nimport { computed, ref, useId, watch } from 'vue'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { showToast } from '../notifications/flash-toasts'\nimport Button from '../ui/Button.vue'\nimport Checkbox from '../ui/Checkbox.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport DialogTrigger from '../ui/DialogTrigger.vue'\nimport Popover from '../ui/Popover.vue'\nimport PopoverContent from '../ui/PopoverContent.vue'\nimport PopoverTrigger from '../ui/PopoverTrigger.vue'\nimport Sheet from '../ui/Sheet.vue'\nimport SheetContent from '../ui/SheetContent.vue'\nimport SheetDescription from '../ui/SheetDescription.vue'\nimport SheetHeader from '../ui/SheetHeader.vue'\nimport SheetTitle from '../ui/SheetTitle.vue'\nimport SheetTrigger from '../ui/SheetTrigger.vue'\nimport TableTriggerButton from './TableTriggerButton.vue'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and, with\n * `reorderableColumns()`, move columns (stored per user on the server) by dragging a row\n * onto another or with the move up / down buttons (the keyboard path).\n *\n * `deferColumnManager()` keeps toggles in a draft until \"Apply\"; `columnManagerLayout()`\n * opens it in a dialog; `columnManagerColumns()` lays the list out in a grid.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst t = useT()\nconst panel = usePanel()\nconst direction = useDirection()\nconst open = ref(false)\n// `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\nconst modal = computed(() => props.table.triggerActions?.columnManager?.modal)\nconst modalHeading = computed(() => modal.value?.heading ?? t('table.columns', {}, 'Columns'))\nconst modalDescription = computed(() => modal.value?.description ?? props.table.pluralModelLabel)\nconst modalWidthClass = computed(() =>\n modal.value?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.value.width] : undefined,\n)\nconst saving = ref(false)\nconst dragging = ref<string | null>(null)\nconst dropTarget = ref<string | null>(null)\n// Toggles of a deferred column manager wait here until \"Apply\".\nconst draft = ref<string[] | null>(null)\nconst reorderable = computed(() =>\n Boolean(props.table.reorderableColumns && panel.value.endpoints.tableColumns),\n)\nconst toggleable = computed(() => Boolean(props.table.toggleableColumns))\nconst deferred = computed(() => Boolean(props.table.deferColumnManager))\n// A moved column order: the draft of a deferred column manager, or shown at once while\n// it is saved.\nconst orderDraft = ref<string[] | null>(null)\n// Screen-reader announcement of the last move.\nconst announcement = ref('')\nconst idBase = useId()\nconst columns = computed(() =>\n orderColumnsBy(\n reorderable.value\n ? props.table.columns\n : props.table.columns.filter((column) => column.toggleable),\n orderDraft.value,\n ),\n)\nconst appliedNames = computed(() => props.table.columns.map((column) => column.name))\nconst names = computed(() => orderDraft.value ?? appliedNames.value)\nconst orderChanged = computed(\n () => orderDraft.value !== null && columnOrderChanged(appliedNames.value, orderDraft.value),\n)\nconst positions = computed(() =>\n Object.fromEntries(\n columns.value.map((column) => [\n column.name,\n columnManagerPosition(names.value, column.name, props.table.columnGroups),\n ]),\n ),\n)\n\n// The server's order replaces the one shown while saving.\nwatch(\n () => props.table.columns,\n () => {\n if (!deferred.value) orderDraft.value = null\n },\n)\nconst visible = computed(() => draft.value ?? props.table.visibleColumns)\n\nconst trigger = computed(() =>\n triggerButtonView(props.table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n }),\n)\nconst resetView = computed(() =>\n triggerButtonView(props.table, 'columnManagerReset', {\n label: toggleable.value\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n }),\n)\nconst applyView = computed(() =>\n deferred.value\n ? triggerButtonView(props.table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null,\n)\nconst inHeader = computed(() => props.table.columnManagerResetActionPosition === 'header')\nconst draftChanged = computed(\n () => draft.value !== null && columnDraftChanged(props.table.visibleColumns, draft.value),\n)\n\nconst toggle = (name: string) => {\n const next = toggleVisibleColumn(visible.value, name)\n\n if (deferred.value) {\n draft.value = next\n } else {\n visitTable(props.table.identifier, { columns: next }, ['page'])\n }\n}\n\nconst dragStart = (event: DragEvent, name: string) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n dragging.value = name\n}\n\nconst dragOver = (event: DragEvent, name: string) => {\n if (dragging.value === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = name\n}\n\nconst drop = (name: string) => {\n if (dragging.value !== null && dragging.value !== name) {\n reorder(dragging.value, moveColumnTo(names.value, dragging.value, name))\n }\n\n dragging.value = null\n dropTarget.value = null\n}\n\nconst save = async (order: string[], options: { reload?: boolean } = {}): Promise<boolean> => {\n const endpoint = panel.value.endpoints.tableColumns\n\n if (!endpoint) return false\n\n saving.value = true\n\n try {\n await saveColumnOrder(endpoint, props.token, props.component, order, options)\n\n return true\n } catch (error) {\n showToast({\n id: 'alfakit-column-order-failed',\n title: t('table.column_order_failed', {}, \"Couldn't save the column order\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n\n return false\n } finally {\n saving.value = false\n }\n}\n\nconst positionText = (name: string) => {\n const { position, count, group } = positions.value[name] ?? { position: 0, count: 0 }\n\n return group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t('table.column_manager.position', { position, count }, `Position ${position} of ${count}.`)\n}\n\nconst announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, props.table.columnGroups)\n const label = props.table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n announcement.value = group\n ? deferred.value\n ? t(\n 'table.column_manager.reordered_in_group_deferred',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered_in_group',\n replace,\n `${label} moved to position ${position} of ${count} in ${group}.`,\n )\n : deferred.value\n ? t(\n 'table.column_manager.reordered_deferred',\n replace,\n `${label} moved to position ${position} of ${count}. Apply columns to save your changes.`,\n )\n : t(\n 'table.column_manager.reordered',\n replace,\n `${label} moved to position ${position} of ${count}.`,\n )\n}\n\n// A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\nconst reorder = (name: string, order: string[]) => {\n if (saving.value || !columnOrderChanged(names.value, order)) return\n\n orderDraft.value = order\n announcement.value = ''\n\n if (deferred.value) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => {\n if (saved) announce(name, order)\n else orderDraft.value = null\n })\n}\n\nconst move = (name: string, offset: -1 | 1, event: MouseEvent) => {\n keepMoveButtonFocus(event.currentTarget as HTMLElement | null)\n reorder(name, moveColumnInManager(names.value, name, offset, props.table.columnGroups))\n}\n\n// The order is saved before the visibility visit: both requests share the session, and a\n// visit running alongside the save would write the old order back when it finishes.\nconst apply = async () => {\n const order = orderDraft.value && orderChanged.value ? orderDraft.value : null\n const columns =\n draft.value && columnDraftChanged(props.table.visibleColumns, draft.value) ? draft.value : null\n\n draft.value = null\n orderDraft.value = null\n announcement.value = ''\n open.value = false\n\n // The visibility visit renders the new order too: no second reload racing it.\n if (order) await save(order, { reload: !columns })\n\n if (columns) visitTable(props.table.identifier, { columns }, ['page'])\n}\n\n// Reset: the defined column order and, for toggleable columns, the default visibility.\nconst reset = async () => {\n if (reorderable.value) {\n orderDraft.value = null\n // The visibility visit below renders the defined order too.\n await save([], { reload: !toggleable.value })\n }\n\n if (toggleable.value) {\n draft.value = null\n visitTable(props.table.identifier, { columns: defaultVisibleColumns(props.table) }, ['page'])\n }\n}\n\nconst setOpen = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n draft.value = null\n if (deferred.value) orderDraft.value = null\n announcement.value = ''\n }\n open.value = next\n}\n\nconst [DefineBody, ManagerBody] = createReusableTemplate()\nconst [DefineReset, ResetButton] = createReusableTemplate()\n</script>\n\n<template>\n <template v-if=\"(toggleable || reorderable) && trigger\">\n <DefineReset>\n <TableTriggerButton\n v-if=\"resetView\"\n :view=\"resetView\"\n :disabled=\"saving\"\n data-slot=\"column-manager-reset\"\n :class=\"cn(!applyView && 'w-full')\"\n @click=\"reset\"\n />\n </DefineReset>\n <DefineBody>\n <div class=\"flex flex-col gap-1\">\n <div v-if=\"inHeader && resetView\" class=\"flex justify-end\"><ResetButton /></div>\n <ul\n :class=\"\n cn(\n props.table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(props.table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )\n \"\n :aria-label=\"t('table.columns', {}, 'Columns')\"\n >\n <li\n v-for=\"column in columns\"\n :key=\"column.name\"\n :data-column=\"column.name\"\n :draggable=\"reorderable && !saving\"\n :class=\"\n cn(\n 'flex items-center gap-2 rounded-md px-2 py-1 text-sm',\n reorderable && 'cursor-grab',\n dragging === column.name && 'opacity-50',\n dropTarget === column.name && dragging !== column.name && 'bg-muted',\n )\n \"\n @dragstart=\"dragStart($event, column.name)\"\n @dragover=\"dragOver($event, column.name)\"\n @dragleave=\"dropTarget === column.name && (dropTarget = null)\"\n @dragend=\"((dragging = null), (dropTarget = null))\"\n @drop.prevent=\"drop(column.name)\"\n >\n <GripVerticalIcon\n v-if=\"reorderable\"\n class=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n <label v-if=\"column.toggleable\" class=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n :model-value=\"visible.includes(column.name)\"\n @update:model-value=\"toggle(column.name)\"\n />\n <span class=\"truncate\">{{ column.label }}</span>\n </label>\n <span v-else class=\"min-w-0 flex-1 truncate\">{{ column.label }}</span>\n <span v-if=\"reorderable\" class=\"flex shrink-0 items-center\">\n <span\n :id=\"`${idBase}-${column.name}-position`\"\n class=\"sr-only\"\n data-slot=\"column-manager-position\"\n >{{ positionText(column.name) }}</span\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"(positions[column.name]?.position ?? 0) <= 1\"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_up', { column: column.label }, `Move ${column.label} up`)\n \"\n @click=\"move(column.name, -1, $event)\"\n ><ChevronUpIcon\n /></Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n :disabled=\"\n (positions[column.name]?.position ?? 0) >= (positions[column.name]?.count ?? 0)\n \"\n :aria-disabled=\"saving || undefined\"\n :aria-describedby=\"`${idBase}-${column.name}-position`\"\n :aria-label=\"\n t('table.move_column_down', { column: column.label }, `Move ${column.label} down`)\n \"\n @click=\"move(column.name, 1, $event)\"\n ><ChevronDownIcon\n /></Button>\n </span>\n </li>\n </ul>\n <div\n v-if=\"reorderable\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {{ announcement }}\n </div>\n <div\n v-if=\"(!inHeader && resetView) || applyView\"\n class=\"mt-1 flex items-center justify-end gap-2\"\n >\n <ResetButton v-if=\"!inHeader\" />\n <TableTriggerButton\n v-if=\"applyView\"\n :view=\"applyView\"\n data-slot=\"column-manager-apply\"\n :disabled=\"!draftChanged && !orderChanged\"\n @click=\"apply\"\n />\n </div>\n </div>\n </DefineBody>\n <Sheet\n v-if=\"props.table.columnManagerLayout === 'modal' && modal?.slideOver\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <SheetTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </SheetTrigger>\n <SheetContent\n :side=\"slideOverSide(modal.slideOverPosition, direction)\"\n :class=\"cn('flex w-full flex-col gap-0 p-0', modalWidthClass ?? 'sm:max-w-md')\"\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader class=\"border-b\">\n <SheetTitle>{{ modalHeading }}</SheetTitle>\n <SheetDescription :class=\"modal.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</SheetDescription>\n </SheetHeader>\n <div class=\"flex-1 overflow-y-auto p-4\"><ManagerBody /></div>\n </SheetContent>\n </Sheet>\n <Dialog\n v-else-if=\"props.table.columnManagerLayout === 'modal'\"\n :open=\"open\"\n @update:open=\"setOpen\"\n >\n <DialogTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </DialogTrigger>\n <DialogContent :class=\"modalWidthClass ?? 'sm:max-w-lg'\" data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{{ modalHeading }}</DialogTitle>\n <DialogDescription :class=\"modal?.description ? undefined : 'sr-only'\">{{\n modalDescription\n }}</DialogDescription>\n </DialogHeader>\n <div class=\"max-h-[60vh] overflow-y-auto\"><ManagerBody /></div>\n </DialogContent>\n </Dialog>\n <Popover v-else :open=\"open\" @update:open=\"setOpen\">\n <PopoverTrigger as-child>\n <TableTriggerButton :view=\"trigger\">\n <template #icon\n ><Icon name=\"alfakit::tables.actions.column-manager\" :fallback=\"Columns3Icon\"\n /></template>\n </TableTriggerButton>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n :class=\"cn('p-2', props.table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')\"\n data-slot=\"column-manager\"\n >\n <ManagerBody />\n </PopoverContent>\n </Popover>\n </template>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spreadsheet.js","names":["_sfc_main","Spreadsheet_vue_vue_type_script_setup_true_lang_default","Spreadsheet_default"],"sources":["../../../src/components/table/Spreadsheet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n PastePlan,\n SpreadsheetAnnouncement,\n SpreadsheetSaveResult,\n TableSchema,\n} from '@alfakit/core'\nimport {\n SPREADSHEET_CLASSES,\n SPREADSHEET_ISSUE_MESSAGES,\n SpreadsheetGrid,\n VIRTUAL_ESTIMATES,\n inertiaReload,\n saveSpreadsheetChanges,\n spreadsheetAnnouncement,\n spreadsheetAria,\n spreadsheetCellClass,\n spreadsheetCellId,\n spreadsheetColumns,\n spreadsheetRows,\n spreadsheetSelectionSize,\n virtualizeOptions,\n} from '@alfakit/core'\nimport { CheckIcon } from '@lucide/vue'\nimport { computed, nextTick, ref, useId, watch } from 'vue'\nimport { useStore } from '../../composables/use-store'\nimport { useVirtualList } from '../../composables/use-virtual-list'\nimport { useChoice, useDirection, usePanel, useT } from '../../context'\nimport { showToast } from '../notifications/flash-toasts'\nimport VirtualSpacerRow from '../schema/VirtualSpacerRow.vue'\nimport Button from '../ui/Button.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogFooter from '../ui/DialogFooter.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport Spinner from '../ui/Spinner.vue'\n\n/**\n * Spreadsheet mode of a `Table::spreadsheet()` table (loaded on first use): a keyboard\n * grid over the page's records. Edits stay local (highlighted) until \"Save\" sends them in\n * one request; the server validates each cell with its column's rules and the record's\n * `update` policy, and its errors show on the cells.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst emit = defineEmits<{ close: [] }>()\n\ninterface PastePreview {\n plan: PastePlan\n errors: SpreadsheetSaveResult['errors']\n validating: boolean\n}\n\nconst t = useT()\nconst choice = useChoice()\nconst direction = useDirection()\nconst panel = usePanel()\nconst baseId = `spreadsheet-${useId()}`\nconst columns = computed(() => spreadsheetColumns(props.table))\nconst rows = computed(() => spreadsheetRows(props.table.records ?? [], columns.value))\nconst grid = new SpreadsheetGrid(rows.value, columns.value)\nconst state = useStore(grid.store)\nconst announcement = ref('')\nconst preview = ref<PastePreview | null>(null)\nconst gridElement = ref<HTMLTableElement | null>(null)\nconst helpId = `${baseId}-help`\n\n// A reload (save, live update, polling) brings new records; edits of rows still on the\n// page are kept.\nwatch([rows, columns], ([nextRows, nextColumns]) => grid.setData(nextRows, nextColumns))\n\nconst virtual = useVirtualList(\n () => rows.value.length,\n () => virtualizeOptions(props.table.virtualize, VIRTUAL_ESTIMATES.row),\n)\n\nconst endpoint = computed(() => panel.value.endpoints.tableCells)\nconst errorList = computed(() => {\n void state.value\n\n return grid.errorCells()\n})\n\nfunction announce(message: SpreadsheetAnnouncement | null) {\n if (message) announcement.value = choice(message.key, message.count, {}, message.fallback)\n}\n\nfunction focusCell(row: number, col: number) {\n void nextTick(() => {\n const cell = document.getElementById(spreadsheetCellId(baseId, row, col))\n\n if (cell) {\n cell.focus()\n cell.scrollIntoView?.({ block: 'nearest', inline: 'nearest' })\n } else {\n virtual.list.scrollToIndex(row)\n }\n })\n}\n\n// Keep focus on the active cell (roving tabindex).\nwatch(\n () => [state.value.active, state.value.editing] as const,\n ([active, editing]) => {\n if (editing || !gridElement.value?.contains(document.activeElement)) return\n\n focusCell(active.row, active.col)\n },\n)\n\nasync function save() {\n const changes = grid.changes()\n\n if (!endpoint.value || changes.length === 0 || state.value.saving) return\n\n grid.setSaving(true)\n\n try {\n const result = await saveSpreadsheetChanges(\n endpoint.value,\n props.token,\n props.component,\n changes,\n )\n\n grid.applySaveResult(result)\n\n if (result.saved.length > 0) {\n announce(spreadsheetAnnouncement('saved', result.saved.length))\n inertiaReload()\n }\n\n if (result.errors.length > 0) announce(spreadsheetAnnouncement('failed', result.errors.length))\n } catch (error) {\n showToast({\n id: 'alfakit-spreadsheet-failed',\n title: t('table.spreadsheet.request_failed', {}, \"Couldn't save the changes\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n } finally {\n grid.setSaving(false)\n }\n}\n\nasync function undoSave() {\n const changes = grid.undoSaveChanges()\n\n if (!endpoint.value || changes.length === 0) return\n\n grid.setSaving(true)\n\n try {\n const result = await saveSpreadsheetChanges(\n endpoint.value,\n props.token,\n props.component,\n changes,\n )\n\n grid.applySaveResult(result)\n announce(spreadsheetAnnouncement('undo', result.saved.length))\n\n if (result.saved.length > 0) inertiaReload()\n } finally {\n grid.setSaving(false)\n }\n}\n\nfunction close() {\n if (\n grid.dirtyCount > 0 &&\n !window.confirm(t('table.spreadsheet.discard_confirm', {}, 'Discard your unsaved changes?'))\n ) {\n return\n }\n\n grid.discard()\n emit('close')\n}\n\nfunction onKeyDown(event: KeyboardEvent) {\n const result = grid.handleKey(event, direction.value)\n\n if (result === 'ignored') return\n\n event.preventDefault()\n\n if (result === 'save') {\n void save()\n } else if (result === 'undo') {\n announce(spreadsheetAnnouncement('undo', grid.undo()))\n } else if (result === 'fill' || result === 'clear') {\n announce(spreadsheetAnnouncement(result, grid.lastApplied))\n }\n\n if (result === 'commit' || result === 'cancel' || result === 'toggle') {\n focusCell(grid.state.active.row, grid.state.active.col)\n }\n}\n\nfunction onCopy(event: ClipboardEvent) {\n if (grid.state.editing || !event.clipboardData) return\n\n event.preventDefault()\n event.clipboardData.setData('text/plain', grid.copyText())\n announce(spreadsheetAnnouncement('copy', spreadsheetSelectionSize(grid.bounds())))\n}\n\nfunction onPaste(event: ClipboardEvent) {\n if (grid.state.editing || !event.clipboardData) return\n\n event.preventDefault()\n\n const plan = grid.planPaste(event.clipboardData.getData('text/plain'))\n\n if (plan.changes.length === 0 && plan.issues.length === 0) return\n\n const url = endpoint.value\n\n preview.value = { plan, errors: [], validating: Boolean(url && plan.changes.length > 0) }\n\n if (!url || plan.changes.length === 0) return\n\n // Validate the pasted values on the server before they are applied.\n saveSpreadsheetChanges(\n url,\n props.token,\n props.component,\n plan.changes.map((change) => ({\n record: grid.getRows()[change.row]?.key ?? '',\n column: grid.getColumns()[change.col]?.name ?? '',\n state: change.value,\n })),\n { dryRun: true },\n )\n .then((result) => {\n if (preview.value)\n preview.value = { ...preview.value, errors: result.errors, validating: false }\n })\n .catch(() => {\n if (preview.value) preview.value = { ...preview.value, validating: false }\n })\n}\n\nfunction applyPaste() {\n const current = preview.value\n\n if (!current) return\n\n const count = grid.apply(current.plan.changes)\n\n if (current.errors.length > 0) grid.setErrors(current.errors)\n\n announce(spreadsheetAnnouncement('paste', count))\n preview.value = null\n}\n\nfunction issueText(issue: PastePlan['issues'][number]): string {\n const message = SPREADSHEET_ISSUE_MESSAGES[issue.reason]\n\n return t(\n message.key,\n { text: issue.text, column: columns.value[issue.col]?.label ?? '', row: issue.row + 1 },\n message.fallback,\n )\n}\n\nfunction errorText(record: SpreadsheetSaveResult['errors'][number]): string {\n const position = grid.position(record.record, record.column)\n\n return t(\n 'table.spreadsheet.cell_error',\n {\n row: (position?.row ?? -1) + 1,\n column: columns.value.find((column) => column.name === record.column)?.label ?? record.column,\n message: record.messages.join(' '),\n },\n 'Row :row, :column: :message',\n )\n}\n\nfunction draft(): string {\n const value = state.value.editing?.value\n\n return value === null || value === undefined ? '' : String(value)\n}\n\nfunction isEditing(row: number, col: number): boolean {\n return state.value.editing?.row === row && state.value.editing.col === col\n}\n\nfunction onSelectChange(event: Event, row: number, col: number) {\n grid.updateDraft((event.target as HTMLSelectElement).value)\n grid.commitEdit()\n focusCell(row, col)\n}\n\nconst optionText = (label: string) => label.replace(/<[^>]*>/g, '')\n\n/** Focus an editor as soon as it is shown. */\nconst vFocus = { mounted: (element: HTMLElement) => element.focus() }\n</script>\n\n<template>\n <div data-slot=\"spreadsheet\" class=\"flex flex-col gap-3\">\n <div data-slot=\"spreadsheet-toolbar\" class=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n :disabled=\"grid.dirtyCount === 0 || state.saving\"\n @click=\"save\"\n >\n <Spinner v-if=\"state.saving\" />\n {{ t('table.spreadsheet.save', {}, 'Save changes') }}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"!grid.canUndo || state.saving\"\n @click=\"announce(spreadsheetAnnouncement('undo', grid.undo()))\"\n >\n {{ t('table.spreadsheet.undo', {}, 'Undo') }}\n </Button>\n <Button\n v-if=\"grid.canUndoSave\"\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"state.saving\"\n @click=\"undoSave\"\n >\n {{ t('table.spreadsheet.undo_save', {}, 'Undo last save') }}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n :disabled=\"grid.dirtyCount === 0 || state.saving\"\n @click=\"grid.discard()\"\n >\n {{ t('table.spreadsheet.discard', {}, 'Discard') }}\n </Button>\n <span class=\"text-muted-foreground text-sm\" aria-live=\"polite\">\n <template v-if=\"grid.dirtyCount > 0\">{{\n choice(\n 'table.spreadsheet.unsaved',\n grid.dirtyCount,\n {},\n ':count unsaved change|:count unsaved changes',\n )\n }}</template>\n <span v-if=\"grid.errorCount > 0\" class=\"text-destructive ms-2\">{{\n choice('table.spreadsheet.errors', grid.errorCount, {}, ':count error|:count errors')\n }}</span>\n </span>\n <span class=\"flex-1\" />\n <Button type=\"button\" size=\"sm\" variant=\"outline\" @click=\"close\">\n {{ t('table.spreadsheet.close', {}, 'Done') }}\n </Button>\n </div>\n <p :id=\"helpId\" class=\"sr-only\">\n {{\n t(\n 'table.spreadsheet.help',\n {},\n 'Arrow keys move between cells. Enter or F2 edits a cell, Escape cancels, Tab moves to the next cell. Ctrl+C copies, Ctrl+V pastes, Ctrl+D fills down, Ctrl+Z undoes and Ctrl+S saves.',\n )\n }}\n </p>\n <div data-surface=\"table\" :class=\"SPREADSHEET_CLASSES.root\" style=\"max-height: 70vh\">\n <table\n ref=\"gridElement\"\n role=\"grid\"\n :aria-label=\"props.table.heading ?? t('table.spreadsheet.label', {}, 'Spreadsheet')\"\n :aria-describedby=\"helpId\"\n :aria-rowcount=\"rows.length + 1\"\n :aria-colcount=\"columns.length\"\n aria-multiselectable=\"true\"\n :aria-busy=\"state.saving || undefined\"\n data-slot=\"spreadsheet-grid\"\n :class=\"SPREADSHEET_CLASSES.table\"\n @keydown=\"onKeyDown\"\n @copy=\"onCopy\"\n @paste=\"onPaste\"\n >\n <thead>\n <tr aria-rowindex=\"1\">\n <th\n v-for=\"(column, col) in columns\"\n :key=\"column.name\"\n role=\"columnheader\"\n scope=\"col\"\n :aria-colindex=\"col + 1\"\n :class=\"SPREADSHEET_CLASSES.header\"\n >\n {{ column.label }}\n </th>\n </tr>\n </thead>\n <tbody :ref=\"virtual.attach\">\n <VirtualSpacerRow :height=\"virtual.range.value.before\" :col-span=\"columns.length\" />\n <template v-for=\"row in virtual.indexes.value\" :key=\"String(rows[row]?.key ?? row)\">\n <tr v-if=\"rows[row]\" :ref=\"virtual.item\" :data-index=\"row\" :aria-rowindex=\"row + 2\">\n <td\n v-for=\"(column, col) in columns\"\n :id=\"spreadsheetCellId(baseId, row, col)\"\n :key=\"column.name\"\n role=\"gridcell\"\n v-bind=\"spreadsheetAria(row, col)\"\n :tabindex=\"grid.isActive(row, col) ? 0 : -1\"\n :aria-selected=\"grid.isSelected(row, col)\"\n :aria-readonly=\"!grid.isEditable(row, col) || undefined\"\n :aria-invalid=\"grid.errorsAt(row, col).length > 0 || undefined\"\n :title=\"grid.errorsAt(row, col).join(' ') || undefined\"\n data-slot=\"spreadsheet-cell\"\n :data-dirty=\"grid.isDirty(row, col) || undefined\"\n :data-active=\"grid.isActive(row, col) || undefined\"\n :class=\"spreadsheetCellClass(grid, row, col)\"\n @mousedown=\"!isEditing(row, col) && grid.moveTo(row, col, $event.shiftKey)\"\n @dblclick=\"grid.beginEdit()\"\n >\n <select\n v-if=\"isEditing(row, col) && column.kind === 'select'\"\n v-focus\n :class=\"SPREADSHEET_CLASSES.editor\"\n :aria-label=\"column.label\"\n :value=\"draft()\"\n @change=\"onSelectChange($event, row, col)\"\n @blur=\"grid.cancelEdit()\"\n >\n <option value=\"\">{{ t('table.spreadsheet.no_value', {}, '—') }}</option>\n <option\n v-for=\"option in column.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ optionText(option.label) }}\n </option>\n </select>\n <input\n v-else-if=\"isEditing(row, col)\"\n v-focus\n :type=\"column.kind === 'number' ? 'text' : (column.inputType ?? 'text')\"\n :inputmode=\"column.kind === 'number' ? 'decimal' : undefined\"\n :class=\"SPREADSHEET_CLASSES.editor\"\n :aria-label=\"column.label\"\n :value=\"draft()\"\n @input=\"grid.updateDraft(($event.target as HTMLInputElement).value)\"\n @blur=\"grid.commitEdit()\"\n />\n <span v-else-if=\"column.kind === 'boolean'\" class=\"inline-flex items-center gap-1\">\n <span\n aria-hidden=\"true\"\n class=\"border-input data-[checked=true]:bg-primary data-[checked=true]:text-primary-foreground inline-flex size-4 items-center justify-center rounded-[4px] border\"\n :data-checked=\"Boolean(grid.value(row, col))\"\n >\n <CheckIcon v-if=\"grid.value(row, col)\" class=\"size-3\" />\n </span>\n <span class=\"sr-only\">{{\n grid.value(row, col)\n ? t('table.spreadsheet.yes', {}, 'Yes')\n : t('table.spreadsheet.no', {}, 'No')\n }}</span>\n </span>\n <template v-else>{{ grid.text(row, col) }}</template>\n <span v-if=\"grid.errorsAt(row, col).length > 0\" class=\"sr-only\">{{\n grid.errorsAt(row, col).join(' ')\n }}</span>\n </td>\n </tr>\n </template>\n <VirtualSpacerRow :height=\"virtual.range.value.after\" :col-span=\"columns.length\" />\n </tbody>\n </table>\n <p v-if=\"rows.length === 0\" class=\"text-muted-foreground p-6 text-center text-sm\">\n {{ t('table.spreadsheet.empty', {}, 'No records to edit.') }}\n </p>\n </div>\n <ul\n v-if=\"errorList.length > 0\"\n data-slot=\"spreadsheet-errors\"\n class=\"text-destructive flex flex-col gap-1 text-sm\"\n >\n <li v-for=\"error in errorList\" :key=\"error.key\">\n <button\n type=\"button\"\n class=\"text-start hover:underline\"\n @click=\"\n () => {\n grid.moveTo(error.row, error.col)\n focusCell(error.row, error.col)\n }\n \"\n >\n {{\n t(\n 'table.spreadsheet.cell_error',\n {\n row: error.row + 1,\n column: columns[error.col]?.label ?? '',\n message: error.messages.join(' '),\n },\n 'Row :row, :column: :message',\n )\n }}\n </button>\n </li>\n </ul>\n <span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"spreadsheet-announcement\"\n >{{ announcement }}</span\n >\n\n <Dialog :open=\"preview !== null\" @update:open=\"(value) => !value && (preview = null)\">\n <DialogContent class=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{{ t('table.spreadsheet.paste_title', {}, 'Paste cells') }}</DialogTitle>\n <DialogDescription>\n {{\n choice(\n 'table.spreadsheet.paste_summary',\n preview?.plan.changes.length ?? 0,\n {},\n ':count cell will change|:count cells will change',\n )\n }}\n <template v-if=\"(preview?.plan.skipped ?? 0) > 0\">\n {{\n choice(\n 'table.spreadsheet.paste_skipped',\n preview?.plan.skipped ?? 0,\n {},\n ':count read-only cell is skipped|:count read-only cells are skipped',\n )\n }}\n </template>\n </DialogDescription>\n </DialogHeader>\n <div data-slot=\"spreadsheet-paste\" class=\"flex flex-col gap-3\">\n <p\n v-if=\"preview?.validating\"\n class=\"text-muted-foreground flex items-center gap-2 text-sm\"\n >\n <Spinner />\n {{ t('table.spreadsheet.paste_validating', {}, 'Checking the values…') }}\n </p>\n <ul\n v-if=\"preview && (preview.plan.issues.length > 0 || preview.errors.length > 0)\"\n class=\"text-destructive flex max-h-48 flex-col gap-1 overflow-auto text-sm\"\n >\n <li v-for=\"issue in preview.plan.issues\" :key=\"`issue-${issue.row}-${issue.col}`\">\n {{ issueText(issue) }}\n </li>\n <li\n v-for=\"error in preview.errors\"\n :key=\"`error-${String(error.record)}-${error.column}`\"\n >\n {{ errorText(error) }}\n </li>\n </ul>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" @click=\"preview = null\">{{\n t('cancel', {}, 'Cancel')\n }}</Button>\n <Button\n type=\"button\"\n :disabled=\"!preview || preview.plan.changes.length === 0\"\n @click=\"applyPaste\"\n >\n {{ t('table.spreadsheet.paste_apply', {}, 'Apply') }}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Spreadsheet.js","names":["_sfc_main","Spreadsheet_vue_vue_type_script_setup_true_lang_default","Spreadsheet_default"],"sources":["../../../src/components/table/Spreadsheet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n PastePlan,\n SpreadsheetAnnouncement,\n SpreadsheetSaveResult,\n TableSchema,\n} from '@alfakit/core'\nimport {\n SPREADSHEET_CLASSES,\n SPREADSHEET_ISSUE_MESSAGES,\n SpreadsheetGrid,\n VIRTUAL_ESTIMATES,\n inertiaReload,\n saveSpreadsheetChanges,\n spreadsheetAnnouncement,\n spreadsheetAria,\n spreadsheetCellClass,\n spreadsheetCellId,\n spreadsheetColumns,\n spreadsheetRows,\n spreadsheetSelectionSize,\n virtualizeOptions,\n} from '@alfakit/core'\nimport { CheckIcon } from '@lucide/vue'\nimport { computed, nextTick, ref, useId, watch } from 'vue'\nimport { useStore } from '../../composables/use-store'\nimport { useVirtualList } from '../../composables/use-virtual-list'\nimport { useChoice, useDirection, usePanel, useT } from '../../context'\nimport { showToast } from '../notifications/flash-toasts'\nimport VirtualSpacerRow from '../schema/VirtualSpacerRow.vue'\nimport Button from '../ui/Button.vue'\nimport Dialog from '../ui/Dialog.vue'\nimport DialogContent from '../ui/DialogContent.vue'\nimport DialogDescription from '../ui/DialogDescription.vue'\nimport DialogFooter from '../ui/DialogFooter.vue'\nimport DialogHeader from '../ui/DialogHeader.vue'\nimport DialogTitle from '../ui/DialogTitle.vue'\nimport Spinner from '../ui/Spinner.vue'\n\n/**\n * Spreadsheet mode of a `Table::spreadsheet()` table (loaded on first use): a keyboard\n * grid over the page's records. Edits stay local (highlighted) until \"Save\" sends them in\n * one request; the server validates each cell with its column's rules and the record's\n * `update` policy, and its errors show on the cells.\n */\nconst props = defineProps<{ table: TableSchema; token: string; component: string }>()\nconst emit = defineEmits<{ close: [] }>()\n\ninterface PastePreview {\n plan: PastePlan\n errors: SpreadsheetSaveResult['errors']\n validating: boolean\n}\n\nconst t = useT()\nconst choice = useChoice()\nconst direction = useDirection()\nconst panel = usePanel()\nconst baseId = `spreadsheet-${useId()}`\nconst columns = computed(() => spreadsheetColumns(props.table))\nconst rows = computed(() => spreadsheetRows(props.table.records ?? [], columns.value))\nconst grid = new SpreadsheetGrid(rows.value, columns.value)\nconst state = useStore(grid.store)\nconst announcement = ref('')\nconst preview = ref<PastePreview | null>(null)\nconst gridElement = ref<HTMLTableElement | null>(null)\nconst helpId = `${baseId}-help`\n\n// A reload (save, live update, polling) brings new records; edits of rows still on the\n// page are kept.\nwatch([rows, columns], ([nextRows, nextColumns]) => grid.setData(nextRows, nextColumns))\n\nconst virtual = useVirtualList(\n () => rows.value.length,\n () => virtualizeOptions(props.table.virtualize, VIRTUAL_ESTIMATES.row),\n)\n\nconst endpoint = computed(() => panel.value.endpoints.tableCells)\nconst errorList = computed(() => {\n void state.value\n\n return grid.errorCells()\n})\n\nfunction announce(message: SpreadsheetAnnouncement | null) {\n if (message) announcement.value = choice(message.key, message.count, {}, message.fallback)\n}\n\nfunction focusCell(row: number, col: number) {\n void nextTick(() => {\n const cell = document.getElementById(spreadsheetCellId(baseId, row, col))\n\n if (cell) {\n cell.focus()\n cell.scrollIntoView?.({ block: 'nearest', inline: 'nearest' })\n } else {\n virtual.list.scrollToIndex(row)\n }\n })\n}\n\n// Keep focus on the active cell (roving tabindex).\nwatch(\n () => [state.value.active, state.value.editing] as const,\n ([active, editing]) => {\n if (editing || !gridElement.value?.contains(document.activeElement)) return\n\n focusCell(active.row, active.col)\n },\n)\n\nasync function save() {\n const changes = grid.changes()\n\n if (!endpoint.value || changes.length === 0 || state.value.saving) return\n\n grid.setSaving(true)\n\n try {\n const result = await saveSpreadsheetChanges(\n endpoint.value,\n props.token,\n props.component,\n changes,\n )\n\n grid.applySaveResult(result)\n\n if (result.saved.length > 0) {\n announce(spreadsheetAnnouncement('saved', result.saved.length))\n inertiaReload()\n }\n\n if (result.errors.length > 0) announce(spreadsheetAnnouncement('failed', result.errors.length))\n } catch (error) {\n showToast({\n id: 'alfakit-spreadsheet-failed',\n title: t('table.spreadsheet.request_failed', {}, \"Couldn't save the changes\"),\n body: error instanceof Error ? error.message : String(error),\n status: 'danger',\n format: 'alfakit',\n })\n } finally {\n grid.setSaving(false)\n }\n}\n\nasync function undoSave() {\n const changes = grid.undoSaveChanges()\n\n if (!endpoint.value || changes.length === 0) return\n\n grid.setSaving(true)\n\n try {\n const result = await saveSpreadsheetChanges(\n endpoint.value,\n props.token,\n props.component,\n changes,\n )\n\n grid.applySaveResult(result)\n announce(spreadsheetAnnouncement('undo', result.saved.length))\n\n if (result.saved.length > 0) inertiaReload()\n } finally {\n grid.setSaving(false)\n }\n}\n\nfunction close() {\n if (\n grid.dirtyCount > 0 &&\n !window.confirm(t('table.spreadsheet.discard_confirm', {}, 'Discard your unsaved changes?'))\n ) {\n return\n }\n\n grid.discard()\n emit('close')\n}\n\nfunction onKeyDown(event: KeyboardEvent) {\n const result = grid.handleKey(event, direction.value)\n\n if (result === 'ignored') return\n\n event.preventDefault()\n\n if (result === 'save') {\n void save()\n } else if (result === 'undo') {\n announce(spreadsheetAnnouncement('undo', grid.undo()))\n } else if (result === 'fill' || result === 'clear') {\n announce(spreadsheetAnnouncement(result, grid.lastApplied))\n }\n\n if (result === 'commit' || result === 'cancel' || result === 'toggle') {\n focusCell(grid.state.active.row, grid.state.active.col)\n }\n}\n\nfunction onCopy(event: ClipboardEvent) {\n if (grid.state.editing || !event.clipboardData) return\n\n event.preventDefault()\n event.clipboardData.setData('text/plain', grid.copyText())\n announce(spreadsheetAnnouncement('copy', spreadsheetSelectionSize(grid.bounds())))\n}\n\nfunction onPaste(event: ClipboardEvent) {\n if (grid.state.editing || !event.clipboardData) return\n\n event.preventDefault()\n\n const plan = grid.planPaste(event.clipboardData.getData('text/plain'))\n\n if (plan.changes.length === 0 && plan.issues.length === 0) return\n\n const url = endpoint.value\n\n preview.value = { plan, errors: [], validating: Boolean(url && plan.changes.length > 0) }\n\n if (!url || plan.changes.length === 0) return\n\n // Validate the pasted values on the server before they are applied.\n saveSpreadsheetChanges(\n url,\n props.token,\n props.component,\n plan.changes.map((change) => ({\n record: grid.getRows()[change.row]?.key ?? '',\n column: grid.getColumns()[change.col]?.name ?? '',\n state: change.value,\n })),\n { dryRun: true },\n )\n .then((result) => {\n if (preview.value)\n preview.value = { ...preview.value, errors: result.errors, validating: false }\n })\n .catch(() => {\n if (preview.value) preview.value = { ...preview.value, validating: false }\n })\n}\n\nfunction applyPaste() {\n const current = preview.value\n\n if (!current) return\n\n const count = grid.apply(current.plan.changes)\n\n if (current.errors.length > 0) grid.setErrors(current.errors)\n\n announce(spreadsheetAnnouncement('paste', count))\n preview.value = null\n}\n\nfunction issueText(issue: PastePlan['issues'][number]): string {\n const message = SPREADSHEET_ISSUE_MESSAGES[issue.reason]\n\n return t(\n message.key,\n { text: issue.text, column: columns.value[issue.col]?.label ?? '', row: issue.row + 1 },\n message.fallback,\n )\n}\n\nfunction errorText(record: SpreadsheetSaveResult['errors'][number]): string {\n const position = grid.position(record.record, record.column)\n\n return t(\n 'table.spreadsheet.cell_error',\n {\n row: (position?.row ?? -1) + 1,\n column: columns.value.find((column) => column.name === record.column)?.label ?? record.column,\n message: record.messages.join(' '),\n },\n 'Row :row, :column: :message',\n )\n}\n\nfunction draft(): string {\n const value = state.value.editing?.value\n\n return value === null || value === undefined ? '' : String(value)\n}\n\nfunction isEditing(row: number, col: number): boolean {\n return state.value.editing?.row === row && state.value.editing.col === col\n}\n\nfunction onSelectChange(event: Event, row: number, col: number) {\n grid.updateDraft((event.target as HTMLSelectElement).value)\n grid.commitEdit()\n focusCell(row, col)\n}\n\nconst optionText = (label: string) => label.replace(/<[^>]*>/g, '')\n\n/** Focus an editor as soon as it is shown. */\nconst vFocus = { mounted: (element: HTMLElement) => element.focus() }\n</script>\n\n<template>\n <div data-slot=\"spreadsheet\" class=\"flex flex-col gap-3\">\n <div data-slot=\"spreadsheet-toolbar\" class=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n :disabled=\"grid.dirtyCount === 0 || state.saving\"\n @click=\"save\"\n >\n <Spinner v-if=\"state.saving\" />\n {{ t('table.spreadsheet.save', {}, 'Save changes') }}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"!grid.canUndo || state.saving\"\n @click=\"announce(spreadsheetAnnouncement('undo', grid.undo()))\"\n >\n {{ t('table.spreadsheet.undo', {}, 'Undo') }}\n </Button>\n <Button\n v-if=\"grid.canUndoSave\"\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"state.saving\"\n @click=\"undoSave\"\n >\n {{ t('table.spreadsheet.undo_save', {}, 'Undo last save') }}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n :disabled=\"grid.dirtyCount === 0 || state.saving\"\n @click=\"grid.discard()\"\n >\n {{ t('table.spreadsheet.discard', {}, 'Discard') }}\n </Button>\n <span class=\"text-muted-foreground text-sm\" aria-live=\"polite\">\n <template v-if=\"grid.dirtyCount > 0\">{{\n choice(\n 'table.spreadsheet.unsaved',\n grid.dirtyCount,\n {},\n ':count unsaved change|:count unsaved changes',\n )\n }}</template>\n <span v-if=\"grid.errorCount > 0\" class=\"text-destructive ms-2\">{{\n choice('table.spreadsheet.errors', grid.errorCount, {}, ':count error|:count errors')\n }}</span>\n </span>\n <span class=\"flex-1\" />\n <Button type=\"button\" size=\"sm\" variant=\"outline\" @click=\"close\">\n {{ t('table.spreadsheet.close', {}, 'Done') }}\n </Button>\n </div>\n <p :id=\"helpId\" class=\"sr-only\">\n {{\n t(\n 'table.spreadsheet.help',\n {},\n 'Arrow keys move between cells. Enter or F2 edits a cell, Escape cancels, Tab moves to the next cell. Ctrl+C copies, Ctrl+V pastes, Ctrl+D fills down, Ctrl+Z undoes and Ctrl+S saves.',\n )\n }}\n </p>\n <div data-surface=\"table\" :class=\"SPREADSHEET_CLASSES.root\" style=\"max-height: 70vh\">\n <table\n ref=\"gridElement\"\n role=\"grid\"\n :aria-label=\"props.table.heading ?? t('table.spreadsheet.label', {}, 'Spreadsheet')\"\n :aria-describedby=\"helpId\"\n :aria-rowcount=\"rows.length + 1\"\n :aria-colcount=\"columns.length\"\n aria-multiselectable=\"true\"\n :aria-busy=\"state.saving || undefined\"\n data-slot=\"spreadsheet-grid\"\n :class=\"SPREADSHEET_CLASSES.table\"\n @keydown=\"onKeyDown\"\n @copy=\"onCopy\"\n @paste=\"onPaste\"\n >\n <thead>\n <tr aria-rowindex=\"1\">\n <th\n v-for=\"(column, col) in columns\"\n :key=\"column.name\"\n role=\"columnheader\"\n scope=\"col\"\n :aria-colindex=\"col + 1\"\n :class=\"SPREADSHEET_CLASSES.header\"\n >\n {{ column.label }}\n </th>\n </tr>\n </thead>\n <tbody :ref=\"virtual.attach\">\n <VirtualSpacerRow :height=\"virtual.range.value.before\" :col-span=\"columns.length\" />\n <template v-for=\"row in virtual.indexes.value\" :key=\"String(rows[row]?.key ?? row)\">\n <tr v-if=\"rows[row]\" :ref=\"virtual.item\" :data-index=\"row\" :aria-rowindex=\"row + 2\">\n <td\n v-for=\"(column, col) in columns\"\n :id=\"spreadsheetCellId(baseId, row, col)\"\n :key=\"column.name\"\n role=\"gridcell\"\n v-bind=\"spreadsheetAria(row, col)\"\n :tabindex=\"grid.isActive(row, col) ? 0 : -1\"\n :aria-selected=\"grid.isSelected(row, col)\"\n :aria-readonly=\"!grid.isEditable(row, col) || undefined\"\n :aria-invalid=\"grid.errorsAt(row, col).length > 0 || undefined\"\n :title=\"grid.errorsAt(row, col).join(' ') || undefined\"\n data-slot=\"spreadsheet-cell\"\n :data-dirty=\"grid.isDirty(row, col) || undefined\"\n :data-active=\"grid.isActive(row, col) || undefined\"\n :class=\"spreadsheetCellClass(grid, row, col)\"\n @mousedown=\"!isEditing(row, col) && grid.moveTo(row, col, $event.shiftKey)\"\n @dblclick=\"grid.beginEdit()\"\n >\n <select\n v-if=\"isEditing(row, col) && column.kind === 'select'\"\n v-focus\n :class=\"SPREADSHEET_CLASSES.editor\"\n :aria-label=\"column.label\"\n :value=\"draft()\"\n @change=\"onSelectChange($event, row, col)\"\n @blur=\"grid.cancelEdit()\"\n >\n <option value=\"\">{{ t('table.spreadsheet.no_value', {}, '—') }}</option>\n <option\n v-for=\"option in column.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ optionText(option.label) }}\n </option>\n </select>\n <input\n v-else-if=\"isEditing(row, col)\"\n v-focus\n :type=\"column.kind === 'number' ? 'text' : (column.inputType ?? 'text')\"\n :inputmode=\"column.kind === 'number' ? 'decimal' : undefined\"\n :class=\"SPREADSHEET_CLASSES.editor\"\n :aria-label=\"column.label\"\n :value=\"draft()\"\n @input=\"grid.updateDraft(($event.target as HTMLInputElement).value)\"\n @blur=\"grid.commitEdit()\"\n />\n <span v-else-if=\"column.kind === 'boolean'\" class=\"inline-flex items-center gap-1\">\n <span\n aria-hidden=\"true\"\n class=\"border-input data-[checked=true]:bg-primary data-[checked=true]:text-primary-foreground inline-flex size-4 items-center justify-center rounded-[4px] border\"\n :data-checked=\"Boolean(grid.value(row, col))\"\n >\n <CheckIcon v-if=\"grid.value(row, col)\" class=\"size-3\" />\n </span>\n <span class=\"sr-only\">{{\n grid.value(row, col)\n ? t('table.spreadsheet.yes', {}, 'Yes')\n : t('table.spreadsheet.no', {}, 'No')\n }}</span>\n </span>\n <template v-else>{{ grid.text(row, col) }}</template>\n <span v-if=\"grid.errorsAt(row, col).length > 0\" class=\"sr-only\">{{\n grid.errorsAt(row, col).join(' ')\n }}</span>\n </td>\n </tr>\n </template>\n <VirtualSpacerRow :height=\"virtual.range.value.after\" :col-span=\"columns.length\" />\n </tbody>\n </table>\n <p v-if=\"rows.length === 0\" class=\"text-muted-foreground p-6 text-center text-sm\">\n {{ t('table.spreadsheet.empty', {}, 'No records to edit.') }}\n </p>\n </div>\n <ul\n v-if=\"errorList.length > 0\"\n data-slot=\"spreadsheet-errors\"\n class=\"text-destructive flex flex-col gap-1 text-sm\"\n >\n <li v-for=\"error in errorList\" :key=\"error.key\">\n <button\n type=\"button\"\n class=\"text-start hover:underline\"\n @click=\"\n () => {\n grid.moveTo(error.row, error.col)\n focusCell(error.row, error.col)\n }\n \"\n >\n {{\n t(\n 'table.spreadsheet.cell_error',\n {\n row: error.row + 1,\n column: columns[error.col]?.label ?? '',\n message: error.messages.join(' '),\n },\n 'Row :row, :column: :message',\n )\n }}\n </button>\n </li>\n </ul>\n <span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n class=\"sr-only\"\n data-slot=\"spreadsheet-announcement\"\n >{{ announcement }}</span\n >\n\n <Dialog :open=\"preview !== null\" @update:open=\"(value) => !value && (preview = null)\">\n <DialogContent class=\"sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{{ t('table.spreadsheet.paste_title', {}, 'Paste cells') }}</DialogTitle>\n <DialogDescription>\n <!-- One line per count: translations can't join two sentences. -->\n <span class=\"block\">{{\n choice(\n 'table.spreadsheet.paste_summary',\n preview?.plan.changes.length ?? 0,\n {},\n ':count cell will change|:count cells will change',\n )\n }}</span>\n <span\n v-if=\"(preview?.plan.skipped ?? 0) > 0\"\n class=\"block\"\n data-slot=\"spreadsheet-paste-skipped\"\n >\n {{\n choice(\n 'table.spreadsheet.paste_skipped',\n preview?.plan.skipped ?? 0,\n {},\n ':count read-only cell is skipped|:count read-only cells are skipped',\n )\n }}\n </span>\n </DialogDescription>\n </DialogHeader>\n <div data-slot=\"spreadsheet-paste\" class=\"flex flex-col gap-3\">\n <p\n v-if=\"preview?.validating\"\n class=\"text-muted-foreground flex items-center gap-2 text-sm\"\n >\n <Spinner />\n {{ t('table.spreadsheet.paste_validating', {}, 'Checking the values…') }}\n </p>\n <ul\n v-if=\"preview && (preview.plan.issues.length > 0 || preview.errors.length > 0)\"\n class=\"text-destructive flex max-h-48 flex-col gap-1 overflow-auto text-sm\"\n >\n <li v-for=\"issue in preview.plan.issues\" :key=\"`issue-${issue.row}-${issue.col}`\">\n {{ issueText(issue) }}\n </li>\n <li\n v-for=\"error in preview.errors\"\n :key=\"`error-${String(error.record)}-${error.column}`\"\n >\n {{ errorText(error) }}\n </li>\n </ul>\n </div>\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" @click=\"preview = null\">{{\n t('cancel', {}, 'Cancel')\n }}</Button>\n <Button\n type=\"button\"\n :disabled=\"!preview || preview.plan.changes.length === 0\"\n @click=\"applyPaste\"\n >\n {{ t('table.spreadsheet.paste_apply', {}, 'Apply') }}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spreadsheet.vue.d.ts","sourceRoot":"","sources":["../../../src/components/table/Spreadsheet.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Spreadsheet.vue.d.ts","sourceRoot":"","sources":["../../../src/components/table/Spreadsheet.vue"],"names":[],"mappings":"AAglBA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,eAAe,CAAA;AAiCtB;;;;;GAKG;AACH,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;;;;;;AAg8B5E,wBAOG"}
|