@alfakit/react 0.1.1 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/components/actions/action-button.d.ts.map +1 -1
- package/dist/components/actions/action-button.js +5 -4
- package/dist/components/actions/action-button.js.map +1 -1
- package/dist/components/actions/action-modals.d.ts.map +1 -1
- package/dist/components/actions/action-modals.js +6 -5
- package/dist/components/actions/action-modals.js.map +1 -1
- package/dist/components/ai/copilot.d.ts +3 -1
- package/dist/components/ai/copilot.d.ts.map +1 -1
- package/dist/components/ai/copilot.js +4 -1
- package/dist/components/ai/copilot.js.map +1 -1
- package/dist/components/cms/live-preview.js +3 -2
- package/dist/components/cms/live-preview.js.map +1 -1
- package/dist/components/cms/page.js +1 -1
- package/dist/components/fields/builder.js +15 -6
- package/dist/components/fields/builder.js.map +1 -1
- package/dist/components/fields/color-picker.js +1 -0
- package/dist/components/fields/color-picker.js.map +1 -1
- package/dist/components/fields/file-upload.js +11 -10
- package/dist/components/fields/file-upload.js.map +1 -1
- package/dist/components/fields/image-editor-dialog.js +4 -2
- package/dist/components/fields/image-editor-dialog.js.map +1 -1
- package/dist/components/fields/item-actions.js +10 -10
- package/dist/components/fields/item-actions.js.map +1 -1
- package/dist/components/fields/key-value.d.ts.map +1 -1
- package/dist/components/fields/key-value.js +3 -3
- package/dist/components/fields/key-value.js.map +1 -1
- package/dist/components/fields/modal-table-select.js +6 -5
- package/dist/components/fields/modal-table-select.js.map +1 -1
- package/dist/components/fields/one-time-code-input.js +1 -1
- package/dist/components/fields/one-time-code-input.js.map +1 -1
- package/dist/components/fields/repeater-table.js +12 -1
- package/dist/components/fields/repeater-table.js.map +1 -1
- package/dist/components/fields/repeater.js +2 -2
- package/dist/components/fields/repeater.js.map +1 -1
- package/dist/components/fields/rich-editor.js +2 -1
- package/dist/components/fields/rich-editor.js.map +1 -1
- package/dist/components/fields/select.js +3 -3
- package/dist/components/fields/select.js.map +1 -1
- package/dist/components/file-manager/file-manager.js +1 -1
- package/dist/components/file-manager/file-manager.js.map +1 -1
- package/dist/components/form/form-block.d.ts.map +1 -1
- package/dist/components/form/form-block.js +14 -2
- package/dist/components/form/form-block.js.map +1 -1
- package/dist/components/form/form-context.js +7 -3
- package/dist/components/form/form-context.js.map +1 -1
- package/dist/components/kanban/kanban.js +4 -3
- package/dist/components/kanban/kanban.js.map +1 -1
- package/dist/components/layout/fused-group.js +1 -0
- package/dist/components/layout/fused-group.js.map +1 -1
- package/dist/components/layout/section.js +2 -2
- package/dist/components/layout/section.js.map +1 -1
- package/dist/components/layout/tabs.d.ts.map +1 -1
- package/dist/components/layout/tabs.js +54 -15
- package/dist/components/layout/tabs.js.map +1 -1
- package/dist/components/panel/app-sidebar.d.ts +2 -3
- package/dist/components/panel/app-sidebar.d.ts.map +1 -1
- package/dist/components/panel/app-sidebar.js +1 -24
- package/dist/components/panel/app-sidebar.js.map +1 -1
- package/dist/components/panel/brand.d.ts +10 -0
- package/dist/components/panel/brand.d.ts.map +1 -0
- package/dist/components/panel/brand.js +37 -0
- package/dist/components/panel/brand.js.map +1 -0
- package/dist/components/panel/global-search.js +1 -0
- package/dist/components/panel/global-search.js.map +1 -1
- package/dist/components/panel/index.d.ts +2 -1
- package/dist/components/panel/shortcut-keys.js +1 -0
- package/dist/components/panel/shortcut-keys.js.map +1 -1
- package/dist/components/panel/tenant-menu.js +2 -2
- package/dist/components/panel/tenant-menu.js.map +1 -1
- package/dist/components/panel/topbar.js +1 -1
- package/dist/components/realtime/live-records-pill.d.ts.map +1 -1
- package/dist/components/realtime/live-records-pill.js +6 -3
- package/dist/components/realtime/live-records-pill.js.map +1 -1
- package/dist/components/table/cells.d.ts +6 -0
- package/dist/components/table/cells.d.ts.map +1 -1
- package/dist/components/table/cells.js +15 -3
- package/dist/components/table/cells.js.map +1 -1
- package/dist/components/table/column-manager.js +8 -7
- package/dist/components/table/column-manager.js.map +1 -1
- package/dist/components/table/content-layout.js +2 -1
- package/dist/components/table/content-layout.js.map +1 -1
- package/dist/components/table/grouping.js +11 -1
- package/dist/components/table/grouping.js.map +1 -1
- package/dist/components/table/pagination.js +16 -13
- package/dist/components/table/pagination.js.map +1 -1
- package/dist/components/table/table-block.js +14 -4
- package/dist/components/table/table-block.js.map +1 -1
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +3 -1
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +3 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +3 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/tooltip.js +3 -1
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/components.css +347 -0
- package/dist/context.d.ts +5 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +5 -1
- package/dist/context.js.map +1 -1
- package/dist/create-alfakit.d.ts.map +1 -1
- package/dist/create-alfakit.js +8 -5
- package/dist/create-alfakit.js.map +1 -1
- package/dist/eject-manifest.json +16 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +24 -21
- package/dist/layouts/app-layout.d.ts.map +1 -1
- package/dist/layouts/app-layout.js +2 -29
- package/dist/layouts/app-layout.js.map +1 -1
- package/dist/layouts/auth-layout.js +2 -2
- package/dist/layouts/auth-layout.js.map +1 -1
- package/dist/layouts/fullscreen-layout.js +1 -1
- package/dist/layouts/fullscreen-layout.js.map +1 -1
- package/dist/layouts/lazy-layouts.js +15 -0
- package/dist/layouts/lazy-layouts.js.map +1 -0
- package/dist/layouts/max-widths.js +34 -0
- package/dist/layouts/max-widths.js.map +1 -0
- package/dist/layouts/panel-layouts.d.ts +10 -0
- package/dist/layouts/panel-layouts.d.ts.map +1 -0
- package/dist/layouts/panel-layouts.js +20 -0
- package/dist/layouts/panel-layouts.js.map +1 -0
- package/dist/layouts/top-nav-layout.js +2 -2
- package/dist/layouts/top-nav-layout.js.map +1 -1
- package/dist/pages/page.d.ts.map +1 -1
- package/dist/pages/page.js +12 -7
- package/dist/pages/page.js.map +1 -1
- package/dist/standalone/alfakit-component.js +11 -6
- package/dist/standalone/alfakit-component.js.map +1 -1
- package/dist/standalone/alfakit-toaster.d.ts.map +1 -1
- package/dist/standalone/alfakit-toaster.js +8 -1
- package/dist/standalone/alfakit-toaster.js.map +1 -1
- package/dist/standalone.css +54 -0
- package/dist/starter-kit/dashboard-widgets.d.ts +33 -0
- package/dist/starter-kit/dashboard-widgets.d.ts.map +1 -0
- package/dist/starter-kit/dashboard-widgets.js +53 -0
- package/dist/starter-kit/dashboard-widgets.js.map +1 -0
- package/dist/starter-kit/impersonation-banner.js +34 -0
- package/dist/starter-kit/impersonation-banner.js.map +1 -0
- package/dist/starter-kit/index.d.ts +3 -2
- package/dist/starter-kit/index.js +3 -2
- package/dist/starter-kit/starter-kit.d.ts +6 -0
- package/dist/starter-kit/starter-kit.d.ts.map +1 -1
- package/dist/starter-kit/starter-kit.js +18 -7
- package/dist/starter-kit/starter-kit.js.map +1 -1
- package/dist/starter-kit/widgets-loaded.js +21 -5
- package/dist/starter-kit/widgets-loaded.js.map +1 -1
- package/dist/starter-kit/widgets-triggers.js +35 -3
- package/dist/starter-kit/widgets-triggers.js.map +1 -1
- package/dist/starter-kit/widgets.d.ts +13 -5
- package/dist/starter-kit/widgets.d.ts.map +1 -1
- package/dist/starter-kit/widgets.js +56 -18
- package/dist/starter-kit/widgets.js.map +1 -1
- package/dist/styles.css +15 -2
- package/dist/themes.css +8 -335
- package/package.json +2 -2
- package/src/components/actions/action-button.tsx +7 -4
- package/src/components/actions/action-modals.tsx +13 -1
- package/src/components/ai/copilot.tsx +14 -1
- package/src/components/cms/live-preview.tsx +5 -2
- package/src/components/fields/builder.tsx +14 -5
- package/src/components/fields/color-picker.tsx +1 -0
- package/src/components/fields/file-upload.tsx +21 -10
- package/src/components/fields/image-editor-dialog.tsx +7 -2
- package/src/components/fields/item-actions.tsx +32 -9
- package/src/components/fields/key-value.tsx +3 -2
- package/src/components/fields/modal-table-select.tsx +8 -3
- package/src/components/fields/one-time-code-input.tsx +1 -1
- package/src/components/fields/repeater-table.tsx +12 -1
- package/src/components/fields/repeater.tsx +2 -3
- package/src/components/fields/rich-editor.tsx +3 -0
- package/src/components/fields/select.tsx +7 -3
- package/src/components/file-manager/file-manager.tsx +1 -1
- package/src/components/form/form-block.tsx +26 -1
- package/src/components/form/form-context.tsx +3 -6
- package/src/components/kanban/kanban.tsx +16 -3
- package/src/components/layout/fused-group.tsx +5 -1
- package/src/components/layout/section.tsx +2 -2
- package/src/components/layout/tabs.tsx +72 -17
- package/src/components/panel/app-sidebar.tsx +2 -38
- package/src/components/panel/brand.tsx +46 -0
- package/src/components/panel/global-search.tsx +1 -1
- package/src/components/panel/shortcut-keys.tsx +4 -1
- package/src/components/panel/tenant-menu.tsx +2 -2
- package/src/components/realtime/live-records-pill.tsx +4 -3
- package/src/components/table/cells.tsx +27 -5
- package/src/components/table/column-manager.tsx +12 -10
- package/src/components/table/content-layout.tsx +5 -1
- package/src/components/table/grouping.tsx +15 -1
- package/src/components/table/pagination.tsx +16 -9
- package/src/components/table/table-block.tsx +25 -12
- package/src/components/ui/dialog.tsx +5 -1
- package/src/components/ui/popover.tsx +5 -1
- package/src/components/ui/sheet.tsx +5 -1
- package/src/components/ui/tooltip.tsx +5 -1
- package/src/context.tsx +10 -0
- package/src/create-alfakit.tsx +11 -2
- package/src/index.ts +2 -0
- package/src/layouts/app-layout.tsx +4 -28
- package/src/layouts/auth-layout.tsx +2 -2
- package/src/layouts/fullscreen-layout.tsx +1 -1
- package/src/layouts/lazy-layouts.ts +14 -0
- package/src/layouts/max-widths.ts +29 -0
- package/src/layouts/panel-layouts.ts +17 -0
- package/src/layouts/top-nav-layout.tsx +2 -2
- package/src/pages/page.tsx +21 -12
- package/src/standalone/alfakit-component.tsx +18 -7
- package/src/standalone/alfakit-toaster.tsx +21 -2
- package/src/starter-kit/dashboard-widgets.tsx +78 -0
- package/src/starter-kit/impersonation-banner.tsx +48 -0
- package/src/starter-kit/index.ts +2 -0
- package/src/starter-kit/starter-kit.tsx +31 -4
- package/src/starter-kit/widgets-loaded.tsx +23 -1
- package/src/starter-kit/widgets-triggers.tsx +37 -2
- package/src/starter-kit/widgets.tsx +95 -20
- package/src/styles.css +15 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cells.js","names":[],"sources":["../../../src/components/table/cells.tsx"],"sourcesContent":["import type {\n CellSchema,\n ColorColumnSchema,\n ColumnSchema,\n IconColumnSchema,\n Option,\n OptionOrGroup,\n RecordKey,\n SelectColumnSchema,\n TextInputColumnSchema,\n ToggleColumnSchema,\n ViewColumnSchema,\n} from '@alfakit/core'\nimport {\n columnRegistryKey,\n filterSelectColumnOptions,\n isOptionGroup,\n optionText,\n remainingTextSizeClass,\n resolvePartialProps,\n searchTableColumnOptions,\n selectColumnEmptyMessage,\n stackedImageStyle,\n updateTableCell,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport { usePanel, useRegistry, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconSizeClasses,\n switchOffColorClasses,\n switchOnColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormattedText } from '../entries/text-entry'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../ui/command'\nimport { Input } from '../ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\nexport interface CellProps {\n column: ColumnSchema\n cell: CellSchema\n recordKey: RecordKey\n token: string\n component: string\n}\n\nexport function TableCellContent(props: CellProps) {\n const { column } = props\n // A component registered as `column:<type>` replaces the built-in cell renderer.\n const Custom = useRegistry().resolve(columnRegistryKey(column))\n\n if (Custom) {\n return createElement(Custom, props)\n }\n\n switch (column.type) {\n case 'IconColumn':\n return <IconCell {...props} />\n case 'ImageColumn':\n return <ImageCell {...props} />\n case 'ColorColumn':\n return <ColorCell {...props} />\n case 'ToggleColumn':\n case 'CheckboxColumn':\n return <BooleanCell {...props} />\n case 'SelectColumn':\n return <SelectCell {...props} />\n case 'TextInputColumn':\n return <TextInputCell {...props} />\n case 'ViewColumn':\n return <ViewCell {...props} />\n default:\n return <TextCell {...props} />\n }\n}\n\n/** `IconColumn`: one icon, or one per item of a list state (`wrap()` lets them wrap). */\nfunction IconCell({ column, cell }: CellProps) {\n const icons = cell.icons ?? (cell.icon ? [{ icon: cell.icon, color: cell.color }] : [])\n const sizeClass = colorClass(iconSizeClasses, cell.size, 'md')\n\n if (icons.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n if (!cell.icons) {\n return (\n <Icon\n name={icons[0]!.icon}\n className={cn(sizeClass, icons[0]!.color && textColorClasses[icons[0]!.color])}\n />\n )\n }\n\n return (\n <div\n data-slot=\"icon-column-icons\"\n className={cn(\n 'flex items-center gap-1.5',\n (column as IconColumnSchema).wrap ? 'flex-wrap' : 'flex-nowrap',\n )}\n >\n {icons.map((item, index) => (\n <Icon\n key={index}\n name={item.icon}\n className={cn(sizeClass, item.color && textColorClasses[item.color])}\n />\n ))}\n </div>\n )\n}\n\n/** `ViewColumn`: the application component registered under the column's `view()` name. */\nfunction ViewCell(props: CellProps) {\n const t = useT()\n const name = (props.column as ViewColumnSchema).component ?? ''\n const Custom = useRegistry().resolve(name)\n\n if (!name || !Custom) {\n return (\n <span className=\"text-destructive text-xs\">\n {t('component_not_registered', { component: name }, `Component ${name} is not registered.`)}\n </span>\n )\n }\n\n return createElement(Custom, { ...(props.cell.props ?? {}), state: props.cell.state, ...props })\n}\n\nfunction TextCell({ column, cell }: CellProps) {\n const text = (\n <FormattedText\n value={cell.state}\n schema={{\n ...(column as object),\n color: cell.color,\n icon: cell.icon,\n iconPosition: cell.iconPosition,\n iconColor: cell.iconColor,\n itemColors: cell.itemColors,\n itemIcons: cell.itemIcons,\n itemUrls: cell.itemUrls,\n }}\n />\n )\n const description = cell.description ? (\n <span className=\"text-muted-foreground block text-xs\">{cell.description}</span>\n ) : null\n const position =\n (column as { descriptionPosition?: 'above' | 'below' }).descriptionPosition ?? 'below'\n\n return (\n <div className=\"flex flex-col gap-0.5\">\n {position === 'above' && description}\n {text}\n {position === 'below' && description}\n </div>\n )\n}\n\nfunction ImageCell({ column, cell }: CellProps) {\n const urls = Array.isArray(cell.state) ? (cell.state as string[]) : []\n const schema = column as {\n circular?: boolean\n square?: boolean\n size?: number | string\n height?: number | string\n width?: string\n stacked?: boolean\n limit?: number\n limitedRemainingText?: boolean\n limitedRemainingTextSize?: string\n wrap?: boolean\n ring?: number\n overlap?: number\n }\n const limit = schema.limit ?? urls.length\n const size = schema.size ?? 40\n\n if (urls.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n return (\n <div\n className={cn(\n 'flex items-center',\n schema.wrap && 'flex-wrap',\n schema.stacked ? usesDefaultImageStack(schema) && '-space-x-2' : 'gap-1.5',\n schema.wrap && schema.stacked && 'gap-y-1.5',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n alt={cell.alt ?? ''}\n style={{\n height: schema.height ?? size,\n width: schema.width ?? (schema.square || schema.circular ? size : undefined),\n ...stackedImageStyle(index, schema),\n }}\n {...extraProps(\n cell.extraImgAttributes,\n cn(\n 'object-cover',\n schema.circular ? 'rounded-full' : 'rounded-md',\n schema.stacked && usesDefaultImageStack(schema) && 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {schema.limitedRemainingText && urls.length > limit && (\n <span\n data-slot=\"image-remaining\"\n className={cn(\n 'text-muted-foreground ms-1',\n remainingTextSizeClass(schema.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n )\n}\n\nfunction ColorCell({ column, cell }: CellProps) {\n const t = useT()\n const schema = column as ColorColumnSchema\n const [copied, setCopied] = useState<string | null>(null)\n const colors = Array.isArray(cell.state)\n ? (cell.state as string[])\n : cell.state\n ? [String(cell.state)]\n : []\n\n if (colors.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n const copy = (color: string) => {\n void navigator.clipboard?.writeText(color).then(() => {\n setCopied(color)\n setTimeout(() => setCopied(null), schema.copyMessageDuration ?? 1500)\n })\n }\n\n return (\n <div className={cn('flex items-center gap-1.5', schema.wrap ? 'flex-wrap' : 'flex-nowrap')}>\n {colors.map((color) =>\n schema.copyable ? (\n <button\n key={color}\n type=\"button\"\n data-slot=\"color-copy\"\n title={color}\n aria-label={`${t('copy', {}, 'Copy')} ${color}`}\n className=\"focus-visible:ring-ring/50 inline-flex rounded outline-none focus-visible:ring-[3px]\"\n onClick={(event) => {\n // The cell may sit inside a link or a clickable row.\n event.preventDefault()\n event.stopPropagation()\n copy(color)\n }}\n >\n <span\n className=\"inline-flex size-5 items-center justify-center rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n >\n {copied === color && <CheckIcon className=\"size-3 text-white mix-blend-difference\" />}\n </span>\n </button>\n ) : (\n <span\n key={color}\n title={color}\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n ),\n )}\n {schema.copyable && copied && (\n <span role=\"status\" className={cn('text-xs', !schema.copyMessage && 'sr-only')}>\n {schema.copyMessage ?? t('copied', {}, 'Copied')}\n </span>\n )}\n </div>\n )\n}\n\nfunction useCellUpdate({ recordKey, column, token, component }: CellProps) {\n const panel = usePanel()\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const update = async (state: unknown) => {\n if (!panel.endpoints.tableCell || !panel.endpoints.tableReorder) return\n\n setSaving(true)\n setError(null)\n\n try {\n await updateTableCell(\n { cell: panel.endpoints.tableCell, reorder: panel.endpoints.tableReorder },\n token,\n component,\n recordKey,\n column.name,\n state,\n )\n router.reload({ only: resolvePartialProps(['page']) })\n } catch (caught) {\n const errors = (caught as { errors?: Record<string, string[]> }).errors\n setError(errors?.state?.[0] ?? (caught as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n return { update, saving, error }\n}\n\nfunction BooleanCell(props: CellProps) {\n const { column, cell } = props\n const { update, saving } = useCellUpdate(props)\n const [checked, setChecked] = useState(Boolean(cell.state))\n const disabled = Boolean(cell.disabled) || saving\n const toggle = column as ToggleColumnSchema\n\n const onChange = (next: boolean) => {\n setChecked(next)\n void update(next)\n }\n\n if (column.type === 'CheckboxColumn') {\n return (\n <Checkbox\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={(value) => onChange(value === true)}\n onClick={(event) => event.stopPropagation()}\n />\n )\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\" onClick={(event) => event.stopPropagation()}>\n <Switch\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={onChange}\n className={cn(\n toggle.onColor && colorClass(switchOnColorClasses, toggle.onColor),\n toggle.offColor && colorClass(switchOffColorClasses, toggle.offColor),\n )}\n />\n {(checked ? toggle.onIcon : toggle.offIcon) && (\n <Icon\n name={checked ? toggle.onIcon : toggle.offIcon}\n className={cn(\n colorClass(textColorClasses, checked ? toggle.onColor : toggle.offColor, 'gray'),\n )}\n />\n )}\n </span>\n )\n}\n\nfunction flatten(options: OptionOrGroup[]) {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nfunction SelectCell(props: CellProps) {\n const schema = props.column as SelectColumnSchema\n\n return schema.native === false ? (\n <SelectComboboxCell {...props} />\n ) : (\n <NativeSelectCell {...props} />\n )\n}\n\n/**\n * An option label: sanitised HTML under `allowOptionsHtml()`, text otherwise. Long labels\n * wrap unless `wrapOptionLabels(false)` truncates them.\n */\nfunction SelectOptionLabel({\n label,\n html,\n truncate,\n}: {\n label: string\n html?: boolean\n truncate?: boolean\n}) {\n const className = truncate ? 'min-w-0 truncate' : 'min-w-0 break-words whitespace-normal'\n\n // The server sanitises HTML labels (`Support\\Html::sanitize`).\n return html ? (\n <span className={className} dangerouslySetInnerHTML={{ __html: label }} />\n ) : (\n <span className={className}>{label}</span>\n )\n}\n\n/**\n * `SelectColumn::native(false)`: a combobox, searchable with `searchableOptions()`. Options\n * come with the table, or from the server (`optionsSearchPath`) as the user types.\n */\nfunction SelectComboboxCell(props: CellProps) {\n const { column, cell, recordKey, token, component } = props\n const schema = column as SelectColumnSchema\n const t = useT()\n const panel = usePanel()\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const [label, setLabel] = useState(cell.optionLabel)\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [loading, setLoading] = useState(false)\n const [remote, setRemote] = useState<Option[]>([])\n const request = useRef(0)\n const isRemote = Boolean(schema.optionsSearchPath)\n const debounce = schema.optionsSearchDebounce ?? 300\n const staticOptions = flatten(schema.options)\n\n useEffect(() => {\n if (!open || !isRemote || !panel.endpoints.schemaOptions) return\n\n const url = panel.endpoints.schemaOptions\n const timer = setTimeout(() => {\n const current = ++request.current\n setLoading(true)\n\n void searchTableColumnOptions(url, token, component, schema, search)\n .then((results) => current === request.current && setRemote(results))\n .catch(() => current === request.current && setRemote([]))\n .finally(() => current === request.current && setLoading(false))\n }, debounce)\n\n return () => clearTimeout(timer)\n }, [open, search, isRemote, panel.endpoints.schemaOptions, token, component, schema, debounce])\n\n const options = filterSelectColumnOptions(\n isRemote ? remote : staticOptions,\n schema,\n isRemote ? '' : search,\n recordKey,\n )\n const emptyMessage = selectColumnEmptyMessage(schema, {\n loading,\n search,\n optionCount: options.length,\n })\n const currentLabel =\n staticOptions.find((option) => option.value === value)?.label ??\n remote.find((option) => option.value === value)?.label ??\n label\n\n const choose = (next: string, nextLabel?: string) => {\n setValue(next)\n setLabel(nextLabel)\n setOpen(false)\n void update(next === '' ? null : next)\n }\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next)\n\n if (!next) setSearch('')\n }}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n disabled={Boolean(cell.disabled) || saving}\n className={cn(\n 'h-8 min-w-32 justify-between font-normal',\n error && 'border-destructive',\n )}\n >\n {value !== '' && currentLabel !== undefined ? (\n <SelectOptionLabel label={currentLabel} html={schema.allowOptionsHtml} truncate />\n ) : (\n <span className=\"text-muted-foreground\">\n {schema.placeholder ??\n (value !== '' ? value : t('select_option', {}, 'Select an option'))}\n </span>\n )}\n {loading ? (\n <Spinner className=\"opacity-50\" />\n ) : (\n <ChevronsUpDownIcon className=\"size-4 shrink-0 opacity-50\" />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-0\" align=\"start\">\n <Command shouldFilter={false}>\n {schema.searchableOptions && (\n <CommandInput\n placeholder={\n schema.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n }\n value={search}\n onValueChange={setSearch}\n />\n )}\n <CommandList>\n <CommandEmpty data-slot=\"select-column-message\">\n {emptyMessage.message ?? t(emptyMessage.key, {}, emptyMessage.fallback)}\n </CommandEmpty>\n <CommandGroup>\n {schema.selectablePlaceholder && value !== '' && (\n <CommandItem value=\"\" onSelect={() => choose('')}>\n <span className=\"text-muted-foreground\">\n {schema.placeholder ?? t('clear', {}, 'Clear')}\n </span>\n </CommandItem>\n )}\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.value}\n keywords={[optionText(option.label)]}\n disabled={option.disabled}\n onSelect={() => choose(option.value, option.label)}\n >\n <CheckIcon\n className={cn('size-4', option.value === value ? 'opacity-100' : 'opacity-0')}\n />\n <SelectOptionLabel\n label={option.label}\n html={schema.allowOptionsHtml}\n truncate={schema.wrapOptionLabels === false}\n />\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n\nfunction NativeSelectCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as SelectColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <select\n value={value}\n disabled={Boolean(cell.disabled) || saving}\n onChange={(event) => {\n setValue(event.target.value)\n void update(event.target.value === '' ? null : event.target.value)\n }}\n aria-invalid={error ? true : undefined}\n className={cn(\n 'border-input dark:bg-input/30 focus-visible:ring-ring/50 h-8 min-w-32 rounded-md border bg-transparent px-2 text-sm shadow-xs outline-none focus-visible:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {schema.selectablePlaceholder && <option value=\"\">{schema.placeholder ?? ''}</option>}\n {flatten(schema.options)\n .filter((option) => !(schema.ignoreRecord && option.value === String(props.recordKey)))\n .map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n\nfunction TextInputCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as TextInputColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const initial = cell.state === null || cell.state === undefined ? '' : String(cell.state)\n // `prefix()` / `suffix()`: text affixes around the input, like the TextInput field.\n const grouped = Boolean(cell.prefix || cell.suffix)\n\n const field = (\n <span className={cn('relative block', grouped && 'min-w-0 flex-1')}>\n {schema.prefixIcon && (\n <span\n data-slot=\"input-prefix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute start-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.prefixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.prefixIcon} className=\"size-4\" />\n </span>\n )}\n <Input\n type={schema.inputType}\n value={value}\n placeholder={schema.placeholder}\n step={schema.step}\n inputMode={schema.inputMode as React.HTMLAttributes<HTMLInputElement>['inputMode']}\n disabled={Boolean(cell.disabled) || saving}\n aria-invalid={error ? true : undefined}\n className={cn(\n 'h-8 min-w-32',\n schema.prefixIcon && 'ps-8',\n schema.suffixIcon && 'pe-8',\n grouped &&\n 'h-full rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',\n )}\n onChange={(event) => setValue(event.target.value)}\n onBlur={() => value !== initial && void update(value)}\n onKeyDown={(event) => event.key === 'Enter' && (event.target as HTMLInputElement).blur()}\n />\n {schema.suffixIcon && (\n <span\n data-slot=\"input-suffix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute end-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.suffixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.suffixIcon} className=\"size-4\" />\n </span>\n )}\n </span>\n )\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n {grouped ? (\n <span\n data-slot=\"input-group\"\n className={cn(\n 'border-input dark:bg-input/30 has-[input:focus-visible]:border-ring has-[input:focus-visible]:ring-ring/50 flex h-8 min-w-32 items-stretch overflow-hidden rounded-md border shadow-xs has-[input:focus-visible]:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {cell.prefix && (\n <span\n data-slot=\"input-prefix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-e px-2 text-sm\"\n >\n {cell.prefix}\n </span>\n )}\n {field}\n {cell.suffix && (\n <span\n data-slot=\"input-suffix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-s px-2 text-sm\"\n >\n {cell.suffix}\n </span>\n )}\n </span>\n ) : (\n field\n )}\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cells.js","names":[],"sources":["../../../src/components/table/cells.tsx"],"sourcesContent":["import type {\n CellSchema,\n ColorColumnSchema,\n ColumnSchema,\n IconColumnSchema,\n Option,\n OptionOrGroup,\n RecordKey,\n SelectColumnSchema,\n TextInputColumnSchema,\n ToggleColumnSchema,\n ViewColumnSchema,\n} from '@alfakit/core'\nimport {\n columnRegistryKey,\n filterSelectColumnOptions,\n isOptionGroup,\n optionText,\n remainingTextSizeClass,\n resolvePartialProps,\n searchTableColumnOptions,\n selectColumnEmptyMessage,\n stackedImageStyle,\n updateTableCell,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'\nimport { createElement, useEffect, useId, useRef, useState } from 'react'\nimport { usePanel, useRegistry, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconSizeClasses,\n switchOffColorClasses,\n switchOnColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormattedText } from '../entries/text-entry'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../ui/command'\nimport { Input } from '../ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\nexport interface CellProps {\n column: ColumnSchema\n cell: CellSchema\n recordKey: RecordKey\n token: string\n component: string\n /**\n * Set when this renderer was picked from the registry (`column:<type>`): it then renders\n * the built-in cell instead of looking itself up again, so an ejected `TableCellContent`\n * registered for a column type doesn't render itself forever.\n */\n fromRegistry?: boolean\n}\n\nexport function TableCellContent(props: CellProps) {\n const { column } = props\n const registry = useRegistry()\n // A component registered as `column:<type>` replaces the built-in cell renderer.\n const Custom = props.fromRegistry ? undefined : registry.resolve(columnRegistryKey(column))\n\n if (Custom) {\n return createElement(Custom, { ...props, fromRegistry: true })\n }\n\n switch (column.type) {\n case 'IconColumn':\n return <IconCell {...props} />\n case 'ImageColumn':\n return <ImageCell {...props} />\n case 'ColorColumn':\n return <ColorCell {...props} />\n case 'ToggleColumn':\n case 'CheckboxColumn':\n return <BooleanCell {...props} />\n case 'SelectColumn':\n return <SelectCell {...props} />\n case 'TextInputColumn':\n return <TextInputCell {...props} />\n case 'ViewColumn':\n return <ViewCell {...props} />\n default:\n return <TextCell {...props} />\n }\n}\n\n/** `IconColumn`: one icon, or one per item of a list state (`wrap()` lets them wrap). */\nfunction IconCell({ column, cell }: CellProps) {\n const icons = cell.icons ?? (cell.icon ? [{ icon: cell.icon, color: cell.color }] : [])\n const sizeClass = colorClass(iconSizeClasses, cell.size, 'md')\n\n if (icons.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n if (!cell.icons) {\n return (\n <Icon\n name={icons[0]!.icon}\n className={cn(sizeClass, icons[0]!.color && textColorClasses[icons[0]!.color])}\n />\n )\n }\n\n return (\n <div\n data-slot=\"icon-column-icons\"\n className={cn(\n 'flex items-center gap-1.5',\n (column as IconColumnSchema).wrap ? 'flex-wrap' : 'flex-nowrap',\n )}\n >\n {icons.map((item, index) => (\n <Icon\n key={index}\n name={item.icon}\n className={cn(sizeClass, item.color && textColorClasses[item.color])}\n />\n ))}\n </div>\n )\n}\n\n/** `ViewColumn`: the application component registered under the column's `view()` name. */\nfunction ViewCell(props: CellProps) {\n const t = useT()\n const name = (props.column as ViewColumnSchema).component ?? ''\n const Custom = useRegistry().resolve(name)\n\n if (!name || !Custom) {\n return (\n <span className=\"text-destructive text-xs\">\n {t('component_not_registered', { component: name }, `Component ${name} is not registered.`)}\n </span>\n )\n }\n\n return createElement(Custom, { ...(props.cell.props ?? {}), state: props.cell.state, ...props })\n}\n\nfunction TextCell({ column, cell }: CellProps) {\n const text = (\n <FormattedText\n value={cell.state}\n schema={{\n ...(column as object),\n color: cell.color,\n icon: cell.icon,\n iconPosition: cell.iconPosition,\n iconColor: cell.iconColor,\n itemColors: cell.itemColors,\n itemIcons: cell.itemIcons,\n itemUrls: cell.itemUrls,\n }}\n />\n )\n const description = cell.description ? (\n <span className=\"text-muted-foreground block text-xs\">{cell.description}</span>\n ) : null\n const position =\n (column as { descriptionPosition?: 'above' | 'below' }).descriptionPosition ?? 'below'\n\n return (\n <div className=\"flex flex-col gap-0.5\">\n {position === 'above' && description}\n {text}\n {position === 'below' && description}\n </div>\n )\n}\n\nfunction ImageCell({ column, cell }: CellProps) {\n const urls = Array.isArray(cell.state) ? (cell.state as string[]) : []\n const schema = column as {\n circular?: boolean\n square?: boolean\n size?: number | string\n height?: number | string\n width?: string\n stacked?: boolean\n limit?: number\n limitedRemainingText?: boolean\n limitedRemainingTextSize?: string\n wrap?: boolean\n ring?: number\n overlap?: number\n }\n const limit = schema.limit ?? urls.length\n const size = schema.size ?? 40\n\n if (urls.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n return (\n <div\n className={cn(\n 'flex items-center',\n schema.wrap && 'flex-wrap',\n schema.stacked ? usesDefaultImageStack(schema) && '-space-x-2' : 'gap-1.5',\n schema.wrap && schema.stacked && 'gap-y-1.5',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n alt={cell.alt ?? ''}\n style={{\n height: schema.height ?? size,\n width: schema.width ?? (schema.square || schema.circular ? size : undefined),\n ...stackedImageStyle(index, schema),\n }}\n {...extraProps(\n cell.extraImgAttributes,\n cn(\n 'object-cover',\n schema.circular ? 'rounded-full' : 'rounded-md',\n schema.stacked && usesDefaultImageStack(schema) && 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {schema.limitedRemainingText && urls.length > limit && (\n <span\n data-slot=\"image-remaining\"\n className={cn(\n 'text-muted-foreground ms-1',\n remainingTextSizeClass(schema.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n )\n}\n\nfunction ColorCell({ column, cell }: CellProps) {\n const t = useT()\n const schema = column as ColorColumnSchema\n const [copied, setCopied] = useState<string | null>(null)\n const colors = Array.isArray(cell.state)\n ? (cell.state as string[])\n : cell.state\n ? [String(cell.state)]\n : []\n\n if (colors.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n const copy = (color: string) => {\n void navigator.clipboard?.writeText(color).then(() => {\n setCopied(color)\n setTimeout(() => setCopied(null), schema.copyMessageDuration ?? 1500)\n })\n }\n\n return (\n <div className={cn('flex items-center gap-1.5', schema.wrap ? 'flex-wrap' : 'flex-nowrap')}>\n {colors.map((color) =>\n schema.copyable ? (\n <button\n key={color}\n type=\"button\"\n data-slot=\"color-copy\"\n title={color}\n aria-label={`${t('copy', {}, 'Copy')} ${color}`}\n className=\"focus-visible:ring-ring/50 inline-flex rounded outline-none focus-visible:ring-[3px]\"\n onClick={(event) => {\n // The cell may sit inside a link or a clickable row.\n event.preventDefault()\n event.stopPropagation()\n copy(color)\n }}\n >\n <span\n className=\"inline-flex size-5 items-center justify-center rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n >\n {copied === color && <CheckIcon className=\"size-3 text-white mix-blend-difference\" />}\n </span>\n </button>\n ) : (\n <span\n key={color}\n title={color}\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n ),\n )}\n {schema.copyable && copied && (\n <span role=\"status\" className={cn('text-xs', !schema.copyMessage && 'sr-only')}>\n {schema.copyMessage ?? t('copied', {}, 'Copied')}\n </span>\n )}\n </div>\n )\n}\n\nfunction useCellUpdate({ recordKey, column, token, component }: CellProps) {\n const panel = usePanel()\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const update = async (state: unknown) => {\n if (!panel.endpoints.tableCell || !panel.endpoints.tableReorder) return\n\n setSaving(true)\n setError(null)\n\n try {\n await updateTableCell(\n { cell: panel.endpoints.tableCell, reorder: panel.endpoints.tableReorder },\n token,\n component,\n recordKey,\n column.name,\n state,\n )\n router.reload({ only: resolvePartialProps(['page']) })\n } catch (caught) {\n const errors = (caught as { errors?: Record<string, string[]> }).errors\n setError(errors?.state?.[0] ?? (caught as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n return { update, saving, error }\n}\n\nfunction BooleanCell(props: CellProps) {\n const { column, cell } = props\n const { update, saving } = useCellUpdate(props)\n const [checked, setChecked] = useState(Boolean(cell.state))\n const disabled = Boolean(cell.disabled) || saving\n const toggle = column as ToggleColumnSchema\n\n const onChange = (next: boolean) => {\n setChecked(next)\n void update(next)\n }\n\n if (column.type === 'CheckboxColumn') {\n return (\n <Checkbox\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={(value) => onChange(value === true)}\n onClick={(event) => event.stopPropagation()}\n />\n )\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\" onClick={(event) => event.stopPropagation()}>\n <Switch\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={onChange}\n className={cn(\n toggle.onColor && colorClass(switchOnColorClasses, toggle.onColor),\n toggle.offColor && colorClass(switchOffColorClasses, toggle.offColor),\n )}\n />\n {(checked ? toggle.onIcon : toggle.offIcon) && (\n <Icon\n name={checked ? toggle.onIcon : toggle.offIcon}\n className={cn(\n colorClass(textColorClasses, checked ? toggle.onColor : toggle.offColor, 'gray'),\n )}\n />\n )}\n </span>\n )\n}\n\nfunction flatten(options: OptionOrGroup[]) {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nfunction SelectCell(props: CellProps) {\n const schema = props.column as SelectColumnSchema\n\n return schema.native === false ? (\n <SelectComboboxCell {...props} />\n ) : (\n <NativeSelectCell {...props} />\n )\n}\n\n/**\n * An option label: sanitised HTML under `allowOptionsHtml()`, text otherwise. Long labels\n * wrap unless `wrapOptionLabels(false)` truncates them.\n */\nfunction SelectOptionLabel({\n label,\n html,\n truncate,\n}: {\n label: string\n html?: boolean\n truncate?: boolean\n}) {\n const className = truncate ? 'min-w-0 truncate' : 'min-w-0 break-words whitespace-normal'\n\n // The server sanitises HTML labels (`Support\\Html::sanitize`).\n return html ? (\n <span className={className} dangerouslySetInnerHTML={{ __html: label }} />\n ) : (\n <span className={className}>{label}</span>\n )\n}\n\n/**\n * `SelectColumn::native(false)`: a combobox, searchable with `searchableOptions()`. Options\n * come with the table, or from the server (`optionsSearchPath`) as the user types.\n */\nfunction SelectComboboxCell(props: CellProps) {\n const { column, cell, recordKey, token, component } = props\n const schema = column as SelectColumnSchema\n const t = useT()\n const panel = usePanel()\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const [label, setLabel] = useState(cell.optionLabel)\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [loading, setLoading] = useState(false)\n const [remote, setRemote] = useState<Option[]>([])\n const request = useRef(0)\n const isRemote = Boolean(schema.optionsSearchPath)\n const debounce = schema.optionsSearchDebounce ?? 300\n const staticOptions = flatten(schema.options)\n\n useEffect(() => {\n if (!open || !isRemote || !panel.endpoints.schemaOptions) return\n\n const url = panel.endpoints.schemaOptions\n const timer = setTimeout(() => {\n const current = ++request.current\n setLoading(true)\n\n void searchTableColumnOptions(url, token, component, schema, search)\n .then((results) => current === request.current && setRemote(results))\n .catch(() => current === request.current && setRemote([]))\n .finally(() => current === request.current && setLoading(false))\n }, debounce)\n\n return () => clearTimeout(timer)\n }, [open, search, isRemote, panel.endpoints.schemaOptions, token, component, schema, debounce])\n\n const options = filterSelectColumnOptions(\n isRemote ? remote : staticOptions,\n schema,\n isRemote ? '' : search,\n recordKey,\n )\n const emptyMessage = selectColumnEmptyMessage(schema, {\n loading,\n search,\n optionCount: options.length,\n })\n const currentLabel =\n staticOptions.find((option) => option.value === value)?.label ??\n remote.find((option) => option.value === value)?.label ??\n label\n\n const choose = (next: string, nextLabel?: string) => {\n setValue(next)\n setLabel(nextLabel)\n setOpen(false)\n void update(next === '' ? null : next)\n }\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next)\n\n if (!next) setSearch('')\n }}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n disabled={Boolean(cell.disabled) || saving}\n className={cn(\n 'h-8 min-w-32 justify-between font-normal',\n error && 'border-destructive',\n )}\n >\n {value !== '' && currentLabel !== undefined ? (\n <SelectOptionLabel label={currentLabel} html={schema.allowOptionsHtml} truncate />\n ) : (\n <span className=\"text-muted-foreground\">\n {schema.placeholder ??\n (value !== '' ? value : t('select_option', {}, 'Select an option'))}\n </span>\n )}\n {loading ? (\n <Spinner className=\"opacity-50\" />\n ) : (\n <ChevronsUpDownIcon className=\"size-4 shrink-0 opacity-50\" />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-0\" align=\"start\">\n <Command shouldFilter={false}>\n {schema.searchableOptions && (\n <CommandInput\n placeholder={\n schema.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n }\n value={search}\n onValueChange={setSearch}\n />\n )}\n <CommandList>\n <CommandEmpty data-slot=\"select-column-message\">\n {emptyMessage.message ?? t(emptyMessage.key, {}, emptyMessage.fallback)}\n </CommandEmpty>\n <CommandGroup>\n {schema.selectablePlaceholder && value !== '' && (\n <CommandItem value=\"\" onSelect={() => choose('')}>\n <span className=\"text-muted-foreground\">\n {schema.placeholder ?? t('clear', {}, 'Clear')}\n </span>\n </CommandItem>\n )}\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.value}\n keywords={[optionText(option.label)]}\n disabled={option.disabled}\n onSelect={() => choose(option.value, option.label)}\n >\n <CheckIcon\n className={cn('size-4', option.value === value ? 'opacity-100' : 'opacity-0')}\n />\n <SelectOptionLabel\n label={option.label}\n html={schema.allowOptionsHtml}\n truncate={schema.wrapOptionLabels === false}\n />\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n\nfunction NativeSelectCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as SelectColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n\n const errorId = useId()\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <select\n value={value}\n disabled={Boolean(cell.disabled) || saving}\n onChange={(event) => {\n setValue(event.target.value)\n void update(event.target.value === '' ? null : event.target.value)\n }}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'border-input dark:bg-input/30 focus-visible:ring-ring/50 h-8 min-w-32 rounded-md border bg-transparent px-2 text-sm shadow-xs outline-none focus-visible:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {schema.selectablePlaceholder && <option value=\"\">{schema.placeholder ?? ''}</option>}\n {flatten(schema.options)\n .filter((option) => !(schema.ignoreRecord && option.value === String(props.recordKey)))\n .map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n\nfunction TextInputCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as TextInputColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const initial = cell.state === null || cell.state === undefined ? '' : String(cell.state)\n // `prefix()` / `suffix()`: text affixes around the input, like the TextInput field.\n const grouped = Boolean(cell.prefix || cell.suffix)\n const errorId = useId()\n\n const field = (\n <span className={cn('relative block', grouped && 'min-w-0 flex-1')}>\n {schema.prefixIcon && (\n <span\n data-slot=\"input-prefix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute start-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.prefixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.prefixIcon} className=\"size-4\" />\n </span>\n )}\n <Input\n type={schema.inputType}\n value={value}\n placeholder={schema.placeholder}\n step={schema.step}\n inputMode={schema.inputMode as React.HTMLAttributes<HTMLInputElement>['inputMode']}\n disabled={Boolean(cell.disabled) || saving}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'h-8 min-w-32',\n schema.prefixIcon && 'ps-8',\n schema.suffixIcon && 'pe-8',\n grouped &&\n 'h-full rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',\n )}\n onChange={(event) => setValue(event.target.value)}\n onBlur={() => value !== initial && void update(value)}\n onKeyDown={(event) => event.key === 'Enter' && (event.target as HTMLInputElement).blur()}\n />\n {schema.suffixIcon && (\n <span\n data-slot=\"input-suffix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute end-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.suffixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.suffixIcon} className=\"size-4\" />\n </span>\n )}\n </span>\n )\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n {grouped ? (\n <span\n data-slot=\"input-group\"\n className={cn(\n 'border-input dark:bg-input/30 has-[input:focus-visible]:border-ring has-[input:focus-visible]:ring-ring/50 flex h-8 min-w-32 items-stretch overflow-hidden rounded-md border shadow-xs has-[input:focus-visible]:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {cell.prefix && (\n <span\n data-slot=\"input-prefix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-e px-2 text-sm\"\n >\n {cell.prefix}\n </span>\n )}\n {field}\n {cell.suffix && (\n <span\n data-slot=\"input-suffix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-s px-2 text-sm\"\n >\n {cell.suffix}\n </span>\n )}\n </span>\n ) : (\n field\n )}\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n"],"mappings":"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"}
|
|
@@ -101,18 +101,19 @@ function ColumnManager({ table, token, component }) {
|
|
|
101
101
|
reorder(name, moveColumnInManager(names, name, offset, table.columnGroups));
|
|
102
102
|
};
|
|
103
103
|
const orderChanged = Boolean(orderDraft && columnOrderChanged(appliedNames, orderDraft));
|
|
104
|
-
const apply = () => {
|
|
105
|
-
|
|
106
|
-
if (draft && columnDraftChanged(table.visibleColumns, draft)) visitTable(table.identifier, { columns: draft }, ["page"]);
|
|
104
|
+
const apply = async () => {
|
|
105
|
+
const columns = draft && columnDraftChanged(table.visibleColumns, draft) ? draft : null;
|
|
107
106
|
setDraft(null);
|
|
108
107
|
setOrderDraft(null);
|
|
109
108
|
setAnnouncement("");
|
|
110
109
|
setOpen(false);
|
|
110
|
+
if (orderDraft && orderChanged) await save(orderDraft);
|
|
111
|
+
if (columns) visitTable(table.identifier, { columns }, ["page"]);
|
|
111
112
|
};
|
|
112
|
-
const reset = () => {
|
|
113
|
+
const reset = async () => {
|
|
113
114
|
if (reorderable) {
|
|
114
115
|
setOrderDraft(null);
|
|
115
|
-
save([]);
|
|
116
|
+
await save([]);
|
|
116
117
|
}
|
|
117
118
|
if (toggleable) {
|
|
118
119
|
setDraft(null);
|
|
@@ -132,7 +133,7 @@ function ColumnManager({ table, token, component }) {
|
|
|
132
133
|
disabled: saving,
|
|
133
134
|
"data-slot": "column-manager-reset",
|
|
134
135
|
className: cn(!applyView && "w-full"),
|
|
135
|
-
onClick: reset
|
|
136
|
+
onClick: () => void reset()
|
|
136
137
|
}) : null;
|
|
137
138
|
const inHeader = table.columnManagerResetActionPosition === "header";
|
|
138
139
|
const list = /* @__PURE__ */ jsx("ul", {
|
|
@@ -250,7 +251,7 @@ function ColumnManager({ table, token, component }) {
|
|
|
250
251
|
view: applyView,
|
|
251
252
|
"data-slot": "column-manager-apply",
|
|
252
253
|
disabled: !orderChanged && (!draft || !columnDraftChanged(table.visibleColumns, draft)),
|
|
253
|
-
onClick: apply
|
|
254
|
+
onClick: () => void apply()
|
|
254
255
|
})]
|
|
255
256
|
})
|
|
256
257
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"column-manager.js","names":[],"sources":["../../../src/components/table/column-manager.tsx"],"sourcesContent":["import 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-react'\nimport { useId, useState, type ReactNode } from 'react'\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'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../ui/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '../ui/sheet'\nimport { TableTriggerButton } from './trigger-button'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and,\n * with `reorderableColumns()`, move columns (stored per user on the server) by dragging a\n * row 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 */\nexport function ColumnManager({\n table,\n token,\n component,\n}: {\n table: TableSchema\n token: string\n component: string\n}) {\n const t = useT()\n const panel = usePanel()\n const direction = useDirection()\n const [open, setOpen] = useState(false)\n const [saving, setSaving] = useState(false)\n const [dragging, setDragging] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n // Toggles of a deferred column manager wait here until \"Apply\".\n const [draft, setDraft] = useState<string[] | null>(null)\n // A moved column order: the draft of a deferred column manager, or shown at once\n // while it is saved.\n // It belongs to the columns it was made from: the server's next order replaces the\n // one shown while saving.\n const [orderState, setOrderState] = useState<{\n order: string[]\n columns: TableSchema['columns']\n } | null>(null)\n // Screen-reader announcement of the last move.\n const [announcement, setAnnouncement] = useState('')\n const idBase = useId()\n const reorderable = Boolean(table.reorderableColumns && panel.endpoints.tableColumns)\n const toggleable = Boolean(table.toggleableColumns)\n const deferred = Boolean(table.deferColumnManager)\n const orderDraft =\n orderState && (deferred || orderState.columns === table.columns) ? orderState.order : null\n const setOrderDraft = (order: string[] | null) =>\n setOrderState(order ? { order, columns: table.columns } : null)\n const appliedNames = table.columns.map((column) => column.name)\n const names = orderDraft ?? appliedNames\n const columns = orderColumnsBy(\n reorderable ? table.columns : table.columns.filter((column) => column.toggleable),\n orderDraft,\n )\n const visible = draft ?? table.visibleColumns\n\n if (!toggleable && !reorderable) return null\n\n const trigger = triggerButtonView(table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n })\n\n if (!trigger) return null\n\n const toggle = (name: string) => {\n const next = toggleVisibleColumn(visible, name)\n\n if (deferred) {\n setDraft(next)\n } else {\n visitTable(table.identifier, { columns: next }, ['page'])\n }\n }\n\n const save = async (order: string[]): Promise<boolean> => {\n if (!panel.endpoints.tableColumns) return false\n\n setSaving(true)\n\n try {\n await saveColumnOrder(panel.endpoints.tableColumns, token, 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 setSaving(false)\n }\n }\n\n const announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, table.columnGroups)\n const label = table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n setAnnouncement(\n group\n ? deferred\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\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\n // A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\n const reorder = (name: string, order: string[]) => {\n if (saving || !columnOrderChanged(names, order)) return\n\n setOrderDraft(order)\n setAnnouncement('')\n\n if (deferred) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => (saved ? announce(name, order) : setOrderDraft(null)))\n }\n\n const move = (name: string, offset: -1 | 1, button: HTMLElement) => {\n keepMoveButtonFocus(button)\n reorder(name, moveColumnInManager(names, name, offset, table.columnGroups))\n }\n\n const orderChanged = Boolean(orderDraft && columnOrderChanged(appliedNames, orderDraft))\n\n const apply = () => {\n if (orderDraft && orderChanged) void save(orderDraft)\n\n if (draft && columnDraftChanged(table.visibleColumns, draft)) {\n visitTable(table.identifier, { columns: draft }, ['page'])\n }\n\n setDraft(null)\n setOrderDraft(null)\n setAnnouncement('')\n setOpen(false)\n }\n\n // Reset: the defined column order and, for toggleable columns, the default visibility.\n const reset = () => {\n if (reorderable) {\n setOrderDraft(null)\n void save([])\n }\n\n if (toggleable) {\n setDraft(null)\n visitTable(table.identifier, { columns: defaultVisibleColumns(table) }, ['page'])\n }\n }\n\n const resetView = triggerButtonView(table, 'columnManagerReset', {\n label: toggleable\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n })\n const applyView = deferred\n ? triggerButtonView(table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null\n const resetButton = resetView ? (\n <TableTriggerButton\n view={resetView}\n disabled={saving}\n data-slot=\"column-manager-reset\"\n className={cn(!applyView && 'w-full')}\n onClick={reset}\n />\n ) : null\n const inHeader = table.columnManagerResetActionPosition === 'header'\n\n const list = (\n <ul\n className={cn(\n table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )}\n aria-label={t('table.columns', {}, 'Columns')}\n >\n {columns.map((column) => {\n const { position, count, group } = columnManagerPosition(\n names,\n column.name,\n table.columnGroups,\n )\n const positionId = `${idBase}-${column.name}-position`\n\n return (\n <li\n key={column.name}\n data-column={column.name}\n draggable={reorderable && !saving}\n onDragStart={(event) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n setDragging(column.name)\n }}\n onDragOver={(event) => {\n if (dragging === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDropTarget(column.name)\n }}\n onDragLeave={() => dropTarget === column.name && setDropTarget(null)}\n onDragEnd={() => {\n setDragging(null)\n setDropTarget(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n\n if (dragging !== null && dragging !== column.name) {\n reorder(dragging, moveColumnTo(names, dragging, column.name))\n }\n\n setDragging(null)\n setDropTarget(null)\n }}\n className={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 {reorderable && (\n <GripVerticalIcon\n className=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n {column.toggleable ? (\n <label className=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n checked={visible.includes(column.name)}\n onCheckedChange={() => toggle(column.name)}\n />\n <span className=\"truncate\">{column.label}</span>\n </label>\n ) : (\n <span className=\"min-w-0 flex-1 truncate\">{column.label}</span>\n )}\n {reorderable && (\n <span className=\"flex shrink-0 items-center\">\n <span id={positionId} className=\"sr-only\" data-slot=\"column-manager-position\">\n {group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t(\n 'table.column_manager.position',\n { position, count },\n `Position ${position} of ${count}.`,\n )}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n disabled={position <= 1}\n aria-disabled={saving || undefined}\n aria-describedby={positionId}\n aria-label={t(\n 'table.move_column_up',\n { column: column.label },\n `Move ${column.label} up`,\n )}\n onClick={(event) => move(column.name, -1, event.currentTarget)}\n >\n <ChevronUpIcon />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n disabled={position >= count}\n aria-disabled={saving || undefined}\n aria-describedby={positionId}\n aria-label={t(\n 'table.move_column_down',\n { column: column.label },\n `Move ${column.label} down`,\n )}\n onClick={(event) => move(column.name, 1, event.currentTarget)}\n >\n <ChevronDownIcon />\n </Button>\n </span>\n )}\n </li>\n )\n })}\n </ul>\n )\n\n const body: ReactNode = (\n <div className=\"flex flex-col gap-1\">\n {inHeader && resetButton && <div className=\"flex justify-end\">{resetButton}</div>}\n {list}\n {reorderable && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {announcement}\n </div>\n )}\n {((!inHeader && resetButton) || applyView) && (\n <div className=\"mt-1 flex items-center justify-end gap-2\">\n {!inHeader && resetButton}\n {applyView && (\n <TableTriggerButton\n view={applyView}\n data-slot=\"column-manager-apply\"\n disabled={\n !orderChanged && (!draft || !columnDraftChanged(table.visibleColumns, draft))\n }\n onClick={apply}\n />\n )}\n </div>\n )}\n </div>\n )\n\n const onOpenChange = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n setDraft(null)\n if (deferred) setOrderDraft(null)\n setAnnouncement('')\n }\n setOpen(next)\n }\n\n const triggerButton = (\n <TableTriggerButton\n view={trigger}\n defaultIcon={<Icon name=\"alfakit::tables.actions.column-manager\" fallback={Columns3Icon} />}\n aria-label={trigger.showLabel ? undefined : trigger.label}\n />\n )\n\n if (table.columnManagerLayout === 'modal') {\n // `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\n const modal = table.triggerActions?.columnManager?.modal\n const heading = modal?.heading ?? t('table.columns', {}, 'Columns')\n const description = modal?.description ?? table.pluralModelLabel\n const widthClass = modal?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.width] : undefined\n\n if (modal?.slideOver) {\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetTrigger asChild>{triggerButton}</SheetTrigger>\n <SheetContent\n side={slideOverSide(modal.slideOverPosition, direction)}\n className={cn('flex w-full flex-col gap-0 p-0', widthClass ?? 'sm:max-w-md')}\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader className=\"border-b\">\n <SheetTitle>{heading}</SheetTitle>\n <SheetDescription className={modal.description ? undefined : 'sr-only'}>\n {description}\n </SheetDescription>\n </SheetHeader>\n <div className=\"flex-1 overflow-y-auto p-4\">{body}</div>\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogTrigger asChild>{triggerButton}</DialogTrigger>\n <DialogContent className={widthClass ?? 'sm:max-w-lg'} data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{heading}</DialogTitle>\n <DialogDescription className={modal?.description ? undefined : 'sr-only'}>\n {description}\n </DialogDescription>\n </DialogHeader>\n <div className=\"max-h-[60vh] overflow-y-auto\">{body}</div>\n </DialogContent>\n </Dialog>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>{triggerButton}</PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className={cn('p-2', table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')}\n data-slot=\"column-manager\"\n >\n {body}\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"column-manager.js","names":[],"sources":["../../../src/components/table/column-manager.tsx"],"sourcesContent":["import 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-react'\nimport { useId, useState, type ReactNode } from 'react'\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'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../ui/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '../ui/sheet'\nimport { TableTriggerButton } from './trigger-button'\n\n/**\n * The column manager: show / hide toggleable columns (kept in the query string) and,\n * with `reorderableColumns()`, move columns (stored per user on the server) by dragging a\n * row 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 */\nexport function ColumnManager({\n table,\n token,\n component,\n}: {\n table: TableSchema\n token: string\n component: string\n}) {\n const t = useT()\n const panel = usePanel()\n const direction = useDirection()\n const [open, setOpen] = useState(false)\n const [saving, setSaving] = useState(false)\n const [dragging, setDragging] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n // Toggles of a deferred column manager wait here until \"Apply\".\n const [draft, setDraft] = useState<string[] | null>(null)\n // A moved column order: the draft of a deferred column manager, or shown at once\n // while it is saved.\n // It belongs to the columns it was made from: the server's next order replaces the\n // one shown while saving.\n const [orderState, setOrderState] = useState<{\n order: string[]\n columns: TableSchema['columns']\n } | null>(null)\n // Screen-reader announcement of the last move.\n const [announcement, setAnnouncement] = useState('')\n const idBase = useId()\n const reorderable = Boolean(table.reorderableColumns && panel.endpoints.tableColumns)\n const toggleable = Boolean(table.toggleableColumns)\n const deferred = Boolean(table.deferColumnManager)\n const orderDraft =\n orderState && (deferred || orderState.columns === table.columns) ? orderState.order : null\n const setOrderDraft = (order: string[] | null) =>\n setOrderState(order ? { order, columns: table.columns } : null)\n const appliedNames = table.columns.map((column) => column.name)\n const names = orderDraft ?? appliedNames\n const columns = orderColumnsBy(\n reorderable ? table.columns : table.columns.filter((column) => column.toggleable),\n orderDraft,\n )\n const visible = draft ?? table.visibleColumns\n\n if (!toggleable && !reorderable) return null\n\n const trigger = triggerButtonView(table, 'columnManager', {\n label: t('table.columns', {}, 'Columns'),\n showLabel: false,\n })\n\n if (!trigger) return null\n\n const toggle = (name: string) => {\n const next = toggleVisibleColumn(visible, name)\n\n if (deferred) {\n setDraft(next)\n } else {\n visitTable(table.identifier, { columns: next }, ['page'])\n }\n }\n\n const save = async (order: string[]): Promise<boolean> => {\n if (!panel.endpoints.tableColumns) return false\n\n setSaving(true)\n\n try {\n await saveColumnOrder(panel.endpoints.tableColumns, token, 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 setSaving(false)\n }\n }\n\n const announce = (name: string, order: string[]) => {\n const { position, count, group } = columnManagerPosition(order, name, table.columnGroups)\n const label = table.columns.find((column) => column.name === name)?.label ?? name\n const replace = { column: label, position, count, group: group ?? '' }\n\n setAnnouncement(\n group\n ? deferred\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\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\n // A new order: kept as a draft until \"Apply\" (`deferColumnManager()`), else saved now.\n const reorder = (name: string, order: string[]) => {\n if (saving || !columnOrderChanged(names, order)) return\n\n setOrderDraft(order)\n setAnnouncement('')\n\n if (deferred) {\n announce(name, order)\n\n return\n }\n\n void save(order).then((saved) => (saved ? announce(name, order) : setOrderDraft(null)))\n }\n\n const move = (name: string, offset: -1 | 1, button: HTMLElement) => {\n keepMoveButtonFocus(button)\n reorder(name, moveColumnInManager(names, name, offset, table.columnGroups))\n }\n\n const orderChanged = Boolean(orderDraft && columnOrderChanged(appliedNames, orderDraft))\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.\n const apply = async () => {\n const columns = draft && columnDraftChanged(table.visibleColumns, draft) ? draft : null\n\n setDraft(null)\n setOrderDraft(null)\n setAnnouncement('')\n setOpen(false)\n\n if (orderDraft && orderChanged) await save(orderDraft)\n\n if (columns) visitTable(table.identifier, { columns }, ['page'])\n }\n\n // Reset: the defined column order and, for toggleable columns, the default visibility.\n const reset = async () => {\n if (reorderable) {\n setOrderDraft(null)\n await save([])\n }\n\n if (toggleable) {\n setDraft(null)\n visitTable(table.identifier, { columns: defaultVisibleColumns(table) }, ['page'])\n }\n }\n\n const resetView = triggerButtonView(table, 'columnManagerReset', {\n label: toggleable\n ? t('table.reset_columns', {}, 'Reset')\n : t('table.reset_column_order', {}, 'Reset column order'),\n variant: 'ghost',\n })\n const applyView = deferred\n ? triggerButtonView(table, 'columnManagerApply', {\n label: t('table_filters.apply', {}, 'Apply'),\n variant: 'default',\n })\n : null\n const resetButton = resetView ? (\n <TableTriggerButton\n view={resetView}\n disabled={saving}\n data-slot=\"column-manager-reset\"\n className={cn(!applyView && 'w-full')}\n onClick={() => void reset()}\n />\n ) : null\n const inHeader = table.columnManagerResetActionPosition === 'header'\n\n const list = (\n <ul\n className={cn(\n table.columnManagerColumns\n ? ['grid gap-0.5', gridColumnsClasses(table.columnManagerColumns)]\n : 'flex flex-col gap-0.5',\n )}\n aria-label={t('table.columns', {}, 'Columns')}\n >\n {columns.map((column) => {\n const { position, count, group } = columnManagerPosition(\n names,\n column.name,\n table.columnGroups,\n )\n const positionId = `${idBase}-${column.name}-position`\n\n return (\n <li\n key={column.name}\n data-column={column.name}\n draggable={reorderable && !saving}\n onDragStart={(event) => {\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n setDragging(column.name)\n }}\n onDragOver={(event) => {\n if (dragging === null) return\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDropTarget(column.name)\n }}\n onDragLeave={() => dropTarget === column.name && setDropTarget(null)}\n onDragEnd={() => {\n setDragging(null)\n setDropTarget(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n\n if (dragging !== null && dragging !== column.name) {\n reorder(dragging, moveColumnTo(names, dragging, column.name))\n }\n\n setDragging(null)\n setDropTarget(null)\n }}\n className={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 {reorderable && (\n <GripVerticalIcon\n className=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n {column.toggleable ? (\n <label className=\"flex min-w-0 flex-1 items-center gap-2\">\n <Checkbox\n checked={visible.includes(column.name)}\n onCheckedChange={() => toggle(column.name)}\n />\n <span className=\"truncate\">{column.label}</span>\n </label>\n ) : (\n <span className=\"min-w-0 flex-1 truncate\">{column.label}</span>\n )}\n {reorderable && (\n <span className=\"flex shrink-0 items-center\">\n <span id={positionId} className=\"sr-only\" data-slot=\"column-manager-position\">\n {group\n ? t(\n 'table.column_manager.position_in_group',\n { position, count, group },\n `Position ${position} of ${count} in ${group}.`,\n )\n : t(\n 'table.column_manager.position',\n { position, count },\n `Position ${position} of ${count}.`,\n )}\n </span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n disabled={position <= 1}\n aria-disabled={saving || undefined}\n aria-describedby={positionId}\n aria-label={t(\n 'table.move_column_up',\n { column: column.label },\n `Move ${column.label} up`,\n )}\n onClick={(event) => move(column.name, -1, event.currentTarget)}\n >\n <ChevronUpIcon />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n disabled={position >= count}\n aria-disabled={saving || undefined}\n aria-describedby={positionId}\n aria-label={t(\n 'table.move_column_down',\n { column: column.label },\n `Move ${column.label} down`,\n )}\n onClick={(event) => move(column.name, 1, event.currentTarget)}\n >\n <ChevronDownIcon />\n </Button>\n </span>\n )}\n </li>\n )\n })}\n </ul>\n )\n\n const body: ReactNode = (\n <div className=\"flex flex-col gap-1\">\n {inHeader && resetButton && <div className=\"flex justify-end\">{resetButton}</div>}\n {list}\n {reorderable && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className=\"sr-only\"\n data-slot=\"column-manager-announcement\"\n >\n {announcement}\n </div>\n )}\n {((!inHeader && resetButton) || applyView) && (\n <div className=\"mt-1 flex items-center justify-end gap-2\">\n {!inHeader && resetButton}\n {applyView && (\n <TableTriggerButton\n view={applyView}\n data-slot=\"column-manager-apply\"\n disabled={\n !orderChanged && (!draft || !columnDraftChanged(table.visibleColumns, draft))\n }\n onClick={() => void apply()}\n />\n )}\n </div>\n )}\n </div>\n )\n\n const onOpenChange = (next: boolean) => {\n // Closing without \"Apply\" drops the draft.\n if (!next) {\n setDraft(null)\n if (deferred) setOrderDraft(null)\n setAnnouncement('')\n }\n setOpen(next)\n }\n\n const triggerButton = (\n <TableTriggerButton\n view={trigger}\n defaultIcon={<Icon name=\"alfakit::tables.actions.column-manager\" fallback={Columns3Icon} />}\n aria-label={trigger.showLabel ? undefined : trigger.label}\n />\n )\n\n if (table.columnManagerLayout === 'modal') {\n // `columnManagerTriggerAction()` may set `slideOver()`, `modalWidth()` and the heading.\n const modal = table.triggerActions?.columnManager?.modal\n const heading = modal?.heading ?? t('table.columns', {}, 'Columns')\n const description = modal?.description ?? table.pluralModelLabel\n const widthClass = modal?.width ? FILTERS_MODAL_WIDTH_CLASSES[modal.width] : undefined\n\n if (modal?.slideOver) {\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetTrigger asChild>{triggerButton}</SheetTrigger>\n <SheetContent\n side={slideOverSide(modal.slideOverPosition, direction)}\n className={cn('flex w-full flex-col gap-0 p-0', widthClass ?? 'sm:max-w-md')}\n data-slot=\"column-manager\"\n data-slide-over=\"\"\n >\n <SheetHeader className=\"border-b\">\n <SheetTitle>{heading}</SheetTitle>\n <SheetDescription className={modal.description ? undefined : 'sr-only'}>\n {description}\n </SheetDescription>\n </SheetHeader>\n <div className=\"flex-1 overflow-y-auto p-4\">{body}</div>\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogTrigger asChild>{triggerButton}</DialogTrigger>\n <DialogContent className={widthClass ?? 'sm:max-w-lg'} data-slot=\"column-manager\">\n <DialogHeader>\n <DialogTitle>{heading}</DialogTitle>\n <DialogDescription className={modal?.description ? undefined : 'sr-only'}>\n {description}\n </DialogDescription>\n </DialogHeader>\n <div className=\"max-h-[60vh] overflow-y-auto\">{body}</div>\n </DialogContent>\n </Dialog>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>{triggerButton}</PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className={cn('p-2', table.columnManagerColumns ? 'w-auto min-w-64' : 'w-64')}\n data-slot=\"column-manager\"\n >\n {body}\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":"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"}
|
|
@@ -183,6 +183,7 @@ function ContentLayoutSort({ table, columns }) {
|
|
|
183
183
|
const sortable = columns.filter((column) => column.sortable);
|
|
184
184
|
if (sortable.length === 0) return null;
|
|
185
185
|
const direction = table.sort?.direction ?? "asc";
|
|
186
|
+
const selected = sortable.some((column) => column.name === table.sort?.column) ? table.sort?.column ?? "" : "";
|
|
186
187
|
return /* @__PURE__ */ jsxs("div", {
|
|
187
188
|
className: "flex items-center gap-1",
|
|
188
189
|
children: [/* @__PURE__ */ jsxs("label", {
|
|
@@ -191,7 +192,7 @@ function ContentLayoutSort({ table, columns }) {
|
|
|
191
192
|
className: "sr-only",
|
|
192
193
|
children: t("table.sort_by", {}, "Sort by")
|
|
193
194
|
}), /* @__PURE__ */ jsxs("select", {
|
|
194
|
-
value:
|
|
195
|
+
value: selected,
|
|
195
196
|
onChange: (event) => visitTable(table.identifier, {
|
|
196
197
|
sort: event.target.value || null,
|
|
197
198
|
direction: event.target.value ? direction : null
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-layout.js","names":[],"sources":["../../../src/components/table/content-layout.tsx"],"sourcesContent":["import type {\n ActionContext,\n ColumnLayoutNode,\n ColumnSchema,\n TableRecordSchema,\n TableSchema,\n TableSelection,\n} from '@alfakit/core'\nimport {\n VIRTUAL_ESTIMATES,\n columnSpanClasses,\n columnStartClasses,\n columnVisibilityClasses,\n gridColumnsClasses,\n layoutNodeGrows,\n layoutVisibilityClasses,\n primaryColumnName,\n recordActionsAtStart,\n splitClasses,\n stackClasses,\n virtualizeOptions,\n visitTable,\n} from '@alfakit/core'\nimport { ArrowDownIcon, ArrowUpIcon, ChevronDownIcon } from 'lucide-react'\nimport { useId, useState, type ReactNode } from 'react'\nimport { useT } from '../../context'\nimport { useVirtualList } from '../../hooks/use-virtual-list'\nimport { extraProps } from '../../lib/attributes'\nimport { cn } from '../../lib/utils'\nimport { ActionList } from '../actions/action-list'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport { RecordCell } from './record-cell'\n\nexport interface ContentLayoutProps {\n table: TableSchema\n records: TableRecordSchema[]\n columns: ColumnSchema[]\n token: string\n component: string\n context: ActionContext\n selectable: boolean\n selection: TableSelection\n onRecordClick: (record: TableRecordSchema) => void\n}\n\nfunction hasCollapsiblePanel(nodes: ColumnLayoutNode[]): boolean {\n return nodes.some(\n (node) =>\n node.type !== 'Column' &&\n ((node.type === 'Panel' && Boolean(node.collapsible)) || hasCollapsiblePanel(node.schema)),\n )\n}\n\n/**\n * Records arranged by a content layout (`Split`, `Stack`, `Panel`, `Grid`) instead of\n * a row of cells, optionally as cards in a `contentGrid()`. Long pages render only the\n * records in view (plus a few around them), by whole rows of the card grid; the list\n * keeps `aria-setsize` / `aria-posinset` so assistive technology knows the real positions.\n */\nexport function ContentLayout(props: ContentLayoutProps) {\n const { table, records } = props\n const grid = table.contentGrid\n const { range, indexes, style, attach, item } = useVirtualList(\n records.length,\n virtualizeOptions(table.virtualize, VIRTUAL_ESTIMATES.item, grid ? 16 : 0, grid ? 'auto' : 1),\n )\n\n return (\n <div\n data-slot=\"table-content\"\n role={range.active ? 'list' : undefined}\n ref={attach}\n style={style}\n className={cn(grid ? cn('grid gap-4 p-4', gridColumnsClasses(grid)) : 'divide-y')}\n >\n {indexes.map((index) => {\n const record = records[index] as TableRecordSchema\n\n return (\n <ContentRecord\n key={String(record.key)}\n {...props}\n record={record}\n card={Boolean(grid)}\n index={index}\n position={range.active ? { size: records.length, item } : undefined}\n />\n )\n })}\n </div>\n )\n}\n\nfunction ContentRecord({\n table,\n record,\n columns,\n token,\n component,\n context,\n selectable,\n selection,\n onRecordClick,\n card,\n index,\n position,\n}: ContentLayoutProps & {\n record: TableRecordSchema\n card: boolean\n index: number\n /** Set while the list is windowed: the real list size and the item ref callback. */\n position?: { size: number; item: (element: HTMLElement | null) => void | (() => void) }\n}) {\n const t = useT()\n const panelId = useId()\n const layout = table.contentLayout ?? []\n const collapsible = hasCollapsiblePanel(layout)\n const startsCollapsed = !layout.some(\n (node) => node.type === 'Panel' && node.collapsible && !node.collapsed,\n )\n const [open, setOpen] = useState(!startsCollapsed)\n const clickable = Boolean(record.url || table.recordAction)\n const primary = primaryColumnName(columns)\n const visible = new Set(columns.map((column) => column.name))\n const selected = selection.isSelected(record.key)\n\n const renderNode = (node: ColumnLayoutNode, index: number, inSplit = false): ReactNode => {\n const grows = inSplit && layoutNodeGrows(node, columns)\n const sizing = inSplit ? (grows ? 'min-w-0 flex-1' : 'shrink-0') : undefined\n // `columnSpan()` / `columnStart()` of a column or layout inside a `Grid`.\n const span = cn(columnSpanClasses(node.columnSpan), columnStartClasses(node.columnStart))\n\n if (node.type === 'Column') {\n const column = columns.find((item) => item.name === node.name)\n\n if (!column || !visible.has(column.name)) return null\n\n return (\n <div\n key={`column:${column.name}`}\n data-column={column.name}\n className={cn(sizing, span, columnVisibilityClasses(column, 'block'))}\n >\n <RecordCell\n column={column}\n record={record}\n token={token}\n component={component}\n context={context}\n clickable={clickable}\n primary={column.name === primary}\n hasRecordAction={Boolean(table.recordAction)}\n onRecordClick={onRecordClick}\n />\n </div>\n )\n }\n\n const visibility = layoutVisibilityClasses(node)\n const key = `${node.type}:${index}`\n\n switch (node.type) {\n case 'Split':\n return (\n <div\n key={key}\n data-layout=\"split\"\n {...extraProps(node.extraAttributes, cn(splitClasses(node), visibility, sizing, span))}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex, true))}\n </div>\n )\n case 'Stack':\n return (\n <div\n key={key}\n data-layout=\"stack\"\n {...extraProps(node.extraAttributes, cn(stackClasses(node), visibility, sizing, span))}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n case 'Grid':\n return (\n <div\n key={key}\n data-layout=\"grid\"\n {...extraProps(\n node.extraAttributes,\n cn('grid gap-2', gridColumnsClasses(node.columns), visibility, sizing, span),\n )}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n case 'Panel':\n if (node.collapsible && !open) return null\n\n return (\n <div\n key={key}\n data-layout=\"panel\"\n {...extraProps(\n node.extraAttributes,\n cn('bg-muted/50 flex flex-col gap-2 rounded-lg p-3', visibility, sizing, span),\n )}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n default:\n return null\n }\n }\n\n // Collapsible panels at the top level go under the record's main row.\n const main = layout.filter((node) => !(node.type === 'Panel' && node.collapsible))\n const details = layout.filter((node) => node.type === 'Panel' && node.collapsible)\n const atStart = recordActionsAtStart(table.recordActionsPosition)\n const actions = record.actions?.length ? (\n <div className=\"shrink-0\" onClick={(event) => event.stopPropagation()}>\n <ActionList\n actions={record.actions}\n context={{ ...context, record: record.key }}\n size=\"sm\"\n align=\"end\"\n className=\"flex-nowrap\"\n />\n </div>\n ) : null\n\n return (\n <div\n data-slot=\"table-record\"\n data-state={selected ? 'selected' : undefined}\n data-index={index}\n ref={position?.item}\n role={position ? 'listitem' : undefined}\n aria-setsize={position?.size}\n aria-posinset={position ? index + 1 : undefined}\n className={cn(\n 'flex flex-col gap-3 p-4',\n card && 'bg-card rounded-lg border shadow-xs',\n clickable && 'hover:bg-muted/40 cursor-pointer',\n selected && 'bg-muted/60',\n record.classes,\n )}\n onClick={() => clickable && onRecordClick(record)}\n >\n <div className=\"flex items-start gap-3\">\n {atStart && table.recordActionsPosition === 'before-cells' && actions}\n {selectable && (\n <div className=\"pt-0.5\" onClick={(event) => event.stopPropagation()}>\n {record.selectable !== false && (\n <Checkbox\n checked={selected}\n onCheckedChange={(checked) => selection.toggle(record.key, checked === true)}\n aria-label={t('table.select_row', {}, 'Select row')}\n />\n )}\n </div>\n )}\n {atStart && table.recordActionsPosition === 'before-columns' && actions}\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {main.map((node, index) => renderNode(node, index))}\n </div>\n {!atStart && actions}\n {collapsible && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n aria-label={\n open\n ? t('table.hide_details', {}, 'Hide details')\n : t('table.show_details', {}, 'Show details')\n }\n onClick={(event) => {\n event.stopPropagation()\n setOpen(!open)\n }}\n >\n <ChevronDownIcon className={cn('transition-transform', open && 'rotate-180')} />\n </Button>\n )}\n </div>\n {open && details.length > 0 && (\n <div id={panelId} className=\"flex flex-col gap-3\">\n {details.map((node, index) => renderNode(node, main.length + index))}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Sorting for content layouts, which have no column headers: a select of the sortable\n * columns and a direction toggle.\n */\nexport function ContentLayoutSort({\n table,\n columns,\n}: {\n table: TableSchema\n columns: ColumnSchema[]\n}) {\n const t = useT()\n const sortable = columns.filter((column) => column.sortable)\n\n if (sortable.length === 0) return null\n\n const direction = table.sort?.direction ?? 'asc'\n\n return (\n <div className=\"flex items-center gap-1\">\n <label className=\"flex items-center gap-2 text-sm\">\n <span className=\"sr-only\">{t('table.sort_by', {}, 'Sort by')}</span>\n <select\n value={table.sort?.column ?? ''}\n onChange={(event) =>\n visitTable(\n table.identifier,\n {\n sort: event.target.value || null,\n direction: event.target.value ? direction : null,\n },\n ['page'],\n )\n }\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"\">\n {table.defaultSortOptionLabel ?? t('table.sort_by', {}, 'Sort by')}\n </option>\n {sortable.map((column) => (\n <option key={column.name} value={column.name}>\n {column.label}\n </option>\n ))}\n </select>\n </label>\n {table.sort && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={\n direction === 'asc'\n ? t('table.sort_ascending', {}, 'Ascending')\n : t('table.sort_descending', {}, 'Descending')\n }\n onClick={() =>\n visitTable(\n table.identifier,\n { sort: table.sort?.column ?? null, direction: direction === 'asc' ? 'desc' : 'asc' },\n ['page'],\n )\n }\n >\n {direction === 'asc' ? <ArrowUpIcon /> : <ArrowDownIcon />}\n </Button>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content-layout.js","names":[],"sources":["../../../src/components/table/content-layout.tsx"],"sourcesContent":["import type {\n ActionContext,\n ColumnLayoutNode,\n ColumnSchema,\n TableRecordSchema,\n TableSchema,\n TableSelection,\n} from '@alfakit/core'\nimport {\n VIRTUAL_ESTIMATES,\n columnSpanClasses,\n columnStartClasses,\n columnVisibilityClasses,\n gridColumnsClasses,\n layoutNodeGrows,\n layoutVisibilityClasses,\n primaryColumnName,\n recordActionsAtStart,\n splitClasses,\n stackClasses,\n virtualizeOptions,\n visitTable,\n} from '@alfakit/core'\nimport { ArrowDownIcon, ArrowUpIcon, ChevronDownIcon } from 'lucide-react'\nimport { useId, useState, type ReactNode } from 'react'\nimport { useT } from '../../context'\nimport { useVirtualList } from '../../hooks/use-virtual-list'\nimport { extraProps } from '../../lib/attributes'\nimport { cn } from '../../lib/utils'\nimport { ActionList } from '../actions/action-list'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport { RecordCell } from './record-cell'\n\nexport interface ContentLayoutProps {\n table: TableSchema\n records: TableRecordSchema[]\n columns: ColumnSchema[]\n token: string\n component: string\n context: ActionContext\n selectable: boolean\n selection: TableSelection\n onRecordClick: (record: TableRecordSchema) => void\n}\n\nfunction hasCollapsiblePanel(nodes: ColumnLayoutNode[]): boolean {\n return nodes.some(\n (node) =>\n node.type !== 'Column' &&\n ((node.type === 'Panel' && Boolean(node.collapsible)) || hasCollapsiblePanel(node.schema)),\n )\n}\n\n/**\n * Records arranged by a content layout (`Split`, `Stack`, `Panel`, `Grid`) instead of\n * a row of cells, optionally as cards in a `contentGrid()`. Long pages render only the\n * records in view (plus a few around them), by whole rows of the card grid; the list\n * keeps `aria-setsize` / `aria-posinset` so assistive technology knows the real positions.\n */\nexport function ContentLayout(props: ContentLayoutProps) {\n const { table, records } = props\n const grid = table.contentGrid\n const { range, indexes, style, attach, item } = useVirtualList(\n records.length,\n virtualizeOptions(table.virtualize, VIRTUAL_ESTIMATES.item, grid ? 16 : 0, grid ? 'auto' : 1),\n )\n\n return (\n <div\n data-slot=\"table-content\"\n role={range.active ? 'list' : undefined}\n ref={attach}\n style={style}\n className={cn(grid ? cn('grid gap-4 p-4', gridColumnsClasses(grid)) : 'divide-y')}\n >\n {indexes.map((index) => {\n const record = records[index] as TableRecordSchema\n\n return (\n <ContentRecord\n key={String(record.key)}\n {...props}\n record={record}\n card={Boolean(grid)}\n index={index}\n position={range.active ? { size: records.length, item } : undefined}\n />\n )\n })}\n </div>\n )\n}\n\nfunction ContentRecord({\n table,\n record,\n columns,\n token,\n component,\n context,\n selectable,\n selection,\n onRecordClick,\n card,\n index,\n position,\n}: ContentLayoutProps & {\n record: TableRecordSchema\n card: boolean\n index: number\n /** Set while the list is windowed: the real list size and the item ref callback. */\n position?: { size: number; item: (element: HTMLElement | null) => void | (() => void) }\n}) {\n const t = useT()\n const panelId = useId()\n const layout = table.contentLayout ?? []\n const collapsible = hasCollapsiblePanel(layout)\n const startsCollapsed = !layout.some(\n (node) => node.type === 'Panel' && node.collapsible && !node.collapsed,\n )\n const [open, setOpen] = useState(!startsCollapsed)\n const clickable = Boolean(record.url || table.recordAction)\n const primary = primaryColumnName(columns)\n const visible = new Set(columns.map((column) => column.name))\n const selected = selection.isSelected(record.key)\n\n const renderNode = (node: ColumnLayoutNode, index: number, inSplit = false): ReactNode => {\n const grows = inSplit && layoutNodeGrows(node, columns)\n const sizing = inSplit ? (grows ? 'min-w-0 flex-1' : 'shrink-0') : undefined\n // `columnSpan()` / `columnStart()` of a column or layout inside a `Grid`.\n const span = cn(columnSpanClasses(node.columnSpan), columnStartClasses(node.columnStart))\n\n if (node.type === 'Column') {\n const column = columns.find((item) => item.name === node.name)\n\n if (!column || !visible.has(column.name)) return null\n\n return (\n <div\n key={`column:${column.name}`}\n data-column={column.name}\n className={cn(sizing, span, columnVisibilityClasses(column, 'block'))}\n >\n <RecordCell\n column={column}\n record={record}\n token={token}\n component={component}\n context={context}\n clickable={clickable}\n primary={column.name === primary}\n hasRecordAction={Boolean(table.recordAction)}\n onRecordClick={onRecordClick}\n />\n </div>\n )\n }\n\n const visibility = layoutVisibilityClasses(node)\n const key = `${node.type}:${index}`\n\n switch (node.type) {\n case 'Split':\n return (\n <div\n key={key}\n data-layout=\"split\"\n {...extraProps(node.extraAttributes, cn(splitClasses(node), visibility, sizing, span))}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex, true))}\n </div>\n )\n case 'Stack':\n return (\n <div\n key={key}\n data-layout=\"stack\"\n {...extraProps(node.extraAttributes, cn(stackClasses(node), visibility, sizing, span))}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n case 'Grid':\n return (\n <div\n key={key}\n data-layout=\"grid\"\n {...extraProps(\n node.extraAttributes,\n cn('grid gap-2', gridColumnsClasses(node.columns), visibility, sizing, span),\n )}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n case 'Panel':\n if (node.collapsible && !open) return null\n\n return (\n <div\n key={key}\n data-layout=\"panel\"\n {...extraProps(\n node.extraAttributes,\n cn('bg-muted/50 flex flex-col gap-2 rounded-lg p-3', visibility, sizing, span),\n )}\n >\n {node.schema.map((child, childIndex) => renderNode(child, childIndex))}\n </div>\n )\n default:\n return null\n }\n }\n\n // Collapsible panels at the top level go under the record's main row.\n const main = layout.filter((node) => !(node.type === 'Panel' && node.collapsible))\n const details = layout.filter((node) => node.type === 'Panel' && node.collapsible)\n const atStart = recordActionsAtStart(table.recordActionsPosition)\n const actions = record.actions?.length ? (\n <div className=\"shrink-0\" onClick={(event) => event.stopPropagation()}>\n <ActionList\n actions={record.actions}\n context={{ ...context, record: record.key }}\n size=\"sm\"\n align=\"end\"\n className=\"flex-nowrap\"\n />\n </div>\n ) : null\n\n return (\n <div\n data-slot=\"table-record\"\n data-state={selected ? 'selected' : undefined}\n data-index={index}\n ref={position?.item}\n role={position ? 'listitem' : undefined}\n aria-setsize={position?.size}\n aria-posinset={position ? index + 1 : undefined}\n className={cn(\n 'flex flex-col gap-3 p-4',\n card && 'bg-card rounded-lg border shadow-xs',\n clickable && 'hover:bg-muted/40 cursor-pointer',\n selected && 'bg-muted/60',\n record.classes,\n )}\n onClick={() => clickable && onRecordClick(record)}\n >\n <div className=\"flex items-start gap-3\">\n {atStart && table.recordActionsPosition === 'before-cells' && actions}\n {selectable && (\n <div className=\"pt-0.5\" onClick={(event) => event.stopPropagation()}>\n {record.selectable !== false && (\n <Checkbox\n checked={selected}\n onCheckedChange={(checked) => selection.toggle(record.key, checked === true)}\n aria-label={t('table.select_row', {}, 'Select row')}\n />\n )}\n </div>\n )}\n {atStart && table.recordActionsPosition === 'before-columns' && actions}\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {main.map((node, index) => renderNode(node, index))}\n </div>\n {!atStart && actions}\n {collapsible && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n aria-label={\n open\n ? t('table.hide_details', {}, 'Hide details')\n : t('table.show_details', {}, 'Show details')\n }\n onClick={(event) => {\n event.stopPropagation()\n setOpen(!open)\n }}\n >\n <ChevronDownIcon className={cn('transition-transform', open && 'rotate-180')} />\n </Button>\n )}\n </div>\n {open && details.length > 0 && (\n <div id={panelId} className=\"flex flex-col gap-3\">\n {details.map((node, index) => renderNode(node, main.length + index))}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Sorting for content layouts, which have no column headers: a select of the sortable\n * columns and a direction toggle.\n */\nexport function ContentLayoutSort({\n table,\n columns,\n}: {\n table: TableSchema\n columns: ColumnSchema[]\n}) {\n const t = useT()\n const sortable = columns.filter((column) => column.sortable)\n\n if (sortable.length === 0) return null\n\n const direction = table.sort?.direction ?? 'asc'\n // A default sort on a column the select doesn't list (`created_at`) shows the placeholder.\n const selected = sortable.some((column) => column.name === table.sort?.column)\n ? (table.sort?.column ?? '')\n : ''\n\n return (\n <div className=\"flex items-center gap-1\">\n <label className=\"flex items-center gap-2 text-sm\">\n <span className=\"sr-only\">{t('table.sort_by', {}, 'Sort by')}</span>\n <select\n value={selected}\n onChange={(event) =>\n visitTable(\n table.identifier,\n {\n sort: event.target.value || null,\n direction: event.target.value ? direction : null,\n },\n ['page'],\n )\n }\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"\">\n {table.defaultSortOptionLabel ?? t('table.sort_by', {}, 'Sort by')}\n </option>\n {sortable.map((column) => (\n <option key={column.name} value={column.name}>\n {column.label}\n </option>\n ))}\n </select>\n </label>\n {table.sort && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={\n direction === 'asc'\n ? t('table.sort_ascending', {}, 'Ascending')\n : t('table.sort_descending', {}, 'Descending')\n }\n onClick={() =>\n visitTable(\n table.identifier,\n { sort: table.sort?.column ?? null, direction: direction === 'asc' ? 'desc' : 'asc' },\n ['page'],\n )\n }\n >\n {direction === 'asc' ? <ArrowUpIcon /> : <ArrowDownIcon />}\n </Button>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useT } from "../../context.js";
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { colorClass, textColorClasses } from "../../lib/colors.js";
|
|
3
4
|
import { Icon } from "../../lib/icons.js";
|
|
4
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
|
|
5
6
|
import { Checkbox } from "../ui/checkbox.js";
|
|
@@ -122,7 +123,7 @@ function TableGroupingDropdown({ table, groups, trigger, active, onGroup, onDire
|
|
|
122
123
|
/** A group heading row (`data-slot="table-group"`), with a collapse toggle when collapsible. */
|
|
123
124
|
function GroupHeaderRow({ group, collapsed, collapsible, colSpan, onToggle, selection }) {
|
|
124
125
|
const t = useT();
|
|
125
|
-
const
|
|
126
|
+
const text = /* @__PURE__ */ jsxs("span", {
|
|
126
127
|
className: "flex flex-col items-start",
|
|
127
128
|
children: [/* @__PURE__ */ jsx("span", {
|
|
128
129
|
className: "font-medium",
|
|
@@ -132,6 +133,15 @@ function GroupHeaderRow({ group, collapsed, collapsible, colSpan, onToggle, sele
|
|
|
132
133
|
children: group.description
|
|
133
134
|
})]
|
|
134
135
|
});
|
|
136
|
+
const content = group.icon ? /* @__PURE__ */ jsxs("span", {
|
|
137
|
+
"data-slot": "table-group-title",
|
|
138
|
+
className: "flex items-center gap-2",
|
|
139
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
140
|
+
name: group.icon,
|
|
141
|
+
className: cn("size-4 shrink-0", colorClass(textColorClasses, group.color, "gray")),
|
|
142
|
+
"aria-hidden": "true"
|
|
143
|
+
}), text]
|
|
144
|
+
}) : text;
|
|
135
145
|
return /* @__PURE__ */ jsx(TableRow, {
|
|
136
146
|
"data-slot": "table-group",
|
|
137
147
|
className: "bg-muted/50 hover:bg-muted/50",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grouping.js","names":[],"sources":["../../../src/components/table/grouping.tsx"],"sourcesContent":["import type {\n ColumnSchema,\n TableRecordGroupSchema,\n TableSchema,\n TriggerButtonView,\n} from '@alfakit/core'\nimport {\n initialCollapsedGroups,\n registerTablePersistence,\n toggleCollapsedGroup,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronRightIcon, LayersIcon } from 'lucide-react'\nimport { useEffect, useState } from 'react'\nimport { useT } from '../../context'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { TableCell, TableRow } from '../ui/table'\nimport { TableTriggerButton } from './trigger-button'\n\n/** The \"group by\" menu: one of the table's groups (or none) and the direction. */\nexport function TableGroupingMenu({ table }: { table: TableSchema }) {\n const t = useT()\n\n if (!table.groups || table.groups.length === 0) {\n return null\n }\n\n const active = table.grouping?.group ?? ''\n const groups = table.groups\n // `groupRecordsTriggerAction()`.\n const trigger = triggerButtonView(table, 'groupRecords', {\n label: t('table_grouping.group_by', {}, 'Group by'),\n })\n const changeGroup = (group: string) => visitTable(table.identifier, { group }, ['page'])\n const changeDirection = (direction: string) =>\n visitTable(table.identifier, { groupDirection: direction === 'desc' ? 'desc' : 'asc' }, [\n 'page',\n ])\n const inDropdownOnly = Boolean(table.groupingSettingsInDropdownOnDesktop)\n\n // Desktop default: the settings inline in the toolbar (the dropdown is for phones).\n const inline = inDropdownOnly ? null : (\n <div data-slot=\"table-grouping-inline\" className=\"hidden items-center gap-2 text-sm sm:flex\">\n <label className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground\">\n {t('table_grouping.group_by', {}, 'Group by')}\n </span>\n <select\n value={active}\n onChange={(event) => changeGroup(event.target.value)}\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"\">{t('table_grouping.none', {}, 'No grouping')}</option>\n {groups.map((group) => (\n <option key={group.id} value={group.id}>\n {group.label}\n </option>\n ))}\n </select>\n </label>\n {table.grouping && !table.groupingDirectionSettingHidden && (\n <label className=\"flex items-center\">\n <span className=\"sr-only\">{t('table_grouping.direction', {}, 'Group direction')}</span>\n <select\n value={table.grouping.direction}\n onChange={(event) => changeDirection(event.target.value)}\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"asc\">{t('table_grouping.ascending', {}, 'Ascending')}</option>\n <option value=\"desc\">{t('table_grouping.descending', {}, 'Descending')}</option>\n </select>\n </label>\n )}\n </div>\n )\n\n if (!trigger) return inline\n\n return (\n <>\n {inline}\n <div className={cn(!inDropdownOnly && 'sm:hidden')}>\n <TableGroupingDropdown\n table={table}\n groups={groups}\n trigger={trigger}\n active={active}\n onGroup={changeGroup}\n onDirection={changeDirection}\n />\n </div>\n </>\n )\n}\n\nfunction TableGroupingDropdown({\n table,\n groups,\n trigger,\n active,\n onGroup,\n onDirection,\n}: {\n table: TableSchema\n groups: NonNullable<TableSchema['groups']>\n trigger: TriggerButtonView\n active: string\n onGroup: (group: string) => void\n onDirection: (direction: string) => void\n}) {\n const t = useT()\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <TableTriggerButton\n view={trigger}\n defaultIcon={<Icon name=\"alfakit::tables.actions.group\" fallback={LayersIcon} />}\n responsiveLabel={!table.triggerActions?.groupRecords}\n active={Boolean(table.grouping)}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-48\">\n <DropdownMenuLabel>{t('table_grouping.group_by', {}, 'Group by')}</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={active} onValueChange={onGroup}>\n <DropdownMenuRadioItem value=\"\">\n {t('table_grouping.none', {}, 'No grouping')}\n </DropdownMenuRadioItem>\n {groups.map((group) => (\n <DropdownMenuRadioItem key={group.id} value={group.id}>\n {group.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {table.grouping && !table.groupingDirectionSettingHidden && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuLabel>\n {t('table_grouping.direction', {}, 'Group direction')}\n </DropdownMenuLabel>\n <DropdownMenuRadioGroup value={table.grouping.direction} onValueChange={onDirection}>\n <DropdownMenuRadioItem value=\"asc\">\n {t('table_grouping.ascending', {}, 'Ascending')}\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"desc\">\n {t('table_grouping.descending', {}, 'Descending')}\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/** A group heading row (`data-slot=\"table-group\"`), with a collapse toggle when collapsible. */\nexport function GroupHeaderRow({\n group,\n collapsed,\n collapsible,\n colSpan,\n onToggle,\n selection,\n}: {\n group: TableRecordGroupSchema\n collapsed: boolean\n collapsible: boolean\n colSpan: number\n onToggle: () => void\n /** `selectGroupsOnly()`: a checkbox selecting the group's records on the page. */\n selection?: { checked: boolean | 'indeterminate'; onChange: (checked: boolean) => void }\n}) {\n const t = useT()\n const content = (\n <span className=\"flex flex-col items-start\">\n <span className=\"font-medium\">{group.title}</span>\n {group.description && (\n <span className=\"text-muted-foreground text-xs font-normal\">{group.description}</span>\n )}\n </span>\n )\n\n return (\n <TableRow data-slot=\"table-group\" className=\"bg-muted/50 hover:bg-muted/50\">\n <TableCell colSpan={colSpan} className=\"py-2\">\n <span className=\"flex items-center gap-3\">\n {selection && (\n <Checkbox\n checked={selection.checked}\n onCheckedChange={(checked) => selection.onChange(checked === true)}\n aria-label={t('table.select_group', { group: group.title }, 'Select group :group')}\n />\n )}\n {collapsible ? (\n <button\n type=\"button\"\n className=\"focus-visible:ring-ring/50 flex items-center gap-2 rounded-sm text-start outline-none focus-visible:ring-[3px]\"\n aria-expanded={!collapsed}\n aria-label={`${collapsed ? t('table_grouping.expand', {}, 'Expand group') : t('table_grouping.collapse', {}, 'Collapse group')}: ${group.title}`}\n onClick={onToggle}\n >\n {collapsed ? (\n <ChevronRightIcon className=\"size-4 shrink-0 rtl:rotate-180\" aria-hidden=\"true\" />\n ) : (\n <Icon\n name=\"alfakit::tables.grouping.collapse-button\"\n fallback={ChevronDownIcon}\n className=\"size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n {content}\n </button>\n ) : (\n content\n )}\n </span>\n </TableCell>\n </TableRow>\n )\n}\n\n/**\n * Collapsed group keys. Starts collapsed for `collapsedGroupsByDefault()` and resets when\n * the table is grouped differently.\n */\nexport function useCollapsedGroups(\n table: Pick<TableSchema, 'grouping' | 'recordGroups'>,\n): [Set<string>, (key: string) => void] {\n const groupId = table.grouping ? `${table.grouping.group}:${table.grouping.direction}` : ''\n const [state, setState] = useState(() => ({\n groupId,\n collapsed: initialCollapsedGroups(table),\n }))\n\n if (state.groupId !== groupId) {\n setState({ groupId, collapsed: initialCollapsedGroups(table) })\n }\n\n return [\n state.collapsed,\n (key: string) =>\n setState((current) => ({\n ...current,\n collapsed: toggleCollapsedGroup(current.collapsed, key),\n })),\n ]\n}\n\n/** Register the table's session-persisted keys so cleared values are sent explicitly. */\nexport function useTablePersistence(table: Pick<TableSchema, 'identifier' | 'persistedState'>) {\n const keys = (table.persistedState ?? []).join(',')\n\n useEffect(\n () => registerTablePersistence(table.identifier, keys ? keys.split(',') : undefined),\n [table.identifier, keys],\n )\n}\n\n/** Number of cells in a row of `columns` plus leading and trailing cells. */\nexport function rowCellCount(columns: ColumnSchema[], leading: number, trailing: number): number {\n return columns.length + leading + trailing\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"grouping.js","names":[],"sources":["../../../src/components/table/grouping.tsx"],"sourcesContent":["import type {\n ColumnSchema,\n TableRecordGroupSchema,\n TableSchema,\n TriggerButtonView,\n} from '@alfakit/core'\nimport {\n initialCollapsedGroups,\n registerTablePersistence,\n toggleCollapsedGroup,\n triggerButtonView,\n visitTable,\n} from '@alfakit/core'\nimport { ChevronDownIcon, ChevronRightIcon, LayersIcon } from 'lucide-react'\nimport { useEffect, useState } from 'react'\nimport { useT } from '../../context'\nimport { colorClass, textColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { TableCell, TableRow } from '../ui/table'\nimport { TableTriggerButton } from './trigger-button'\n\n/** The \"group by\" menu: one of the table's groups (or none) and the direction. */\nexport function TableGroupingMenu({ table }: { table: TableSchema }) {\n const t = useT()\n\n if (!table.groups || table.groups.length === 0) {\n return null\n }\n\n const active = table.grouping?.group ?? ''\n const groups = table.groups\n // `groupRecordsTriggerAction()`.\n const trigger = triggerButtonView(table, 'groupRecords', {\n label: t('table_grouping.group_by', {}, 'Group by'),\n })\n const changeGroup = (group: string) => visitTable(table.identifier, { group }, ['page'])\n const changeDirection = (direction: string) =>\n visitTable(table.identifier, { groupDirection: direction === 'desc' ? 'desc' : 'asc' }, [\n 'page',\n ])\n const inDropdownOnly = Boolean(table.groupingSettingsInDropdownOnDesktop)\n\n // Desktop default: the settings inline in the toolbar (the dropdown is for phones).\n const inline = inDropdownOnly ? null : (\n <div data-slot=\"table-grouping-inline\" className=\"hidden items-center gap-2 text-sm sm:flex\">\n <label className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground\">\n {t('table_grouping.group_by', {}, 'Group by')}\n </span>\n <select\n value={active}\n onChange={(event) => changeGroup(event.target.value)}\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"\">{t('table_grouping.none', {}, 'No grouping')}</option>\n {groups.map((group) => (\n <option key={group.id} value={group.id}>\n {group.label}\n </option>\n ))}\n </select>\n </label>\n {table.grouping && !table.groupingDirectionSettingHidden && (\n <label className=\"flex items-center\">\n <span className=\"sr-only\">{t('table_grouping.direction', {}, 'Group direction')}</span>\n <select\n value={table.grouping.direction}\n onChange={(event) => changeDirection(event.target.value)}\n className=\"border-input h-8 rounded-md border bg-transparent px-2 text-sm\"\n >\n <option value=\"asc\">{t('table_grouping.ascending', {}, 'Ascending')}</option>\n <option value=\"desc\">{t('table_grouping.descending', {}, 'Descending')}</option>\n </select>\n </label>\n )}\n </div>\n )\n\n if (!trigger) return inline\n\n return (\n <>\n {inline}\n <div className={cn(!inDropdownOnly && 'sm:hidden')}>\n <TableGroupingDropdown\n table={table}\n groups={groups}\n trigger={trigger}\n active={active}\n onGroup={changeGroup}\n onDirection={changeDirection}\n />\n </div>\n </>\n )\n}\n\nfunction TableGroupingDropdown({\n table,\n groups,\n trigger,\n active,\n onGroup,\n onDirection,\n}: {\n table: TableSchema\n groups: NonNullable<TableSchema['groups']>\n trigger: TriggerButtonView\n active: string\n onGroup: (group: string) => void\n onDirection: (direction: string) => void\n}) {\n const t = useT()\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <TableTriggerButton\n view={trigger}\n defaultIcon={<Icon name=\"alfakit::tables.actions.group\" fallback={LayersIcon} />}\n responsiveLabel={!table.triggerActions?.groupRecords}\n active={Boolean(table.grouping)}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-48\">\n <DropdownMenuLabel>{t('table_grouping.group_by', {}, 'Group by')}</DropdownMenuLabel>\n <DropdownMenuRadioGroup value={active} onValueChange={onGroup}>\n <DropdownMenuRadioItem value=\"\">\n {t('table_grouping.none', {}, 'No grouping')}\n </DropdownMenuRadioItem>\n {groups.map((group) => (\n <DropdownMenuRadioItem key={group.id} value={group.id}>\n {group.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n {table.grouping && !table.groupingDirectionSettingHidden && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuLabel>\n {t('table_grouping.direction', {}, 'Group direction')}\n </DropdownMenuLabel>\n <DropdownMenuRadioGroup value={table.grouping.direction} onValueChange={onDirection}>\n <DropdownMenuRadioItem value=\"asc\">\n {t('table_grouping.ascending', {}, 'Ascending')}\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"desc\">\n {t('table_grouping.descending', {}, 'Descending')}\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/** A group heading row (`data-slot=\"table-group\"`), with a collapse toggle when collapsible. */\nexport function GroupHeaderRow({\n group,\n collapsed,\n collapsible,\n colSpan,\n onToggle,\n selection,\n}: {\n group: TableRecordGroupSchema\n collapsed: boolean\n collapsible: boolean\n colSpan: number\n onToggle: () => void\n /** `selectGroupsOnly()`: a checkbox selecting the group's records on the page. */\n selection?: { checked: boolean | 'indeterminate'; onChange: (checked: boolean) => void }\n}) {\n const t = useT()\n const text = (\n <span className=\"flex flex-col items-start\">\n <span className=\"font-medium\">{group.title}</span>\n {group.description && (\n <span className=\"text-muted-foreground text-xs font-normal\">{group.description}</span>\n )}\n </span>\n )\n // An enum group shows its `HasIcon` icon, in its `HasColor` colour, before the title.\n const content = group.icon ? (\n <span data-slot=\"table-group-title\" className=\"flex items-center gap-2\">\n <Icon\n name={group.icon}\n className={cn('size-4 shrink-0', colorClass(textColorClasses, group.color, 'gray'))}\n aria-hidden=\"true\"\n />\n {text}\n </span>\n ) : (\n text\n )\n\n return (\n <TableRow data-slot=\"table-group\" className=\"bg-muted/50 hover:bg-muted/50\">\n <TableCell colSpan={colSpan} className=\"py-2\">\n <span className=\"flex items-center gap-3\">\n {selection && (\n <Checkbox\n checked={selection.checked}\n onCheckedChange={(checked) => selection.onChange(checked === true)}\n aria-label={t('table.select_group', { group: group.title }, 'Select group :group')}\n />\n )}\n {collapsible ? (\n <button\n type=\"button\"\n className=\"focus-visible:ring-ring/50 flex items-center gap-2 rounded-sm text-start outline-none focus-visible:ring-[3px]\"\n aria-expanded={!collapsed}\n aria-label={`${collapsed ? t('table_grouping.expand', {}, 'Expand group') : t('table_grouping.collapse', {}, 'Collapse group')}: ${group.title}`}\n onClick={onToggle}\n >\n {collapsed ? (\n <ChevronRightIcon className=\"size-4 shrink-0 rtl:rotate-180\" aria-hidden=\"true\" />\n ) : (\n <Icon\n name=\"alfakit::tables.grouping.collapse-button\"\n fallback={ChevronDownIcon}\n className=\"size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n {content}\n </button>\n ) : (\n content\n )}\n </span>\n </TableCell>\n </TableRow>\n )\n}\n\n/**\n * Collapsed group keys. Starts collapsed for `collapsedGroupsByDefault()` and resets when\n * the table is grouped differently.\n */\nexport function useCollapsedGroups(\n table: Pick<TableSchema, 'grouping' | 'recordGroups'>,\n): [Set<string>, (key: string) => void] {\n const groupId = table.grouping ? `${table.grouping.group}:${table.grouping.direction}` : ''\n const [state, setState] = useState(() => ({\n groupId,\n collapsed: initialCollapsedGroups(table),\n }))\n\n if (state.groupId !== groupId) {\n setState({ groupId, collapsed: initialCollapsedGroups(table) })\n }\n\n return [\n state.collapsed,\n (key: string) =>\n setState((current) => ({\n ...current,\n collapsed: toggleCollapsedGroup(current.collapsed, key),\n })),\n ]\n}\n\n/** Register the table's session-persisted keys so cleared values are sent explicitly. */\nexport function useTablePersistence(table: Pick<TableSchema, 'identifier' | 'persistedState'>) {\n const keys = (table.persistedState ?? []).join(',')\n\n useEffect(\n () => registerTablePersistence(table.identifier, keys ? keys.split(',') : undefined),\n [table.identifier, keys],\n )\n}\n\n/** Number of cells in a row of `columns` plus leading and trailing cells. */\nexport function rowCellCount(columns: ColumnSchema[], leading: number, trailing: number): number {\n return columns.length + leading + trailing\n}\n"],"mappings":"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"}
|