@alfakit/react 0.1.10 → 0.1.12
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 +12 -0
- package/dist/components/actions/action-button.js +13 -1
- package/dist/components/actions/action-button.js.map +1 -1
- package/dist/components/dev-tools/dev-tools-inspector.js +2 -2
- package/dist/components/dev-tools/dev-tools-inspector.js.map +1 -1
- package/dist/components/entries/other-entries.d.ts.map +1 -1
- package/dist/components/entries/other-entries.js +1 -1
- package/dist/components/entries/other-entries.js.map +1 -1
- package/dist/components/fields/builder.d.ts.map +1 -1
- package/dist/components/fields/builder.js +3 -3
- package/dist/components/fields/builder.js.map +1 -1
- package/dist/components/fields/file-upload.d.ts.map +1 -1
- package/dist/components/fields/file-upload.js +2 -5
- package/dist/components/fields/file-upload.js.map +1 -1
- package/dist/components/fields/money-input.d.ts +5 -0
- package/dist/components/fields/money-input.d.ts.map +1 -1
- package/dist/components/fields/money-input.js +2 -5
- package/dist/components/fields/money-input.js.map +1 -1
- package/dist/components/fields/repeater.d.ts.map +1 -1
- package/dist/components/fields/repeater.js +3 -3
- package/dist/components/fields/repeater.js.map +1 -1
- package/dist/components/fields/rich-editor-extensions.js +5 -2
- package/dist/components/fields/rich-editor-extensions.js.map +1 -1
- package/dist/components/layout/wizard.d.ts.map +1 -1
- package/dist/components/layout/wizard.js +4 -1
- package/dist/components/layout/wizard.js.map +1 -1
- package/dist/components/notifications/flash-toasts.d.ts.map +1 -1
- package/dist/components/notifications/flash-toasts.js +11 -1
- package/dist/components/notifications/flash-toasts.js.map +1 -1
- package/dist/components/notifications/notifications-menu.js +3 -3
- package/dist/components/notifications/notifications-menu.js.map +1 -1
- package/dist/components/panel/lazy-placeholder.d.ts +1 -0
- package/dist/components/panel/lazy-placeholder.d.ts.map +1 -1
- package/dist/components/panel/lazy-placeholder.js +38 -4
- package/dist/components/panel/lazy-placeholder.js.map +1 -1
- package/dist/components/panel/pwa.d.ts.map +1 -1
- package/dist/components/panel/pwa.js +8 -7
- package/dist/components/panel/pwa.js.map +1 -1
- package/dist/components/panel/shortcuts-overlay.js +1 -1
- package/dist/components/panel/shortcuts-overlay.js.map +1 -1
- package/dist/components/table/cells.js +2 -2
- package/dist/components/table/cells.js.map +1 -1
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/calendar.js +7 -0
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/widgets/dashboard-customizer.js +2 -2
- package/dist/components/widgets/dashboard-customizer.js.map +1 -1
- package/dist/components/widgets/widgets-block.js +2 -2
- package/dist/components/widgets/widgets-block.js.map +1 -1
- package/dist/components.css +19 -0
- package/dist/create-alfakit.d.ts.map +1 -1
- package/dist/create-alfakit.js +3 -2
- package/dist/create-alfakit.js.map +1 -1
- package/dist/eject-manifest.json +4 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/pages/page.js +1 -1
- package/dist/pages/page.js.map +1 -1
- package/dist/standalone/alfakit-component.js +2 -2
- package/dist/standalone/alfakit-component.js.map +1 -1
- package/dist/standalone.css +21 -0
- package/dist/starter-kit/index.d.ts +2 -2
- package/dist/starter-kit/index.js +2 -2
- package/dist/starter-kit/starter-kit.d.ts +9 -0
- package/dist/starter-kit/starter-kit.d.ts.map +1 -1
- package/dist/starter-kit/starter-kit.js +21 -2
- package/dist/starter-kit/starter-kit.js.map +1 -1
- package/package.json +2 -2
- package/src/components/actions/action-button.tsx +15 -1
- package/src/components/dev-tools/dev-tools-inspector.tsx +6 -1
- package/src/components/entries/other-entries.tsx +2 -0
- package/src/components/fields/builder.tsx +7 -2
- package/src/components/fields/file-upload.tsx +2 -7
- package/src/components/fields/money-input.tsx +3 -14
- package/src/components/fields/repeater.tsx +6 -3
- package/src/components/fields/rich-editor-extensions.tsx +9 -5
- package/src/components/layout/wizard.tsx +5 -1
- package/src/components/notifications/flash-toasts.tsx +8 -0
- package/src/components/notifications/notifications-menu.tsx +3 -3
- package/src/components/panel/lazy-placeholder.tsx +41 -1
- package/src/components/panel/pwa.tsx +23 -9
- package/src/components/panel/shortcuts-overlay.tsx +1 -1
- package/src/components/table/cells.tsx +6 -2
- package/src/components/ui/calendar.tsx +8 -0
- package/src/components/widgets/dashboard-customizer.tsx +2 -2
- package/src/components/widgets/widgets-block.tsx +2 -2
- package/src/create-alfakit.tsx +9 -1
- package/src/pages/page.tsx +1 -1
- package/src/standalone/alfakit-component.tsx +2 -2
- package/src/starter-kit/index.ts +1 -0
- package/src/starter-kit/starter-kit.tsx +22 -0
|
@@ -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, 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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"}
|
|
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 // Without `alt()` the first image is named after the column, so a record link that\n // wraps it has a name (W-V4-20).\n alt={cell.alt ?? (index === 0 ? (column.label ?? '') : '')}\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',\n // The last image keeps the stack's negative end margin (W-S3-03).\n schema.stacked && usesDefaultImageStack(schema) ? 'ms-3' : '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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","names":[],"sources":["../../../src/components/ui/calendar.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","names":[],"sources":["../../../src/components/ui/calendar.tsx"],"mappings":";;;iBAOS,WACP,WACA,YACA,oBACG,SACF,sBAAsB,6BAAU,IAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useT } from "../../context.js";
|
|
1
2
|
import { cn } from "../../lib/utils.js";
|
|
2
3
|
import { buttonVariants } from "./button.js";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -5,8 +6,14 @@ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
|
5
6
|
import { DayPicker } from "react-day-picker";
|
|
6
7
|
//#region src/components/ui/calendar.tsx
|
|
7
8
|
function Calendar({ className, classNames, showOutsideDays = true, ...props }) {
|
|
9
|
+
const t = useT();
|
|
8
10
|
return /* @__PURE__ */ jsx(DayPicker, {
|
|
9
11
|
showOutsideDays,
|
|
12
|
+
labels: {
|
|
13
|
+
labelPrevious: () => t("form.previous_month", {}, "Previous month"),
|
|
14
|
+
labelNext: () => t("form.next_month", {}, "Next month"),
|
|
15
|
+
...props.labels
|
|
16
|
+
},
|
|
10
17
|
className: cn("p-3", className),
|
|
11
18
|
classNames: {
|
|
12
19
|
months: "flex flex-col sm:flex-row gap-2",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.js","names":[],"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { DayPicker } from 'react-day-picker'\nimport { cn } from '../../lib/utils'\nimport { buttonVariants } from './button'\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n ...props\n}: ComponentProps<typeof DayPicker>) {\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn('p-3', className)}\n classNames={{\n months: 'flex flex-col sm:flex-row gap-2',\n month: 'flex flex-col gap-4',\n month_caption: 'flex justify-center pt-1 relative items-center w-full',\n caption_label: 'text-sm font-medium',\n nav: 'flex items-center gap-1 absolute inset-x-0 top-3 justify-between px-3 pointer-events-none',\n button_previous: cn(\n buttonVariants({ variant: 'outline' }),\n 'pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n ),\n button_next: cn(\n buttonVariants({ variant: 'outline' }),\n 'pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n ),\n month_grid: 'w-full border-collapse',\n weekdays: 'flex',\n weekday: 'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',\n week: 'flex w-full mt-2',\n day: 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-e-md first:[&:has([aria-selected])]:rounded-s-md last:[&:has([aria-selected])]:rounded-e-md',\n day_button: cn(\n buttonVariants({ variant: 'ghost' }),\n 'size-8 p-0 font-normal aria-selected:opacity-100',\n ),\n range_start:\n 'day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground',\n range_end: 'day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground',\n selected:\n 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground rounded-md',\n today: 'bg-accent text-accent-foreground rounded-md',\n outside: 'day-outside text-muted-foreground aria-selected:text-muted-foreground',\n disabled: 'text-muted-foreground opacity-50',\n range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',\n hidden: 'invisible',\n ...classNames,\n }}\n components={{\n Chevron: ({ orientation, className: chevronClassName, ...rest }) =>\n orientation === 'left' ? (\n <ChevronLeftIcon className={cn('size-4', chevronClassName)} {...rest} />\n ) : (\n <ChevronRightIcon className={cn('size-4', chevronClassName)} {...rest} />\n ),\n }}\n {...props}\n />\n )\n}\n\nexport { Calendar }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"calendar.js","names":[],"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'\nimport type { ComponentProps } from 'react'\nimport { DayPicker } from 'react-day-picker'\nimport { useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { buttonVariants } from './button'\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n ...props\n}: ComponentProps<typeof DayPicker>) {\n const t = useT()\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n labels={{\n labelPrevious: () => t('form.previous_month', {}, 'Previous month'),\n labelNext: () => t('form.next_month', {}, 'Next month'),\n ...props.labels,\n }}\n className={cn('p-3', className)}\n classNames={{\n months: 'flex flex-col sm:flex-row gap-2',\n month: 'flex flex-col gap-4',\n month_caption: 'flex justify-center pt-1 relative items-center w-full',\n caption_label: 'text-sm font-medium',\n nav: 'flex items-center gap-1 absolute inset-x-0 top-3 justify-between px-3 pointer-events-none',\n button_previous: cn(\n buttonVariants({ variant: 'outline' }),\n 'pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n ),\n button_next: cn(\n buttonVariants({ variant: 'outline' }),\n 'pointer-events-auto size-7 bg-transparent p-0 opacity-50 hover:opacity-100',\n ),\n month_grid: 'w-full border-collapse',\n weekdays: 'flex',\n weekday: 'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',\n week: 'flex w-full mt-2',\n day: 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-e-md first:[&:has([aria-selected])]:rounded-s-md last:[&:has([aria-selected])]:rounded-e-md',\n day_button: cn(\n buttonVariants({ variant: 'ghost' }),\n 'size-8 p-0 font-normal aria-selected:opacity-100',\n ),\n range_start:\n 'day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground',\n range_end: 'day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground',\n selected:\n 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground rounded-md',\n today: 'bg-accent text-accent-foreground rounded-md',\n outside: 'day-outside text-muted-foreground aria-selected:text-muted-foreground',\n disabled: 'text-muted-foreground opacity-50',\n range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',\n hidden: 'invisible',\n ...classNames,\n }}\n components={{\n Chevron: ({ orientation, className: chevronClassName, ...rest }) =>\n orientation === 'left' ? (\n <ChevronLeftIcon className={cn('size-4', chevronClassName)} {...rest} />\n ) : (\n <ChevronRightIcon className={cn('size-4', chevronClassName)} {...rest} />\n ),\n }}\n {...props}\n />\n )\n}\n\nexport { Calendar }\n"],"mappings":";;;;;;;AAOA,SAAS,SAAS,EAChB,WACA,YACA,kBAAkB,MAClB,GAAG,SACgC;CACnC,MAAM,IAAI,KAAK;CAEf,OACE,oBAAC,WAAD;EACmB;EACjB,QAAQ;GACN,qBAAqB,EAAE,uBAAuB,CAAC,GAAG,gBAAgB;GAClE,iBAAiB,EAAE,mBAAmB,CAAC,GAAG,YAAY;GACtD,GAAG,MAAM;EACX;EACA,WAAW,GAAG,OAAO,SAAS;EAC9B,YAAY;GACV,QAAQ;GACR,OAAO;GACP,eAAe;GACf,eAAe;GACf,KAAK;GACL,iBAAiB,GACf,eAAe,EAAE,SAAS,UAAU,CAAC,GACrC,4EACF;GACA,aAAa,GACX,eAAe,EAAE,SAAS,UAAU,CAAC,GACrC,4EACF;GACA,YAAY;GACZ,UAAU;GACV,SAAS;GACT,MAAM;GACN,KAAK;GACL,YAAY,GACV,eAAe,EAAE,SAAS,QAAQ,CAAC,GACnC,kDACF;GACA,aACE;GACF,WAAW;GACX,UACE;GACF,OAAO;GACP,SAAS;GACT,UAAU;GACV,cAAc;GACd,QAAQ;GACR,GAAG;EACL;EACA,YAAY,EACV,UAAU,EAAE,aAAa,WAAW,kBAAkB,GAAG,WACvD,gBAAgB,SACd,oBAAC,iBAAD;GAAiB,WAAW,GAAG,UAAU,gBAAgB;GAAG,GAAI;EAAO,CAAA,IAEvE,oBAAC,kBAAD;GAAkB,WAAW,GAAG,UAAU,gBAAgB;GAAG,GAAI;EAAO,CAAA,EAE9E;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -2,7 +2,7 @@ import { useDirection, useT } from "../../context.js";
|
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { Button } from "../ui/button.js";
|
|
4
4
|
import { useStore } from "../../hooks/use-store.js";
|
|
5
|
-
import {
|
|
5
|
+
import { LazyWidgetPlaceholder } from "../panel/lazy-placeholder.js";
|
|
6
6
|
import { DashboardEditor, columnSpanClasses, dashboardMoveForKey, gridColumnsClasses, isLazyPlaceholder } from "@alfakit/core";
|
|
7
7
|
import { router } from "@inertiajs/react";
|
|
8
8
|
import { useEffect, useId, useMemo } from "react";
|
|
@@ -94,7 +94,7 @@ function CustomizableWidgetGrid({ block, customize, renderWidget }) {
|
|
|
94
94
|
const label = editor.label(entry.id);
|
|
95
95
|
const span = editor.spanOf(entry.id);
|
|
96
96
|
const columnSpan = state.editing && library ? library.spans.find((option) => option.value === span)?.columnSpan ?? widget?.columnSpan : widget?.columnSpan;
|
|
97
|
-
const content = widget ? isLazyPlaceholder(widget) ? /* @__PURE__ */ jsx(
|
|
97
|
+
const content = widget ? isLazyPlaceholder(widget) ? /* @__PURE__ */ jsx(LazyWidgetPlaceholder, { widget }) : renderWidget(widget) : /* @__PURE__ */ jsx("div", {
|
|
98
98
|
className: "bg-card text-muted-foreground flex min-h-24 items-center justify-center rounded-xl border border-dashed p-6 text-sm",
|
|
99
99
|
children: t("dashboard.customize.pending", { widget: label }, ":widget appears once you save.")
|
|
100
100
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-customizer.js","names":[],"sources":["../../../src/components/widgets/dashboard-customizer.tsx"],"sourcesContent":["import type {\n DashboardCustomizeSchema,\n WidgetsBlock as WidgetsBlockSchema,\n WidgetSchema,\n} from '@alfakit/core'\nimport {\n columnSpanClasses,\n DashboardEditor,\n dashboardMoveForKey,\n gridColumnsClasses,\n isLazyPlaceholder,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport {\n CheckIcon,\n EyeOffIcon,\n GripVerticalIcon,\n LayoutDashboardIcon,\n PlusIcon,\n RotateCcwIcon,\n XIcon,\n} from 'lucide-react'\nimport { useEffect, useId, useMemo, type ReactNode } from 'react'\nimport { useDirection, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { cn } from '../../lib/utils'\nimport { LazyPlaceholder } from '../panel/lazy-placeholder'\nimport { Button } from '../ui/button'\n\n/**\n * A customizable dashboard (`Dashboard::customizable()`): the widget grid plus an \"Edit\n * dashboard\" mode where widgets are dragged (pointer) or moved with the arrow keys on their\n * handle, resized, hidden and added from the library. The layout is saved on the server.\n */\nexport function CustomizableWidgetGrid({\n block,\n customize,\n renderWidget,\n}: {\n block: WidgetsBlockSchema\n customize: DashboardCustomizeSchema\n renderWidget: (widget: WidgetSchema) => ReactNode\n}) {\n const t = useT()\n const dir = useDirection()\n const helpId = useId()\n const editor = useMemo(\n () => new DashboardEditor(customize, { t, onSaved: () => router.reload() }),\n // One editor per dashboard; `sync()` follows later schemas.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [customize.endpoint, customize.token],\n )\n\n useEffect(() => editor.sync(customize), [editor, customize])\n\n const state = useStore(editor.store)\n const widgets = new Map(block.widgets.map((widget) => [widget.id, widget]))\n const visible = state.layout.filter((entry) => !entry.hidden)\n const available = editor.available()\n\n return (\n <div\n data-slot=\"dashboard\"\n data-editing={state.editing ? '' : undefined}\n className=\"flex flex-col gap-4\"\n >\n <div\n data-slot=\"dashboard-toolbar\"\n role=\"toolbar\"\n aria-label={t('dashboard.customize.toolbar', {}, 'Dashboard layout')}\n className=\"flex flex-wrap items-center justify-end gap-2\"\n >\n {state.editing ? (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => {\n editor.resetDraft()\n }}\n >\n <RotateCcwIcon aria-hidden=\"true\" />\n {t('dashboard.customize.reset', {}, 'Reset to default')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => editor.cancel()}\n >\n <XIcon aria-hidden=\"true\" />\n {t('dashboard.customize.cancel', {}, 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => void editor.save()}\n >\n <CheckIcon aria-hidden=\"true\" />\n {t('dashboard.customize.save', {}, 'Save')}\n </Button>\n </>\n ) : (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => editor.start()}>\n <LayoutDashboardIcon aria-hidden=\"true\" />\n {t('dashboard.customize.edit', {}, 'Edit dashboard')}\n </Button>\n )}\n </div>\n\n {state.error && (\n <p role=\"alert\" className=\"text-destructive text-sm\">\n {state.error}\n </p>\n )}\n\n {state.editing && (\n <p id={helpId} className=\"sr-only\">\n {t(\n 'dashboard.customize.move_help',\n {},\n 'Use the arrow keys to move the widget, Home or End to move it to the start or end.',\n )}\n </p>\n )}\n\n {visible.length === 0 ? (\n <p\n data-slot=\"dashboard-empty\"\n className=\"text-muted-foreground rounded-xl border border-dashed p-6 text-center text-sm\"\n >\n {t('dashboard.customize.empty', {}, 'No widgets on this dashboard yet.')}\n </p>\n ) : (\n <div data-slot=\"widgets\" className={cn('grid gap-6', gridColumnsClasses(block.columns))}>\n {visible.map((entry) => {\n const widget = widgets.get(entry.id)\n const library = editor.widget(entry.id)\n const label = editor.label(entry.id)\n const span = editor.spanOf(entry.id)\n const columnSpan =\n state.editing && library\n ? (library.spans.find((option) => option.value === span)?.columnSpan ??\n widget?.columnSpan)\n : widget?.columnSpan\n\n const content = widget ? (\n isLazyPlaceholder(widget) ? (\n <LazyPlaceholder variant={widget.type === 'TableWidget' ? 'table' : 'widget'} />\n ) : (\n renderWidget(widget)\n )\n ) : (\n <div className=\"bg-card text-muted-foreground flex min-h-24 items-center justify-center rounded-xl border border-dashed p-6 text-sm\">\n {t(\n 'dashboard.customize.pending',\n { widget: label },\n ':widget appears once you save.',\n )}\n </div>\n )\n\n if (!state.editing) {\n return (\n <div key={entry.id} className={cn('min-w-0', columnSpanClasses(columnSpan))}>\n {content}\n </div>\n )\n }\n\n return (\n <div\n key={entry.id}\n data-slot=\"dashboard-widget\"\n data-dragging={state.dragging === entry.id ? '' : undefined}\n role=\"group\"\n aria-label={label}\n className={cn(\n 'relative min-w-0 rounded-xl border-2 border-dashed p-1',\n state.dragging === entry.id && 'opacity-50',\n columnSpanClasses(columnSpan),\n )}\n onDragOver={(event) => {\n if (state.dragging !== null) event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n editor.dropOn(entry.id)\n }}\n >\n <div\n data-slot=\"dashboard-widget-controls\"\n className=\"bg-background/95 absolute inset-x-2 top-2 z-10 flex items-center gap-1 rounded-md border p-1 shadow-sm\"\n >\n <button\n type=\"button\"\n data-slot=\"dashboard-widget-handle\"\n draggable\n aria-label={t('dashboard.customize.move', { widget: label }, 'Move :widget')}\n aria-describedby={helpId}\n aria-roledescription={t('dashboard.customize.sortable', {}, 'sortable')}\n className=\"hover:bg-accent focus-visible:ring-ring/50 cursor-grab rounded p-1 outline-none focus-visible:ring-[3px] active:cursor-grabbing\"\n onDragStart={(event) => {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', entry.id)\n editor.dragStart(entry.id)\n }}\n onDragEnd={() => editor.dragEnd()}\n onKeyDown={(event) => {\n const move = dashboardMoveForKey(event.key, dir)\n\n if (move !== null) {\n event.preventDefault()\n editor.move(entry.id, move)\n // Keep focus on the moved widget's handle.\n const target = event.currentTarget\n requestAnimationFrame(() => target.focus())\n }\n }}\n >\n <GripVerticalIcon className=\"size-4\" aria-hidden=\"true\" />\n </button>\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium\">{label}</span>\n {library && library.spans.length > 1 && (\n <select\n data-slot=\"dashboard-widget-span\"\n aria-label={t(\n 'dashboard.customize.size',\n { widget: label },\n 'Size of :widget',\n )}\n className=\"border-input bg-background h-7 rounded-md border px-1 text-xs\"\n value={span}\n onChange={(event) => editor.setSpan(entry.id, Number(event.target.value))}\n >\n {library.spans.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={t('dashboard.customize.hide', { widget: label }, 'Hide :widget')}\n onClick={() => editor.hide(entry.id)}\n >\n <EyeOffIcon aria-hidden=\"true\" />\n </Button>\n </div>\n {/* The widget itself is inert while editing: focus stays on the controls. */}\n <div inert className=\"pointer-events-none pt-12 select-none\" aria-hidden=\"true\">\n {content}\n </div>\n </div>\n )\n })}\n </div>\n )}\n\n {state.editing && (\n <section\n data-slot=\"dashboard-library\"\n aria-label={t('dashboard.customize.library', {}, 'Widget library')}\n className=\"bg-muted/40 flex flex-col gap-3 rounded-xl border p-4\"\n >\n <h2 className=\"text-sm font-medium\">{t('dashboard.customize.add', {}, 'Add widget')}</h2>\n {available.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">\n {t('dashboard.customize.library_empty', {}, 'Every widget is on the dashboard.')}\n </p>\n ) : (\n <ul className=\"flex flex-wrap gap-2\">\n {available.map((widget) => (\n <li key={widget.id}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n title={widget.description}\n aria-label={t(\n 'dashboard.customize.add_widget',\n { widget: widget.label },\n 'Add :widget',\n )}\n onClick={() => editor.add(widget.id)}\n >\n <PlusIcon aria-hidden=\"true\" />\n {widget.label}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </section>\n )}\n\n <div data-slot=\"dashboard-announcer\" role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {state.announcement}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dashboard-customizer.js","names":[],"sources":["../../../src/components/widgets/dashboard-customizer.tsx"],"sourcesContent":["import type {\n DashboardCustomizeSchema,\n WidgetsBlock as WidgetsBlockSchema,\n WidgetSchema,\n} from '@alfakit/core'\nimport {\n columnSpanClasses,\n DashboardEditor,\n dashboardMoveForKey,\n gridColumnsClasses,\n isLazyPlaceholder,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport {\n CheckIcon,\n EyeOffIcon,\n GripVerticalIcon,\n LayoutDashboardIcon,\n PlusIcon,\n RotateCcwIcon,\n XIcon,\n} from 'lucide-react'\nimport { useEffect, useId, useMemo, type ReactNode } from 'react'\nimport { useDirection, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { cn } from '../../lib/utils'\nimport { LazyWidgetPlaceholder } from '../panel/lazy-placeholder'\nimport { Button } from '../ui/button'\n\n/**\n * A customizable dashboard (`Dashboard::customizable()`): the widget grid plus an \"Edit\n * dashboard\" mode where widgets are dragged (pointer) or moved with the arrow keys on their\n * handle, resized, hidden and added from the library. The layout is saved on the server.\n */\nexport function CustomizableWidgetGrid({\n block,\n customize,\n renderWidget,\n}: {\n block: WidgetsBlockSchema\n customize: DashboardCustomizeSchema\n renderWidget: (widget: WidgetSchema) => ReactNode\n}) {\n const t = useT()\n const dir = useDirection()\n const helpId = useId()\n const editor = useMemo(\n () => new DashboardEditor(customize, { t, onSaved: () => router.reload() }),\n // One editor per dashboard; `sync()` follows later schemas.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [customize.endpoint, customize.token],\n )\n\n useEffect(() => editor.sync(customize), [editor, customize])\n\n const state = useStore(editor.store)\n const widgets = new Map(block.widgets.map((widget) => [widget.id, widget]))\n const visible = state.layout.filter((entry) => !entry.hidden)\n const available = editor.available()\n\n return (\n <div\n data-slot=\"dashboard\"\n data-editing={state.editing ? '' : undefined}\n className=\"flex flex-col gap-4\"\n >\n <div\n data-slot=\"dashboard-toolbar\"\n role=\"toolbar\"\n aria-label={t('dashboard.customize.toolbar', {}, 'Dashboard layout')}\n className=\"flex flex-wrap items-center justify-end gap-2\"\n >\n {state.editing ? (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => {\n editor.resetDraft()\n }}\n >\n <RotateCcwIcon aria-hidden=\"true\" />\n {t('dashboard.customize.reset', {}, 'Reset to default')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => editor.cancel()}\n >\n <XIcon aria-hidden=\"true\" />\n {t('dashboard.customize.cancel', {}, 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={state.saving}\n onClick={() => void editor.save()}\n >\n <CheckIcon aria-hidden=\"true\" />\n {t('dashboard.customize.save', {}, 'Save')}\n </Button>\n </>\n ) : (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => editor.start()}>\n <LayoutDashboardIcon aria-hidden=\"true\" />\n {t('dashboard.customize.edit', {}, 'Edit dashboard')}\n </Button>\n )}\n </div>\n\n {state.error && (\n <p role=\"alert\" className=\"text-destructive text-sm\">\n {state.error}\n </p>\n )}\n\n {state.editing && (\n <p id={helpId} className=\"sr-only\">\n {t(\n 'dashboard.customize.move_help',\n {},\n 'Use the arrow keys to move the widget, Home or End to move it to the start or end.',\n )}\n </p>\n )}\n\n {visible.length === 0 ? (\n <p\n data-slot=\"dashboard-empty\"\n className=\"text-muted-foreground rounded-xl border border-dashed p-6 text-center text-sm\"\n >\n {t('dashboard.customize.empty', {}, 'No widgets on this dashboard yet.')}\n </p>\n ) : (\n <div data-slot=\"widgets\" className={cn('grid gap-6', gridColumnsClasses(block.columns))}>\n {visible.map((entry) => {\n const widget = widgets.get(entry.id)\n const library = editor.widget(entry.id)\n const label = editor.label(entry.id)\n const span = editor.spanOf(entry.id)\n const columnSpan =\n state.editing && library\n ? (library.spans.find((option) => option.value === span)?.columnSpan ??\n widget?.columnSpan)\n : widget?.columnSpan\n\n const content = widget ? (\n isLazyPlaceholder(widget) ? (\n <LazyWidgetPlaceholder widget={widget} />\n ) : (\n renderWidget(widget)\n )\n ) : (\n <div className=\"bg-card text-muted-foreground flex min-h-24 items-center justify-center rounded-xl border border-dashed p-6 text-sm\">\n {t(\n 'dashboard.customize.pending',\n { widget: label },\n ':widget appears once you save.',\n )}\n </div>\n )\n\n if (!state.editing) {\n return (\n <div key={entry.id} className={cn('min-w-0', columnSpanClasses(columnSpan))}>\n {content}\n </div>\n )\n }\n\n return (\n <div\n key={entry.id}\n data-slot=\"dashboard-widget\"\n data-dragging={state.dragging === entry.id ? '' : undefined}\n role=\"group\"\n aria-label={label}\n className={cn(\n 'relative min-w-0 rounded-xl border-2 border-dashed p-1',\n state.dragging === entry.id && 'opacity-50',\n columnSpanClasses(columnSpan),\n )}\n onDragOver={(event) => {\n if (state.dragging !== null) event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n editor.dropOn(entry.id)\n }}\n >\n <div\n data-slot=\"dashboard-widget-controls\"\n className=\"bg-background/95 absolute inset-x-2 top-2 z-10 flex items-center gap-1 rounded-md border p-1 shadow-sm\"\n >\n <button\n type=\"button\"\n data-slot=\"dashboard-widget-handle\"\n draggable\n aria-label={t('dashboard.customize.move', { widget: label }, 'Move :widget')}\n aria-describedby={helpId}\n aria-roledescription={t('dashboard.customize.sortable', {}, 'sortable')}\n className=\"hover:bg-accent focus-visible:ring-ring/50 cursor-grab rounded p-1 outline-none focus-visible:ring-[3px] active:cursor-grabbing\"\n onDragStart={(event) => {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', entry.id)\n editor.dragStart(entry.id)\n }}\n onDragEnd={() => editor.dragEnd()}\n onKeyDown={(event) => {\n const move = dashboardMoveForKey(event.key, dir)\n\n if (move !== null) {\n event.preventDefault()\n editor.move(entry.id, move)\n // Keep focus on the moved widget's handle.\n const target = event.currentTarget\n requestAnimationFrame(() => target.focus())\n }\n }}\n >\n <GripVerticalIcon className=\"size-4\" aria-hidden=\"true\" />\n </button>\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium\">{label}</span>\n {library && library.spans.length > 1 && (\n <select\n data-slot=\"dashboard-widget-span\"\n aria-label={t(\n 'dashboard.customize.size',\n { widget: label },\n 'Size of :widget',\n )}\n className=\"border-input bg-background h-7 rounded-md border px-1 text-xs\"\n value={span}\n onChange={(event) => editor.setSpan(entry.id, Number(event.target.value))}\n >\n {library.spans.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={t('dashboard.customize.hide', { widget: label }, 'Hide :widget')}\n onClick={() => editor.hide(entry.id)}\n >\n <EyeOffIcon aria-hidden=\"true\" />\n </Button>\n </div>\n {/* The widget itself is inert while editing: focus stays on the controls. */}\n <div inert className=\"pointer-events-none pt-12 select-none\" aria-hidden=\"true\">\n {content}\n </div>\n </div>\n )\n })}\n </div>\n )}\n\n {state.editing && (\n <section\n data-slot=\"dashboard-library\"\n aria-label={t('dashboard.customize.library', {}, 'Widget library')}\n className=\"bg-muted/40 flex flex-col gap-3 rounded-xl border p-4\"\n >\n <h2 className=\"text-sm font-medium\">{t('dashboard.customize.add', {}, 'Add widget')}</h2>\n {available.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">\n {t('dashboard.customize.library_empty', {}, 'Every widget is on the dashboard.')}\n </p>\n ) : (\n <ul className=\"flex flex-wrap gap-2\">\n {available.map((widget) => (\n <li key={widget.id}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n title={widget.description}\n aria-label={t(\n 'dashboard.customize.add_widget',\n { widget: widget.label },\n 'Add :widget',\n )}\n onClick={() => editor.add(widget.id)}\n >\n <PlusIcon aria-hidden=\"true\" />\n {widget.label}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </section>\n )}\n\n <div data-slot=\"dashboard-announcer\" role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {state.announcement}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRegistry, useT } from "../../context.js";
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { lazyComponent } from "../../lib/lazy.js";
|
|
4
|
-
import {
|
|
4
|
+
import { LazyWidgetPlaceholder } from "../panel/lazy-placeholder.js";
|
|
5
5
|
import { Slot } from "../panel/slot.js";
|
|
6
6
|
import { DataTable } from "../table/table-block.js";
|
|
7
7
|
import { ChartWidget } from "./chart-lazy.js";
|
|
@@ -30,7 +30,7 @@ function StaticWidgetGrid({ block }) {
|
|
|
30
30
|
className: cn("grid gap-6", gridColumnsClasses(block.columns)),
|
|
31
31
|
children: block.widgets.map((widget) => /* @__PURE__ */ jsx("div", {
|
|
32
32
|
className: cn("min-w-0", columnSpanClasses(widget.columnSpan)),
|
|
33
|
-
children: isLazyPlaceholder(widget) ? /* @__PURE__ */ jsx(
|
|
33
|
+
children: isLazyPlaceholder(widget) ? /* @__PURE__ */ jsx(LazyWidgetPlaceholder, { widget }) : /* @__PURE__ */ jsx(WidgetRenderer, { widget })
|
|
34
34
|
}, widget.key))
|
|
35
35
|
});
|
|
36
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets-block.js","names":[],"sources":["../../../src/components/widgets/widgets-block.tsx"],"sourcesContent":["import type { WidgetSchema, WidgetsBlock as WidgetsBlockSchema } from '@alfakit/core'\nimport {\n columnSpanClasses,\n gridColumnsClasses,\n isLazyPlaceholder,\n resolveFirst,\n widgetRegistryKeys,\n} from '@alfakit/core'\nimport { createElement, type ComponentProps } from 'react'\nimport { useRegistry, useT } from '../../context'\nimport { lazyComponent } from '../../lib/lazy'\nimport { cn } from '../../lib/utils'\nimport {
|
|
1
|
+
{"version":3,"file":"widgets-block.js","names":[],"sources":["../../../src/components/widgets/widgets-block.tsx"],"sourcesContent":["import type { WidgetSchema, WidgetsBlock as WidgetsBlockSchema } from '@alfakit/core'\nimport {\n columnSpanClasses,\n gridColumnsClasses,\n isLazyPlaceholder,\n resolveFirst,\n widgetRegistryKeys,\n} from '@alfakit/core'\nimport { createElement, type ComponentProps } from 'react'\nimport { useRegistry, useT } from '../../context'\nimport { lazyComponent } from '../../lib/lazy'\nimport { cn } from '../../lib/utils'\nimport { LazyWidgetPlaceholder } from '../panel/lazy-placeholder'\nimport { Slot } from '../panel/slot'\nimport { DataTable } from '../table/table-block'\nimport { ChartWidget } from './chart-lazy'\nimport type { CustomizableWidgetGrid as CustomizableWidgetGridComponent } from './dashboard-customizer'\nimport { StatsOverviewWidget } from './stats-overview'\n\n/**\n * `Dashboard::customizable()`: the grid with its \"Edit dashboard\" mode. The editor's code\n * loads with the first customizable dashboard; until then (and on the server) the widgets\n * show in the plain grid.\n */\nconst CustomizableWidgetGrid = lazyComponent<\n ComponentProps<typeof CustomizableWidgetGridComponent>\n>(\n () => import('./dashboard-customizer').then((module) => module.CustomizableWidgetGrid),\n ({ block }) => <StaticWidgetGrid block={block} />,\n)\n\nexport function WidgetGrid({ block }: { block: WidgetsBlockSchema }) {\n if (block.customize) {\n return (\n <CustomizableWidgetGrid\n block={block}\n customize={block.customize}\n renderWidget={(widget) => <WidgetRenderer widget={widget} />}\n />\n )\n }\n\n return <StaticWidgetGrid block={block} />\n}\n\nfunction StaticWidgetGrid({ block }: { block: WidgetsBlockSchema }) {\n return (\n <div data-slot=\"widgets\" className={cn('grid gap-6', gridColumnsClasses(block.columns))}>\n {block.widgets.map((widget) => (\n <div key={widget.key} className={cn('min-w-0', columnSpanClasses(widget.columnSpan))}>\n {isLazyPlaceholder(widget) ? (\n <LazyWidgetPlaceholder widget={widget} />\n ) : (\n <WidgetRenderer widget={widget} />\n )}\n </div>\n ))}\n </div>\n )\n}\n\nexport function WidgetRenderer({ widget }: { widget: WidgetSchema }) {\n const registry = useRegistry()\n const t = useT()\n // Registered components win over the built-in renderers: `Widget::$component`,\n // then `widget:<type>` (e.g. `widget:Chart`), then a custom widget's own type.\n const Custom = resolveFirst(registry, widgetRegistryKeys(widget))\n\n if (Custom) {\n return createElement(Custom, {\n ...(widget.type === 'View' ? (widget.props ?? {}) : {}),\n widget,\n })\n }\n\n switch (widget.type) {\n case 'StatsOverviewWidget':\n return <StatsOverviewWidget widget={widget} />\n case 'Chart':\n return <ChartWidget widget={widget} />\n case 'TableWidget':\n return (\n <>\n <Slot name=\"table-widget.start\" scope={widget.component} />\n <DataTable table={widget.table} component={widget.component} />\n <Slot name=\"table-widget.end\" scope={widget.component} />\n </>\n )\n case 'View':\n return (\n <div className=\"text-destructive rounded-md border border-dashed p-3 text-xs\">\n {t(\n 'widget_not_registered',\n { component: widget.component },\n 'Widget component :component is not registered.',\n )}\n </div>\n )\n default:\n return null\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,MAAM,yBAAyB,oBAGvB,OAAO,4BAAyB,CAAC,MAAM,WAAW,OAAO,sBAAsB,IACpF,EAAE,YAAY,oBAAC,kBAAD,EAAyB,MAAQ,CAAA,CAClD;AAEA,SAAgB,WAAW,EAAE,SAAwC;CACnE,IAAI,MAAM,WACR,OACE,oBAAC,wBAAD;EACS;EACP,WAAW,MAAM;EACjB,eAAe,WAAW,oBAAC,gBAAD,EAAwB,OAAS,CAAA;CAC5D,CAAA;CAIL,OAAO,oBAAC,kBAAD,EAAyB,MAAQ,CAAA;AAC1C;AAEA,SAAS,iBAAiB,EAAE,SAAwC;CAClE,OACE,oBAAC,OAAD;EAAK,aAAU;EAAU,WAAW,GAAG,cAAc,mBAAmB,MAAM,OAAO,CAAC;EACnF,UAAA,MAAM,QAAQ,KAAK,WAClB,oBAAC,OAAD;GAAsB,WAAW,GAAG,WAAW,kBAAkB,OAAO,UAAU,CAAC;GAChF,UAAA,kBAAkB,MAAM,IACvB,oBAAC,uBAAD,EAA+B,OAAS,CAAA,IAExC,oBAAC,gBAAD,EAAwB,OAAS,CAAA;EAEhC,GANK,OAAO,GAMZ,CACN;CACE,CAAA;AAET;AAEA,SAAgB,eAAe,EAAE,UAAoC;CACnE,MAAM,WAAW,YAAY;CAC7B,MAAM,IAAI,KAAK;CAGf,MAAM,SAAS,aAAa,UAAU,mBAAmB,MAAM,CAAC;CAEhE,IAAI,QACF,OAAO,cAAc,QAAQ;EAC3B,GAAI,OAAO,SAAS,SAAU,OAAO,SAAS,CAAC,IAAK,CAAC;EACrD;CACF,CAAC;CAGH,QAAQ,OAAO,MAAf;EACE,KAAK,uBACH,OAAO,oBAAC,qBAAD,EAA6B,OAAS,CAAA;EAC/C,KAAK,SACH,OAAO,oBAAC,aAAD,EAAqB,OAAS,CAAA;EACvC,KAAK,eACH,OACE,qBAAA,YAAA,EAAA,UAAA;GACE,oBAAC,MAAD;IAAM,MAAK;IAAqB,OAAO,OAAO;GAAY,CAAA;GAC1D,oBAAC,WAAD;IAAW,OAAO,OAAO;IAAO,WAAW,OAAO;GAAY,CAAA;GAC9D,oBAAC,MAAD;IAAM,MAAK;IAAmB,OAAO,OAAO;GAAY,CAAA;EACxD,EAAA,CAAA;EAEN,KAAK,QACH,OACE,oBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,EACC,yBACA,EAAE,WAAW,OAAO,UAAU,GAC9B,gDACF;EACG,CAAA;EAET,SACE,OAAO;CACX;AACF"}
|
package/dist/components.css
CHANGED
|
@@ -182,6 +182,13 @@
|
|
|
182
182
|
.fc[data-slot='calendar'] .fc-day-other .fc-daygrid-day-number {
|
|
183
183
|
color: var(--muted-foreground);
|
|
184
184
|
}
|
|
185
|
+
/*
|
|
186
|
+
* FullCalendar fades other months' dates to 30% opacity, which fails WCAG AA contrast
|
|
187
|
+
* (W-V4-20); the muted colour alone sets them apart.
|
|
188
|
+
*/
|
|
189
|
+
.fc[data-slot='calendar'] .fc-day-other .fc-daygrid-day-top {
|
|
190
|
+
opacity: 1;
|
|
191
|
+
}
|
|
185
192
|
/* Today: a tinted cell (`--fc-today-bg-color`) and the date in a primary pill. */
|
|
186
193
|
.fc[data-slot='calendar'] .fc-daygrid-day.fc-day-today .fc-daygrid-day-number {
|
|
187
194
|
display: inline-flex;
|
|
@@ -345,3 +352,15 @@
|
|
|
345
352
|
transition: none;
|
|
346
353
|
}
|
|
347
354
|
}
|
|
355
|
+
|
|
356
|
+
/* ------------------------------------------------------------------ Dev tools */
|
|
357
|
+
|
|
358
|
+
/*
|
|
359
|
+
* The floating debug-panel and schema-inspector buttons (local development) sit at the
|
|
360
|
+
* bottom end, over the helpdesk inbox's reply composer and its Send button: lift them
|
|
361
|
+
* above the composer while the inbox is on the page (W-S4-09).
|
|
362
|
+
*/
|
|
363
|
+
body:has([data-slot='helpdesk-inbox']) [data-slot='dev-tools-toggle'],
|
|
364
|
+
body:has([data-slot='helpdesk-inbox']) [data-slot='starter-kit-debug-panel'] {
|
|
365
|
+
bottom: 14rem;
|
|
366
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-alfakit.d.ts","names":[],"sources":["../src/create-alfakit.tsx"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"create-alfakit.d.ts","names":[],"sources":["../src/create-alfakit.tsx"],"mappings":";;;;;iBA8HiB,6BAA6B,eAAe;;EAE3D,WAAW,KAAK,cAAc;EAC9B;;;;;;;;;;;;;;wBAsLoB,cAAc,UAAS,uBAA4B"}
|
package/dist/create-alfakit.js
CHANGED
|
@@ -13,7 +13,7 @@ import { DevToolsSlot } from "./components/dev-tools/dev-tools-slot.js";
|
|
|
13
13
|
import { SchemaContractGuard } from "./components/panel/schema-contract-guard.js";
|
|
14
14
|
import { Toaster } from "./components/ui/sonner.js";
|
|
15
15
|
import { panelLayouts } from "./layouts/panel-layouts.js";
|
|
16
|
-
import { ActionRunner, PANEL_TOAST_OFFSET, Translator, applyDocumentLocale, applyPanelTheme, configureCharts, configureRealtime, debugRecorder, documentNonce, effectiveErrorNotifications, initializeAppearance, installErrorNotifications, installPrefetchInvalidation, installSpaUrlExceptions, registerRichEditorExtensions, reloadOnExpiredPage, resolveRequestErrorNotification, resolveToastPosition, setRequestErrorReporter, whenIdle } from "@alfakit/core";
|
|
16
|
+
import { ActionRunner, PANEL_TOAST_OFFSET, Translator, applyDocumentLocale, applyPanelTheme, configureCharts, configureRealtime, debugRecorder, documentNonce, effectiveErrorNotifications, initializeAppearance, installErrorNotifications, installPrefetchInvalidation, installSpaUrlExceptions, overlayPlacementDirection, registerRichEditorExtensions, reloadOnExpiredPage, resolveRequestErrorNotification, resolveToastPosition, setIconAliases, setRequestErrorReporter, whenIdle } from "@alfakit/core";
|
|
17
17
|
import { createInertiaApp, router, usePage } from "@inertiajs/react";
|
|
18
18
|
import { StrictMode, createElement, useLayoutEffect } from "react";
|
|
19
19
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -31,6 +31,7 @@ function AlfakitRoot({ children, toasterId }) {
|
|
|
31
31
|
if (panel) {
|
|
32
32
|
translator.setTranslations(panel.translations ?? {});
|
|
33
33
|
translator.setLocale(localization?.tag ?? "en");
|
|
34
|
+
setIconAliases(panel.theme?.icons);
|
|
34
35
|
}
|
|
35
36
|
const ownsDocument = panel?.starterKit !== true;
|
|
36
37
|
useLayoutEffect(() => {
|
|
@@ -56,7 +57,7 @@ function AlfakitRoot({ children, toasterId }) {
|
|
|
56
57
|
panel?.impersonation && createElement(registry.resolve("panel:impersonation-banner") ?? ImpersonationBanner, { impersonation: panel.impersonation }),
|
|
57
58
|
/* @__PURE__ */ jsx(Toaster, {
|
|
58
59
|
id: toasterId,
|
|
59
|
-
position: resolveToastPosition(panel?.toastPosition, direction),
|
|
60
|
+
position: resolveToastPosition(panel?.toastPosition, overlayPlacementDirection(direction, panel?.starterKit)),
|
|
60
61
|
offset: PANEL_TOAST_OFFSET,
|
|
61
62
|
mobileOffset: PANEL_TOAST_OFFSET,
|
|
62
63
|
dir: direction,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-alfakit.js","names":[],"sources":["../src/create-alfakit.tsx"],"sourcesContent":["import {\n ActionRunner,\n PANEL_TOAST_OFFSET,\n Translator,\n applyDocumentLocale,\n applyPanelTheme,\n configureCharts,\n configureRealtime,\n debugRecorder,\n documentNonce,\n effectiveErrorNotifications,\n initializeAppearance,\n installErrorNotifications,\n installPrefetchInvalidation,\n installSpaUrlExceptions,\n registerRichEditorExtensions,\n reloadOnExpiredPage,\n resolveRequestErrorNotification,\n resolveToastPosition,\n setRequestErrorReporter,\n whenIdle,\n type AlfakitOptions,\n type AlfakitPageProps,\n type ErrorNotificationsSchema,\n type PanelSchema,\n} from '@alfakit/core'\nimport type { InertiaAppSSRResponse, Page } from '@inertiajs/core'\nimport { createInertiaApp, router, usePage } from '@inertiajs/react'\nimport { Direction } from 'radix-ui'\nimport {\n StrictMode,\n createElement,\n useLayoutEffect,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport { ActionModals } from './components/actions/action-modals'\nimport { DevToolsSlot } from './components/dev-tools/dev-tools-slot'\nimport { FlashToasts, showToast } from './components/notifications/flash-toasts'\nimport { RealtimeNotifications } from './components/notifications/realtime-notifications'\nimport { ImpersonationBanner } from './components/panel/impersonation-banner'\nimport { PwaManager } from './components/panel/pwa'\nimport { SchemaContractGuard } from './components/panel/schema-contract-guard'\nimport { Toaster } from './components/ui/sonner'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport { AlfakitProvider, useAlfakit, type AlfakitContextValue, type AnyComponent } from './context'\nimport { panelLayouts } from './layouts/panel-layouts'\nimport { discoverComponents } from './lib/discovery'\nimport { preloadIcons } from './lib/icons'\nimport { AlfakitPage } from './pages/page'\nimport { builtInComponents, createDefaultRegistry } from './registry'\n\n/**\n * Rendered inside Inertia's page tree, so it follows the current page: the reading\n * direction for Radix primitives, modals and toasts.\n */\nexport function AlfakitRoot({\n children,\n toasterId,\n}: {\n children: ReactNode\n /** Starter kit mode: Alfakit's toaster gets an id so the host's id-less one never doubles its toasts. */\n toasterId?: string\n}) {\n const panel = usePage<AlfakitPageProps>().props.alfakit\n const { translator, registry } = useAlfakit()\n const localization = panel?.localization\n const direction = localization?.direction ?? 'ltr'\n\n // Every visit (including the redirect after a language switch) may carry new strings,\n // so the translator is updated before children render.\n if (panel) {\n translator.setTranslations(panel.translations ?? {})\n translator.setLocale(localization?.tag ?? 'en')\n }\n\n // Starter kit mode: the host app owns `<html lang dir>` (its own pages and layout are not\n // mirrored); `AlfakitStarterKitRoot` puts the direction on Alfakit's own root element.\n const ownsDocument = panel?.starterKit !== true\n\n useLayoutEffect(() => {\n if (ownsDocument) applyDocumentLocale(localization)\n }, [localization, ownsDocument])\n\n useLayoutEffect(() => {\n applyPanelTheme(panel)\n }, [panel])\n\n return (\n <Direction.Provider dir={direction}>\n <TooltipProvider delayDuration={0}>\n <SchemaContractGuard panel={panel}>{children}</SchemaContractGuard>\n <ActionModals />\n <FlashToasts />\n <RealtimeNotifications />\n {/* `Panel::pwa()`: service worker and the \"new version\" toast. */}\n <PwaManager />\n {/* Schema inspector: renders (and loads its code) only with `alfakitDevTools`. */}\n <DevToolsSlot />\n {/* Replace with createAlfakit({ components: { 'panel:impersonation-banner': … } }). */}\n {panel?.impersonation &&\n createElement(registry.resolve('panel:impersonation-banner') ?? ImpersonationBanner, {\n impersonation: panel.impersonation,\n })}\n <Toaster\n id={toasterId}\n position={resolveToastPosition(panel?.toastPosition, direction)}\n offset={PANEL_TOAST_OFFSET}\n mobileOffset={PANEL_TOAST_OFFSET}\n dir={direction}\n richColors\n closeButton\n />\n </TooltipProvider>\n </Direction.Provider>\n )\n}\n\nexport interface CreateAlfakitOptions extends AlfakitOptions<AnyComponent> {\n /** Wrap the whole app (e.g. with your own providers). */\n withApp?: (app: ReactNode) => ReactNode\n strictMode?: boolean\n}\n\n/** Renders one Inertia page to HTML: what `createInertiaApp` returns on the server. */\nexport type AlfakitRenderFunction = (\n page: Page,\n renderToString: (element: ReactElement) => string,\n) => Promise<InertiaAppSSRResponse>\n\n/**\n * The Inertia app shared by `createAlfakit()` (browser) and `createAlfakitServer()` (Node):\n * the same registry, pages, layout and providers. In the browser it mounts, or hydrates\n * server-rendered markup; on the server (no `window`) Inertia returns a render function.\n */\nexport function bootAlfakit(\n options: CreateAlfakitOptions = {},\n): Promise<void | AlfakitRenderFunction> {\n // Auto-registered files sit between the built-ins and the explicit `components` / `pages`.\n const discovered = discoverComponents(options.discover)\n const registry = createDefaultRegistry()\n .registerMany(panelLayouts)\n .registerMany(discovered.components)\n .registerMany(options.components ?? {})\n const pages: Record<string, AnyComponent> = {\n 'Alfakit/Page': AlfakitPage,\n ...discovered.pages,\n ...options.pages,\n }\n const isServer = typeof window === 'undefined'\n\n // Every panel page draws named icons: fetch `lucide-react/dynamic` while booting.\n preloadIcons()\n\n // `'js:name'` chart option callbacks and Chart.js plugins for every chart widget.\n configureCharts(options)\n // `createAlfakit({ echo })`: Laravel Echo for `Panel::realtime()` without `window.Echo`.\n if (options.echo !== undefined && !isServer) configureRealtime(null, { echo: options.echo })\n // Rich content plugin extensions, loaded when an editor needs them.\n registerRichEditorExtensions(options.richEditorExtensions ?? {})\n\n // The browser's current panel, read by the SPA-exception and error-notification listeners.\n let currentPanel: PanelSchema | null = null\n // The open page's error notifications (its override, else the panel's).\n let currentErrorNotifications: ErrorNotificationsSchema | false | undefined\n\n // One translator and action runner for the browser session; a server render gets fresh\n // ones per request so concurrent renders never share a locale or endpoints.\n const createContext = (): AlfakitContextValue => {\n const translator = new Translator()\n\n return {\n registry,\n pages,\n translator,\n // The runner's endpoints are refreshed on every navigation from the shared props.\n actions: new ActionRunner({\n endpoints: { action: '', modal: '' },\n // A failed action request is a toast, never a silent no-op.\n onError: (error) => {\n const notification = resolveRequestErrorNotification(\n error,\n currentErrorNotifications,\n (key, replacements, fallback) => translator.t(key, replacements, fallback),\n )\n\n if (notification) showToast(notification)\n },\n }),\n }\n }\n\n const browserContext = isServer ? null : createContext()\n\n const sync = (context: AlfakitContextValue, page: Page, applyToDocument: boolean) => {\n const props = page.props as unknown as AlfakitPageProps\n const panel = props.alfakit\n\n if (!panel) return\n\n if (context === browserContext) {\n currentPanel = panel\n currentErrorNotifications = effectiveErrorNotifications(panel, props.page)\n }\n\n context.translator.setTranslations(panel.translations ?? {})\n context.translator.setLocale(panel.localization?.tag ?? 'en')\n context.actions.setEndpoints({\n action: panel.endpoints.action ?? '',\n modal: panel.endpoints.modal ?? '',\n })\n\n if (applyToDocument) {\n applyDocumentLocale(panel.localization)\n applyPanelTheme(panel)\n // Internal requests and form stores are only recorded while the page carries the debug prop.\n debugRecorder.setEnabled(props.alfakitDebug !== undefined)\n }\n }\n\n if (browserContext) {\n router.on('navigate', (event) => sync(browserContext, event.detail.page, true))\n reloadOnExpiredPage()\n // Prefetched pages and deferred table totals are dropped after every mutation.\n installPrefetchInvalidation()\n installSpaUrlExceptions(() => currentPanel)\n installErrorNotifications(\n () => ({ errorNotifications: currentErrorNotifications }),\n (notification) => showToast(notification),\n )\n // Failed internal requests without their own handler (form live refreshes, step\n // validation) show the configured error notification too.\n setRequestErrorReporter((error) => {\n const notification = resolveRequestErrorNotification(\n error,\n currentErrorNotifications,\n (key, replacements, fallback) => browserContext.translator.t(key, replacements, fallback),\n )\n\n if (notification) showToast(notification)\n })\n }\n\n return createInertiaApp<AlfakitPageProps>({\n title: (title) => title,\n progress: options.progress === undefined ? { color: 'var(--primary)' } : options.progress,\n // The page's CSP nonce, so the progress bar's injected <style> passes a nonce-based policy.\n nonce: documentNonce(),\n resolve: (name, page) => {\n const component = pages[name]\n\n if (!component) {\n throw new Error(\n `Alfakit: page component \"${name}\" is not registered. Pass it to createAlfakit({ pages }).`,\n )\n }\n\n // The page's auto-registered components load first, so the server render and the\n // hydration both show them (no flash, no mismatch).\n // Built-ins split per type (`builtInComponents`) load the same way.\n const pending = [\n builtInComponents.preloadFor(page),\n discovered.discovery.preloadFor(page),\n ].filter((promise) => promise !== undefined)\n\n return pending.length > 0 ? Promise.all(pending).then(() => component) : component\n },\n layout: () => AlfakitRoot,\n strictMode: options.strictMode ?? true,\n withApp: (app, { page, ssr }) => {\n const context = browserContext ?? createContext()\n\n sync(context, page as Page, !ssr)\n\n // Panel pages only: other pages (`pages` option) follow the application's own dark\n // mode, like standalone components do.\n const panel = (page.props as unknown as Partial<AlfakitPageProps>).alfakit\n\n if (!ssr && panel) {\n initializeAppearance(panel)\n }\n\n const tree = <AlfakitProvider value={context}>{app}</AlfakitProvider>\n\n const wrapped = options.withApp ? options.withApp(tree) : tree\n\n return options.strictMode === false ? <>{wrapped}</> : <StrictMode>{wrapped}</StrictMode>\n },\n }) as Promise<void | AlfakitRenderFunction>\n}\n\n/**\n * Boot the Alfakit panel frontend. Call once from your panel entry file. Markup rendered by\n * `createAlfakitServer()` (`@alfakit/react/server`) is hydrated instead of re-rendered.\n *\n * ```tsx\n * createAlfakit({\n * // Files in resources/js/alfakit/fields, columns, … register themselves.\n * discover: import.meta.glob('./{fields,columns,entries,widgets,pages,components,blocks}/**\\/*.tsx'),\n * components: { 'map-picker': MapPicker },\n * })\n * ```\n */\nexport async function createAlfakit(options: CreateAlfakitOptions = {}): Promise<void> {\n await bootAlfakit(options)\n\n // After the first render: the remaining split built-ins (action modals' fields).\n whenIdle(() => void builtInComponents.preloadAll())\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-alfakit.js","names":[],"sources":["../src/create-alfakit.tsx"],"sourcesContent":["import {\n ActionRunner,\n PANEL_TOAST_OFFSET,\n Translator,\n applyDocumentLocale,\n applyPanelTheme,\n configureCharts,\n configureRealtime,\n debugRecorder,\n documentNonce,\n effectiveErrorNotifications,\n initializeAppearance,\n installErrorNotifications,\n installPrefetchInvalidation,\n installSpaUrlExceptions,\n overlayPlacementDirection,\n registerRichEditorExtensions,\n reloadOnExpiredPage,\n resolveRequestErrorNotification,\n resolveToastPosition,\n setIconAliases,\n setRequestErrorReporter,\n whenIdle,\n type AlfakitOptions,\n type AlfakitPageProps,\n type ErrorNotificationsSchema,\n type PanelSchema,\n} from '@alfakit/core'\nimport type { InertiaAppSSRResponse, Page } from '@inertiajs/core'\nimport { createInertiaApp, router, usePage } from '@inertiajs/react'\nimport { Direction } from 'radix-ui'\nimport {\n StrictMode,\n createElement,\n useLayoutEffect,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport { ActionModals } from './components/actions/action-modals'\nimport { DevToolsSlot } from './components/dev-tools/dev-tools-slot'\nimport { FlashToasts, showToast } from './components/notifications/flash-toasts'\nimport { RealtimeNotifications } from './components/notifications/realtime-notifications'\nimport { ImpersonationBanner } from './components/panel/impersonation-banner'\nimport { PwaManager } from './components/panel/pwa'\nimport { SchemaContractGuard } from './components/panel/schema-contract-guard'\nimport { Toaster } from './components/ui/sonner'\nimport { TooltipProvider } from './components/ui/tooltip'\nimport { AlfakitProvider, useAlfakit, type AlfakitContextValue, type AnyComponent } from './context'\nimport { panelLayouts } from './layouts/panel-layouts'\nimport { discoverComponents } from './lib/discovery'\nimport { preloadIcons } from './lib/icons'\nimport { AlfakitPage } from './pages/page'\nimport { builtInComponents, createDefaultRegistry } from './registry'\n\n/**\n * Rendered inside Inertia's page tree, so it follows the current page: the reading\n * direction for Radix primitives, modals and toasts.\n */\nexport function AlfakitRoot({\n children,\n toasterId,\n}: {\n children: ReactNode\n /** Starter kit mode: Alfakit's toaster gets an id so the host's id-less one never doubles its toasts. */\n toasterId?: string\n}) {\n const panel = usePage<AlfakitPageProps>().props.alfakit\n const { translator, registry } = useAlfakit()\n const localization = panel?.localization\n const direction = localization?.direction ?? 'ltr'\n\n // Every visit (including the redirect after a language switch) may carry new strings,\n // so the translator is updated before children render.\n if (panel) {\n translator.setTranslations(panel.translations ?? {})\n translator.setLocale(localization?.tag ?? 'en')\n // Chrome icons replaced with `Icons::register()`, also in a server render, so the server\n // and the browser draw the same glyph (W-V3-28).\n setIconAliases(panel.theme?.icons)\n }\n\n // Starter kit mode: the host app owns `<html lang dir>` (its own pages and layout are not\n // mirrored); `AlfakitStarterKitRoot` puts the direction on Alfakit's own root element.\n const ownsDocument = panel?.starterKit !== true\n\n useLayoutEffect(() => {\n if (ownsDocument) applyDocumentLocale(localization)\n }, [localization, ownsDocument])\n\n useLayoutEffect(() => {\n applyPanelTheme(panel)\n }, [panel])\n\n return (\n <Direction.Provider dir={direction}>\n <TooltipProvider delayDuration={0}>\n <SchemaContractGuard panel={panel}>{children}</SchemaContractGuard>\n <ActionModals />\n <FlashToasts />\n <RealtimeNotifications />\n {/* `Panel::pwa()`: service worker and the \"new version\" toast. */}\n <PwaManager />\n {/* Schema inspector: renders (and loads its code) only with `alfakitDevTools`. */}\n <DevToolsSlot />\n {/* Replace with createAlfakit({ components: { 'panel:impersonation-banner': … } }). */}\n {panel?.impersonation &&\n createElement(registry.resolve('panel:impersonation-banner') ?? ImpersonationBanner, {\n impersonation: panel.impersonation,\n })}\n <Toaster\n id={toasterId}\n position={resolveToastPosition(\n panel?.toastPosition,\n overlayPlacementDirection(direction, panel?.starterKit),\n )}\n offset={PANEL_TOAST_OFFSET}\n mobileOffset={PANEL_TOAST_OFFSET}\n dir={direction}\n richColors\n closeButton\n />\n </TooltipProvider>\n </Direction.Provider>\n )\n}\n\nexport interface CreateAlfakitOptions extends AlfakitOptions<AnyComponent> {\n /** Wrap the whole app (e.g. with your own providers). */\n withApp?: (app: ReactNode) => ReactNode\n strictMode?: boolean\n}\n\n/** Renders one Inertia page to HTML: what `createInertiaApp` returns on the server. */\nexport type AlfakitRenderFunction = (\n page: Page,\n renderToString: (element: ReactElement) => string,\n) => Promise<InertiaAppSSRResponse>\n\n/**\n * The Inertia app shared by `createAlfakit()` (browser) and `createAlfakitServer()` (Node):\n * the same registry, pages, layout and providers. In the browser it mounts, or hydrates\n * server-rendered markup; on the server (no `window`) Inertia returns a render function.\n */\nexport function bootAlfakit(\n options: CreateAlfakitOptions = {},\n): Promise<void | AlfakitRenderFunction> {\n // Auto-registered files sit between the built-ins and the explicit `components` / `pages`.\n const discovered = discoverComponents(options.discover)\n const registry = createDefaultRegistry()\n .registerMany(panelLayouts)\n .registerMany(discovered.components)\n .registerMany(options.components ?? {})\n const pages: Record<string, AnyComponent> = {\n 'Alfakit/Page': AlfakitPage,\n ...discovered.pages,\n ...options.pages,\n }\n const isServer = typeof window === 'undefined'\n\n // Every panel page draws named icons: fetch `lucide-react/dynamic` while booting.\n preloadIcons()\n\n // `'js:name'` chart option callbacks and Chart.js plugins for every chart widget.\n configureCharts(options)\n // `createAlfakit({ echo })`: Laravel Echo for `Panel::realtime()` without `window.Echo`.\n if (options.echo !== undefined && !isServer) configureRealtime(null, { echo: options.echo })\n // Rich content plugin extensions, loaded when an editor needs them.\n registerRichEditorExtensions(options.richEditorExtensions ?? {})\n\n // The browser's current panel, read by the SPA-exception and error-notification listeners.\n let currentPanel: PanelSchema | null = null\n // The open page's error notifications (its override, else the panel's).\n let currentErrorNotifications: ErrorNotificationsSchema | false | undefined\n\n // One translator and action runner for the browser session; a server render gets fresh\n // ones per request so concurrent renders never share a locale or endpoints.\n const createContext = (): AlfakitContextValue => {\n const translator = new Translator()\n\n return {\n registry,\n pages,\n translator,\n // The runner's endpoints are refreshed on every navigation from the shared props.\n actions: new ActionRunner({\n endpoints: { action: '', modal: '' },\n // A failed action request is a toast, never a silent no-op.\n onError: (error) => {\n const notification = resolveRequestErrorNotification(\n error,\n currentErrorNotifications,\n (key, replacements, fallback) => translator.t(key, replacements, fallback),\n )\n\n if (notification) showToast(notification)\n },\n }),\n }\n }\n\n const browserContext = isServer ? null : createContext()\n\n const sync = (context: AlfakitContextValue, page: Page, applyToDocument: boolean) => {\n const props = page.props as unknown as AlfakitPageProps\n const panel = props.alfakit\n\n if (!panel) return\n\n if (context === browserContext) {\n currentPanel = panel\n currentErrorNotifications = effectiveErrorNotifications(panel, props.page)\n }\n\n context.translator.setTranslations(panel.translations ?? {})\n context.translator.setLocale(panel.localization?.tag ?? 'en')\n context.actions.setEndpoints({\n action: panel.endpoints.action ?? '',\n modal: panel.endpoints.modal ?? '',\n })\n\n if (applyToDocument) {\n applyDocumentLocale(panel.localization)\n applyPanelTheme(panel)\n // Internal requests and form stores are only recorded while the page carries the debug prop.\n debugRecorder.setEnabled(props.alfakitDebug !== undefined)\n }\n }\n\n if (browserContext) {\n router.on('navigate', (event) => sync(browserContext, event.detail.page, true))\n reloadOnExpiredPage()\n // Prefetched pages and deferred table totals are dropped after every mutation.\n installPrefetchInvalidation()\n installSpaUrlExceptions(() => currentPanel)\n installErrorNotifications(\n () => ({ errorNotifications: currentErrorNotifications }),\n (notification) => showToast(notification),\n )\n // Failed internal requests without their own handler (form live refreshes, step\n // validation) show the configured error notification too.\n setRequestErrorReporter((error) => {\n const notification = resolveRequestErrorNotification(\n error,\n currentErrorNotifications,\n (key, replacements, fallback) => browserContext.translator.t(key, replacements, fallback),\n )\n\n if (notification) showToast(notification)\n })\n }\n\n return createInertiaApp<AlfakitPageProps>({\n title: (title) => title,\n progress: options.progress === undefined ? { color: 'var(--primary)' } : options.progress,\n // The page's CSP nonce, so the progress bar's injected <style> passes a nonce-based policy.\n nonce: documentNonce(),\n resolve: (name, page) => {\n const component = pages[name]\n\n if (!component) {\n throw new Error(\n `Alfakit: page component \"${name}\" is not registered. Pass it to createAlfakit({ pages }).`,\n )\n }\n\n // The page's auto-registered components load first, so the server render and the\n // hydration both show them (no flash, no mismatch).\n // Built-ins split per type (`builtInComponents`) load the same way.\n const pending = [\n builtInComponents.preloadFor(page),\n discovered.discovery.preloadFor(page),\n ].filter((promise) => promise !== undefined)\n\n return pending.length > 0 ? Promise.all(pending).then(() => component) : component\n },\n layout: () => AlfakitRoot,\n strictMode: options.strictMode ?? true,\n withApp: (app, { page, ssr }) => {\n const context = browserContext ?? createContext()\n\n sync(context, page as Page, !ssr)\n\n // Panel pages only: other pages (`pages` option) follow the application's own dark\n // mode, like standalone components do.\n const panel = (page.props as unknown as Partial<AlfakitPageProps>).alfakit\n\n if (!ssr && panel) {\n initializeAppearance(panel)\n }\n\n const tree = <AlfakitProvider value={context}>{app}</AlfakitProvider>\n\n const wrapped = options.withApp ? options.withApp(tree) : tree\n\n return options.strictMode === false ? <>{wrapped}</> : <StrictMode>{wrapped}</StrictMode>\n },\n }) as Promise<void | AlfakitRenderFunction>\n}\n\n/**\n * Boot the Alfakit panel frontend. Call once from your panel entry file. Markup rendered by\n * `createAlfakitServer()` (`@alfakit/react/server`) is hydrated instead of re-rendered.\n *\n * ```tsx\n * createAlfakit({\n * // Files in resources/js/alfakit/fields, columns, … register themselves.\n * discover: import.meta.glob('./{fields,columns,entries,widgets,pages,components,blocks}/**\\/*.tsx'),\n * components: { 'map-picker': MapPicker },\n * })\n * ```\n */\nexport async function createAlfakit(options: CreateAlfakitOptions = {}): Promise<void> {\n await bootAlfakit(options)\n\n // After the first render: the remaining split built-ins (action modals' fields).\n whenIdle(() => void builtInComponents.preloadAll())\n}\n"],"mappings":"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"}
|
package/dist/eject-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@alfakit/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"adapter": "react",
|
|
5
|
-
"generatedAt": "2026-10-
|
|
5
|
+
"generatedAt": "2026-10-26T09:54:26.975Z",
|
|
6
6
|
"components": {
|
|
7
7
|
"TextInput": {
|
|
8
8
|
"file": "src/components/fields/text-input.tsx",
|
|
@@ -915,6 +915,7 @@
|
|
|
915
915
|
"src/starter-kit/starter-kit.tsx": {
|
|
916
916
|
"AlfakitStarterKitRoot": "AlfakitStarterKitRoot",
|
|
917
917
|
"StarterKitContentLayout": "StarterKitContentLayout",
|
|
918
|
+
"StarterKitFullscreenLayout": "StarterKitFullscreenLayout",
|
|
918
919
|
"createStarterKitPage": "createStarterKitPage",
|
|
919
920
|
"StarterKitLayout": "StarterKitLayout",
|
|
920
921
|
"StarterKitPageOptions": "StarterKitPageOptions"
|
|
@@ -934,6 +935,7 @@
|
|
|
934
935
|
"AlfakitNavigation": "AlfakitNavigation",
|
|
935
936
|
"AlfakitStarterKitRoot": "AlfakitStarterKitRoot",
|
|
936
937
|
"StarterKitContentLayout": "StarterKitContentLayout",
|
|
938
|
+
"StarterKitFullscreenLayout": "StarterKitFullscreenLayout",
|
|
937
939
|
"createStarterKitPage": "createStarterKitPage",
|
|
938
940
|
"StarterKitLayout": "StarterKitLayout",
|
|
939
941
|
"StarterKitPageOptions": "StarterKitPageOptions",
|