@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.
Files changed (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/actions/action-button.js +13 -1
  3. package/dist/components/actions/action-button.js.map +1 -1
  4. package/dist/components/dev-tools/dev-tools-inspector.js +2 -2
  5. package/dist/components/dev-tools/dev-tools-inspector.js.map +1 -1
  6. package/dist/components/entries/other-entries.d.ts.map +1 -1
  7. package/dist/components/entries/other-entries.js +1 -1
  8. package/dist/components/entries/other-entries.js.map +1 -1
  9. package/dist/components/fields/builder.d.ts.map +1 -1
  10. package/dist/components/fields/builder.js +3 -3
  11. package/dist/components/fields/builder.js.map +1 -1
  12. package/dist/components/fields/file-upload.d.ts.map +1 -1
  13. package/dist/components/fields/file-upload.js +2 -5
  14. package/dist/components/fields/file-upload.js.map +1 -1
  15. package/dist/components/fields/money-input.d.ts +5 -0
  16. package/dist/components/fields/money-input.d.ts.map +1 -1
  17. package/dist/components/fields/money-input.js +2 -5
  18. package/dist/components/fields/money-input.js.map +1 -1
  19. package/dist/components/fields/repeater.d.ts.map +1 -1
  20. package/dist/components/fields/repeater.js +3 -3
  21. package/dist/components/fields/repeater.js.map +1 -1
  22. package/dist/components/fields/rich-editor-extensions.js +5 -2
  23. package/dist/components/fields/rich-editor-extensions.js.map +1 -1
  24. package/dist/components/layout/wizard.d.ts.map +1 -1
  25. package/dist/components/layout/wizard.js +4 -1
  26. package/dist/components/layout/wizard.js.map +1 -1
  27. package/dist/components/notifications/flash-toasts.d.ts.map +1 -1
  28. package/dist/components/notifications/flash-toasts.js +11 -1
  29. package/dist/components/notifications/flash-toasts.js.map +1 -1
  30. package/dist/components/notifications/notifications-menu.js +3 -3
  31. package/dist/components/notifications/notifications-menu.js.map +1 -1
  32. package/dist/components/panel/lazy-placeholder.d.ts +1 -0
  33. package/dist/components/panel/lazy-placeholder.d.ts.map +1 -1
  34. package/dist/components/panel/lazy-placeholder.js +38 -4
  35. package/dist/components/panel/lazy-placeholder.js.map +1 -1
  36. package/dist/components/panel/pwa.d.ts.map +1 -1
  37. package/dist/components/panel/pwa.js +8 -7
  38. package/dist/components/panel/pwa.js.map +1 -1
  39. package/dist/components/panel/shortcuts-overlay.js +1 -1
  40. package/dist/components/panel/shortcuts-overlay.js.map +1 -1
  41. package/dist/components/table/cells.js +2 -2
  42. package/dist/components/table/cells.js.map +1 -1
  43. package/dist/components/ui/calendar.d.ts.map +1 -1
  44. package/dist/components/ui/calendar.js +7 -0
  45. package/dist/components/ui/calendar.js.map +1 -1
  46. package/dist/components/widgets/dashboard-customizer.js +2 -2
  47. package/dist/components/widgets/dashboard-customizer.js.map +1 -1
  48. package/dist/components/widgets/widgets-block.js +2 -2
  49. package/dist/components/widgets/widgets-block.js.map +1 -1
  50. package/dist/components.css +19 -0
  51. package/dist/create-alfakit.d.ts.map +1 -1
  52. package/dist/create-alfakit.js +3 -2
  53. package/dist/create-alfakit.js.map +1 -1
  54. package/dist/eject-manifest.json +4 -2
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.js +2 -2
  57. package/dist/pages/page.js +1 -1
  58. package/dist/pages/page.js.map +1 -1
  59. package/dist/standalone/alfakit-component.js +2 -2
  60. package/dist/standalone/alfakit-component.js.map +1 -1
  61. package/dist/standalone.css +21 -0
  62. package/dist/starter-kit/index.d.ts +2 -2
  63. package/dist/starter-kit/index.js +2 -2
  64. package/dist/starter-kit/starter-kit.d.ts +9 -0
  65. package/dist/starter-kit/starter-kit.d.ts.map +1 -1
  66. package/dist/starter-kit/starter-kit.js +21 -2
  67. package/dist/starter-kit/starter-kit.js.map +1 -1
  68. package/package.json +2 -2
  69. package/src/components/actions/action-button.tsx +15 -1
  70. package/src/components/dev-tools/dev-tools-inspector.tsx +6 -1
  71. package/src/components/entries/other-entries.tsx +2 -0
  72. package/src/components/fields/builder.tsx +7 -2
  73. package/src/components/fields/file-upload.tsx +2 -7
  74. package/src/components/fields/money-input.tsx +3 -14
  75. package/src/components/fields/repeater.tsx +6 -3
  76. package/src/components/fields/rich-editor-extensions.tsx +9 -5
  77. package/src/components/layout/wizard.tsx +5 -1
  78. package/src/components/notifications/flash-toasts.tsx +8 -0
  79. package/src/components/notifications/notifications-menu.tsx +3 -3
  80. package/src/components/panel/lazy-placeholder.tsx +41 -1
  81. package/src/components/panel/pwa.tsx +23 -9
  82. package/src/components/panel/shortcuts-overlay.tsx +1 -1
  83. package/src/components/table/cells.tsx +6 -2
  84. package/src/components/ui/calendar.tsx +8 -0
  85. package/src/components/widgets/dashboard-customizer.tsx +2 -2
  86. package/src/components/widgets/widgets-block.tsx +2 -2
  87. package/src/create-alfakit.tsx +9 -1
  88. package/src/pages/page.tsx +1 -1
  89. package/src/standalone/alfakit-component.tsx +2 -2
  90. package/src/starter-kit/index.ts +1 -0
  91. package/src/starter-kit/starter-kit.tsx +22 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @alfakit/react
2
2
 
3
+ ## 0.1.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Wizard submit actions run with the form's state; a second custom rich-editor block no longer replaces the first; extra item actions on browser-added repeater items; "+N" no longer overlaps stacked images; PWA and notification toasts in starter kits; infolist blocks follow their `columns()`.
8
+
9
+ ## 0.1.11
10
+
11
+ ### Patch Changes
12
+
13
+ - File downloads from actions and `Action::download()` links; MoneyInput keeps out-of-range amounts for validation; translated calendar month buttons; image cells named for screen readers; `StarterKitFullscreenLayout`; customizable dashboards on the kit's `/dashboard`; failed lazy widgets show Retry; standalone headings keep their width.
14
+
3
15
  ## 0.1.10
4
16
 
5
17
  ### Patch Changes
@@ -90,7 +90,19 @@ function ActionButton({ action, context, size = "default", className, variant: v
90
90
  const sizeProp = isIconOnly ? iconButtonSize(buttonSize) : buttonSize;
91
91
  const classes = cn(buttonClasses(action, variant), labeledFrom && ICON_ONLY_BELOW[labeledFrom], className);
92
92
  const ariaLabel = isIconOnly || labeledFrom ? accessibleName : void 0;
93
- const button = isPlainLink ? action.openUrlInNewTab ? /* @__PURE__ */ jsx(Button, {
93
+ const button = isPlainLink ? action.download ? /* @__PURE__ */ jsx(Button, {
94
+ asChild: true,
95
+ variant: "plain",
96
+ size: sizeProp,
97
+ disabled: action.disabled,
98
+ "aria-label": ariaLabel,
99
+ ...extraProps(action.extraAttributes, classes),
100
+ children: /* @__PURE__ */ jsx("a", {
101
+ href: action.url,
102
+ download: "",
103
+ children: content
104
+ })
105
+ }) : action.openUrlInNewTab ? /* @__PURE__ */ jsx(Button, {
94
106
  asChild: true,
95
107
  variant: "plain",
96
108
  size: sizeProp,
@@ -1 +1 @@
1
- {"version":3,"file":"action-button.js","names":[],"sources":["../../../src/components/actions/action-button.tsx"],"sourcesContent":["import type { ActionContext, ActionSchema } from '@alfakit/core'\nimport {\n bindsKeyBindings,\n formatKeyBinding,\n iconButtonSize,\n isActionLoading,\n isActionProcessing,\n registerKeyBindings,\n resolveActionSize,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { useEffect, type MouseEvent } from 'react'\nimport { useActionRunner, useT } from '../../context'\nimport { useStoreSelector } from '../../hooks/use-store'\nimport { extraProps } from '../../lib/attributes'\nimport {\n badgeColorClasses,\n colorClass,\n ghostButtonColorClasses,\n outlineButtonColorClasses,\n solidButtonColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Spinner } from '../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'\n\n/** `labeledFrom()`: the label shows from the breakpoint up. */\nconst LABEL_FROM: Record<string, string> = {\n sm: 'hidden sm:inline',\n md: 'hidden md:inline',\n lg: 'hidden lg:inline',\n xl: 'hidden xl:inline',\n '2xl': 'hidden 2xl:inline',\n}\n\n/** `labeledFrom()`: square (icon-only) padding below the breakpoint. */\nconst ICON_ONLY_BELOW: Record<string, string> = {\n sm: 'max-sm:aspect-square max-sm:px-0',\n md: 'max-md:aspect-square max-md:px-0',\n lg: 'max-lg:aspect-square max-lg:px-0',\n xl: 'max-xl:aspect-square max-xl:px-0',\n '2xl': 'max-2xl:aspect-square max-2xl:px-0',\n}\n\nexport interface ActionButtonProps {\n action: ActionSchema\n context: ActionContext\n size?: 'xs' | 'sm' | 'default' | 'lg'\n className?: string\n /** Force a variant (e.g. menu items render as plain rows). */\n variant?: ActionSchema['variant']\n /** Bind the action's key bindings globally while mounted. */\n withKeyBindings?: boolean\n /** Override how the action runs (e.g. a form submits its values). */\n onTrigger?: (action: ActionSchema, context: ActionContext) => void | Promise<void>\n}\n\n/** The badge trigger style (`badge()` without an argument). */\nconst BADGE_TRIGGER_CLASSES =\n \"h-auto min-h-6 gap-1 rounded-md border px-2 py-0.5 text-xs font-medium [&_svg:not([class*='size-'])]:size-3.5\"\n\nfunction buttonClasses(action: ActionSchema, variant: ActionSchema['variant']): string {\n const color = action.color ?? 'primary'\n\n switch (variant) {\n case 'outline':\n return cn('bg-background border', colorClass(outlineButtonColorClasses, color, 'gray'))\n case 'ghost':\n return colorClass(ghostButtonColorClasses, color, 'gray')\n case 'link':\n return cn(\n 'h-auto p-0 underline-offset-4 hover:underline has-[>svg]:px-0',\n colorClass(textColorClasses, color, 'primary'),\n )\n case 'icon-button':\n return colorClass(ghostButtonColorClasses, color, 'gray')\n case 'badge':\n // `badge()`: a tinted pill in the action's colour.\n return cn(BADGE_TRIGGER_CLASSES, colorClass(badgeColorClasses, color, 'primary'))\n default:\n return colorClass(solidButtonColorClasses, color, 'primary')\n }\n}\n\nexport function ActionButton({\n action,\n context,\n size = 'default',\n className,\n variant: variantProp,\n withKeyBindings = true,\n onTrigger,\n}: ActionButtonProps) {\n const runner = useActionRunner()\n const t = useT()\n // Only this run (this record's button), not the same action in every table row.\n const loading = useStoreSelector(runner.store, (state) => isActionLoading(state, action, context))\n const processing = useStoreSelector(runner.store, (state) =>\n isActionProcessing(state, action, context),\n )\n\n const variant =\n variantProp ??\n (action.outlined\n ? 'outline'\n : (action.variant ?? (action.hasAction || action.hasModal || action.url ? 'link' : 'button')))\n const isIconOnly = variant === 'icon-button' || !action.label\n // `labeledFrom()`: icon only on small screens, so the label must be the accessible name.\n const labeledFrom = !isIconOnly && action.icon && action.labeledFrom ? action.labeledFrom : null\n const busy = loading || processing\n // Record actions repeat per row: a global shortcut would run them for every record.\n const bindsKeys = withKeyBindings && bindsKeyBindings(action, context)\n\n useEffect(() => {\n if (!bindsKeys || !action.keyBindings) {\n return\n }\n\n return registerKeyBindings([\n {\n keys: action.keyBindings,\n handler: () =>\n void (onTrigger ? onTrigger(action, context) : runner.trigger(action, context)),\n },\n ])\n }, [action, context, runner, bindsKeys, onTrigger])\n\n // A disabled link renders as a disabled button so it cannot navigate.\n const isPlainLink = Boolean(\n action.url && !action.hasAction && !action.hasModal && !action.disabled,\n )\n\n const onClick = (event: MouseEvent) => {\n if (isPlainLink) {\n return // Plain link: let Inertia Link / anchor handle it.\n }\n\n event.preventDefault()\n void (onTrigger ? onTrigger(action, context) : runner.trigger(action, context))\n }\n\n const content = (\n <>\n {busy ? (\n <Spinner />\n ) : action.icon && action.iconPosition !== 'after' ? (\n <Icon name={action.icon} />\n ) : null}\n {!isIconOnly && (\n <span className={labeledFrom ? LABEL_FROM[labeledFrom] : undefined}>{action.label}</span>\n )}\n {action.icon && action.iconPosition === 'after' && !busy && <Icon name={action.icon} />}\n {action.badge !== undefined && action.badge !== null && (\n <Badge\n variant=\"outline\"\n className={cn(\n 'ms-1 px-1.5 py-0',\n colorClass(badgeColorClasses, action.badgeColor, 'gray'),\n )}\n >\n {action.badge}\n </Badge>\n )}\n </>\n )\n\n const accessibleName = action.label ?? action.tooltip ?? action.name\n const newTabHint = t('opens_in_new_tab', {}, '(opens in a new tab)')\n const buttonSize = variant === 'badge' ? 'xs' : resolveActionSize(action.size, size)\n const sizeProp = isIconOnly ? iconButtonSize(buttonSize) : buttonSize\n const classes = cn(\n buttonClasses(action, variant),\n labeledFrom && ICON_ONLY_BELOW[labeledFrom],\n className,\n )\n const ariaLabel = isIconOnly || labeledFrom ? accessibleName : undefined\n\n const button = isPlainLink ? (\n action.openUrlInNewTab ? (\n <Button\n asChild\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled}\n aria-label={ariaLabel ? `${ariaLabel} ${newTabHint}` : undefined}\n {...extraProps(action.extraAttributes, classes)}\n >\n <a href={action.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {content}\n {!isIconOnly && <span className=\"sr-only\"> {newTabHint}</span>}\n </a>\n </Button>\n ) : (\n <Button\n asChild\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled}\n aria-label={ariaLabel}\n {...extraProps(action.extraAttributes, classes)}\n >\n <Link href={action.url ?? '#'}>{content}</Link>\n </Button>\n )\n ) : (\n <Button\n type=\"button\"\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled || busy}\n onClick={onClick}\n aria-label={ariaLabel}\n {...extraProps(action.extraAttributes, classes)}\n >\n {content}\n </Button>\n )\n\n const tooltip = action.tooltip ?? (isIconOnly ? action.label : undefined)\n const shortcut = bindsKeys ? action.keyBindings?.[0] : undefined\n\n if (!tooltip && !shortcut) {\n return button\n }\n\n // A disabled button gets no pointer or focus events, so a focusable wrapper carries the\n // tooltip (e.g. the reason from `authorizationTooltip()`).\n const trigger =\n action.disabled && tooltip ? (\n <span tabIndex={0} className=\"inline-flex\" data-slot=\"action-disabled-tooltip\">\n {button}\n </span>\n ) : (\n button\n )\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{trigger}</TooltipTrigger>\n <TooltipContent>\n {tooltip}\n {shortcut && (\n <kbd dir=\"ltr\" className=\"ms-2 rounded border border-white/20 px-1 font-mono text-[10px]\">\n {formatKeyBinding(shortcut)}\n </kbd>\n )}\n </TooltipContent>\n </Tooltip>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"action-button.js","names":[],"sources":["../../../src/components/actions/action-button.tsx"],"sourcesContent":["import type { ActionContext, ActionSchema } from '@alfakit/core'\nimport {\n bindsKeyBindings,\n formatKeyBinding,\n iconButtonSize,\n isActionLoading,\n isActionProcessing,\n registerKeyBindings,\n resolveActionSize,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { useEffect, type MouseEvent } from 'react'\nimport { useActionRunner, useT } from '../../context'\nimport { useStoreSelector } from '../../hooks/use-store'\nimport { extraProps } from '../../lib/attributes'\nimport {\n badgeColorClasses,\n colorClass,\n ghostButtonColorClasses,\n outlineButtonColorClasses,\n solidButtonColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Spinner } from '../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'\n\n/** `labeledFrom()`: the label shows from the breakpoint up. */\nconst LABEL_FROM: Record<string, string> = {\n sm: 'hidden sm:inline',\n md: 'hidden md:inline',\n lg: 'hidden lg:inline',\n xl: 'hidden xl:inline',\n '2xl': 'hidden 2xl:inline',\n}\n\n/** `labeledFrom()`: square (icon-only) padding below the breakpoint. */\nconst ICON_ONLY_BELOW: Record<string, string> = {\n sm: 'max-sm:aspect-square max-sm:px-0',\n md: 'max-md:aspect-square max-md:px-0',\n lg: 'max-lg:aspect-square max-lg:px-0',\n xl: 'max-xl:aspect-square max-xl:px-0',\n '2xl': 'max-2xl:aspect-square max-2xl:px-0',\n}\n\nexport interface ActionButtonProps {\n action: ActionSchema\n context: ActionContext\n size?: 'xs' | 'sm' | 'default' | 'lg'\n className?: string\n /** Force a variant (e.g. menu items render as plain rows). */\n variant?: ActionSchema['variant']\n /** Bind the action's key bindings globally while mounted. */\n withKeyBindings?: boolean\n /** Override how the action runs (e.g. a form submits its values). */\n onTrigger?: (action: ActionSchema, context: ActionContext) => void | Promise<void>\n}\n\n/** The badge trigger style (`badge()` without an argument). */\nconst BADGE_TRIGGER_CLASSES =\n \"h-auto min-h-6 gap-1 rounded-md border px-2 py-0.5 text-xs font-medium [&_svg:not([class*='size-'])]:size-3.5\"\n\nfunction buttonClasses(action: ActionSchema, variant: ActionSchema['variant']): string {\n const color = action.color ?? 'primary'\n\n switch (variant) {\n case 'outline':\n return cn('bg-background border', colorClass(outlineButtonColorClasses, color, 'gray'))\n case 'ghost':\n return colorClass(ghostButtonColorClasses, color, 'gray')\n case 'link':\n return cn(\n 'h-auto p-0 underline-offset-4 hover:underline has-[>svg]:px-0',\n colorClass(textColorClasses, color, 'primary'),\n )\n case 'icon-button':\n return colorClass(ghostButtonColorClasses, color, 'gray')\n case 'badge':\n // `badge()`: a tinted pill in the action's colour.\n return cn(BADGE_TRIGGER_CLASSES, colorClass(badgeColorClasses, color, 'primary'))\n default:\n return colorClass(solidButtonColorClasses, color, 'primary')\n }\n}\n\nexport function ActionButton({\n action,\n context,\n size = 'default',\n className,\n variant: variantProp,\n withKeyBindings = true,\n onTrigger,\n}: ActionButtonProps) {\n const runner = useActionRunner()\n const t = useT()\n // Only this run (this record's button), not the same action in every table row.\n const loading = useStoreSelector(runner.store, (state) => isActionLoading(state, action, context))\n const processing = useStoreSelector(runner.store, (state) =>\n isActionProcessing(state, action, context),\n )\n\n const variant =\n variantProp ??\n (action.outlined\n ? 'outline'\n : (action.variant ?? (action.hasAction || action.hasModal || action.url ? 'link' : 'button')))\n const isIconOnly = variant === 'icon-button' || !action.label\n // `labeledFrom()`: icon only on small screens, so the label must be the accessible name.\n const labeledFrom = !isIconOnly && action.icon && action.labeledFrom ? action.labeledFrom : null\n const busy = loading || processing\n // Record actions repeat per row: a global shortcut would run them for every record.\n const bindsKeys = withKeyBindings && bindsKeyBindings(action, context)\n\n useEffect(() => {\n if (!bindsKeys || !action.keyBindings) {\n return\n }\n\n return registerKeyBindings([\n {\n keys: action.keyBindings,\n handler: () =>\n void (onTrigger ? onTrigger(action, context) : runner.trigger(action, context)),\n },\n ])\n }, [action, context, runner, bindsKeys, onTrigger])\n\n // A disabled link renders as a disabled button so it cannot navigate.\n const isPlainLink = Boolean(\n action.url && !action.hasAction && !action.hasModal && !action.disabled,\n )\n\n const onClick = (event: MouseEvent) => {\n if (isPlainLink) {\n return // Plain link: let Inertia Link / anchor handle it.\n }\n\n event.preventDefault()\n void (onTrigger ? onTrigger(action, context) : runner.trigger(action, context))\n }\n\n const content = (\n <>\n {busy ? (\n <Spinner />\n ) : action.icon && action.iconPosition !== 'after' ? (\n <Icon name={action.icon} />\n ) : null}\n {!isIconOnly && (\n <span className={labeledFrom ? LABEL_FROM[labeledFrom] : undefined}>{action.label}</span>\n )}\n {action.icon && action.iconPosition === 'after' && !busy && <Icon name={action.icon} />}\n {action.badge !== undefined && action.badge !== null && (\n <Badge\n variant=\"outline\"\n className={cn(\n 'ms-1 px-1.5 py-0',\n colorClass(badgeColorClasses, action.badgeColor, 'gray'),\n )}\n >\n {action.badge}\n </Badge>\n )}\n </>\n )\n\n const accessibleName = action.label ?? action.tooltip ?? action.name\n const newTabHint = t('opens_in_new_tab', {}, '(opens in a new tab)')\n const buttonSize = variant === 'badge' ? 'xs' : resolveActionSize(action.size, size)\n const sizeProp = isIconOnly ? iconButtonSize(buttonSize) : buttonSize\n const classes = cn(\n buttonClasses(action, variant),\n labeledFrom && ICON_ONLY_BELOW[labeledFrom],\n className,\n )\n const ariaLabel = isIconOnly || labeledFrom ? accessibleName : undefined\n\n const button = isPlainLink ? (\n action.download ? (\n // `download()`: a plain link that saves the file (an Inertia visit would discard it).\n <Button\n asChild\n variant=\"plain\"\n size={sizeProp}\n disabled={action.disabled}\n aria-label={ariaLabel}\n {...extraProps(action.extraAttributes, classes)}\n >\n <a href={action.url} download=\"\">\n {content}\n </a>\n </Button>\n ) : action.openUrlInNewTab ? (\n <Button\n asChild\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled}\n aria-label={ariaLabel ? `${ariaLabel} ${newTabHint}` : undefined}\n {...extraProps(action.extraAttributes, classes)}\n >\n <a href={action.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {content}\n {!isIconOnly && <span className=\"sr-only\"> {newTabHint}</span>}\n </a>\n </Button>\n ) : (\n <Button\n asChild\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled}\n aria-label={ariaLabel}\n {...extraProps(action.extraAttributes, classes)}\n >\n <Link href={action.url ?? '#'}>{content}</Link>\n </Button>\n )\n ) : (\n <Button\n type=\"button\"\n variant=\"plain\"\n size={sizeProp}\n\n disabled={action.disabled || busy}\n onClick={onClick}\n aria-label={ariaLabel}\n {...extraProps(action.extraAttributes, classes)}\n >\n {content}\n </Button>\n )\n\n const tooltip = action.tooltip ?? (isIconOnly ? action.label : undefined)\n const shortcut = bindsKeys ? action.keyBindings?.[0] : undefined\n\n if (!tooltip && !shortcut) {\n return button\n }\n\n // A disabled button gets no pointer or focus events, so a focusable wrapper carries the\n // tooltip (e.g. the reason from `authorizationTooltip()`).\n const trigger =\n action.disabled && tooltip ? (\n <span tabIndex={0} className=\"inline-flex\" data-slot=\"action-disabled-tooltip\">\n {button}\n </span>\n ) : (\n button\n )\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{trigger}</TooltipTrigger>\n <TooltipContent>\n {tooltip}\n {shortcut && (\n <kbd dir=\"ltr\" className=\"ms-2 rounded border border-white/20 px-1 font-mono text-[10px]\">\n {formatKeyBinding(shortcut)}\n </kbd>\n )}\n </TooltipContent>\n </Tooltip>\n )\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { Button } from "../ui/button.js";
4
4
  import { Input } from "../ui/input.js";
5
5
  import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "../ui/sheet.js";
6
6
  import { ScrollArea } from "../ui/scroll-area.js";
7
- import { byteLength, formatBytes, inspectableProps, jsonEntries, jsonScalar, jsonSize, latestServerTiming, prettyJson, searchJson } from "@alfakit/core";
7
+ import { byteLength, formatBytes, inspectableProps, jsonEntries, jsonScalar, jsonSize, latestServerTiming, overlayPlacementDirection, prettyJson, searchJson } from "@alfakit/core";
8
8
  import { router } from "@inertiajs/react";
9
9
  import { useEffect, useMemo, useState } from "react";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -52,7 +52,7 @@ function DevToolsInspector() {
52
52
  children: /* @__PURE__ */ jsx(BracesIcon, {})
53
53
  })
54
54
  }), /* @__PURE__ */ jsx(SheetContent, {
55
- side: direction === "rtl" ? "left" : "right",
55
+ side: overlayPlacementDirection(direction, props.alfakit?.starterKit) === "rtl" ? "left" : "right",
56
56
  className: "flex w-full flex-col gap-0 p-0 sm:max-w-xl",
57
57
  children: /* @__PURE__ */ jsxs("div", {
58
58
  "data-slot": "dev-tools",
@@ -1 +1 @@
1
- {"version":3,"file":"dev-tools-inspector.js","names":[],"sources":["../../../src/components/dev-tools/dev-tools-inspector.tsx"],"sourcesContent":["import {\n byteLength,\n formatBytes,\n inspectableProps,\n jsonEntries,\n jsonScalar,\n jsonSize,\n latestServerTiming,\n prettyJson,\n searchJson,\n type JsonEntry,\n type JsonSearchResult,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { BracesIcon, CheckIcon, ChevronRightIcon, CopyIcon, ExternalLinkIcon } from 'lucide-react'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useAlfakitProps, useDirection, useFormatter, useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { Button } from '../ui/button'\nimport { Input } from '../ui/input'\nimport { ScrollArea } from '../ui/scroll-area'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '../ui/sheet'\n\n/**\n * The schema inspector: a floating developer button that opens a drawer with the page's\n * props as a searchable JSON tree (with a copy button), what the context token identifies,\n * the server time reported in `Server-Timing`, the payload size and editor links for the\n * PHP classes behind the page. Rendered only for pages that carry `alfakitDevTools`.\n */\nexport function DevToolsInspector() {\n const props = useAlfakitProps()\n const t = useT()\n const formatter = useFormatter()\n const direction = useDirection()\n const tools = props.alfakitDevTools\n const data = useMemo(() => inspectableProps(props as unknown as Record<string, unknown>), [props])\n const payload = useMemo(() => byteLength(props), [props])\n const [query, setQuery] = useState('')\n const [copied, setCopied] = useState(false)\n // The inspector renders in the browser only (lazy chunk), so the Performance API is there.\n const [serverTime, setServerTime] = useState<number | null>(() => latestServerTiming())\n const search = useMemo(() => searchJson(data, query), [data, query])\n\n // Re-read the timing after every visit: the latest response carries the newest header.\n useEffect(() => router.on('finish', () => setServerTime(latestServerTiming())), [])\n\n useEffect(() => {\n if (!copied) return\n\n const timer = setTimeout(() => setCopied(false), 2000)\n\n return () => clearTimeout(timer)\n }, [copied])\n\n if (!tools) {\n return null\n }\n\n const copy = () => {\n void navigator.clipboard?.writeText(prettyJson(data)).then(() => setCopied(true))\n }\n\n const time = (value: number | null) =>\n value === null ? t('dev_tools.never', {}, 'Never') : formatter.dateTime(value * 1000)\n const parameters = Object.entries(tools.context.parameters)\n\n return (\n <Sheet>\n <SheetTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label={t('dev_tools.open', {}, 'Open schema inspector')}\n data-slot=\"dev-tools-toggle\"\n className=\"fixed end-4 bottom-16 z-40 rounded-full shadow-md [[data-slot=starter-kit-root]_&]:end-auto [[data-slot=starter-kit-root]_&]:right-4\"\n >\n <BracesIcon />\n </Button>\n </SheetTrigger>\n <SheetContent\n side={direction === 'rtl' ? 'left' : 'right'}\n className=\"flex w-full flex-col gap-0 p-0 sm:max-w-xl\"\n >\n <div data-slot=\"dev-tools\" className=\"flex min-h-0 flex-1 flex-col\">\n <SheetHeader className=\"border-b\">\n <SheetTitle className=\"flex items-center gap-2\">\n <BracesIcon className=\"size-4\" />\n {t('dev_tools.title', {}, 'Schema inspector')}\n </SheetTitle>\n <SheetDescription className=\"truncate font-mono text-xs\" title={tools.context.page}>\n {tools.context.page}\n </SheetDescription>\n </SheetHeader>\n <ScrollArea className=\"min-h-0 flex-1\">\n <div className=\"space-y-5 p-4 text-sm\">\n <dl className=\"grid grid-cols-2 gap-3\">\n <Stat\n label={t('dev_tools.server_time', {}, 'Server time')}\n value={\n serverTime === null\n ? t('dev_tools.not_measured', {}, 'Not measured')\n : t(\n 'dev_tools.milliseconds',\n { value: formatter.number(serverTime) },\n ':value ms',\n )\n }\n />\n <Stat\n label={t('dev_tools.payload', {}, 'Payload size')}\n value={formatBytes(payload)}\n />\n </dl>\n\n <section data-slot=\"dev-tools-context\">\n <h3 className=\"mb-2 text-sm font-medium\">\n {t('dev_tools.context', {}, 'Context token')}\n </h3>\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-xs\">\n <Row label={t('dev_tools.panel', {}, 'Panel')} value={tools.context.panel} />\n <Row label={t('dev_tools.page', {}, 'Page')} value={tools.context.page} />\n {tools.context.component && (\n <Row\n label={t('dev_tools.component', {}, 'Component')}\n value={tools.context.component}\n />\n )}\n {tools.tenant !== null && (\n <Row label={t('dev_tools.tenant', {}, 'Tenant')} value={tools.tenant} />\n )}\n {parameters.map(([key, value]) => (\n <Row key={key} label={key} value={jsonScalar(value)} />\n ))}\n <Row\n label={t('dev_tools.bound_to_user', {}, 'Bound to the user')}\n value={\n tools.context.boundToUser\n ? t('dev_tools.yes', {}, 'Yes')\n : t('dev_tools.no', {}, 'No')\n }\n plain\n />\n <Row\n label={t('dev_tools.issued_at', {}, 'Issued')}\n value={time(tools.context.issuedAt)}\n plain\n />\n <Row\n label={t('dev_tools.expires_at', {}, 'Expires')}\n value={time(tools.context.expiresAt)}\n plain\n />\n </dl>\n </section>\n\n <section data-slot=\"dev-tools-classes\">\n <h3 className=\"mb-2 text-sm font-medium\">\n {t('dev_tools.classes', {}, 'Classes')}\n </h3>\n <ul className=\"divide-y rounded-md border text-xs\">\n {tools.classes.map((item) => (\n <li key={item.role} className=\"flex items-center gap-2 px-3 py-1.5\">\n <span className=\"text-muted-foreground w-16 shrink-0\">\n {t(`dev_tools.roles.${item.role}`, {}, item.role)}\n </span>\n <span\n className=\"min-w-0 flex-1 truncate font-mono\"\n title={item.file ? `${item.file}:${item.line ?? 1}` : item.class}\n >\n {item.class}\n </span>\n {item.url && (\n <a\n href={item.url}\n className=\"text-primary inline-flex shrink-0 items-center gap-1 hover:underline\"\n aria-label={t(\n 'dev_tools.open_in_editor',\n { class: item.class, editor: tools.editor ?? '' },\n 'Open :class in :editor',\n )}\n >\n <ExternalLinkIcon className=\"size-3.5\" />\n </a>\n )}\n </li>\n ))}\n </ul>\n {tools.editor === null && (\n <p className=\"text-muted-foreground mt-2 text-xs\">\n {t(\n 'dev_tools.no_editor',\n {},\n 'Set APP_EDITOR (config app.editor) to open these classes in your editor.',\n )}\n </p>\n )}\n </section>\n\n <section data-slot=\"dev-tools-schema\">\n <div className=\"mb-2 flex items-center gap-2\">\n <h3 className=\"me-auto text-sm font-medium\">\n {t('dev_tools.schema', {}, 'Schema')}\n </h3>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={copy}>\n {copied ? <CheckIcon /> : <CopyIcon />}\n {copied\n ? t('dev_tools.copied', {}, 'Copied')\n : t('dev_tools.copy', {}, 'Copy JSON')}\n </Button>\n </div>\n <Input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('dev_tools.search', {}, 'Search keys and values')}\n aria-label={t('dev_tools.search', {}, 'Search keys and values')}\n className=\"mb-2 h-8\"\n />\n {query.trim() !== '' && search.matches.size === 0 ? (\n <p className=\"text-muted-foreground text-sm\">\n {t(\n 'dev_tools.no_matches',\n { query: query.trim() },\n 'Nothing matches “:query”.',\n )}\n </p>\n ) : (\n <ul\n dir=\"ltr\"\n role=\"tree\"\n aria-label={t('dev_tools.schema', {}, 'Schema')}\n className=\"bg-muted/40 overflow-x-auto rounded-md p-2 text-start font-mono text-xs leading-relaxed\"\n >\n {jsonEntries(data).map((entry) => (\n <JsonNode\n key={entry.path}\n entry={entry}\n depth={0}\n search={search}\n query={query}\n />\n ))}\n </ul>\n )}\n </section>\n </div>\n </ScrollArea>\n </div>\n </SheetContent>\n </Sheet>\n )\n}\n\nfunction JsonNode({\n entry,\n depth,\n search,\n query,\n}: {\n entry: JsonEntry\n depth: number\n search: JsonSearchResult\n query: string\n}) {\n const t = useT()\n const formatter = useFormatter()\n const searching = query.trim() !== ''\n const [open, setOpen] = useState(depth < 1)\n\n if (searching && !search.visible.has(entry.path)) {\n return null\n }\n\n const container = entry.kind === 'object' || entry.kind === 'array'\n const expanded = searching || open\n const matched = search.matches.has(entry.path)\n const size = jsonSize(entry.value)\n const key = (\n <span className={cn('text-foreground', matched && 'bg-primary/15 rounded-sm')}>\n {entry.key}\n </span>\n )\n\n if (!container) {\n return (\n <li\n role=\"treeitem\"\n aria-selected={false}\n className=\"py-px\"\n style={{ paddingInlineStart: `${depth * 12 + 16}px` }}\n >\n {key}\n <span className=\"text-muted-foreground\">: </span>\n <span\n className={cn(\n entry.kind === 'string' && 'text-emerald-700 dark:text-emerald-400',\n entry.kind === 'number' && 'text-sky-700 dark:text-sky-400',\n entry.kind === 'boolean' && 'text-violet-700 dark:text-violet-400',\n entry.kind === 'null' && 'text-muted-foreground',\n 'break-all',\n )}\n >\n {jsonScalar(entry.value)}\n </span>\n </li>\n )\n }\n\n return (\n <li role=\"treeitem\" aria-selected={false} aria-expanded={expanded}>\n <button\n type=\"button\"\n className=\"hover:bg-muted flex w-full items-center gap-1 rounded py-px text-start\"\n style={{ paddingInlineStart: `${depth * 12}px` }}\n onClick={() => setOpen(!open)}\n aria-label={\n expanded\n ? t('dev_tools.collapse', { key: entry.key }, 'Collapse :key')\n : t('dev_tools.expand', { key: entry.key }, 'Expand :key')\n }\n >\n {/* The tree is always left to right (it is code), so the chevron does not flip. */}\n <ChevronRightIcon\n className={cn('size-3 shrink-0 transition-transform', expanded && 'rotate-90')}\n />\n {key}\n <span className=\"text-muted-foreground\">\n {entry.kind === 'array' ? `[${formatter.number(size)}]` : `{${formatter.number(size)}}`}\n </span>\n </button>\n {expanded && size > 0 && (\n <ul role=\"group\">\n {jsonEntries(entry.value, entry.path).map((child) => (\n <JsonNode\n key={child.path}\n entry={child}\n depth={depth + 1}\n search={search}\n query={query}\n />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nfunction Stat({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"rounded-md border p-3\">\n <dt className=\"text-muted-foreground text-xs\">{label}</dt>\n <dd className=\"font-mono text-sm font-semibold tabular-nums\">{value}</dd>\n </div>\n )\n}\n\nfunction Row({ label, value, plain = false }: { label: string; value: string; plain?: boolean }) {\n return (\n <>\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className={cn('min-w-0 truncate', !plain && 'font-mono')} title={value}>\n {value}\n </dd>\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev-tools-inspector.js","names":[],"sources":["../../../src/components/dev-tools/dev-tools-inspector.tsx"],"sourcesContent":["import {\n byteLength,\n formatBytes,\n inspectableProps,\n jsonEntries,\n jsonScalar,\n jsonSize,\n latestServerTiming,\n overlayPlacementDirection,\n prettyJson,\n searchJson,\n type JsonEntry,\n type JsonSearchResult,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { BracesIcon, CheckIcon, ChevronRightIcon, CopyIcon, ExternalLinkIcon } from 'lucide-react'\nimport { useEffect, useMemo, useState } from 'react'\nimport { useAlfakitProps, useDirection, useFormatter, useT } from '../../context'\nimport { cn } from '../../lib/utils'\nimport { Button } from '../ui/button'\nimport { Input } from '../ui/input'\nimport { ScrollArea } from '../ui/scroll-area'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from '../ui/sheet'\n\n/**\n * The schema inspector: a floating developer button that opens a drawer with the page's\n * props as a searchable JSON tree (with a copy button), what the context token identifies,\n * the server time reported in `Server-Timing`, the payload size and editor links for the\n * PHP classes behind the page. Rendered only for pages that carry `alfakitDevTools`.\n */\nexport function DevToolsInspector() {\n const props = useAlfakitProps()\n const t = useT()\n const formatter = useFormatter()\n const direction = useDirection()\n const tools = props.alfakitDevTools\n const data = useMemo(() => inspectableProps(props as unknown as Record<string, unknown>), [props])\n const payload = useMemo(() => byteLength(props), [props])\n const [query, setQuery] = useState('')\n const [copied, setCopied] = useState(false)\n // The inspector renders in the browser only (lazy chunk), so the Performance API is there.\n const [serverTime, setServerTime] = useState<number | null>(() => latestServerTiming())\n const search = useMemo(() => searchJson(data, query), [data, query])\n\n // Re-read the timing after every visit: the latest response carries the newest header.\n useEffect(() => router.on('finish', () => setServerTime(latestServerTiming())), [])\n\n useEffect(() => {\n if (!copied) return\n\n const timer = setTimeout(() => setCopied(false), 2000)\n\n return () => clearTimeout(timer)\n }, [copied])\n\n if (!tools) {\n return null\n }\n\n const copy = () => {\n void navigator.clipboard?.writeText(prettyJson(data)).then(() => setCopied(true))\n }\n\n const time = (value: number | null) =>\n value === null ? t('dev_tools.never', {}, 'Never') : formatter.dateTime(value * 1000)\n const parameters = Object.entries(tools.context.parameters)\n\n return (\n <Sheet>\n <SheetTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label={t('dev_tools.open', {}, 'Open schema inspector')}\n data-slot=\"dev-tools-toggle\"\n className=\"fixed end-4 bottom-16 z-40 rounded-full shadow-md [[data-slot=starter-kit-root]_&]:end-auto [[data-slot=starter-kit-root]_&]:right-4\"\n >\n <BracesIcon />\n </Button>\n </SheetTrigger>\n <SheetContent\n side={\n overlayPlacementDirection(direction, props.alfakit?.starterKit) === 'rtl'\n ? 'left'\n : 'right'\n }\n className=\"flex w-full flex-col gap-0 p-0 sm:max-w-xl\"\n >\n <div data-slot=\"dev-tools\" className=\"flex min-h-0 flex-1 flex-col\">\n <SheetHeader className=\"border-b\">\n <SheetTitle className=\"flex items-center gap-2\">\n <BracesIcon className=\"size-4\" />\n {t('dev_tools.title', {}, 'Schema inspector')}\n </SheetTitle>\n <SheetDescription className=\"truncate font-mono text-xs\" title={tools.context.page}>\n {tools.context.page}\n </SheetDescription>\n </SheetHeader>\n <ScrollArea className=\"min-h-0 flex-1\">\n <div className=\"space-y-5 p-4 text-sm\">\n <dl className=\"grid grid-cols-2 gap-3\">\n <Stat\n label={t('dev_tools.server_time', {}, 'Server time')}\n value={\n serverTime === null\n ? t('dev_tools.not_measured', {}, 'Not measured')\n : t(\n 'dev_tools.milliseconds',\n { value: formatter.number(serverTime) },\n ':value ms',\n )\n }\n />\n <Stat\n label={t('dev_tools.payload', {}, 'Payload size')}\n value={formatBytes(payload)}\n />\n </dl>\n\n <section data-slot=\"dev-tools-context\">\n <h3 className=\"mb-2 text-sm font-medium\">\n {t('dev_tools.context', {}, 'Context token')}\n </h3>\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-xs\">\n <Row label={t('dev_tools.panel', {}, 'Panel')} value={tools.context.panel} />\n <Row label={t('dev_tools.page', {}, 'Page')} value={tools.context.page} />\n {tools.context.component && (\n <Row\n label={t('dev_tools.component', {}, 'Component')}\n value={tools.context.component}\n />\n )}\n {tools.tenant !== null && (\n <Row label={t('dev_tools.tenant', {}, 'Tenant')} value={tools.tenant} />\n )}\n {parameters.map(([key, value]) => (\n <Row key={key} label={key} value={jsonScalar(value)} />\n ))}\n <Row\n label={t('dev_tools.bound_to_user', {}, 'Bound to the user')}\n value={\n tools.context.boundToUser\n ? t('dev_tools.yes', {}, 'Yes')\n : t('dev_tools.no', {}, 'No')\n }\n plain\n />\n <Row\n label={t('dev_tools.issued_at', {}, 'Issued')}\n value={time(tools.context.issuedAt)}\n plain\n />\n <Row\n label={t('dev_tools.expires_at', {}, 'Expires')}\n value={time(tools.context.expiresAt)}\n plain\n />\n </dl>\n </section>\n\n <section data-slot=\"dev-tools-classes\">\n <h3 className=\"mb-2 text-sm font-medium\">\n {t('dev_tools.classes', {}, 'Classes')}\n </h3>\n <ul className=\"divide-y rounded-md border text-xs\">\n {tools.classes.map((item) => (\n <li key={item.role} className=\"flex items-center gap-2 px-3 py-1.5\">\n <span className=\"text-muted-foreground w-16 shrink-0\">\n {t(`dev_tools.roles.${item.role}`, {}, item.role)}\n </span>\n <span\n className=\"min-w-0 flex-1 truncate font-mono\"\n title={item.file ? `${item.file}:${item.line ?? 1}` : item.class}\n >\n {item.class}\n </span>\n {item.url && (\n <a\n href={item.url}\n className=\"text-primary inline-flex shrink-0 items-center gap-1 hover:underline\"\n aria-label={t(\n 'dev_tools.open_in_editor',\n { class: item.class, editor: tools.editor ?? '' },\n 'Open :class in :editor',\n )}\n >\n <ExternalLinkIcon className=\"size-3.5\" />\n </a>\n )}\n </li>\n ))}\n </ul>\n {tools.editor === null && (\n <p className=\"text-muted-foreground mt-2 text-xs\">\n {t(\n 'dev_tools.no_editor',\n {},\n 'Set APP_EDITOR (config app.editor) to open these classes in your editor.',\n )}\n </p>\n )}\n </section>\n\n <section data-slot=\"dev-tools-schema\">\n <div className=\"mb-2 flex items-center gap-2\">\n <h3 className=\"me-auto text-sm font-medium\">\n {t('dev_tools.schema', {}, 'Schema')}\n </h3>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={copy}>\n {copied ? <CheckIcon /> : <CopyIcon />}\n {copied\n ? t('dev_tools.copied', {}, 'Copied')\n : t('dev_tools.copy', {}, 'Copy JSON')}\n </Button>\n </div>\n <Input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('dev_tools.search', {}, 'Search keys and values')}\n aria-label={t('dev_tools.search', {}, 'Search keys and values')}\n className=\"mb-2 h-8\"\n />\n {query.trim() !== '' && search.matches.size === 0 ? (\n <p className=\"text-muted-foreground text-sm\">\n {t(\n 'dev_tools.no_matches',\n { query: query.trim() },\n 'Nothing matches “:query”.',\n )}\n </p>\n ) : (\n <ul\n dir=\"ltr\"\n role=\"tree\"\n aria-label={t('dev_tools.schema', {}, 'Schema')}\n className=\"bg-muted/40 overflow-x-auto rounded-md p-2 text-start font-mono text-xs leading-relaxed\"\n >\n {jsonEntries(data).map((entry) => (\n <JsonNode\n key={entry.path}\n entry={entry}\n depth={0}\n search={search}\n query={query}\n />\n ))}\n </ul>\n )}\n </section>\n </div>\n </ScrollArea>\n </div>\n </SheetContent>\n </Sheet>\n )\n}\n\nfunction JsonNode({\n entry,\n depth,\n search,\n query,\n}: {\n entry: JsonEntry\n depth: number\n search: JsonSearchResult\n query: string\n}) {\n const t = useT()\n const formatter = useFormatter()\n const searching = query.trim() !== ''\n const [open, setOpen] = useState(depth < 1)\n\n if (searching && !search.visible.has(entry.path)) {\n return null\n }\n\n const container = entry.kind === 'object' || entry.kind === 'array'\n const expanded = searching || open\n const matched = search.matches.has(entry.path)\n const size = jsonSize(entry.value)\n const key = (\n <span className={cn('text-foreground', matched && 'bg-primary/15 rounded-sm')}>\n {entry.key}\n </span>\n )\n\n if (!container) {\n return (\n <li\n role=\"treeitem\"\n aria-selected={false}\n className=\"py-px\"\n style={{ paddingInlineStart: `${depth * 12 + 16}px` }}\n >\n {key}\n <span className=\"text-muted-foreground\">: </span>\n <span\n className={cn(\n entry.kind === 'string' && 'text-emerald-700 dark:text-emerald-400',\n entry.kind === 'number' && 'text-sky-700 dark:text-sky-400',\n entry.kind === 'boolean' && 'text-violet-700 dark:text-violet-400',\n entry.kind === 'null' && 'text-muted-foreground',\n 'break-all',\n )}\n >\n {jsonScalar(entry.value)}\n </span>\n </li>\n )\n }\n\n return (\n <li role=\"treeitem\" aria-selected={false} aria-expanded={expanded}>\n <button\n type=\"button\"\n className=\"hover:bg-muted flex w-full items-center gap-1 rounded py-px text-start\"\n style={{ paddingInlineStart: `${depth * 12}px` }}\n onClick={() => setOpen(!open)}\n aria-label={\n expanded\n ? t('dev_tools.collapse', { key: entry.key }, 'Collapse :key')\n : t('dev_tools.expand', { key: entry.key }, 'Expand :key')\n }\n >\n {/* The tree is always left to right (it is code), so the chevron does not flip. */}\n <ChevronRightIcon\n className={cn('size-3 shrink-0 transition-transform', expanded && 'rotate-90')}\n />\n {key}\n <span className=\"text-muted-foreground\">\n {entry.kind === 'array' ? `[${formatter.number(size)}]` : `{${formatter.number(size)}}`}\n </span>\n </button>\n {expanded && size > 0 && (\n <ul role=\"group\">\n {jsonEntries(entry.value, entry.path).map((child) => (\n <JsonNode\n key={child.path}\n entry={child}\n depth={depth + 1}\n search={search}\n query={query}\n />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nfunction Stat({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"rounded-md border p-3\">\n <dt className=\"text-muted-foreground text-xs\">{label}</dt>\n <dd className=\"font-mono text-sm font-semibold tabular-nums\">{value}</dd>\n </div>\n )\n}\n\nfunction Row({ label, value, plain = false }: { label: string; value: string; plain?: boolean }) {\n return (\n <>\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className={cn('min-w-0 truncate', !plain && 'font-mono')} title={value}>\n {value}\n </dd>\n </>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"other-entries.d.ts","names":[],"sources":["../../../src/components/entries/other-entries.tsx"],"mappings":";;;wBAqDgB,qBAAqB;EAAe,WAAW;oBAAiB,IAAA;wBAoBhE,sBAAsB;EAAe,WAAW;oBAAkB,IAAA;wBAyDlE,sBAAsB;EAAe,WAAW;oBAAkB,IAAA;wBA4ClE,yBAAyB;EAAe,WAAW;oBAAqB,IAAA;wBAiCxE,2BAA2B;EAAe,WAAW;oBAAuB,IAAA"}
1
+ {"version":3,"file":"other-entries.d.ts","names":[],"sources":["../../../src/components/entries/other-entries.tsx"],"mappings":";;;wBAqDgB,qBAAqB;EAAe,WAAW;oBAAiB,IAAA;wBAoBhE,sBAAsB;EAAe,WAAW;oBAAkB,IAAA;wBA2DlE,sBAAsB;EAAe,WAAW;oBAAkB,IAAA;wBA4ClE,yBAAyB;EAAe,WAAW;oBAAqB,IAAA;wBAiCxE,2BAA2B;EAAe,WAAW;oBAAuB,IAAA"}
@@ -70,7 +70,7 @@ function ImageEntryComponent({ component }) {
70
70
  ...extraProps(component.extraImgAttributes, cn("object-cover", component.circular ? "rounded-full" : "rounded-md", component.stacked && usesDefaultImageStack(component) && "ring-background ring-2"))
71
71
  }, index)), component.limitedRemainingText !== false && urls.length > limit && /* @__PURE__ */ jsxs("span", {
72
72
  "data-slot": "image-remaining",
73
- className: cn("text-muted-foreground", remainingTextSizeClass(component.limitedRemainingTextSize)),
73
+ className: cn("text-muted-foreground", component.stacked && usesDefaultImageStack(component) && "ms-3", remainingTextSizeClass(component.limitedRemainingTextSize)),
74
74
  children: ["+", urls.length - limit]
75
75
  })]
76
76
  })
@@ -1 +1 @@
1
- {"version":3,"file":"other-entries.js","names":[],"sources":["../../../src/components/entries/other-entries.tsx"],"sourcesContent":["import type {\n BaseEntry,\n ColorEntrySchema,\n IconEntrySchema,\n ImageEntrySchema,\n KeyValueEntrySchema,\n RepeatableEntrySchema,\n} from '@alfakit/core'\nimport {\n gridColumnsClasses,\n remainingTextSizeClass,\n stackedImageStyle,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport type { ReactNode } from 'react'\nimport { extraProps } from '../../lib/attributes'\nimport { textColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { NewTabHint } from '../../lib/new-tab-hint'\nimport { cn } from '../../lib/utils'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { EntryPlaceholder, EntryWrapper } from './entry-wrapper'\nimport { RepeatableEntryTable } from './repeatable-entry-table'\nimport { FormattedText } from './text-entry'\n\n/** Wrap an entry's content in its `url()` link (icon, image and colour entries). */\nexport function EntryLink({ entry, children }: { entry: BaseEntry; children: ReactNode }) {\n if (!entry.url) {\n return <>{children}</>\n }\n\n return entry.openUrlInNewTab ? (\n <a href={entry.url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"inline-flex w-fit\">\n {children}\n <NewTabHint />\n </a>\n ) : (\n <Link href={entry.url} className=\"inline-flex w-fit\">\n {children}\n </Link>\n )\n}\n\nconst ICON_SIZES: Record<string, string> = {\n xs: 'size-3',\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n xl: 'size-8',\n '2xl': 'size-10',\n}\n\nexport function IconEntryComponent({ component }: { component: IconEntrySchema }) {\n return (\n <EntryWrapper entry={component}>\n {component.icon ? (\n <EntryLink entry={component}>\n <Icon\n name={component.icon}\n className={cn(\n ICON_SIZES[component.size ?? 'md'],\n component.color && textColorClasses[component.color],\n )}\n />\n </EntryLink>\n ) : (\n <EntryPlaceholder entry={component} />\n )}\n </EntryWrapper>\n )\n}\n\nexport function ImageEntryComponent({ component }: { component: ImageEntrySchema }) {\n const urls = component.state ?? []\n const limit = component.limit ?? urls.length\n const size = component.size ?? 40\n const style = {\n height: component.height ?? size,\n width: component.width ?? (component.square || component.circular ? size : undefined),\n }\n\n return (\n <EntryWrapper entry={component}>\n {urls.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <EntryLink entry={component}>\n <div\n className={cn(\n 'flex items-center',\n component.stacked ? usesDefaultImageStack(component) && '-space-x-2' : 'gap-2',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n alt={component.alts?.[index] ?? component.alt ?? ''}\n style={{ ...style, ...stackedImageStyle(index, component) }}\n {...extraProps(\n component.extraImgAttributes,\n cn(\n 'object-cover',\n component.circular ? 'rounded-full' : 'rounded-md',\n component.stacked &&\n usesDefaultImageStack(component) &&\n 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {component.limitedRemainingText !== false && urls.length > limit && (\n <span\n data-slot=\"image-remaining\"\n className={cn(\n 'text-muted-foreground',\n remainingTextSizeClass(component.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n </EntryLink>\n )}\n </EntryWrapper>\n )\n}\n\nexport function ColorEntryComponent({ component }: { component: ColorEntrySchema }) {\n const colors = Array.isArray(component.state)\n ? component.state\n : component.state\n ? [component.state]\n : []\n\n return (\n <EntryWrapper entry={component}>\n {colors.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <EntryLink entry={component}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {colors.map((color, index) => (\n <span key={index} className=\"inline-flex items-center gap-1.5 text-sm\">\n <span\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n {/* The code honours the entry's text styles and copy settings. */}\n {component.copyable && (\n <FormattedText\n value={color}\n className=\"text-xs\"\n schema={{\n size: component.size,\n weight: component.weight,\n fontFamily: component.fontFamily ?? 'mono',\n copyable: true,\n copyMessage: component.copyMessage,\n copyMessageDuration: component.copyMessageDuration,\n }}\n />\n )}\n </span>\n ))}\n </div>\n </EntryLink>\n )}\n </EntryWrapper>\n )\n}\n\nexport function KeyValueEntryComponent({ component }: { component: KeyValueEntrySchema }) {\n const entries = Object.entries(component.state ?? {})\n\n return (\n <EntryWrapper entry={component}>\n {entries.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <div className=\"overflow-hidden rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-muted-foreground text-xs\">\n <tr>\n <th className=\"px-3 py-1.5 text-start font-medium\">{component.keyLabel}</th>\n <th className=\"px-3 py-1.5 text-start font-medium\">{component.valueLabel}</th>\n </tr>\n </thead>\n <tbody>\n {entries.map(([key, value]) => (\n <tr key={key} className=\"border-t\">\n <td className=\"px-3 py-1.5 font-medium\">{key}</td>\n <td className=\"px-3 py-1.5\">\n {typeof value === 'object' ? JSON.stringify(value) : String(value)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </EntryWrapper>\n )\n}\n\nexport function RepeatableEntryComponent({ component }: { component: RepeatableEntrySchema }) {\n return (\n <EntryWrapper entry={component}>\n {component.items.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : component.table?.length ? (\n <RepeatableEntryTable columns={component.table} items={component.items} />\n ) : (\n <div className={cn('grid gap-3', component.grid && gridColumnsClasses(component.grid))}>\n {component.items.map((item, index) => (\n <div\n key={index}\n className={cn(component.contained && 'bg-card rounded-lg border p-4 shadow-xs')}\n >\n <div className=\"contents\">\n <SchemaRenderer\n components={item}\n columns={component.columns ?? { default: 1, lg: 2 }}\n className=\"gap-4\"\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </EntryWrapper>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"other-entries.js","names":[],"sources":["../../../src/components/entries/other-entries.tsx"],"sourcesContent":["import type {\n BaseEntry,\n ColorEntrySchema,\n IconEntrySchema,\n ImageEntrySchema,\n KeyValueEntrySchema,\n RepeatableEntrySchema,\n} from '@alfakit/core'\nimport {\n gridColumnsClasses,\n remainingTextSizeClass,\n stackedImageStyle,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport type { ReactNode } from 'react'\nimport { extraProps } from '../../lib/attributes'\nimport { textColorClasses } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { NewTabHint } from '../../lib/new-tab-hint'\nimport { cn } from '../../lib/utils'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { EntryPlaceholder, EntryWrapper } from './entry-wrapper'\nimport { RepeatableEntryTable } from './repeatable-entry-table'\nimport { FormattedText } from './text-entry'\n\n/** Wrap an entry's content in its `url()` link (icon, image and colour entries). */\nexport function EntryLink({ entry, children }: { entry: BaseEntry; children: ReactNode }) {\n if (!entry.url) {\n return <>{children}</>\n }\n\n return entry.openUrlInNewTab ? (\n <a href={entry.url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"inline-flex w-fit\">\n {children}\n <NewTabHint />\n </a>\n ) : (\n <Link href={entry.url} className=\"inline-flex w-fit\">\n {children}\n </Link>\n )\n}\n\nconst ICON_SIZES: Record<string, string> = {\n xs: 'size-3',\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n xl: 'size-8',\n '2xl': 'size-10',\n}\n\nexport function IconEntryComponent({ component }: { component: IconEntrySchema }) {\n return (\n <EntryWrapper entry={component}>\n {component.icon ? (\n <EntryLink entry={component}>\n <Icon\n name={component.icon}\n className={cn(\n ICON_SIZES[component.size ?? 'md'],\n component.color && textColorClasses[component.color],\n )}\n />\n </EntryLink>\n ) : (\n <EntryPlaceholder entry={component} />\n )}\n </EntryWrapper>\n )\n}\n\nexport function ImageEntryComponent({ component }: { component: ImageEntrySchema }) {\n const urls = component.state ?? []\n const limit = component.limit ?? urls.length\n const size = component.size ?? 40\n const style = {\n height: component.height ?? size,\n width: component.width ?? (component.square || component.circular ? size : undefined),\n }\n\n return (\n <EntryWrapper entry={component}>\n {urls.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <EntryLink entry={component}>\n <div\n className={cn(\n 'flex items-center',\n component.stacked ? usesDefaultImageStack(component) && '-space-x-2' : 'gap-2',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n alt={component.alts?.[index] ?? component.alt ?? ''}\n style={{ ...style, ...stackedImageStyle(index, component) }}\n {...extraProps(\n component.extraImgAttributes,\n cn(\n 'object-cover',\n component.circular ? 'rounded-full' : 'rounded-md',\n component.stacked &&\n usesDefaultImageStack(component) &&\n 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {component.limitedRemainingText !== false && 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 component.stacked && usesDefaultImageStack(component) && 'ms-3',\n remainingTextSizeClass(component.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n </EntryLink>\n )}\n </EntryWrapper>\n )\n}\n\nexport function ColorEntryComponent({ component }: { component: ColorEntrySchema }) {\n const colors = Array.isArray(component.state)\n ? component.state\n : component.state\n ? [component.state]\n : []\n\n return (\n <EntryWrapper entry={component}>\n {colors.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <EntryLink entry={component}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {colors.map((color, index) => (\n <span key={index} className=\"inline-flex items-center gap-1.5 text-sm\">\n <span\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n {/* The code honours the entry's text styles and copy settings. */}\n {component.copyable && (\n <FormattedText\n value={color}\n className=\"text-xs\"\n schema={{\n size: component.size,\n weight: component.weight,\n fontFamily: component.fontFamily ?? 'mono',\n copyable: true,\n copyMessage: component.copyMessage,\n copyMessageDuration: component.copyMessageDuration,\n }}\n />\n )}\n </span>\n ))}\n </div>\n </EntryLink>\n )}\n </EntryWrapper>\n )\n}\n\nexport function KeyValueEntryComponent({ component }: { component: KeyValueEntrySchema }) {\n const entries = Object.entries(component.state ?? {})\n\n return (\n <EntryWrapper entry={component}>\n {entries.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : (\n <div className=\"overflow-hidden rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50 text-muted-foreground text-xs\">\n <tr>\n <th className=\"px-3 py-1.5 text-start font-medium\">{component.keyLabel}</th>\n <th className=\"px-3 py-1.5 text-start font-medium\">{component.valueLabel}</th>\n </tr>\n </thead>\n <tbody>\n {entries.map(([key, value]) => (\n <tr key={key} className=\"border-t\">\n <td className=\"px-3 py-1.5 font-medium\">{key}</td>\n <td className=\"px-3 py-1.5\">\n {typeof value === 'object' ? JSON.stringify(value) : String(value)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </EntryWrapper>\n )\n}\n\nexport function RepeatableEntryComponent({ component }: { component: RepeatableEntrySchema }) {\n return (\n <EntryWrapper entry={component}>\n {component.items.length === 0 ? (\n <EntryPlaceholder entry={component} />\n ) : component.table?.length ? (\n <RepeatableEntryTable columns={component.table} items={component.items} />\n ) : (\n <div className={cn('grid gap-3', component.grid && gridColumnsClasses(component.grid))}>\n {component.items.map((item, index) => (\n <div\n key={index}\n className={cn(component.contained && 'bg-card rounded-lg border p-4 shadow-xs')}\n >\n <div className=\"contents\">\n <SchemaRenderer\n components={item}\n columns={component.columns ?? { default: 1, lg: 2 }}\n className=\"gap-4\"\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </EntryWrapper>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","names":[],"sources":["../../../src/components/fields/builder.tsx"],"mappings":";;;;;;;;;;;wBAmEgB,eAAe;EAAe,WAAW;oBAAe,IAAA"}
1
+ {"version":3,"file":"builder.d.ts","names":[],"sources":["../../../src/components/fields/builder.tsx"],"mappings":";;;;;;;;;;;wBAoEgB,eAAe;EAAe,WAAW;oBAAe,IAAA"}
@@ -12,7 +12,7 @@ import { FieldWrapper } from "../schema/field-wrapper.js";
12
12
  import { Collapsible, CollapsibleContent } from "../ui/collapsible.js";
13
13
  import { BuilderBlockPicker, BuilderBlockPickerPanel } from "./builder-picker.js";
14
14
  import { AddActionRow, CollapseAllActions, ItemHeaderActions, useItemActionFallbacks, useItemActions, useItemMoves, useRunItemAction } from "./item-actions.js";
15
- import { VIRTUAL_ESTIMATES, builderItemLabel, builderItemSchema, builderItems, canAddBuilderBlock, cloneIndexedState, cloneRepeaterItem, fieldIds, insertBuilderItem, isDeferredPlaceholder, itemActionContext, moveIndexedState, moveRepeaterItem, removeIndexedState, removeRepeaterItem, reorderedItemKeys, virtualizeOptions } from "@alfakit/core";
15
+ import { VIRTUAL_ESTIMATES, builderItemLabel, builderItemSchema, builderItems, canAddBuilderBlock, cloneIndexedState, cloneRepeaterItem, fieldIds, insertBuilderItem, isDeferredPlaceholder, itemActionContext, itemExtraActions, moveIndexedState, moveRepeaterItem, removeIndexedState, removeRepeaterItem, reorderedItemKeys, virtualizeOptions } from "@alfakit/core";
16
16
  import { createElement, useState } from "react";
17
17
  import { jsx, jsxs } from "react/jsx-runtime";
18
18
  import { EllipsisIcon, PencilIcon } from "lucide-react";
@@ -210,7 +210,7 @@ function BuilderFieldContent({ component }) {
210
210
  index,
211
211
  count: items.length,
212
212
  resolve: (name) => resolve(name, serverItem),
213
- extraActions: menu || locked ? void 0 : serverItem?.actions,
213
+ extraActions: menu || locked ? void 0 : itemExtraActions(component, serverItem),
214
214
  buttons: buttons && !menu,
215
215
  canClone: canClone && !menu,
216
216
  canDelete: canDelete && !menu,
@@ -244,7 +244,7 @@ function BuilderFieldContent({ component }) {
244
244
  canMoveDown: buttons && index < items.length - 1,
245
245
  canClone,
246
246
  canDelete,
247
- extraActions: locked ? void 0 : serverItem?.actions,
247
+ extraActions: locked ? void 0 : itemExtraActions(component, serverItem),
248
248
  onAction: (name, onClient) => runItemAction(resolve(name, serverItem), { item: String(index) }, onClient),
249
249
  onExtraAction: (action) => void runner.trigger(action, itemActionContext(context, { item: String(index) })),
250
250
  onMove: (to) => moveWithButton(index, to),
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","names":[],"sources":["../../../src/components/fields/builder.tsx"],"sourcesContent":["import type {\n ActionSchema,\n BuilderBlockSchema,\n BuilderItemState,\n BuilderSchema,\n ItemActionName,\n ItemActionSchema,\n} from '@alfakit/core'\nimport {\n builderItemLabel,\n builderItems,\n builderItemSchema,\n canAddBuilderBlock,\n cloneIndexedState,\n cloneRepeaterItem,\n fieldIds,\n insertBuilderItem,\n isDeferredPlaceholder,\n itemActionContext,\n moveIndexedState,\n moveRepeaterItem,\n removeIndexedState,\n removeRepeaterItem,\n reorderedItemKeys,\n VIRTUAL_ESTIMATES,\n virtualizeOptions,\n} from '@alfakit/core'\nimport { EllipsisIcon, PencilIcon } from 'lucide-react'\nimport { createElement, useState, type KeyboardEvent } from 'react'\nimport { useActionRunner, useFormatter, useOptionalRegistry, useT } from '../../context'\nimport { useScrollToErroredItem, useVirtualList } from '../../hooks/use-virtual-list'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { useField, useOptionalFormStore } from '../form/form-context'\nimport { DeferredSchema } from '../layout/deferred'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport { FieldWrapper } from '../schema/field-wrapper'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { Button } from '../ui/button'\nimport { Collapsible, CollapsibleContent } from '../ui/collapsible'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { BuilderBlockPicker, BuilderBlockPickerPanel } from './builder-picker'\nimport {\n AddActionRow,\n CollapseAllActions,\n ItemHeaderActions,\n useItemActionFallbacks,\n useItemActions,\n useItemMoves,\n useRunItemAction,\n} from './item-actions'\n\n/**\n * A list of blocks, each with its own schema. Items use the server's per-item schema\n * when it describes the same block, else the block's template (fresh, moved or cloned\n * items the server has not seen yet).\n */\n/**\n * `deferLoading()`: a skeleton until the field scrolls into view, then the field with its\n * items (loaded from the server).\n */\nexport function BuilderField({ component }: { component: BuilderSchema }) {\n if (!isDeferredPlaceholder(component)) return <BuilderFieldContent component={component} />\n\n return (\n <DeferredSchema component={component}>\n {(loaded) => <BuilderFieldContent component={loaded} />}\n </DeferredSchema>\n )\n}\n\nfunction BuilderFieldContent({ component }: { component: BuilderSchema }) {\n const t = useT()\n const formatter = useFormatter()\n const registry = useOptionalRegistry()\n const form = useOptionalFormStore()\n const { value, setValue, error, id, disabled, readOnly, aria } = useField<unknown>(component)\n const locked = disabled || readOnly\n const items = builderItems(value)\n const [collapsed, setCollapsed] = useState<Record<number, boolean>>({})\n const [editing, setEditing] = useState<Record<number, boolean>>({})\n const [dragIndex, setDragIndex] = useState<number | null>(null)\n const context = useFieldActionContext(component)\n const runner = useActionRunner()\n const runItemAction = useRunItemAction(context)\n const fallbacks = useItemActionFallbacks(component.addActionLabel, 'builder')\n const resolve = useItemActions(component, fallbacks)\n // Long lists render only the visible blocks (plus a few around them).\n const virtual = useVirtualList(\n items.length,\n virtualizeOptions(component.virtualize, VIRTUAL_ESTIMATES.item, 12),\n )\n const { range, indexes, style, attach, item: attachItem } = virtual\n useScrollToErroredItem(virtual.list, range.active, component.statePath)\n\n const update = (next: unknown[]) => setValue(next as BuilderItemState[])\n const moveItem = (from: number, to: number) => {\n update(moveRepeaterItem(items, from, to))\n setCollapsed((state) => moveIndexedState(state, from, to))\n setEditing((state) => moveIndexedState(state, from, to))\n }\n const removeItem = (index: number) => {\n update(removeRepeaterItem(items, index))\n setCollapsed((state) => removeIndexedState(state, index))\n setEditing((state) => removeIndexedState(state, index))\n }\n const cloneItem = (index: number) => {\n update(cloneRepeaterItem(items, index))\n setCollapsed((state) => cloneIndexedState(state, index))\n setEditing((state) => cloneIndexedState(state, index))\n }\n const addBlock = (block: BuilderBlockSchema) =>\n runItemAction(resolve('add'), { block: block.name }, () =>\n update(insertBuilderItem(items, block)),\n )\n // Move buttons: announced, one at a time, focus kept on the moved item.\n const {\n moveWithButton,\n region: moveRegion,\n containerRef: moveContainerRef,\n } = useItemMoves(\n value,\n moveItem,\n (index) => builderItemLabel(component, items, index),\n items.length,\n )\n const dropItem = (from: number, to: number) =>\n runItemAction(resolve('reorder'), { items: reorderedItemKeys(items.length, from, to) }, () =>\n moveItem(from, to),\n )\n // Closing the editor re-renders the previews from the server.\n const toggleEditing = (index: number) => {\n const open = !editing[index]\n setEditing((state) => ({ ...state, [index]: open }))\n if (!open) void form?.refresh(component.statePath)\n }\n\n const blockOf = (type: string) => component.blocks.find((block) => block.name === type)\n // Summaries are made on the server: collapsing items whose block has one brings them up\n // to date with the edits.\n const refreshSummaries = (indexes: number[]) => {\n if (indexes.some((index) => blockOf(items[index]?.type ?? '')?.summary)) {\n void form?.refresh(component.statePath)\n }\n }\n const underMax = component.maxItems == null || items.length < component.maxItems\n const canAdd = component.addable && !locked && underMax\n const canDelete =\n component.deletable &&\n !locked &&\n (component.minItems == null || items.length > component.minItems)\n const draggable =\n component.reorderable &&\n component.reorderableWithDragAndDrop !== false &&\n !locked &&\n resolve('reorder') !== null\n const buttons = component.reorderable && component.reorderableWithButtons !== false && !locked\n const addAction = resolve('add')\n const showEmptyPicker =\n canAdd && addAction !== null && !addAction.disabled && component.blocks.length > 0\n\n return (\n <FieldWrapper field={component} htmlFor={id} error={error}>\n <div ref={moveContainerRef} className=\"flex flex-col gap-3\" data-slot=\"builder\">\n {moveRegion}\n {items.length === 0 &&\n (showEmptyPicker ? (\n // No blocks yet: the picker is right there instead of behind a button.\n <div\n data-slot=\"builder-empty\"\n className=\"bg-muted/30 flex flex-col gap-3 rounded-lg border border-dashed p-3 sm:p-4\"\n >\n <div className=\"px-1\">\n <p className=\"text-sm font-medium\">\n {t('builder.empty_heading', {}, 'Add your first block')}\n </p>\n <p className=\"text-muted-foreground text-sm\">\n {t(\n 'builder.empty_description',\n {},\n 'Pick one to start. You can add more, reorder and remove them at any time.',\n )}\n </p>\n </div>\n <BuilderBlockPickerPanel\n component={component}\n items={items}\n onPick={addBlock}\n className=\"bg-card max-h-[28rem] rounded-md border\"\n />\n </div>\n ) : (\n <p className=\"text-muted-foreground rounded-lg border border-dashed p-4 text-center text-sm\">\n {t('builder.empty', {}, 'No blocks yet.')}\n </p>\n ))}\n {component.collapsible && items.length > 0 && (\n <CollapseAllActions\n context={context}\n resolve={(name) => resolve(name)}\n onCollapseAll={() => {\n setCollapsed(Object.fromEntries(items.map((_, index) => [index, true])))\n refreshSummaries(items.map((_, index) => index))\n }}\n onExpandAll={() =>\n setCollapsed(Object.fromEntries(items.map((_, index) => [index, false])))\n }\n />\n )}\n <ul\n id={id}\n aria-labelledby={component.label ? fieldIds(id).label : undefined}\n aria-describedby={aria['aria-describedby']}\n ref={attach}\n style={style}\n className={cn('grid gap-3', items.length === 0 && 'hidden')}\n >\n {indexes.map((index) => {\n const item = items[index] as BuilderItemState\n const block = blockOf(item.type)\n const isCollapsed = collapsed[index] ?? component.collapsed ?? false\n const label = component.blockLabels ? builderItemLabel(component, items, index) : null\n const icon = block?.icon ?? component.items[index]?.icon\n const server = component.items[index]\n const serverItem = server?.type === item.type ? server : undefined\n const previewName = serverItem?.previewComponent ?? block?.previewComponent\n const Preview = previewName\n ? registry?.resolve(`builder-preview:${previewName}`)\n : undefined\n const hasPreview =\n component.blockPreviews === true &&\n (Preview !== undefined || Boolean(serverItem?.preview))\n const showPreview = hasPreview && !editing[index]\n const summary = serverItem?.summary\n const menu = Boolean(component.blockActionsMenu) && !locked\n const canClone =\n Boolean(component.cloneable) &&\n canAdd &&\n block !== undefined &&\n canAddBuilderBlock(component, block, items)\n const openEditor = () => {\n if (!locked) toggleEditing(index)\n }\n\n return (\n <li\n key={index}\n ref={attachItem}\n data-index={index}\n aria-setsize={range.active ? items.length : undefined}\n aria-posinset={range.active ? index + 1 : undefined}\n data-block={item.type}\n draggable={draggable}\n onDragStart={() => setDragIndex(index)}\n onDragOver={(event) => draggable && event.preventDefault()}\n onDrop={() => {\n if (dragIndex !== null && dragIndex !== index) dropItem(dragIndex, index)\n setDragIndex(null)\n }}\n onDragEnd={() => setDragIndex(null)}\n className={cn(\n 'bg-card min-w-0 rounded-lg border shadow-xs',\n dragIndex === index && 'opacity-50',\n )}\n >\n <Collapsible open={!isCollapsed}>\n <div\n className=\"flex flex-wrap items-center gap-1 border-b px-2 py-1.5\"\n data-slot=\"item-header\"\n >\n {draggable && (\n <span\n className=\"text-muted-foreground cursor-grab\"\n title={resolve('reorder')?.label ?? undefined}\n aria-hidden=\"true\"\n >\n <Icon name={resolve('reorder')?.icon ?? 'grip-vertical'} />\n </span>\n )}\n {component.blockIcons && icon && (\n <Icon name={icon} className=\"text-muted-foreground size-4\" />\n )}\n {component.blockNumbers && (\n <span className=\"text-muted-foreground text-xs tabular-nums\">\n {formatter.number(index + 1)}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 items-baseline gap-2\">\n {label && (\n <span\n dir=\"auto\"\n className=\"min-w-0 shrink-0 truncate text-sm font-medium sm:max-w-[60%]\"\n >\n {label}\n </span>\n )}\n {summary && (\n <span\n dir=\"auto\"\n className=\"text-muted-foreground min-w-0 truncate text-sm\"\n data-slot=\"builder-item-summary\"\n >\n {summary}\n </span>\n )}\n </span>\n <ItemHeaderActions\n context={context}\n itemKey={String(index)}\n index={index}\n count={items.length}\n resolve={(name) => resolve(name, serverItem)}\n extraActions={menu || locked ? undefined : serverItem?.actions}\n buttons={buttons && !menu}\n canClone={canClone && !menu}\n canDelete={canDelete && !menu}\n collapsible={Boolean(component.collapsible)}\n collapsed={isCollapsed}\n onMove={moveWithButton}\n onClone={() => cloneItem(index)}\n onDelete={() => removeItem(index)}\n onToggle={() => {\n setCollapsed((state) => ({ ...state, [index]: !isCollapsed }))\n if (!isCollapsed) refreshSummaries([index])\n }}\n >\n {hasPreview && !locked && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={t('builder.edit_block', {}, 'Edit block')}\n aria-pressed={Boolean(editing[index])}\n onClick={() => toggleEditing(index)}\n >\n <Icon\n name=\"alfakit::forms.components.builder.actions.edit\"\n fallback={PencilIcon}\n />\n </Button>\n )}\n {menu && (\n <BlockActionsMenu\n label={label ?? ''}\n resolve={(name) => resolve(name, serverItem)}\n canMoveUp={buttons && index > 0}\n canMoveDown={buttons && index < items.length - 1}\n canClone={canClone}\n canDelete={canDelete}\n extraActions={locked ? undefined : serverItem?.actions}\n onAction={(name, onClient) =>\n runItemAction(\n resolve(name, serverItem),\n { item: String(index) },\n onClient,\n )\n }\n onExtraAction={(action) =>\n void runner.trigger(\n action,\n itemActionContext(context, { item: String(index) }),\n )\n }\n onMove={(to) => moveWithButton(index, to)}\n index={index}\n onClone={() => cloneItem(index)}\n onDelete={() => removeItem(index)}\n />\n )}\n </ItemHeaderActions>\n </div>\n <CollapsibleContent>\n {showPreview ? (\n <div\n data-slot=\"builder-preview\"\n className={cn(\n 'p-4',\n !component.interactivePreviews && !locked && 'cursor-pointer',\n )}\n {...(component.interactivePreviews || locked\n ? {}\n : {\n role: 'button',\n tabIndex: 0,\n 'aria-label': t('builder.edit_block', {}, 'Edit block'),\n onClick: openEditor,\n onKeyDown: (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n openEditor()\n }\n },\n })}\n >\n <div\n className={cn(!component.interactivePreviews && 'pointer-events-none')}\n >\n {Preview ? (\n createElement(Preview, { data: item.data, block: item.type, index })\n ) : (\n <div dangerouslySetInnerHTML={{ __html: serverItem?.preview ?? '' }} />\n )}\n </div>\n </div>\n ) : (\n <div className=\"p-4\">\n <SchemaRenderer\n components={builderItemSchema(component, items, index)}\n columns={\n block?.columns ?? component.items[index]?.columns ?? { default: 1 }\n }\n />\n </div>\n )}\n </CollapsibleContent>\n </Collapsible>\n </li>\n )\n })}\n </ul>\n {canAdd && addAction && component.blocks.length > 0 && items.length > 0 && (\n <AddActionRow alignment={component.addActionAlignment}>\n <BuilderBlockPicker\n component={component}\n items={items}\n onPick={addBlock}\n trigger={\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={addAction.disabled}\n data-slot=\"builder-add\"\n >\n {addAction.icon && <Icon name={addAction.icon} />}\n {addAction.label}\n </Button>\n }\n />\n </AddActionRow>\n )}\n </div>\n </FieldWrapper>\n )\n}\n\n/**\n * `blockActionsMenu()`: an item's actions in one compact menu (move up / down, duplicate,\n * extra item actions, delete). Built-in actions run in the browser unless customised.\n */\nfunction BlockActionsMenu({\n label,\n resolve,\n canMoveUp,\n canMoveDown,\n canClone,\n canDelete,\n extraActions,\n index,\n onAction,\n onExtraAction,\n onMove,\n onClone,\n onDelete,\n}: {\n label: string\n resolve: (name: ItemActionName) => ItemActionSchema | null\n canMoveUp: boolean\n canMoveDown: boolean\n canClone: boolean\n canDelete: boolean\n extraActions?: ActionSchema[]\n index: number\n onAction: (name: ItemActionName, onClient: () => void) => void\n onExtraAction: (action: ActionSchema) => void\n onMove: (to: number) => void\n onClone: () => void\n onDelete: () => void\n}) {\n const t = useT()\n const entry = (\n name: ItemActionName,\n enabled: boolean,\n onClient: () => void,\n destructive = false,\n ) => {\n const action = resolve(name)\n\n if (!action || !enabled) return null\n\n return (\n <DropdownMenuItem\n key={name}\n variant={destructive ? 'destructive' : 'default'}\n disabled={action.disabled}\n onSelect={() => onAction(name, onClient)}\n >\n {action.icon && <Icon name={action.icon} />}\n {name === 'clone' ? t('builder.duplicate_block', {}, 'Duplicate') : action.label}\n </DropdownMenuItem>\n )\n }\n const visibleExtras = extraActions ?? []\n const hasMain = canMoveUp || canMoveDown || canClone || visibleExtras.length > 0\n\n if (!hasMain && !canDelete) return null\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n data-slot=\"builder-item-menu\"\n aria-label={t('builder.block_actions', { block: label }, 'Actions for :block')}\n >\n <EllipsisIcon aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-44\">\n {entry('moveUp', canMoveUp, () => onMove(index - 1))}\n {entry('moveDown', canMoveDown, () => onMove(index + 1))}\n {entry('clone', canClone, onClone)}\n {visibleExtras.map((action) => (\n <DropdownMenuItem\n key={action.name}\n disabled={action.disabled}\n onSelect={() => onExtraAction(action)}\n >\n {action.icon && <Icon name={action.icon} />}\n {action.label}\n </DropdownMenuItem>\n ))}\n {canDelete && hasMain && <DropdownMenuSeparator />}\n {entry('delete', canDelete, onDelete, true)}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"builder.js","names":[],"sources":["../../../src/components/fields/builder.tsx"],"sourcesContent":["import type {\n ActionSchema,\n BuilderBlockSchema,\n BuilderItemState,\n BuilderSchema,\n ItemActionName,\n ItemActionSchema,\n} from '@alfakit/core'\nimport {\n builderItemLabel,\n builderItems,\n builderItemSchema,\n canAddBuilderBlock,\n cloneIndexedState,\n cloneRepeaterItem,\n fieldIds,\n insertBuilderItem,\n isDeferredPlaceholder,\n itemActionContext,\n itemExtraActions,\n moveIndexedState,\n moveRepeaterItem,\n removeIndexedState,\n removeRepeaterItem,\n reorderedItemKeys,\n VIRTUAL_ESTIMATES,\n virtualizeOptions,\n} from '@alfakit/core'\nimport { EllipsisIcon, PencilIcon } from 'lucide-react'\nimport { createElement, useState, type KeyboardEvent } from 'react'\nimport { useActionRunner, useFormatter, useOptionalRegistry, useT } from '../../context'\nimport { useScrollToErroredItem, useVirtualList } from '../../hooks/use-virtual-list'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { useField, useOptionalFormStore } from '../form/form-context'\nimport { DeferredSchema } from '../layout/deferred'\nimport { useFieldActionContext } from '../schema/field-actions'\nimport { FieldWrapper } from '../schema/field-wrapper'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { Button } from '../ui/button'\nimport { Collapsible, CollapsibleContent } from '../ui/collapsible'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { BuilderBlockPicker, BuilderBlockPickerPanel } from './builder-picker'\nimport {\n AddActionRow,\n CollapseAllActions,\n ItemHeaderActions,\n useItemActionFallbacks,\n useItemActions,\n useItemMoves,\n useRunItemAction,\n} from './item-actions'\n\n/**\n * A list of blocks, each with its own schema. Items use the server's per-item schema\n * when it describes the same block, else the block's template (fresh, moved or cloned\n * items the server has not seen yet).\n */\n/**\n * `deferLoading()`: a skeleton until the field scrolls into view, then the field with its\n * items (loaded from the server).\n */\nexport function BuilderField({ component }: { component: BuilderSchema }) {\n if (!isDeferredPlaceholder(component)) return <BuilderFieldContent component={component} />\n\n return (\n <DeferredSchema component={component}>\n {(loaded) => <BuilderFieldContent component={loaded} />}\n </DeferredSchema>\n )\n}\n\nfunction BuilderFieldContent({ component }: { component: BuilderSchema }) {\n const t = useT()\n const formatter = useFormatter()\n const registry = useOptionalRegistry()\n const form = useOptionalFormStore()\n const { value, setValue, error, id, disabled, readOnly, aria } = useField<unknown>(component)\n const locked = disabled || readOnly\n const items = builderItems(value)\n const [collapsed, setCollapsed] = useState<Record<number, boolean>>({})\n const [editing, setEditing] = useState<Record<number, boolean>>({})\n const [dragIndex, setDragIndex] = useState<number | null>(null)\n const context = useFieldActionContext(component)\n const runner = useActionRunner()\n const runItemAction = useRunItemAction(context)\n const fallbacks = useItemActionFallbacks(component.addActionLabel, 'builder')\n const resolve = useItemActions(component, fallbacks)\n // Long lists render only the visible blocks (plus a few around them).\n const virtual = useVirtualList(\n items.length,\n virtualizeOptions(component.virtualize, VIRTUAL_ESTIMATES.item, 12),\n )\n const { range, indexes, style, attach, item: attachItem } = virtual\n useScrollToErroredItem(virtual.list, range.active, component.statePath)\n\n const update = (next: unknown[]) => setValue(next as BuilderItemState[])\n const moveItem = (from: number, to: number) => {\n update(moveRepeaterItem(items, from, to))\n setCollapsed((state) => moveIndexedState(state, from, to))\n setEditing((state) => moveIndexedState(state, from, to))\n }\n const removeItem = (index: number) => {\n update(removeRepeaterItem(items, index))\n setCollapsed((state) => removeIndexedState(state, index))\n setEditing((state) => removeIndexedState(state, index))\n }\n const cloneItem = (index: number) => {\n update(cloneRepeaterItem(items, index))\n setCollapsed((state) => cloneIndexedState(state, index))\n setEditing((state) => cloneIndexedState(state, index))\n }\n const addBlock = (block: BuilderBlockSchema) =>\n runItemAction(resolve('add'), { block: block.name }, () =>\n update(insertBuilderItem(items, block)),\n )\n // Move buttons: announced, one at a time, focus kept on the moved item.\n const {\n moveWithButton,\n region: moveRegion,\n containerRef: moveContainerRef,\n } = useItemMoves(\n value,\n moveItem,\n (index) => builderItemLabel(component, items, index),\n items.length,\n )\n const dropItem = (from: number, to: number) =>\n runItemAction(resolve('reorder'), { items: reorderedItemKeys(items.length, from, to) }, () =>\n moveItem(from, to),\n )\n // Closing the editor re-renders the previews from the server.\n const toggleEditing = (index: number) => {\n const open = !editing[index]\n setEditing((state) => ({ ...state, [index]: open }))\n if (!open) void form?.refresh(component.statePath)\n }\n\n const blockOf = (type: string) => component.blocks.find((block) => block.name === type)\n // Summaries are made on the server: collapsing items whose block has one brings them up\n // to date with the edits.\n const refreshSummaries = (indexes: number[]) => {\n if (indexes.some((index) => blockOf(items[index]?.type ?? '')?.summary)) {\n void form?.refresh(component.statePath)\n }\n }\n const underMax = component.maxItems == null || items.length < component.maxItems\n const canAdd = component.addable && !locked && underMax\n const canDelete =\n component.deletable &&\n !locked &&\n (component.minItems == null || items.length > component.minItems)\n const draggable =\n component.reorderable &&\n component.reorderableWithDragAndDrop !== false &&\n !locked &&\n resolve('reorder') !== null\n const buttons = component.reorderable && component.reorderableWithButtons !== false && !locked\n const addAction = resolve('add')\n const showEmptyPicker =\n canAdd && addAction !== null && !addAction.disabled && component.blocks.length > 0\n\n return (\n <FieldWrapper field={component} htmlFor={id} error={error}>\n <div ref={moveContainerRef} className=\"flex flex-col gap-3\" data-slot=\"builder\">\n {moveRegion}\n {items.length === 0 &&\n (showEmptyPicker ? (\n // No blocks yet: the picker is right there instead of behind a button.\n <div\n data-slot=\"builder-empty\"\n className=\"bg-muted/30 flex flex-col gap-3 rounded-lg border border-dashed p-3 sm:p-4\"\n >\n <div className=\"px-1\">\n <p className=\"text-sm font-medium\">\n {t('builder.empty_heading', {}, 'Add your first block')}\n </p>\n <p className=\"text-muted-foreground text-sm\">\n {t(\n 'builder.empty_description',\n {},\n 'Pick one to start. You can add more, reorder and remove them at any time.',\n )}\n </p>\n </div>\n <BuilderBlockPickerPanel\n component={component}\n items={items}\n onPick={addBlock}\n className=\"bg-card max-h-[28rem] rounded-md border\"\n />\n </div>\n ) : (\n <p className=\"text-muted-foreground rounded-lg border border-dashed p-4 text-center text-sm\">\n {t('builder.empty', {}, 'No blocks yet.')}\n </p>\n ))}\n {component.collapsible && items.length > 0 && (\n <CollapseAllActions\n context={context}\n resolve={(name) => resolve(name)}\n onCollapseAll={() => {\n setCollapsed(Object.fromEntries(items.map((_, index) => [index, true])))\n refreshSummaries(items.map((_, index) => index))\n }}\n onExpandAll={() =>\n setCollapsed(Object.fromEntries(items.map((_, index) => [index, false])))\n }\n />\n )}\n <ul\n id={id}\n aria-labelledby={component.label ? fieldIds(id).label : undefined}\n aria-describedby={aria['aria-describedby']}\n ref={attach}\n style={style}\n className={cn('grid gap-3', items.length === 0 && 'hidden')}\n >\n {indexes.map((index) => {\n const item = items[index] as BuilderItemState\n const block = blockOf(item.type)\n const isCollapsed = collapsed[index] ?? component.collapsed ?? false\n const label = component.blockLabels ? builderItemLabel(component, items, index) : null\n const icon = block?.icon ?? component.items[index]?.icon\n const server = component.items[index]\n const serverItem = server?.type === item.type ? server : undefined\n const previewName = serverItem?.previewComponent ?? block?.previewComponent\n const Preview = previewName\n ? registry?.resolve(`builder-preview:${previewName}`)\n : undefined\n const hasPreview =\n component.blockPreviews === true &&\n (Preview !== undefined || Boolean(serverItem?.preview))\n const showPreview = hasPreview && !editing[index]\n const summary = serverItem?.summary\n const menu = Boolean(component.blockActionsMenu) && !locked\n const canClone =\n Boolean(component.cloneable) &&\n canAdd &&\n block !== undefined &&\n canAddBuilderBlock(component, block, items)\n const openEditor = () => {\n if (!locked) toggleEditing(index)\n }\n\n return (\n <li\n key={index}\n ref={attachItem}\n data-index={index}\n aria-setsize={range.active ? items.length : undefined}\n aria-posinset={range.active ? index + 1 : undefined}\n data-block={item.type}\n draggable={draggable}\n onDragStart={() => setDragIndex(index)}\n onDragOver={(event) => draggable && event.preventDefault()}\n onDrop={() => {\n if (dragIndex !== null && dragIndex !== index) dropItem(dragIndex, index)\n setDragIndex(null)\n }}\n onDragEnd={() => setDragIndex(null)}\n className={cn(\n 'bg-card min-w-0 rounded-lg border shadow-xs',\n dragIndex === index && 'opacity-50',\n )}\n >\n <Collapsible open={!isCollapsed}>\n <div\n className=\"flex flex-wrap items-center gap-1 border-b px-2 py-1.5\"\n data-slot=\"item-header\"\n >\n {draggable && (\n <span\n className=\"text-muted-foreground cursor-grab\"\n title={resolve('reorder')?.label ?? undefined}\n aria-hidden=\"true\"\n >\n <Icon name={resolve('reorder')?.icon ?? 'grip-vertical'} />\n </span>\n )}\n {component.blockIcons && icon && (\n <Icon name={icon} className=\"text-muted-foreground size-4\" />\n )}\n {component.blockNumbers && (\n <span className=\"text-muted-foreground text-xs tabular-nums\">\n {formatter.number(index + 1)}\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 items-baseline gap-2\">\n {label && (\n <span\n dir=\"auto\"\n className=\"min-w-0 shrink-0 truncate text-sm font-medium sm:max-w-[60%]\"\n >\n {label}\n </span>\n )}\n {summary && (\n <span\n dir=\"auto\"\n className=\"text-muted-foreground min-w-0 truncate text-sm\"\n data-slot=\"builder-item-summary\"\n >\n {summary}\n </span>\n )}\n </span>\n <ItemHeaderActions\n context={context}\n itemKey={String(index)}\n index={index}\n count={items.length}\n resolve={(name) => resolve(name, serverItem)}\n extraActions={\n menu || locked ? undefined : itemExtraActions(component, serverItem)\n }\n buttons={buttons && !menu}\n canClone={canClone && !menu}\n canDelete={canDelete && !menu}\n collapsible={Boolean(component.collapsible)}\n collapsed={isCollapsed}\n onMove={moveWithButton}\n onClone={() => cloneItem(index)}\n onDelete={() => removeItem(index)}\n onToggle={() => {\n setCollapsed((state) => ({ ...state, [index]: !isCollapsed }))\n if (!isCollapsed) refreshSummaries([index])\n }}\n >\n {hasPreview && !locked && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={t('builder.edit_block', {}, 'Edit block')}\n aria-pressed={Boolean(editing[index])}\n onClick={() => toggleEditing(index)}\n >\n <Icon\n name=\"alfakit::forms.components.builder.actions.edit\"\n fallback={PencilIcon}\n />\n </Button>\n )}\n {menu && (\n <BlockActionsMenu\n label={label ?? ''}\n resolve={(name) => resolve(name, serverItem)}\n canMoveUp={buttons && index > 0}\n canMoveDown={buttons && index < items.length - 1}\n canClone={canClone}\n canDelete={canDelete}\n extraActions={\n locked ? undefined : itemExtraActions(component, serverItem)\n }\n onAction={(name, onClient) =>\n runItemAction(\n resolve(name, serverItem),\n { item: String(index) },\n onClient,\n )\n }\n onExtraAction={(action) =>\n void runner.trigger(\n action,\n itemActionContext(context, { item: String(index) }),\n )\n }\n onMove={(to) => moveWithButton(index, to)}\n index={index}\n onClone={() => cloneItem(index)}\n onDelete={() => removeItem(index)}\n />\n )}\n </ItemHeaderActions>\n </div>\n <CollapsibleContent>\n {showPreview ? (\n <div\n data-slot=\"builder-preview\"\n className={cn(\n 'p-4',\n !component.interactivePreviews && !locked && 'cursor-pointer',\n )}\n {...(component.interactivePreviews || locked\n ? {}\n : {\n role: 'button',\n tabIndex: 0,\n 'aria-label': t('builder.edit_block', {}, 'Edit block'),\n onClick: openEditor,\n onKeyDown: (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n openEditor()\n }\n },\n })}\n >\n <div\n className={cn(!component.interactivePreviews && 'pointer-events-none')}\n >\n {Preview ? (\n createElement(Preview, { data: item.data, block: item.type, index })\n ) : (\n <div dangerouslySetInnerHTML={{ __html: serverItem?.preview ?? '' }} />\n )}\n </div>\n </div>\n ) : (\n <div className=\"p-4\">\n <SchemaRenderer\n components={builderItemSchema(component, items, index)}\n columns={\n block?.columns ?? component.items[index]?.columns ?? { default: 1 }\n }\n />\n </div>\n )}\n </CollapsibleContent>\n </Collapsible>\n </li>\n )\n })}\n </ul>\n {canAdd && addAction && component.blocks.length > 0 && items.length > 0 && (\n <AddActionRow alignment={component.addActionAlignment}>\n <BuilderBlockPicker\n component={component}\n items={items}\n onPick={addBlock}\n trigger={\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={addAction.disabled}\n data-slot=\"builder-add\"\n >\n {addAction.icon && <Icon name={addAction.icon} />}\n {addAction.label}\n </Button>\n }\n />\n </AddActionRow>\n )}\n </div>\n </FieldWrapper>\n )\n}\n\n/**\n * `blockActionsMenu()`: an item's actions in one compact menu (move up / down, duplicate,\n * extra item actions, delete). Built-in actions run in the browser unless customised.\n */\nfunction BlockActionsMenu({\n label,\n resolve,\n canMoveUp,\n canMoveDown,\n canClone,\n canDelete,\n extraActions,\n index,\n onAction,\n onExtraAction,\n onMove,\n onClone,\n onDelete,\n}: {\n label: string\n resolve: (name: ItemActionName) => ItemActionSchema | null\n canMoveUp: boolean\n canMoveDown: boolean\n canClone: boolean\n canDelete: boolean\n extraActions?: ActionSchema[]\n index: number\n onAction: (name: ItemActionName, onClient: () => void) => void\n onExtraAction: (action: ActionSchema) => void\n onMove: (to: number) => void\n onClone: () => void\n onDelete: () => void\n}) {\n const t = useT()\n const entry = (\n name: ItemActionName,\n enabled: boolean,\n onClient: () => void,\n destructive = false,\n ) => {\n const action = resolve(name)\n\n if (!action || !enabled) return null\n\n return (\n <DropdownMenuItem\n key={name}\n variant={destructive ? 'destructive' : 'default'}\n disabled={action.disabled}\n onSelect={() => onAction(name, onClient)}\n >\n {action.icon && <Icon name={action.icon} />}\n {name === 'clone' ? t('builder.duplicate_block', {}, 'Duplicate') : action.label}\n </DropdownMenuItem>\n )\n }\n const visibleExtras = extraActions ?? []\n const hasMain = canMoveUp || canMoveDown || canClone || visibleExtras.length > 0\n\n if (!hasMain && !canDelete) return null\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n data-slot=\"builder-item-menu\"\n aria-label={t('builder.block_actions', { block: label }, 'Actions for :block')}\n >\n <EllipsisIcon aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-44\">\n {entry('moveUp', canMoveUp, () => onMove(index - 1))}\n {entry('moveDown', canMoveDown, () => onMove(index + 1))}\n {entry('clone', canClone, onClone)}\n {visibleExtras.map((action) => (\n <DropdownMenuItem\n key={action.name}\n disabled={action.disabled}\n onSelect={() => onExtraAction(action)}\n >\n {action.icon && <Icon name={action.icon} />}\n {action.label}\n </DropdownMenuItem>\n ))}\n {canDelete && hasMain && <DropdownMenuSeparator />}\n {entry('delete', canDelete, onDelete, true)}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-upload.d.ts","names":[],"sources":["../../../src/components/fields/file-upload.tsx"],"mappings":";;wBA+CgB,kBAAkB;EAAe,WAAW;oBAAkB,IAAA"}
1
+ {"version":3,"file":"file-upload.d.ts","names":[],"sources":["../../../src/components/fields/file-upload.tsx"],"mappings":";;wBAgDgB,kBAAkB;EAAe,WAAW;oBAAkB,IAAA"}
@@ -6,7 +6,7 @@ import { Spinner } from "../ui/spinner.js";
6
6
  import { useField, useFormStore } from "../form/form-context.js";
7
7
  import { FieldWrapper } from "../schema/field-wrapper.js";
8
8
  import { ImageEditorDialog } from "./image-editor-dialog-lazy.js";
9
- import { canEditImage, concurrencyLimiter, cssAspectRatio, editableImageName, fetchEditableImage, fieldMemory, fileManagerSize, fileUploadAccept, fileUploadPaths, filesFromClipboard, insertUploadedFile, moveRepeaterItem, panelUploadOptions, requestJson, transformImage, uploadTemporaryFile, uploadedFileLinks } from "@alfakit/core";
9
+ import { canEditImage, concurrencyLimiter, cssAspectRatio, editableImageName, fetchEditableImage, fieldMemory, fileManagerSize, fileUploadAccept, fileUploadEntries, fileUploadPaths, filesFromClipboard, insertUploadedFile, moveRepeaterItem, panelUploadOptions, requestJson, transformImage, uploadTemporaryFile, uploadedFileLinks } from "@alfakit/core";
10
10
  import { useRef, useState } from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { ChevronDownIcon, ChevronUpIcon, DownloadIcon, ExternalLinkIcon, FileIcon, PencilIcon, UploadCloudIcon, XIcon } from "lucide-react";
@@ -42,10 +42,7 @@ function FileUploadField({ component }) {
42
42
  if (url && objectUrls.delete(url)) URL.revokeObjectURL(url);
43
43
  };
44
44
  const paths = fileUploadPaths(value);
45
- const files = paths.map((path) => [...component.files, ...uploaded].find((file) => file.path === path) ?? {
46
- path,
47
- name: path.replace(/^tmp:/, "")
48
- });
45
+ const files = fileUploadEntries(paths, component.files, uploaded);
49
46
  const maxFiles = component.multiple ? component.maxFiles ?? Infinity : 1;
50
47
  const canAddMore = files.length + uploading.length < maxFiles;
51
48
  const commit = (next) => setValue(component.multiple ? next : next[0] ?? null);