@alfakit/react 0.1.7 → 0.1.9

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 (54) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/components/actions/action-modals.js +1 -1
  3. package/dist/components/actions/action-modals.js.map +1 -1
  4. package/dist/components/form/form-block.d.ts.map +1 -1
  5. package/dist/components/form/form-block.js +6 -3
  6. package/dist/components/form/form-block.js.map +1 -1
  7. package/dist/components/panel/app-sidebar.js +3 -3
  8. package/dist/components/panel/app-sidebar.js.map +1 -1
  9. package/dist/components/panel/bottom-nav.js +2 -2
  10. package/dist/components/panel/bottom-nav.js.map +1 -1
  11. package/dist/components/panel/keyboard-shortcuts.js +1 -1
  12. package/dist/components/panel/keyboard-shortcuts.js.map +1 -1
  13. package/dist/components/panel/locale-switcher.d.ts.map +1 -1
  14. package/dist/components/panel/locale-switcher.js +2 -1
  15. package/dist/components/panel/locale-switcher.js.map +1 -1
  16. package/dist/components/ui/tooltip.d.ts.map +1 -1
  17. package/dist/components/ui/tooltip.js +1 -1
  18. package/dist/components/ui/tooltip.js.map +1 -1
  19. package/dist/eject-manifest.json +4 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +2 -2
  22. package/dist/layouts/top-nav-layout.js +3 -3
  23. package/dist/layouts/top-nav-layout.js.map +1 -1
  24. package/dist/standalone/alfakit-component.js +17 -7
  25. package/dist/standalone/alfakit-component.js.map +1 -1
  26. package/dist/standalone.css +10 -6
  27. package/dist/starter-kit/index.d.ts +2 -2
  28. package/dist/starter-kit/index.js +2 -2
  29. package/dist/starter-kit/starter-kit.d.ts.map +1 -1
  30. package/dist/starter-kit/starter-kit.js +7 -2
  31. package/dist/starter-kit/starter-kit.js.map +1 -1
  32. package/dist/starter-kit/widgets-loaded.js +74 -13
  33. package/dist/starter-kit/widgets-loaded.js.map +1 -1
  34. package/dist/starter-kit/widgets-triggers.js +34 -3
  35. package/dist/starter-kit/widgets-triggers.js.map +1 -1
  36. package/dist/starter-kit/widgets.d.ts +11 -2
  37. package/dist/starter-kit/widgets.d.ts.map +1 -1
  38. package/dist/starter-kit/widgets.js +20 -2
  39. package/dist/starter-kit/widgets.js.map +1 -1
  40. package/package.json +2 -2
  41. package/src/components/actions/action-modals.tsx +2 -0
  42. package/src/components/form/form-block.tsx +15 -2
  43. package/src/components/panel/app-sidebar.tsx +3 -3
  44. package/src/components/panel/bottom-nav.tsx +2 -2
  45. package/src/components/panel/keyboard-shortcuts.tsx +1 -1
  46. package/src/components/panel/locale-switcher.tsx +2 -1
  47. package/src/components/ui/tooltip.tsx +10 -1
  48. package/src/layouts/top-nav-layout.tsx +3 -3
  49. package/src/standalone/alfakit-component.tsx +11 -1
  50. package/src/starter-kit/index.ts +1 -0
  51. package/src/starter-kit/starter-kit.tsx +9 -1
  52. package/src/starter-kit/widgets-loaded.tsx +86 -10
  53. package/src/starter-kit/widgets-triggers.tsx +41 -1
  54. package/src/starter-kit/widgets.tsx +33 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @alfakit/react
2
2
 
3
+ ## 0.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Row action modals work on every list tab; starter kits: the debug panel, "G then D" to the app dashboard and badge contrast; two standalone forms on a page get their own field ids; an `<AlfakitComponent>` in a panel page posts to its own endpoint; tooltips keep the button's `data-slot`; navigation items keyed by URL; modal headers leave room for the close button.
8
+ - Updated dependencies
9
+ - @alfakit/core@0.1.9
10
+
11
+ ## 0.1.8
12
+
13
+ ### Patch Changes
14
+
15
+ - `<AlfakitLanguageSwitcher />`: a drop-in header component for starter kits, so users can switch the panel's language from the app header. `LocaleOptions` is exported for custom switchers.
16
+ - Updated dependencies
17
+ - @alfakit/core@0.1.8
18
+
3
19
  ## 0.1.7
4
20
 
5
21
  ### Patch Changes
@@ -345,7 +345,7 @@ function ActionModal({ modal, hidden = false }) {
345
345
  onEscapeKeyDown: (event) => !schema.closeByEscaping && event.preventDefault(),
346
346
  children: [
347
347
  /* @__PURE__ */ jsx(DialogHeader, {
348
- className: cn(schema.stickyHeader && "bg-background sticky -top-6 z-10 -mx-6 -mt-6 rounded-t-lg border-b px-6 pt-6 pb-4"),
348
+ className: cn(schema.stickyHeader && "bg-background sticky -top-6 z-10 -mx-6 -mt-6 rounded-t-lg border-b px-6 pt-6 pb-4", schema.closeButton !== false && (schema.stickyHeader ? "pe-12" : "pe-6")),
349
349
  children: header
350
350
  }),
351
351
  (schema.content || form || schema.contentFooter || schema.passwordConfirmation) && body,
@@ -1 +1 @@
1
- {"version":3,"file":"action-modals.js","names":[],"sources":["../../../src/components/actions/action-modals.tsx"],"sourcesContent":["import type { FormState, OpenModal } from '@alfakit/core'\nimport {\n actionFormComponent,\n actionFormParams,\n actionPath,\n confirmModalClose,\n formIdPrefix,\n FormStore,\n guardModalUnload,\n isModalHiddenByChild,\n isModalSubmitVisible,\n modalFooterAlignment,\n nestedActionContext,\n PASSWORD_CONFIRMATION_INPUT,\n scheduleAutofocus,\n slideOverSide,\n wizardProgressStore,\n} from '@alfakit/core'\nimport { useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { useActionRunner, useDirection, usePanel, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconBackgroundColorClasses,\n outlineButtonColorClasses,\n solidButtonColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormProvider } from '../form/form-context'\nimport { showToast } from '../notifications/flash-toasts'\nimport { Slot } from '../panel/slot'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { Button } from '../ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../ui/dialog'\nimport { Input } from '../ui/input'\nimport { Label } from '../ui/label'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n} from '../ui/sheet'\nimport { Spinner } from '../ui/spinner'\nimport { ActionList } from './action-list'\n\nconst WIDTHS: Record<string, string> = {\n '3xs': 'sm:max-w-3xs',\n '2xs': 'sm:max-w-2xs',\n xs: 'sm:max-w-xs',\n sm: 'sm:max-w-sm',\n md: 'sm:max-w-md',\n lg: 'sm:max-w-lg',\n xl: 'sm:max-w-xl',\n '2xl': 'sm:max-w-2xl',\n '3xl': 'sm:max-w-3xl',\n '4xl': 'sm:max-w-4xl',\n '5xl': 'sm:max-w-5xl',\n '6xl': 'sm:max-w-6xl',\n '7xl': 'sm:max-w-7xl',\n none: 'sm:max-w-none',\n min: 'sm:max-w-min',\n max: 'sm:max-w-max',\n fit: 'sm:max-w-fit',\n prose: 'sm:max-w-prose',\n container: 'sm:container',\n 'screen-sm': 'sm:max-w-(--breakpoint-sm)',\n 'screen-md': 'sm:max-w-(--breakpoint-md)',\n 'screen-lg': 'sm:max-w-(--breakpoint-lg)',\n 'screen-xl': 'sm:max-w-(--breakpoint-xl)',\n 'screen-2xl': 'sm:max-w-(--breakpoint-2xl)',\n full: 'sm:max-w-[calc(100%-2rem)]',\n screen: 'sm:max-w-[calc(100%-2rem)] h-[calc(100%-2rem)]',\n}\n\n// Module-level so the snapshot stays stable (a new object per read re-renders forever).\nconst EMPTY_PROGRESS = { step: 0, count: 0 }\nconst NO_PROGRESS = { get: () => EMPTY_PROGRESS, subscribe: () => () => {} }\nconst NO_FORM = { get: () => null, subscribe: () => () => {} }\n\n/** Renders every open action modal from the runner store. Mount once per app. */\nexport function ActionModals() {\n const runner = useActionRunner()\n const { modals } = useStore(runner.store)\n\n // Notifications from JSON responses (a modal `mountUsing()` cancelled, field actions).\n useEffect(() => {\n runner.setNotificationHandler((notifications) => notifications.forEach((n) => showToast(n)))\n\n return () => runner.setNotificationHandler(undefined)\n }, [runner])\n\n return (\n <>\n {modals.map((modal, index) => (\n <ActionModal\n key={`${actionPath(modal.action, modal.context)}-${modal.context.record ?? ''}`}\n modal={modal}\n hidden={isModalHiddenByChild(modals, index)}\n />\n ))}\n </>\n )\n}\n\nconst FOOTER_JUSTIFY: Record<string, string> = {\n start: 'sm:justify-start',\n center: 'sm:justify-center',\n end: 'sm:justify-end',\n between: 'sm:justify-between',\n}\n\n/**\n * Put PHP attributes (`extraModalOverlayAttributes()`) on an element the renderer doesn't\n * create itself (the dialog's backdrop). `class` is added to the element's classes.\n */\nfunction applyAttributes(\n element: Element | null | undefined,\n attributes: Record<string, unknown> | undefined,\n) {\n if (!element || !attributes) return\n\n for (const [name, value] of Object.entries(attributes)) {\n if (value === null || value === undefined || value === false) continue\n\n if (name === 'class' || name === 'className') {\n element.classList.add(...String(value).split(/\\s+/).filter(Boolean))\n } else {\n element.setAttribute(name, value === true ? '' : String(value))\n }\n }\n}\n\nfunction ActionModal({ modal, hidden = false }: { modal: OpenModal; hidden?: boolean }) {\n const runner = useActionRunner()\n const panel = usePanel()\n const direction = useDirection()\n const t = useT()\n const { action, context, modal: schema } = modal\n const submittedRef = useRef(false)\n\n const form = useMemo(() => {\n if (!schema.form) {\n return null\n }\n\n return new FormStore(schema.form, {\n token: context.token,\n component: actionFormComponent(action, context),\n // Own field ids: the modal opens over a form whose fields may share names.\n idPrefix: formIdPrefix(actionFormComponent(action, context)),\n record: context.record ?? null,\n refreshUrl: panel.endpoints.schemaRefresh,\n optionsUrl: panel.endpoints.schemaOptions,\n validateUrl: panel.endpoints.schemaValidate,\n // A halted wizard step shows what its hook sent.\n onNotifications: (notifications) => notifications.forEach((n) => showToast(n)),\n params: actionFormParams(context),\n })\n }, [schema.form, context, action, panel.endpoints])\n\n useEffect(() => {\n form?.setErrors(modal.errors)\n }, [form, modal.errors])\n\n useEffect(() => () => form?.destroy(), [form])\n\n // \"Create & create another\": back to the `autofocus()` field of the fresh form.\n const refocus = modal.refocus\n\n useEffect(() => {\n if (!refocus || !form) return\n\n return scheduleAutofocus(\n () => document.getElementById(`action-form-${action.name}`),\n form.state.components,\n )\n }, [refocus, form, action.name])\n\n // Nested actions read this form (`$mountedActions`, `getRawData()`) and write into it\n // (`fillData()`).\n useEffect(\n () => (form ? runner.registerModalForm(action, context, form) : undefined),\n [runner, form, action, context],\n )\n\n // `extraModalOverlayAttributes()`: the backdrop is the content's previous sibling.\n // A callback ref: the portal renders the content after the first commit.\n const [content, setContent] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (hidden) return\n\n const overlay = content?.previousElementSibling\n\n if (overlay?.getAttribute('data-slot')?.endsWith('-overlay')) {\n applyAttributes(overlay, schema.overlayAttributes)\n }\n }, [schema.overlayAttributes, hidden, content])\n\n // Live updates replace the form's components (fields shown, options changed).\n const formState = useStore<FormState | null>(form?.store ?? NO_FORM)\n\n // A wizard form reports its step, so the submit button waits for the last one.\n const progressStore = useMemo(() => (form ? wizardProgressStore(form) : null), [form])\n const progress = useStore(progressStore ?? NO_PROGRESS)\n const submitVisible = isModalSubmitVisible(schema, progressStore ? progress : null)\n\n const [password, setPassword] = useState('')\n const passwordId = useId()\n const passwordError = modal.errors[PASSWORD_CONFIRMATION_INPUT]?.[0]\n\n const submit = (createAnother = false) => {\n submittedRef.current = true\n\n // A click that blurred a live field started a refresh: send the state it returns.\n if (form) {\n form.whenSettled(() => run(createAnother))\n } else {\n run(createAnother)\n }\n }\n\n const run = (createAnother: boolean) => {\n const values = form?.state.values ?? {}\n const options = {\n ...(schema.passwordConfirmation ? { password } : {}),\n ...(createAnother ? { createAnother: true } : {}),\n }\n\n if (Object.keys(options).length > 0) {\n runner.execute(action, context, values, options)\n } else {\n runner.execute(action, context, values)\n }\n }\n\n const footerContext = useMemo(() => nestedActionContext(context, action), [context, action])\n\n // Dismissing the modal (not submitting it): `cancelParentActionsOnClose()` applies.\n const close = () => runner.cancelModal(action.name)\n const unsavedMessage = t('unsaved_changes_close', {}, 'You have unsaved changes. Close anyway?')\n // Cancel, Escape, clicking away and the close button ask first when the form changed.\n const requestClose = () => {\n if (confirmModalClose(schema, form, unsavedMessage)) close()\n }\n\n useEffect(() => guardModalUnload(schema, form, unsavedMessage), [schema, form, unsavedMessage])\n const title = schema.heading ?? action.label ?? t('actions', {}, 'Actions')\n\n const header = (\n <div\n className={cn(\n 'flex gap-4',\n schema.alignment === 'center' && 'flex-col items-center text-center',\n )}\n >\n {schema.icon && (\n <div\n className={cn(\n 'flex size-10 shrink-0 items-center justify-center rounded-full',\n colorClass(iconBackgroundColorClasses, schema.iconColor, 'primary'),\n )}\n >\n <Icon name={schema.icon} className=\"size-5\" />\n </div>\n )}\n <div className=\"flex flex-col gap-1.5\">\n {/* Every dialog needs an accessible name: without a heading it stays visually hidden. */}\n {schema.slideOver ? (\n <SheetTitle className={cn(!schema.heading && 'sr-only')}>{title}</SheetTitle>\n ) : (\n <DialogTitle className={cn(!schema.heading && 'sr-only')}>{title}</DialogTitle>\n )}\n {schema.description ? (\n schema.slideOver ? (\n <SheetDescription dangerouslySetInnerHTML={{ __html: schema.description }} />\n ) : (\n <DialogDescription dangerouslySetInnerHTML={{ __html: schema.description }} />\n )\n ) : schema.slideOver ? (\n <SheetDescription className=\"sr-only\">{title}</SheetDescription>\n ) : (\n <DialogDescription className=\"sr-only\">{title}</DialogDescription>\n )}\n </div>\n </div>\n )\n\n const body = (\n <div className=\"flex flex-col gap-6\">\n {schema.content && <SchemaRenderer components={schema.content} />}\n {form && <Slot name=\"modal.schema.before\" data={{ action: action.name }} />}\n {form && (\n <FormProvider store={form}>\n <form\n id={`action-form-${action.name}`}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n >\n <SchemaRenderer\n components={formState?.components ?? form.state.components}\n columns={formState?.columns ?? form.state.columns}\n />\n </form>\n </FormProvider>\n )}\n {form && <Slot name=\"modal.schema.after\" data={{ action: action.name }} />}\n {schema.contentFooter && <SchemaRenderer components={schema.contentFooter} />}\n {schema.passwordConfirmation && (\n <div data-slot=\"password-confirmation\" className=\"flex flex-col gap-2\">\n <Label htmlFor={passwordId}>{t('current_password', {}, 'Current password')}</Label>\n <Input\n id={passwordId}\n type=\"password\"\n autoComplete=\"current-password\"\n value={password}\n aria-invalid={passwordError ? true : undefined}\n aria-describedby={passwordError ? `${passwordId}-error` : `${passwordId}-hint`}\n onChange={(event) => setPassword(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n submit()\n }\n }}\n />\n {passwordError ? (\n <p id={`${passwordId}-error`} className=\"text-destructive text-sm\">\n {passwordError}\n </p>\n ) : (\n <p id={`${passwordId}-hint`} className=\"text-muted-foreground text-sm\">\n {t('confirm_password', {}, 'Confirm your password to continue')}\n </p>\n )}\n </div>\n )}\n </div>\n )\n\n const footerAlignment = modalFooterAlignment(schema)\n const footer = (schema.submitLabel ||\n schema.cancelLabel ||\n schema.extraFooterActions?.length) && (\n <div\n className={cn(\n 'flex w-full flex-col-reverse gap-2 sm:flex-row sm:items-center',\n FOOTER_JUSTIFY[footerAlignment],\n )}\n >\n {schema.extraFooterActions && (\n <ActionList\n actions={schema.extraFooterActions}\n context={footerContext}\n className={footerAlignment === 'end' ? 'sm:me-auto' : undefined}\n />\n )}\n {schema.cancelLabel && (\n <Button\n type=\"button\"\n variant=\"outline\"\n className={\n schema.cancelColor\n ? colorClass(outlineButtonColorClasses, schema.cancelColor)\n : undefined\n }\n onClick={requestClose}\n disabled={modal.processing}\n >\n {schema.cancelLabel}\n </Button>\n )}\n {schema.createAnotherLabel && schema.submitLabel && submitVisible && (\n <Button\n type=\"button\"\n variant=\"outline\"\n data-slot=\"create-another\"\n className={\n schema.createAnotherColor\n ? colorClass(outlineButtonColorClasses, schema.createAnotherColor)\n : undefined\n }\n onClick={() => submit(true)}\n disabled={modal.processing}\n >\n {schema.createAnotherIcon && <Icon name={schema.createAnotherIcon} />}\n {schema.createAnotherLabel}\n </Button>\n )}\n {schema.submitLabel && submitVisible && (\n <Button\n type=\"button\"\n className={colorClass(solidButtonColorClasses, schema.submitColor, 'primary')}\n onClick={() => submit()}\n disabled={modal.processing}\n autoFocus={schema.autofocus && !form}\n >\n {modal.processing ? <Spinner /> : schema.submitIcon && <Icon name={schema.submitIcon} />}\n {schema.submitLabel}\n </Button>\n )}\n </div>\n )\n\n const onOpenChange = (open: boolean) => {\n if (!open && !modal.processing) {\n requestClose()\n }\n }\n\n // `modalClickThrough()`: a non-modal dialog has no backdrop and leaves the page usable.\n const isModal = !schema.clickThrough\n\n if (schema.slideOver) {\n return (\n <Sheet open={!hidden} modal={isModal} onOpenChange={onOpenChange}>\n <SheetContent\n ref={setContent}\n side={slideOverSide(schema.slideOverPosition, direction)}\n {...extraProps(\n schema.windowAttributes,\n cn('flex w-full flex-col gap-0 p-0', WIDTHS[schema.width] ?? 'sm:max-w-md'),\n )}\n showCloseButton={schema.closeButton}\n onInteractOutside={(event) => !schema.closeByClickingAway && event.preventDefault()}\n onEscapeKeyDown={(event) => !schema.closeByEscaping && event.preventDefault()}\n >\n <SheetHeader className=\"border-b\">{header}</SheetHeader>\n <div className=\"flex-1 overflow-y-auto p-4\">{body}</div>\n {footer && (\n <SheetFooter\n className={cn('border-t', schema.stickyFooter && 'bg-background sticky bottom-0')}\n >\n {footer}\n </SheetFooter>\n )}\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <Dialog open={!hidden} modal={isModal} onOpenChange={onOpenChange}>\n <DialogContent\n ref={setContent}\n {...extraProps(\n schema.windowAttributes,\n cn('max-h-[calc(100vh-2rem)] overflow-y-auto', WIDTHS[schema.width] ?? 'sm:max-w-lg'),\n )}\n showCloseButton={schema.closeButton}\n onInteractOutside={(event) => !schema.closeByClickingAway && event.preventDefault()}\n onEscapeKeyDown={(event) => !schema.closeByEscaping && event.preventDefault()}\n >\n <DialogHeader\n className={cn(\n // Pinned to the top of the scrolling dialog, edge to edge.\n schema.stickyHeader &&\n 'bg-background sticky -top-6 z-10 -mx-6 -mt-6 rounded-t-lg border-b px-6 pt-6 pb-4',\n )}\n >\n {header}\n </DialogHeader>\n {(schema.content || form || schema.contentFooter || schema.passwordConfirmation) && body}\n {footer && (\n <DialogFooter\n className={cn(\n // Pinned to the bottom of the scrolling dialog, edge to edge.\n schema.stickyFooter &&\n 'bg-background sticky bottom-0 -mx-6 -mb-6 rounded-b-lg border-t px-6 py-4',\n )}\n >\n {footer}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"action-modals.js","names":[],"sources":["../../../src/components/actions/action-modals.tsx"],"sourcesContent":["import type { FormState, OpenModal } from '@alfakit/core'\nimport {\n actionFormComponent,\n actionFormParams,\n actionPath,\n confirmModalClose,\n formIdPrefix,\n FormStore,\n guardModalUnload,\n isModalHiddenByChild,\n isModalSubmitVisible,\n modalFooterAlignment,\n nestedActionContext,\n PASSWORD_CONFIRMATION_INPUT,\n scheduleAutofocus,\n slideOverSide,\n wizardProgressStore,\n} from '@alfakit/core'\nimport { useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { useActionRunner, useDirection, usePanel, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconBackgroundColorClasses,\n outlineButtonColorClasses,\n solidButtonColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormProvider } from '../form/form-context'\nimport { showToast } from '../notifications/flash-toasts'\nimport { Slot } from '../panel/slot'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { Button } from '../ui/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../ui/dialog'\nimport { Input } from '../ui/input'\nimport { Label } from '../ui/label'\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n} from '../ui/sheet'\nimport { Spinner } from '../ui/spinner'\nimport { ActionList } from './action-list'\n\nconst WIDTHS: Record<string, string> = {\n '3xs': 'sm:max-w-3xs',\n '2xs': 'sm:max-w-2xs',\n xs: 'sm:max-w-xs',\n sm: 'sm:max-w-sm',\n md: 'sm:max-w-md',\n lg: 'sm:max-w-lg',\n xl: 'sm:max-w-xl',\n '2xl': 'sm:max-w-2xl',\n '3xl': 'sm:max-w-3xl',\n '4xl': 'sm:max-w-4xl',\n '5xl': 'sm:max-w-5xl',\n '6xl': 'sm:max-w-6xl',\n '7xl': 'sm:max-w-7xl',\n none: 'sm:max-w-none',\n min: 'sm:max-w-min',\n max: 'sm:max-w-max',\n fit: 'sm:max-w-fit',\n prose: 'sm:max-w-prose',\n container: 'sm:container',\n 'screen-sm': 'sm:max-w-(--breakpoint-sm)',\n 'screen-md': 'sm:max-w-(--breakpoint-md)',\n 'screen-lg': 'sm:max-w-(--breakpoint-lg)',\n 'screen-xl': 'sm:max-w-(--breakpoint-xl)',\n 'screen-2xl': 'sm:max-w-(--breakpoint-2xl)',\n full: 'sm:max-w-[calc(100%-2rem)]',\n screen: 'sm:max-w-[calc(100%-2rem)] h-[calc(100%-2rem)]',\n}\n\n// Module-level so the snapshot stays stable (a new object per read re-renders forever).\nconst EMPTY_PROGRESS = { step: 0, count: 0 }\nconst NO_PROGRESS = { get: () => EMPTY_PROGRESS, subscribe: () => () => {} }\nconst NO_FORM = { get: () => null, subscribe: () => () => {} }\n\n/** Renders every open action modal from the runner store. Mount once per app. */\nexport function ActionModals() {\n const runner = useActionRunner()\n const { modals } = useStore(runner.store)\n\n // Notifications from JSON responses (a modal `mountUsing()` cancelled, field actions).\n useEffect(() => {\n runner.setNotificationHandler((notifications) => notifications.forEach((n) => showToast(n)))\n\n return () => runner.setNotificationHandler(undefined)\n }, [runner])\n\n return (\n <>\n {modals.map((modal, index) => (\n <ActionModal\n key={`${actionPath(modal.action, modal.context)}-${modal.context.record ?? ''}`}\n modal={modal}\n hidden={isModalHiddenByChild(modals, index)}\n />\n ))}\n </>\n )\n}\n\nconst FOOTER_JUSTIFY: Record<string, string> = {\n start: 'sm:justify-start',\n center: 'sm:justify-center',\n end: 'sm:justify-end',\n between: 'sm:justify-between',\n}\n\n/**\n * Put PHP attributes (`extraModalOverlayAttributes()`) on an element the renderer doesn't\n * create itself (the dialog's backdrop). `class` is added to the element's classes.\n */\nfunction applyAttributes(\n element: Element | null | undefined,\n attributes: Record<string, unknown> | undefined,\n) {\n if (!element || !attributes) return\n\n for (const [name, value] of Object.entries(attributes)) {\n if (value === null || value === undefined || value === false) continue\n\n if (name === 'class' || name === 'className') {\n element.classList.add(...String(value).split(/\\s+/).filter(Boolean))\n } else {\n element.setAttribute(name, value === true ? '' : String(value))\n }\n }\n}\n\nfunction ActionModal({ modal, hidden = false }: { modal: OpenModal; hidden?: boolean }) {\n const runner = useActionRunner()\n const panel = usePanel()\n const direction = useDirection()\n const t = useT()\n const { action, context, modal: schema } = modal\n const submittedRef = useRef(false)\n\n const form = useMemo(() => {\n if (!schema.form) {\n return null\n }\n\n return new FormStore(schema.form, {\n token: context.token,\n component: actionFormComponent(action, context),\n // Own field ids: the modal opens over a form whose fields may share names.\n idPrefix: formIdPrefix(actionFormComponent(action, context)),\n record: context.record ?? null,\n refreshUrl: panel.endpoints.schemaRefresh,\n optionsUrl: panel.endpoints.schemaOptions,\n validateUrl: panel.endpoints.schemaValidate,\n // A halted wizard step shows what its hook sent.\n onNotifications: (notifications) => notifications.forEach((n) => showToast(n)),\n params: actionFormParams(context),\n })\n }, [schema.form, context, action, panel.endpoints])\n\n useEffect(() => {\n form?.setErrors(modal.errors)\n }, [form, modal.errors])\n\n useEffect(() => () => form?.destroy(), [form])\n\n // \"Create & create another\": back to the `autofocus()` field of the fresh form.\n const refocus = modal.refocus\n\n useEffect(() => {\n if (!refocus || !form) return\n\n return scheduleAutofocus(\n () => document.getElementById(`action-form-${action.name}`),\n form.state.components,\n )\n }, [refocus, form, action.name])\n\n // Nested actions read this form (`$mountedActions`, `getRawData()`) and write into it\n // (`fillData()`).\n useEffect(\n () => (form ? runner.registerModalForm(action, context, form) : undefined),\n [runner, form, action, context],\n )\n\n // `extraModalOverlayAttributes()`: the backdrop is the content's previous sibling.\n // A callback ref: the portal renders the content after the first commit.\n const [content, setContent] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (hidden) return\n\n const overlay = content?.previousElementSibling\n\n if (overlay?.getAttribute('data-slot')?.endsWith('-overlay')) {\n applyAttributes(overlay, schema.overlayAttributes)\n }\n }, [schema.overlayAttributes, hidden, content])\n\n // Live updates replace the form's components (fields shown, options changed).\n const formState = useStore<FormState | null>(form?.store ?? NO_FORM)\n\n // A wizard form reports its step, so the submit button waits for the last one.\n const progressStore = useMemo(() => (form ? wizardProgressStore(form) : null), [form])\n const progress = useStore(progressStore ?? NO_PROGRESS)\n const submitVisible = isModalSubmitVisible(schema, progressStore ? progress : null)\n\n const [password, setPassword] = useState('')\n const passwordId = useId()\n const passwordError = modal.errors[PASSWORD_CONFIRMATION_INPUT]?.[0]\n\n const submit = (createAnother = false) => {\n submittedRef.current = true\n\n // A click that blurred a live field started a refresh: send the state it returns.\n if (form) {\n form.whenSettled(() => run(createAnother))\n } else {\n run(createAnother)\n }\n }\n\n const run = (createAnother: boolean) => {\n const values = form?.state.values ?? {}\n const options = {\n ...(schema.passwordConfirmation ? { password } : {}),\n ...(createAnother ? { createAnother: true } : {}),\n }\n\n if (Object.keys(options).length > 0) {\n runner.execute(action, context, values, options)\n } else {\n runner.execute(action, context, values)\n }\n }\n\n const footerContext = useMemo(() => nestedActionContext(context, action), [context, action])\n\n // Dismissing the modal (not submitting it): `cancelParentActionsOnClose()` applies.\n const close = () => runner.cancelModal(action.name)\n const unsavedMessage = t('unsaved_changes_close', {}, 'You have unsaved changes. Close anyway?')\n // Cancel, Escape, clicking away and the close button ask first when the form changed.\n const requestClose = () => {\n if (confirmModalClose(schema, form, unsavedMessage)) close()\n }\n\n useEffect(() => guardModalUnload(schema, form, unsavedMessage), [schema, form, unsavedMessage])\n const title = schema.heading ?? action.label ?? t('actions', {}, 'Actions')\n\n const header = (\n <div\n className={cn(\n 'flex gap-4',\n schema.alignment === 'center' && 'flex-col items-center text-center',\n )}\n >\n {schema.icon && (\n <div\n className={cn(\n 'flex size-10 shrink-0 items-center justify-center rounded-full',\n colorClass(iconBackgroundColorClasses, schema.iconColor, 'primary'),\n )}\n >\n <Icon name={schema.icon} className=\"size-5\" />\n </div>\n )}\n <div className=\"flex flex-col gap-1.5\">\n {/* Every dialog needs an accessible name: without a heading it stays visually hidden. */}\n {schema.slideOver ? (\n <SheetTitle className={cn(!schema.heading && 'sr-only')}>{title}</SheetTitle>\n ) : (\n <DialogTitle className={cn(!schema.heading && 'sr-only')}>{title}</DialogTitle>\n )}\n {schema.description ? (\n schema.slideOver ? (\n <SheetDescription dangerouslySetInnerHTML={{ __html: schema.description }} />\n ) : (\n <DialogDescription dangerouslySetInnerHTML={{ __html: schema.description }} />\n )\n ) : schema.slideOver ? (\n <SheetDescription className=\"sr-only\">{title}</SheetDescription>\n ) : (\n <DialogDescription className=\"sr-only\">{title}</DialogDescription>\n )}\n </div>\n </div>\n )\n\n const body = (\n <div className=\"flex flex-col gap-6\">\n {schema.content && <SchemaRenderer components={schema.content} />}\n {form && <Slot name=\"modal.schema.before\" data={{ action: action.name }} />}\n {form && (\n <FormProvider store={form}>\n <form\n id={`action-form-${action.name}`}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n >\n <SchemaRenderer\n components={formState?.components ?? form.state.components}\n columns={formState?.columns ?? form.state.columns}\n />\n </form>\n </FormProvider>\n )}\n {form && <Slot name=\"modal.schema.after\" data={{ action: action.name }} />}\n {schema.contentFooter && <SchemaRenderer components={schema.contentFooter} />}\n {schema.passwordConfirmation && (\n <div data-slot=\"password-confirmation\" className=\"flex flex-col gap-2\">\n <Label htmlFor={passwordId}>{t('current_password', {}, 'Current password')}</Label>\n <Input\n id={passwordId}\n type=\"password\"\n autoComplete=\"current-password\"\n value={password}\n aria-invalid={passwordError ? true : undefined}\n aria-describedby={passwordError ? `${passwordId}-error` : `${passwordId}-hint`}\n onChange={(event) => setPassword(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n submit()\n }\n }}\n />\n {passwordError ? (\n <p id={`${passwordId}-error`} className=\"text-destructive text-sm\">\n {passwordError}\n </p>\n ) : (\n <p id={`${passwordId}-hint`} className=\"text-muted-foreground text-sm\">\n {t('confirm_password', {}, 'Confirm your password to continue')}\n </p>\n )}\n </div>\n )}\n </div>\n )\n\n const footerAlignment = modalFooterAlignment(schema)\n const footer = (schema.submitLabel ||\n schema.cancelLabel ||\n schema.extraFooterActions?.length) && (\n <div\n className={cn(\n 'flex w-full flex-col-reverse gap-2 sm:flex-row sm:items-center',\n FOOTER_JUSTIFY[footerAlignment],\n )}\n >\n {schema.extraFooterActions && (\n <ActionList\n actions={schema.extraFooterActions}\n context={footerContext}\n className={footerAlignment === 'end' ? 'sm:me-auto' : undefined}\n />\n )}\n {schema.cancelLabel && (\n <Button\n type=\"button\"\n variant=\"outline\"\n className={\n schema.cancelColor\n ? colorClass(outlineButtonColorClasses, schema.cancelColor)\n : undefined\n }\n onClick={requestClose}\n disabled={modal.processing}\n >\n {schema.cancelLabel}\n </Button>\n )}\n {schema.createAnotherLabel && schema.submitLabel && submitVisible && (\n <Button\n type=\"button\"\n variant=\"outline\"\n data-slot=\"create-another\"\n className={\n schema.createAnotherColor\n ? colorClass(outlineButtonColorClasses, schema.createAnotherColor)\n : undefined\n }\n onClick={() => submit(true)}\n disabled={modal.processing}\n >\n {schema.createAnotherIcon && <Icon name={schema.createAnotherIcon} />}\n {schema.createAnotherLabel}\n </Button>\n )}\n {schema.submitLabel && submitVisible && (\n <Button\n type=\"button\"\n className={colorClass(solidButtonColorClasses, schema.submitColor, 'primary')}\n onClick={() => submit()}\n disabled={modal.processing}\n autoFocus={schema.autofocus && !form}\n >\n {modal.processing ? <Spinner /> : schema.submitIcon && <Icon name={schema.submitIcon} />}\n {schema.submitLabel}\n </Button>\n )}\n </div>\n )\n\n const onOpenChange = (open: boolean) => {\n if (!open && !modal.processing) {\n requestClose()\n }\n }\n\n // `modalClickThrough()`: a non-modal dialog has no backdrop and leaves the page usable.\n const isModal = !schema.clickThrough\n\n if (schema.slideOver) {\n return (\n <Sheet open={!hidden} modal={isModal} onOpenChange={onOpenChange}>\n <SheetContent\n ref={setContent}\n side={slideOverSide(schema.slideOverPosition, direction)}\n {...extraProps(\n schema.windowAttributes,\n cn('flex w-full flex-col gap-0 p-0', WIDTHS[schema.width] ?? 'sm:max-w-md'),\n )}\n showCloseButton={schema.closeButton}\n onInteractOutside={(event) => !schema.closeByClickingAway && event.preventDefault()}\n onEscapeKeyDown={(event) => !schema.closeByEscaping && event.preventDefault()}\n >\n <SheetHeader className=\"border-b\">{header}</SheetHeader>\n <div className=\"flex-1 overflow-y-auto p-4\">{body}</div>\n {footer && (\n <SheetFooter\n className={cn('border-t', schema.stickyFooter && 'bg-background sticky bottom-0')}\n >\n {footer}\n </SheetFooter>\n )}\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <Dialog open={!hidden} modal={isModal} onOpenChange={onOpenChange}>\n <DialogContent\n ref={setContent}\n {...extraProps(\n schema.windowAttributes,\n cn('max-h-[calc(100vh-2rem)] overflow-y-auto', WIDTHS[schema.width] ?? 'sm:max-w-lg'),\n )}\n showCloseButton={schema.closeButton}\n onInteractOutside={(event) => !schema.closeByClickingAway && event.preventDefault()}\n onEscapeKeyDown={(event) => !schema.closeByEscaping && event.preventDefault()}\n >\n <DialogHeader\n className={cn(\n // Pinned to the top of the scrolling dialog, edge to edge.\n schema.stickyHeader &&\n 'bg-background sticky -top-6 z-10 -mx-6 -mt-6 rounded-t-lg border-b px-6 pt-6 pb-4',\n // Room for the close button (X): a long heading would run under it.\n schema.closeButton !== false && (schema.stickyHeader ? 'pe-12' : 'pe-6'),\n )}\n >\n {header}\n </DialogHeader>\n {(schema.content || form || schema.contentFooter || schema.passwordConfirmation) && body}\n {footer && (\n <DialogFooter\n className={cn(\n // Pinned to the bottom of the scrolling dialog, edge to edge.\n schema.stickyFooter &&\n 'bg-background sticky bottom-0 -mx-6 -mb-6 rounded-b-lg border-t px-6 py-4',\n )}\n >\n {footer}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-block.d.ts","names":["FormBlock","FormBlockSchema"],"sources":["../../../src/components/form/form-block.tsx"],"mappings":";;;;;;iBA+BgBA,cAAY;EAAW,OAAOC;oBAAiB,IAAA"}
1
+ {"version":3,"file":"form-block.d.ts","names":["FormBlock","FormBlockSchema"],"sources":["../../../src/components/form/form-block.tsx"],"mappings":";;;;;;iBAuCgBA,cAAY;EAAW,OAAOC;oBAAiB,IAAA"}
@@ -1,12 +1,12 @@
1
- import { useActionRunner, useAlfakitProps, usePageSchema, usePanel, useT } from "../../context.js";
1
+ import { useActionRunner, useAlfakitProps, usePageSchema, usePanel, useStandalone, useT } from "../../context.js";
2
2
  import { useStore } from "../../hooks/use-store.js";
3
3
  import { ActionList } from "../actions/action-list.js";
4
4
  import { FormProvider } from "./form-context.js";
5
5
  import { SchemaRenderer } from "../schema/schema-renderer.js";
6
6
  import { showToast } from "../notifications/flash-toasts.js";
7
- import { FormStore, applyFormRefresh, formActionsVisible, formIdentity, formRefreshFor, isImplicitSubmit, isLeavingVisit, pageFlash, pageFormName, registerConflictForm, registerFormSubmitter, registerPageForm, resolvePartialProps, shouldResyncForm, wizardProgressStore } from "@alfakit/core";
7
+ import { FormStore, applyFormRefresh, formActionsVisible, formIdPrefix, formIdentity, formRefreshFor, isImplicitSubmit, isLeavingVisit, pageFlash, pageFormName, registerConflictForm, registerFormSubmitter, registerPageForm, resolvePartialProps, shouldResyncForm, wizardProgressStore } from "@alfakit/core";
8
8
  import { router } from "@inertiajs/react";
9
- import { useCallback, useEffect, useMemo, useRef } from "react";
9
+ import { useCallback, useEffect, useId, useMemo, useRef } from "react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
11
  //#region src/components/form/form-block.tsx
12
12
  /**
@@ -21,9 +21,12 @@ function FormBlock$1({ block }) {
21
21
  const t = useT();
22
22
  const token = page?.token ?? "";
23
23
  const identity = formIdentity(page, block);
24
+ const standalone = useStandalone();
25
+ const instanceId = useId();
24
26
  const store = useMemo(() => new FormStore(block, {
25
27
  token,
26
28
  component: block.component,
29
+ idPrefix: standalone ? formIdPrefix(`form-${instanceId}`) : void 0,
27
30
  refreshUrl: panel.endpoints.schemaRefresh,
28
31
  optionsUrl: panel.endpoints.schemaOptions,
29
32
  validateUrl: panel.endpoints.schemaValidate,
@@ -1 +1 @@
1
- {"version":3,"file":"form-block.js","names":["FormBlock"],"sources":["../../../src/components/form/form-block.tsx"],"sourcesContent":["import type { ActionContext, ActionSchema, FormBlock as FormBlockSchema } from '@alfakit/core'\nimport {\n FormStore,\n applyFormRefresh,\n formActionsVisible,\n formIdentity,\n formRefreshFor,\n isImplicitSubmit,\n isLeavingVisit,\n pageFlash,\n pageFormName,\n registerConflictForm,\n registerFormSubmitter,\n registerPageForm,\n resolvePartialProps,\n shouldResyncForm,\n wizardProgressStore,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { useActionRunner, useAlfakitProps, usePageSchema, usePanel, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { ActionList } from '../actions/action-list'\nimport { showToast } from '../notifications/flash-toasts'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { FormProvider } from './form-context'\n\n/**\n * A page form: its state lives in a FormStore, and its buttons are actions that post the\n * current values through the action endpoint.\n */\nexport function FormBlock({ block }: { block: FormBlockSchema }) {\n const panel = usePanel()\n const page = usePageSchema()\n const props = useAlfakitProps()\n const runner = useActionRunner()\n const t = useT()\n const token = page?.token ?? ''\n\n // The store survives reloads of the same form (table sorting/paging, polling,\n // relation-manager actions all deliver a new `block` object); only a different form\n // identity starts from server state again. The token is re-issued per response, so it\n // is swapped in place instead of being part of the identity.\n const identity = formIdentity(page, block)\n\n const store = useMemo(\n () =>\n new FormStore(block, {\n token,\n component: block.component,\n refreshUrl: panel.endpoints.schemaRefresh,\n optionsUrl: panel.endpoints.schemaOptions,\n validateUrl: panel.endpoints.schemaValidate,\n // A halted wizard step shows what its hook sent.\n onNotifications: (notifications) => notifications.forEach((n) => showToast(n)),\n // Live dashboard filters are applied by the refresh; the widgets reload to follow.\n onRefreshed: () => {\n if (latestBlock.current.liveFilters)\n router.reload({ only: resolvePartialProps(['page']) })\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [identity],\n )\n\n useEffect(() => () => store.destroy(), [store])\n\n // Named forms of custom pages (`<PageForm name>`) publish their store for page method calls.\n useEffect(() => {\n const name = pageFormName(block.component)\n\n return name ? registerPageForm(name, store) : undefined\n }, [block.component, store])\n\n // Edit pages: the conflict banner diffs and merges the main form (`detectConflicts()`).\n useEffect(\n () => (block.component === 'form' ? registerConflictForm(store) : undefined),\n [block.component, store],\n )\n\n useEffect(() => {\n store.setToken(token)\n }, [store, token])\n\n useEffect(() => {\n store.setErrors((props.errors as Record<string, string>) ?? {})\n }, [props.errors, store])\n\n const state = useStore(store.store)\n const context = useMemo<ActionContext>(() => ({ token, component: 'page' }), [token])\n const submitting = useRef(false)\n // After this form's own successful submit, refill from the server's new state. The new\n // block may arrive before or after Inertia's `success` event, so handle both orders.\n const latestBlock = useRef(block)\n const submittedBlock = useRef<FormBlockSchema | null>(null)\n const refillPending = useRef(false)\n // Another action succeeded and its new block has not arrived yet.\n const resyncPending = useRef(false)\n // The block when the latest non-GET visit started, to tell whether its response arrived.\n const visitBlock = useRef<FormBlockSchema | null>(null)\n\n useEffect(() => {\n latestBlock.current = block\n\n if (refillPending.current) {\n refillPending.current = false\n store.fill(block)\n }\n\n if (resyncPending.current) {\n resyncPending.current = false\n\n if (!store.isDirty) store.fill(block)\n }\n }, [block, store])\n\n const actions: ActionSchema[] = useMemo(\n () => block.actions.map((action) => ({ ...action, hasModal: false })),\n [block.actions],\n )\n\n const trigger = useCallback(\n async (action: ActionSchema, ctx: ActionContext) => {\n if (action.url && !action.hasAction) {\n return runner.trigger(action, ctx)\n }\n\n submitting.current = true\n submittedBlock.current = latestBlock.current\n store.setProcessing(true)\n // A click that blurred a live field started a refresh: send the state it returns.\n store.whenSettled(() => runner.execute(action, ctx, store.state.values))\n },\n [runner, store],\n )\n\n // Buttons elsewhere with `formId('form')` (a header \"Save\") submit through this form.\n useEffect(() => registerFormSubmitter(block.component, trigger), [block.component, trigger])\n\n // `refreshFormData([...])`: an action changed the record; copy those fields in.\n useEffect(\n () =>\n router.on('success', (event) => {\n const refresh = formRefreshFor(pageFlash(event.detail.page), block.component)\n\n if (refresh) applyFormRefresh(store, refresh)\n }),\n [block.component, store],\n )\n\n // Wizard forms (`HasWizard` pages) show their buttons on the last step only.\n const progressStore = useMemo(() => wizardProgressStore(store), [store])\n const progress = useStore(progressStore)\n const showActions = formActionsVisible(block, progress)\n\n // Track this form's own submit visit so unrelated reloads finishing meanwhile (polling,\n // relation-manager paging) neither end its processing state nor count as its success.\n const submitVisit = useRef<string | null>(null)\n\n useEffect(() => {\n const offStart = router.on('start', (event) => {\n const visit = event.detail.visit\n\n if (submitting.current && !submitVisit.current && visit.method !== 'get') {\n submitVisit.current = visit.id\n }\n\n if (visit.method !== 'get') visitBlock.current = latestBlock.current\n })\n\n const offSuccess = router.on('success', (event) => {\n const id = submitVisit.current\n\n // Another action changed the record: show the server's new form unless it holds edits.\n if (!id && shouldResyncForm(pageFlash(event.detail.page), store)) {\n if (latestBlock.current !== visitBlock.current) {\n store.fill(latestBlock.current)\n } else {\n resyncPending.current = true\n }\n\n return\n }\n\n if (!id || (event.detail.visitId ?? id) !== id) return\n\n // A halted action (rate limit, `halt()`) saved nothing: keep what the user typed.\n if (pageFlash(event.detail.page).alfakitAction?.status === 'halted') return\n\n if (latestBlock.current !== submittedBlock.current) {\n store.fill(latestBlock.current)\n } else {\n refillPending.current = true\n }\n })\n\n const offFinish = router.on('finish', (event) => {\n if (submitVisit.current && event.detail.visit.id !== submitVisit.current) return\n\n store.setProcessing(false)\n submitting.current = false\n submitVisit.current = null\n })\n\n return () => {\n offStart()\n offSuccess()\n offFinish()\n }\n }, [store])\n\n useEffect(() => {\n if (!panel.unsavedChangesAlerts) {\n return\n }\n\n return router.on('before', (event) => {\n if (\n store.isDirty &&\n !submitting.current &&\n isLeavingVisit(event.detail.visit) &&\n !window.confirm(t('unsaved_changes', {}, 'You have unsaved changes. Leave anyway?'))\n ) {\n event.preventDefault()\n }\n })\n }, [panel.unsavedChangesAlerts, store, t])\n\n const primary = actions[0]\n\n return (\n <FormProvider store={store}>\n <form\n data-slot=\"form-block\"\n className=\"flex flex-col gap-6\"\n onSubmit={(event) => {\n event.preventDefault()\n\n if (primary && showActions) {\n void trigger(primary, context)\n }\n }}\n onKeyDown={(event) => {\n // Enter in a text field runs the primary action (actions are type=\"button\").\n if (primary && showActions && isImplicitSubmit(event.nativeEvent, event.currentTarget)) {\n event.preventDefault()\n void trigger(primary, context)\n }\n }}\n >\n <fieldset disabled={state.processing || block.operation === 'view'} className=\"contents\">\n <SchemaRenderer components={state.components} columns={state.columns} />\n </fieldset>\n {actions.length > 0 && showActions && (\n <div data-slot=\"form-actions\" className=\"border-t pt-6\">\n <ActionList actions={actions} context={context} onTrigger={trigger} />\n </div>\n )}\n </form>\n </FormProvider>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"form-block.js","names":["FormBlock"],"sources":["../../../src/components/form/form-block.tsx"],"sourcesContent":["import type { ActionContext, ActionSchema, FormBlock as FormBlockSchema } from '@alfakit/core'\nimport {\n FormStore,\n applyFormRefresh,\n formActionsVisible,\n formIdPrefix,\n formIdentity,\n formRefreshFor,\n isImplicitSubmit,\n isLeavingVisit,\n pageFlash,\n pageFormName,\n registerConflictForm,\n registerFormSubmitter,\n registerPageForm,\n resolvePartialProps,\n shouldResyncForm,\n wizardProgressStore,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { useCallback, useEffect, useId, useMemo, useRef } from 'react'\nimport {\n useActionRunner,\n useAlfakitProps,\n usePageSchema,\n usePanel,\n useStandalone,\n useT,\n} from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { ActionList } from '../actions/action-list'\nimport { showToast } from '../notifications/flash-toasts'\nimport { SchemaRenderer } from '../schema/schema-renderer'\nimport { FormProvider } from './form-context'\n\n/**\n * A page form: its state lives in a FormStore, and its buttons are actions that post the\n * current values through the action endpoint.\n */\nexport function FormBlock({ block }: { block: FormBlockSchema }) {\n const panel = usePanel()\n const page = usePageSchema()\n const props = useAlfakitProps()\n const runner = useActionRunner()\n const t = useT()\n const token = page?.token ?? ''\n\n // The store survives reloads of the same form (table sorting/paging, polling,\n // relation-manager actions all deliver a new `block` object); only a different form\n // identity starts from server state again. The token is re-issued per response, so it\n // is swapped in place instead of being part of the identity.\n const identity = formIdentity(page, block)\n // Several standalone components can share a page (and field names): each form gets its\n // own field id prefix, so labels and errors point at their own inputs.\n const standalone = useStandalone()\n const instanceId = useId()\n\n const store = useMemo(\n () =>\n new FormStore(block, {\n token,\n component: block.component,\n idPrefix: standalone ? formIdPrefix(`form-${instanceId}`) : undefined,\n refreshUrl: panel.endpoints.schemaRefresh,\n optionsUrl: panel.endpoints.schemaOptions,\n validateUrl: panel.endpoints.schemaValidate,\n // A halted wizard step shows what its hook sent.\n onNotifications: (notifications) => notifications.forEach((n) => showToast(n)),\n // Live dashboard filters are applied by the refresh; the widgets reload to follow.\n onRefreshed: () => {\n if (latestBlock.current.liveFilters)\n router.reload({ only: resolvePartialProps(['page']) })\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [identity],\n )\n\n useEffect(() => () => store.destroy(), [store])\n\n // Named forms of custom pages (`<PageForm name>`) publish their store for page method calls.\n useEffect(() => {\n const name = pageFormName(block.component)\n\n return name ? registerPageForm(name, store) : undefined\n }, [block.component, store])\n\n // Edit pages: the conflict banner diffs and merges the main form (`detectConflicts()`).\n useEffect(\n () => (block.component === 'form' ? registerConflictForm(store) : undefined),\n [block.component, store],\n )\n\n useEffect(() => {\n store.setToken(token)\n }, [store, token])\n\n useEffect(() => {\n store.setErrors((props.errors as Record<string, string>) ?? {})\n }, [props.errors, store])\n\n const state = useStore(store.store)\n const context = useMemo<ActionContext>(() => ({ token, component: 'page' }), [token])\n const submitting = useRef(false)\n // After this form's own successful submit, refill from the server's new state. The new\n // block may arrive before or after Inertia's `success` event, so handle both orders.\n const latestBlock = useRef(block)\n const submittedBlock = useRef<FormBlockSchema | null>(null)\n const refillPending = useRef(false)\n // Another action succeeded and its new block has not arrived yet.\n const resyncPending = useRef(false)\n // The block when the latest non-GET visit started, to tell whether its response arrived.\n const visitBlock = useRef<FormBlockSchema | null>(null)\n\n useEffect(() => {\n latestBlock.current = block\n\n if (refillPending.current) {\n refillPending.current = false\n store.fill(block)\n }\n\n if (resyncPending.current) {\n resyncPending.current = false\n\n if (!store.isDirty) store.fill(block)\n }\n }, [block, store])\n\n const actions: ActionSchema[] = useMemo(\n () => block.actions.map((action) => ({ ...action, hasModal: false })),\n [block.actions],\n )\n\n const trigger = useCallback(\n async (action: ActionSchema, ctx: ActionContext) => {\n if (action.url && !action.hasAction) {\n return runner.trigger(action, ctx)\n }\n\n submitting.current = true\n submittedBlock.current = latestBlock.current\n store.setProcessing(true)\n // A click that blurred a live field started a refresh: send the state it returns.\n store.whenSettled(() => runner.execute(action, ctx, store.state.values))\n },\n [runner, store],\n )\n\n // Buttons elsewhere with `formId('form')` (a header \"Save\") submit through this form.\n useEffect(() => registerFormSubmitter(block.component, trigger), [block.component, trigger])\n\n // `refreshFormData([...])`: an action changed the record; copy those fields in.\n useEffect(\n () =>\n router.on('success', (event) => {\n const refresh = formRefreshFor(pageFlash(event.detail.page), block.component)\n\n if (refresh) applyFormRefresh(store, refresh)\n }),\n [block.component, store],\n )\n\n // Wizard forms (`HasWizard` pages) show their buttons on the last step only.\n const progressStore = useMemo(() => wizardProgressStore(store), [store])\n const progress = useStore(progressStore)\n const showActions = formActionsVisible(block, progress)\n\n // Track this form's own submit visit so unrelated reloads finishing meanwhile (polling,\n // relation-manager paging) neither end its processing state nor count as its success.\n const submitVisit = useRef<string | null>(null)\n\n useEffect(() => {\n const offStart = router.on('start', (event) => {\n const visit = event.detail.visit\n\n if (submitting.current && !submitVisit.current && visit.method !== 'get') {\n submitVisit.current = visit.id\n }\n\n if (visit.method !== 'get') visitBlock.current = latestBlock.current\n })\n\n const offSuccess = router.on('success', (event) => {\n const id = submitVisit.current\n\n // Another action changed the record: show the server's new form unless it holds edits.\n if (!id && shouldResyncForm(pageFlash(event.detail.page), store)) {\n if (latestBlock.current !== visitBlock.current) {\n store.fill(latestBlock.current)\n } else {\n resyncPending.current = true\n }\n\n return\n }\n\n if (!id || (event.detail.visitId ?? id) !== id) return\n\n // A halted action (rate limit, `halt()`) saved nothing: keep what the user typed.\n if (pageFlash(event.detail.page).alfakitAction?.status === 'halted') return\n\n if (latestBlock.current !== submittedBlock.current) {\n store.fill(latestBlock.current)\n } else {\n refillPending.current = true\n }\n })\n\n const offFinish = router.on('finish', (event) => {\n if (submitVisit.current && event.detail.visit.id !== submitVisit.current) return\n\n store.setProcessing(false)\n submitting.current = false\n submitVisit.current = null\n })\n\n return () => {\n offStart()\n offSuccess()\n offFinish()\n }\n }, [store])\n\n useEffect(() => {\n if (!panel.unsavedChangesAlerts) {\n return\n }\n\n return router.on('before', (event) => {\n if (\n store.isDirty &&\n !submitting.current &&\n isLeavingVisit(event.detail.visit) &&\n !window.confirm(t('unsaved_changes', {}, 'You have unsaved changes. Leave anyway?'))\n ) {\n event.preventDefault()\n }\n })\n }, [panel.unsavedChangesAlerts, store, t])\n\n const primary = actions[0]\n\n return (\n <FormProvider store={store}>\n <form\n data-slot=\"form-block\"\n className=\"flex flex-col gap-6\"\n onSubmit={(event) => {\n event.preventDefault()\n\n if (primary && showActions) {\n void trigger(primary, context)\n }\n }}\n onKeyDown={(event) => {\n // Enter in a text field runs the primary action (actions are type=\"button\").\n if (primary && showActions && isImplicitSubmit(event.nativeEvent, event.currentTarget)) {\n event.preventDefault()\n void trigger(primary, context)\n }\n }}\n >\n <fieldset disabled={state.processing || block.operation === 'view'} className=\"contents\">\n <SchemaRenderer components={state.components} columns={state.columns} />\n </fieldset>\n {actions.length > 0 && showActions && (\n <div data-slot=\"form-actions\" className=\"border-t pt-6\">\n <ActionList actions={actions} context={context} onTrigger={trigger} />\n </div>\n )}\n </form>\n </FormProvider>\n )\n}\n"],"mappings":"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"}
@@ -80,7 +80,7 @@ function NavSubItem({ item }) {
80
80
  ]
81
81
  })
82
82
  })
83
- }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.children.map((child) => /* @__PURE__ */ jsx(NavSubItem, { item: child }, child.label)) }) })]
83
+ }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.children.map((child) => /* @__PURE__ */ jsx(NavSubItem, { item: child }, child.url ?? child.label)) }) })]
84
84
  }) });
85
85
  return /* @__PURE__ */ jsx(SidebarMenuSubItem, { children: /* @__PURE__ */ jsx(SidebarMenuSubButton, {
86
86
  asChild: true,
@@ -113,7 +113,7 @@ function NavItem({ item }) {
113
113
  /* @__PURE__ */ jsx(ChevronRightIcon, { className: "ms-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/collapsible:rotate-90" })
114
114
  ]
115
115
  })
116
- }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.children.map((child) => /* @__PURE__ */ jsx(NavSubItem, { item: child }, child.label)) }) })] })
116
+ }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.children.map((child) => /* @__PURE__ */ jsx(NavSubItem, { item: child }, child.url ?? child.label)) }) })] })
117
117
  });
118
118
  return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, {
119
119
  asChild: true,
@@ -181,7 +181,7 @@ function NavGroup({ group }) {
181
181
  const flyout = Boolean(group.label && group.icon);
182
182
  const content = /* @__PURE__ */ jsx(SidebarGroupContent, {
183
183
  className: cn(flyout && "group-data-[collapsible=icon]:hidden"),
184
- children: /* @__PURE__ */ jsx(SidebarMenu, { children: group.items.map((item) => /* @__PURE__ */ jsx(NavItem, { item }, item.label)) })
184
+ children: /* @__PURE__ */ jsx(SidebarMenu, { children: group.items.map((item) => /* @__PURE__ */ jsx(NavItem, { item }, item.url ?? item.label)) })
185
185
  });
186
186
  const flyoutMenu = flyout ? /* @__PURE__ */ jsx(NavGroupFlyout, { group }) : null;
187
187
  if (!group.label) return /* @__PURE__ */ jsx(SidebarGroup, {
@@ -1 +1 @@
1
- {"version":3,"file":"app-sidebar.js","names":[],"sources":["../../../src/components/panel/app-sidebar.tsx"],"sourcesContent":["import {\n PREFETCH_CACHE_FOR,\n flattenNavigationItems,\n hasActiveNavigationItem,\n navigationItemText,\n type NavigationGroupSchema,\n type NavigationItemSchema,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { ChevronRightIcon } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport { badgeColorClasses, colorClass } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { NewTabHint } from '../../lib/new-tab-hint'\nimport { cn } from '../../lib/utils'\nimport { NotificationsMenu } from '../notifications/notifications-menu'\nimport { Badge } from '../ui/badge'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarRail,\n} from '../ui/sidebar'\nimport { Brand } from './brand'\nimport { GlobalSearch } from './global-search'\nimport { NavLabel } from './nav-label'\nimport { Slot } from './slot'\nimport { TenantMenu } from './tenant-menu'\nimport { UserMenu } from './user-menu'\n\nexport { Brand } from './brand'\n\nfunction NavBadge({ item }: { item: NavigationItemSchema }) {\n const t = useT()\n\n // `deferNavigationBadges()`: a placeholder until the deferred badges arrive.\n if (item.badgePending) {\n return (\n <span\n data-slot=\"badge\"\n aria-busy=\"true\"\n aria-label={t('loading', {}, 'Loading')}\n className=\"bg-muted-foreground/20 ms-auto inline-block h-4 w-5 animate-pulse rounded-md\"\n />\n )\n }\n\n if (item.badge === undefined || item.badge === null) return null\n\n return (\n <Badge\n variant=\"outline\"\n className={cn('ms-auto px-1.5 py-0', colorClass(badgeColorClasses, item.badgeColor, 'gray'))}\n title={item.badgeTooltip}\n >\n {item.badge}\n </Badge>\n )\n}\n\n/**\n * A navigation link: an Inertia link, or a plain anchor for `openUrlInNewTab()`. Used as an\n * `asChild` target, so it must pass on the classes, data attributes and handlers it receives.\n */\nfunction NavLink({\n item,\n children,\n ...props\n}: { item: NavigationItemSchema; children: ReactNode } & Omit<\n ComponentProps<'a'>,\n 'href' | 'children'\n>) {\n // `NavigationItem::extraAttributes()`; `class` is merged with the link's own.\n const attributes = {\n ...props,\n ...extraProps(item.extraAttributes, props.className),\n } as ComponentProps<'a'>\n\n return item.openInNewTab ? (\n <a {...attributes} href={item.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n <NewTabHint />\n </a>\n ) : (\n // Slot passes DOM props; Inertia's Link redefines a few event names, so pass them through untyped.\n <Link\n {...(attributes as Record<string, unknown>)}\n href={item.url ?? '#'}\n prefetch\n cacheFor={PREFETCH_CACHE_FOR}\n >\n {children}\n </Link>\n )\n}\n\n/** Child items (any depth) with their own badges, new-tab links and nested children. */\nfunction NavSubItem({ item }: { item: NavigationItemSchema }) {\n if (item.children && item.children.length > 0) {\n return (\n <SidebarMenuSubItem>\n <Collapsible defaultOpen={item.active} className=\"group/nav-sub\">\n <CollapsibleTrigger asChild>\n <SidebarMenuSubButton asChild isActive={item.active}>\n <button type=\"button\" {...extraProps(item.extraAttributes, 'w-full')}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n <ChevronRightIcon className=\"ms-auto transition-transform duration-200 group-data-[state=open]/nav-sub:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/nav-sub:rotate-90\" />\n </button>\n </SidebarMenuSubButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => (\n <NavSubItem key={child.label} item={child} />\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </Collapsible>\n </SidebarMenuSubItem>\n )\n }\n\n return (\n <SidebarMenuSubItem>\n <SidebarMenuSubButton asChild isActive={item.active}>\n <NavLink item={item}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n </NavLink>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n )\n}\n\nfunction NavItem({ item }: { item: NavigationItemSchema }) {\n if (item.children && item.children.length > 0) {\n return (\n <Collapsible asChild defaultOpen={item.active} className=\"group/collapsible\">\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton\n tooltip={navigationItemText(item)}\n isActive={item.active}\n {...extraProps(item.extraAttributes)}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n <ChevronRightIcon className=\"ms-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => (\n <NavSubItem key={child.label} item={child} />\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n )\n }\n\n return (\n <SidebarMenuItem>\n <SidebarMenuButton asChild isActive={item.active} tooltip={navigationItemText(item)}>\n <NavLink item={item}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n </NavLink>\n </SidebarMenuButton>\n </SidebarMenuItem>\n )\n}\n\n/** A group's items in its flyout menu, children indented under their parent. */\nfunction FlyoutItems({ items }: { items: NavigationItemSchema[] }) {\n return flattenNavigationItems(items).map(({ item, depth }, index) => (\n <DropdownMenuItem\n key={`${depth}-${item.label}-${index}`}\n asChild\n data-active={item.active ? 'true' : undefined}\n >\n <NavLink\n item={item}\n aria-current={item.active ? 'page' : undefined}\n className={cn(\n 'data-[active=true]:bg-accent data-[active=true]:font-medium',\n depth === 1 && 'ps-6',\n depth > 1 && 'ps-9',\n )}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"truncate\" />\n <NavBadge item={item} />\n </NavLink>\n </DropdownMenuItem>\n ))\n}\n\n/**\n * In the icon-only (collapsed) sidebar, a group with an icon is one button that opens its\n * items in a flyout menu, like Filament. Hidden while the sidebar is expanded.\n */\nfunction NavGroupFlyout({ group }: { group: NavigationGroupSchema }) {\n const direction = useDirection()\n const active = hasActiveNavigationItem(group.items)\n\n return (\n <SidebarMenu className=\"hidden group-data-[collapsible=icon]:flex\" data-nav-flyout=\"\">\n <SidebarMenuItem>\n <DropdownMenu dir={direction}>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton isActive={active}>\n <Icon name={group.icon!} />\n <span>{group.label}</span>\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n side={direction === 'rtl' ? 'left' : 'right'}\n align=\"start\"\n className=\"min-w-56\"\n >\n <DropdownMenuLabel>{group.label}</DropdownMenuLabel>\n <FlyoutItems items={group.items} />\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n\nfunction NavGroup({ group }: { group: NavigationGroupSchema }) {\n // `NavigationGroup::extraSidebarAttributes()`; `class` is merged with the group's own.\n const attributes = extraProps(group.extraSidebarAttributes)\n // A labelled group with an icon collapses into a flyout in the icon-only sidebar.\n const flyout = Boolean(group.label && group.icon)\n const content = (\n <SidebarGroupContent className={cn(flyout && 'group-data-[collapsible=icon]:hidden')}>\n <SidebarMenu>\n {group.items.map((item) => (\n <NavItem key={item.label} item={item} />\n ))}\n </SidebarMenu>\n </SidebarGroupContent>\n )\n const flyoutMenu = flyout ? <NavGroupFlyout group={group} /> : null\n\n if (!group.label) {\n return <SidebarGroup {...attributes}>{content}</SidebarGroup>\n }\n\n if (!group.collapsible) {\n return (\n <SidebarGroup {...attributes}>\n <SidebarGroupLabel>\n {group.icon && <Icon name={group.icon} className=\"me-1.5\" />}\n {group.label}\n </SidebarGroupLabel>\n {content}\n {flyoutMenu}\n </SidebarGroup>\n )\n }\n\n return (\n <Collapsible\n defaultOpen={!group.collapsed || group.items.some((item) => item.active)}\n className=\"group/nav-group\"\n >\n <SidebarGroup {...attributes}>\n <CollapsibleTrigger asChild>\n <SidebarGroupLabel\n asChild\n className=\"hover:bg-sidebar-accent w-full cursor-pointer justify-between\"\n >\n <button type=\"button\">\n <span className=\"flex items-center gap-1.5\">\n {group.icon && <Icon name={group.icon} />}\n {group.label}\n </span>\n <Icon\n name=\"alfakit::panels.sidebar.group.collapse-button\"\n fallback={ChevronRightIcon}\n className=\"size-3.5 transition-transform group-data-[state=open]/nav-group:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/nav-group:rotate-90\"\n />\n </button>\n </SidebarGroupLabel>\n </CollapsibleTrigger>\n <CollapsibleContent>{content}</CollapsibleContent>\n {flyoutMenu}\n </SidebarGroup>\n </Collapsible>\n )\n}\n\nexport function AppSidebar() {\n const panel = usePanel()\n const direction = useDirection()\n const t = useT()\n\n return (\n <Sidebar\n side={direction === 'rtl' ? 'right' : 'left'}\n collapsible={\n panel.theme?.design?.layout === 'rail'\n ? 'icon'\n : panel.sidebarFullyCollapsibleOnDesktop\n ? 'offcanvas'\n : panel.sidebarCollapsibleOnDesktop\n ? 'icon'\n : 'none'\n }\n variant={panel.theme?.sidebar === 'flush' ? 'sidebar' : (panel.theme?.sidebar ?? 'inset')}\n >\n <SidebarHeader>\n <Slot name=\"sidebar.start\" />\n <Slot name=\"sidebar.logo.before\" />\n <SidebarMenu>\n <SidebarMenuItem>\n {panel.tenancy?.current ? (\n <>\n <Slot name=\"tenant-menu.before\" />\n <TenantMenu />\n <Slot name=\"tenant-menu.after\" />\n </>\n ) : (\n <SidebarMenuButton size=\"lg\" asChild>\n <div>\n <Brand />\n </div>\n </SidebarMenuButton>\n )}\n </SidebarMenuItem>\n </SidebarMenu>\n <Slot name=\"sidebar.logo.after\" />\n {/* `databaseNotifications(position: 'sidebar')` or `topbar(false)`. */}\n {panel.databaseNotifications?.position === 'sidebar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <NotificationsMenu variant=\"sidebar\" />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n {/* `globalSearch(position: GlobalSearchPosition::Sidebar)` or `topbar(false)`. */}\n {panel.globalSearch?.position === 'sidebar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <GlobalSearch variant=\"sidebar\" />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n <Slot name=\"sidebar.header\" />\n </SidebarHeader>\n <SidebarContent>\n <Slot name=\"sidebar.nav.start\" />\n <nav aria-label={t('sidebar', {}, 'Sidebar')} className=\"contents\">\n {panel.navigation.map((group, index) => (\n <NavGroup key={group.label ?? `group-${index}`} group={group} />\n ))}\n </nav>\n <Slot name=\"sidebar.nav.end\" />\n </SidebarContent>\n <SidebarFooter>\n <Slot name=\"sidebar.footer\" />\n {/* `userMenu(position: UserMenuPosition::Topbar)` shows it in the top bar instead. */}\n {panel.userMenu?.position !== 'topbar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <UserMenu />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n <Slot name=\"sidebar.end\" />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-sidebar.js","names":[],"sources":["../../../src/components/panel/app-sidebar.tsx"],"sourcesContent":["import {\n PREFETCH_CACHE_FOR,\n flattenNavigationItems,\n hasActiveNavigationItem,\n navigationItemText,\n type NavigationGroupSchema,\n type NavigationItemSchema,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { ChevronRightIcon } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { useDirection, usePanel, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport { badgeColorClasses, colorClass } from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { NewTabHint } from '../../lib/new-tab-hint'\nimport { cn } from '../../lib/utils'\nimport { NotificationsMenu } from '../notifications/notifications-menu'\nimport { Badge } from '../ui/badge'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarRail,\n} from '../ui/sidebar'\nimport { Brand } from './brand'\nimport { GlobalSearch } from './global-search'\nimport { NavLabel } from './nav-label'\nimport { Slot } from './slot'\nimport { TenantMenu } from './tenant-menu'\nimport { UserMenu } from './user-menu'\n\nexport { Brand } from './brand'\n\nfunction NavBadge({ item }: { item: NavigationItemSchema }) {\n const t = useT()\n\n // `deferNavigationBadges()`: a placeholder until the deferred badges arrive.\n if (item.badgePending) {\n return (\n <span\n data-slot=\"badge\"\n aria-busy=\"true\"\n aria-label={t('loading', {}, 'Loading')}\n className=\"bg-muted-foreground/20 ms-auto inline-block h-4 w-5 animate-pulse rounded-md\"\n />\n )\n }\n\n if (item.badge === undefined || item.badge === null) return null\n\n return (\n <Badge\n variant=\"outline\"\n className={cn('ms-auto px-1.5 py-0', colorClass(badgeColorClasses, item.badgeColor, 'gray'))}\n title={item.badgeTooltip}\n >\n {item.badge}\n </Badge>\n )\n}\n\n/**\n * A navigation link: an Inertia link, or a plain anchor for `openUrlInNewTab()`. Used as an\n * `asChild` target, so it must pass on the classes, data attributes and handlers it receives.\n */\nfunction NavLink({\n item,\n children,\n ...props\n}: { item: NavigationItemSchema; children: ReactNode } & Omit<\n ComponentProps<'a'>,\n 'href' | 'children'\n>) {\n // `NavigationItem::extraAttributes()`; `class` is merged with the link's own.\n const attributes = {\n ...props,\n ...extraProps(item.extraAttributes, props.className),\n } as ComponentProps<'a'>\n\n return item.openInNewTab ? (\n <a {...attributes} href={item.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n <NewTabHint />\n </a>\n ) : (\n // Slot passes DOM props; Inertia's Link redefines a few event names, so pass them through untyped.\n <Link\n {...(attributes as Record<string, unknown>)}\n href={item.url ?? '#'}\n prefetch\n cacheFor={PREFETCH_CACHE_FOR}\n >\n {children}\n </Link>\n )\n}\n\n/** Child items (any depth) with their own badges, new-tab links and nested children. */\nfunction NavSubItem({ item }: { item: NavigationItemSchema }) {\n if (item.children && item.children.length > 0) {\n return (\n <SidebarMenuSubItem>\n <Collapsible defaultOpen={item.active} className=\"group/nav-sub\">\n <CollapsibleTrigger asChild>\n <SidebarMenuSubButton asChild isActive={item.active}>\n <button type=\"button\" {...extraProps(item.extraAttributes, 'w-full')}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n <ChevronRightIcon className=\"ms-auto transition-transform duration-200 group-data-[state=open]/nav-sub:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/nav-sub:rotate-90\" />\n </button>\n </SidebarMenuSubButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => (\n <NavSubItem key={child.url ?? child.label} item={child} />\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </Collapsible>\n </SidebarMenuSubItem>\n )\n }\n\n return (\n <SidebarMenuSubItem>\n <SidebarMenuSubButton asChild isActive={item.active}>\n <NavLink item={item}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n </NavLink>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n )\n}\n\nfunction NavItem({ item }: { item: NavigationItemSchema }) {\n if (item.children && item.children.length > 0) {\n return (\n <Collapsible asChild defaultOpen={item.active} className=\"group/collapsible\">\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton\n tooltip={navigationItemText(item)}\n isActive={item.active}\n {...extraProps(item.extraAttributes)}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n <ChevronRightIcon className=\"ms-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => (\n <NavSubItem key={child.url ?? child.label} item={child} />\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n )\n }\n\n return (\n <SidebarMenuItem>\n <SidebarMenuButton asChild isActive={item.active} tooltip={navigationItemText(item)}>\n <NavLink item={item}>\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n <NavBadge item={item} />\n </NavLink>\n </SidebarMenuButton>\n </SidebarMenuItem>\n )\n}\n\n/** A group's items in its flyout menu, children indented under their parent. */\nfunction FlyoutItems({ items }: { items: NavigationItemSchema[] }) {\n return flattenNavigationItems(items).map(({ item, depth }, index) => (\n <DropdownMenuItem\n key={`${depth}-${item.label}-${index}`}\n asChild\n data-active={item.active ? 'true' : undefined}\n >\n <NavLink\n item={item}\n aria-current={item.active ? 'page' : undefined}\n className={cn(\n 'data-[active=true]:bg-accent data-[active=true]:font-medium',\n depth === 1 && 'ps-6',\n depth > 1 && 'ps-9',\n )}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"truncate\" />\n <NavBadge item={item} />\n </NavLink>\n </DropdownMenuItem>\n ))\n}\n\n/**\n * In the icon-only (collapsed) sidebar, a group with an icon is one button that opens its\n * items in a flyout menu, like Filament. Hidden while the sidebar is expanded.\n */\nfunction NavGroupFlyout({ group }: { group: NavigationGroupSchema }) {\n const direction = useDirection()\n const active = hasActiveNavigationItem(group.items)\n\n return (\n <SidebarMenu className=\"hidden group-data-[collapsible=icon]:flex\" data-nav-flyout=\"\">\n <SidebarMenuItem>\n <DropdownMenu dir={direction}>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton isActive={active}>\n <Icon name={group.icon!} />\n <span>{group.label}</span>\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n side={direction === 'rtl' ? 'left' : 'right'}\n align=\"start\"\n className=\"min-w-56\"\n >\n <DropdownMenuLabel>{group.label}</DropdownMenuLabel>\n <FlyoutItems items={group.items} />\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n\nfunction NavGroup({ group }: { group: NavigationGroupSchema }) {\n // `NavigationGroup::extraSidebarAttributes()`; `class` is merged with the group's own.\n const attributes = extraProps(group.extraSidebarAttributes)\n // A labelled group with an icon collapses into a flyout in the icon-only sidebar.\n const flyout = Boolean(group.label && group.icon)\n const content = (\n <SidebarGroupContent className={cn(flyout && 'group-data-[collapsible=icon]:hidden')}>\n <SidebarMenu>\n {group.items.map((item) => (\n <NavItem key={item.url ?? item.label} item={item} />\n ))}\n </SidebarMenu>\n </SidebarGroupContent>\n )\n const flyoutMenu = flyout ? <NavGroupFlyout group={group} /> : null\n\n if (!group.label) {\n return <SidebarGroup {...attributes}>{content}</SidebarGroup>\n }\n\n if (!group.collapsible) {\n return (\n <SidebarGroup {...attributes}>\n <SidebarGroupLabel>\n {group.icon && <Icon name={group.icon} className=\"me-1.5\" />}\n {group.label}\n </SidebarGroupLabel>\n {content}\n {flyoutMenu}\n </SidebarGroup>\n )\n }\n\n return (\n <Collapsible\n defaultOpen={!group.collapsed || group.items.some((item) => item.active)}\n className=\"group/nav-group\"\n >\n <SidebarGroup {...attributes}>\n <CollapsibleTrigger asChild>\n <SidebarGroupLabel\n asChild\n className=\"hover:bg-sidebar-accent w-full cursor-pointer justify-between\"\n >\n <button type=\"button\">\n <span className=\"flex items-center gap-1.5\">\n {group.icon && <Icon name={group.icon} />}\n {group.label}\n </span>\n <Icon\n name=\"alfakit::panels.sidebar.group.collapse-button\"\n fallback={ChevronRightIcon}\n className=\"size-3.5 transition-transform group-data-[state=open]/nav-group:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]/nav-group:rotate-90\"\n />\n </button>\n </SidebarGroupLabel>\n </CollapsibleTrigger>\n <CollapsibleContent>{content}</CollapsibleContent>\n {flyoutMenu}\n </SidebarGroup>\n </Collapsible>\n )\n}\n\nexport function AppSidebar() {\n const panel = usePanel()\n const direction = useDirection()\n const t = useT()\n\n return (\n <Sidebar\n side={direction === 'rtl' ? 'right' : 'left'}\n collapsible={\n panel.theme?.design?.layout === 'rail'\n ? 'icon'\n : panel.sidebarFullyCollapsibleOnDesktop\n ? 'offcanvas'\n : panel.sidebarCollapsibleOnDesktop\n ? 'icon'\n : 'none'\n }\n variant={panel.theme?.sidebar === 'flush' ? 'sidebar' : (panel.theme?.sidebar ?? 'inset')}\n >\n <SidebarHeader>\n <Slot name=\"sidebar.start\" />\n <Slot name=\"sidebar.logo.before\" />\n <SidebarMenu>\n <SidebarMenuItem>\n {panel.tenancy?.current ? (\n <>\n <Slot name=\"tenant-menu.before\" />\n <TenantMenu />\n <Slot name=\"tenant-menu.after\" />\n </>\n ) : (\n <SidebarMenuButton size=\"lg\" asChild>\n <div>\n <Brand />\n </div>\n </SidebarMenuButton>\n )}\n </SidebarMenuItem>\n </SidebarMenu>\n <Slot name=\"sidebar.logo.after\" />\n {/* `databaseNotifications(position: 'sidebar')` or `topbar(false)`. */}\n {panel.databaseNotifications?.position === 'sidebar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <NotificationsMenu variant=\"sidebar\" />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n {/* `globalSearch(position: GlobalSearchPosition::Sidebar)` or `topbar(false)`. */}\n {panel.globalSearch?.position === 'sidebar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <GlobalSearch variant=\"sidebar\" />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n <Slot name=\"sidebar.header\" />\n </SidebarHeader>\n <SidebarContent>\n <Slot name=\"sidebar.nav.start\" />\n <nav aria-label={t('sidebar', {}, 'Sidebar')} className=\"contents\">\n {panel.navigation.map((group, index) => (\n <NavGroup key={group.label ?? `group-${index}`} group={group} />\n ))}\n </nav>\n <Slot name=\"sidebar.nav.end\" />\n </SidebarContent>\n <SidebarFooter>\n <Slot name=\"sidebar.footer\" />\n {/* `userMenu(position: UserMenuPosition::Topbar)` shows it in the top bar instead. */}\n {panel.userMenu?.position !== 'topbar' && (\n <SidebarMenu>\n <SidebarMenuItem>\n <UserMenu />\n </SidebarMenuItem>\n </SidebarMenu>\n )}\n <Slot name=\"sidebar.end\" />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n"],"mappings":"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"}
@@ -55,7 +55,7 @@ function BottomNav() {
55
55
  "data-slot": "bottom-nav",
56
56
  "aria-label": t("navigation", {}, "Navigation"),
57
57
  className: "md:hidden",
58
- children: [primary.map((item) => /* @__PURE__ */ jsx(BottomNavLink, { item }, item.label)), overflow.length > 0 && /* @__PURE__ */ jsxs(Sheet, {
58
+ children: [primary.map((item) => /* @__PURE__ */ jsx(BottomNavLink, { item }, item.url ?? item.label)), overflow.length > 0 && /* @__PURE__ */ jsxs(Sheet, {
59
59
  open,
60
60
  onOpenChange: setOpen,
61
61
  children: [/* @__PURE__ */ jsxs("button", {
@@ -88,7 +88,7 @@ function BottomNav() {
88
88
  "data-active": isNavigationItemActive(item) ? "true" : void 0,
89
89
  className: cn("hover:bg-accent data-[active=true]:bg-accent flex min-h-11 items-center gap-3 rounded-md px-3 font-medium [&_svg]:size-4"),
90
90
  children: [item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon }), /* @__PURE__ */ jsx(NavLabel, { item })]
91
- }, item.label))
91
+ }, item.url ?? item.label))
92
92
  })]
93
93
  })]
94
94
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-nav.js","names":[],"sources":["../../../src/components/panel/bottom-nav.tsx"],"sourcesContent":["import {\n PREFETCH_CACHE_FOR,\n bottomNavigationItems,\n isNavigationItemActive,\n navigationItemUrl,\n type NavigationItemSchema,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { MenuIcon } from 'lucide-react'\nimport { useState } from 'react'\nimport { usePanel, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Sheet, SheetContent, SheetHeader, SheetTitle } from '../ui/sheet'\nimport { NavLabel } from './nav-label'\n\nfunction BottomNavLink({\n item,\n onNavigate,\n}: {\n item: NavigationItemSchema\n onNavigate?: () => void\n}) {\n const active = isNavigationItemActive(item)\n const url = navigationItemUrl(item) ?? '#'\n\n if (item.openInNewTab) {\n return (\n <a\n {...extraProps(item.extraAttributes, 'min-w-0')}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-slot=\"bottom-nav-item\"\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"max-w-full truncate\" />\n </a>\n )\n }\n\n return (\n <Link\n {...extraProps(item.extraAttributes, 'min-w-0')}\n href={url}\n prefetch\n cacheFor={PREFETCH_CACHE_FOR}\n data-slot=\"bottom-nav-item\"\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n onClick={onNavigate}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"max-w-full truncate\" />\n </Link>\n )\n}\n\n/**\n * The mobile preset's bottom navigation bar (`Panel::mobile()`): the top-level items, with\n * the rest behind a \"More\" button that opens a sheet. Hidden from `md` up; replace it\n * everywhere with `createAlfakit({ components: { 'panel:bottom-nav': … } })`.\n */\nexport function BottomNav() {\n const panel = usePanel()\n const t = useT()\n const [open, setOpen] = useState(false)\n const { primary, overflow } = bottomNavigationItems(panel.navigation)\n\n if (primary.length === 0 && overflow.length === 0) {\n return null\n }\n\n const overflowActive = overflow.some(isNavigationItemActive)\n\n return (\n <nav\n data-slot=\"bottom-nav\"\n aria-label={t('navigation', {}, 'Navigation')}\n className=\"md:hidden\"\n >\n {primary.map((item) => (\n <BottomNavLink key={item.label} item={item} />\n ))}\n {overflow.length > 0 && (\n <Sheet open={open} onOpenChange={setOpen}>\n <button\n type=\"button\"\n data-slot=\"bottom-nav-item\"\n data-active={overflowActive ? 'true' : undefined}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n className=\"min-w-0 bg-transparent\"\n onClick={() => setOpen(true)}\n >\n <Icon name=\"alfakit::panels.topbar.open-sidebar-button\" fallback={MenuIcon} />\n <span className=\"max-w-full truncate\">{t('more', {}, 'More')}</span>\n </button>\n <SheetContent side=\"bottom\" className=\"max-h-[80dvh] overflow-y-auto rounded-t-xl p-0\">\n <SheetHeader className=\"border-b\">\n <SheetTitle>{t('more', {}, 'More')}</SheetTitle>\n </SheetHeader>\n <nav\n aria-label={t('navigation', {}, 'Navigation')}\n className=\"flex flex-col gap-1 p-3 text-sm\"\n >\n {overflow\n .flatMap((item) => [item, ...(item.children ?? [])])\n .map((item) => (\n <Link\n key={item.label}\n href={navigationItemUrl(item) ?? '#'}\n onClick={() => setOpen(false)}\n data-active={isNavigationItemActive(item) ? 'true' : undefined}\n className={cn(\n 'hover:bg-accent data-[active=true]:bg-accent flex min-h-11 items-center gap-3 rounded-md px-3 font-medium [&_svg]:size-4',\n )}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n </Link>\n ))}\n </nav>\n </SheetContent>\n </Sheet>\n )}\n </nav>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAS,cAAc,EACrB,MACA,cAIC;CACD,MAAM,SAAS,uBAAuB,IAAI;CAC1C,MAAM,MAAM,kBAAkB,IAAI,KAAK;CAEvC,IAAI,KAAK,cACP,OACE,qBAAC,KAAD;EACE,GAAI,WAAW,KAAK,iBAAiB,SAAS;EAC9C,MAAM;EACN,QAAO;EACP,KAAI;EACJ,aAAU;EALZ,UAAA,CAOG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD;GAAgB;GAAM,WAAU;EAAuB,CAAA,CACtD;;CAIP,OACE,qBAAC,MAAD;EACE,GAAI,WAAW,KAAK,iBAAiB,SAAS;EAC9C,MAAM;EACN,UAAA;EACA,UAAU;EACV,aAAU;EACV,eAAa,SAAS,SAAS,KAAA;EAC/B,gBAAc,SAAS,SAAS,KAAA;EAChC,SAAS;EARX,UAAA,CAUG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD;GAAgB;GAAM,WAAU;EAAuB,CAAA,CACnD;;AAEV;;;;;;AAOA,SAAgB,YAAY;CAC1B,MAAM,QAAQ,SAAS;CACvB,MAAM,IAAI,KAAK;CACf,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,EAAE,SAAS,aAAa,sBAAsB,MAAM,UAAU;CAEpE,IAAI,QAAQ,WAAW,KAAK,SAAS,WAAW,GAC9C,OAAO;CAGT,MAAM,iBAAiB,SAAS,KAAK,sBAAsB;CAE3D,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAY,EAAE,cAAc,CAAC,GAAG,YAAY;EAC5C,WAAU;EAHZ,UAAA,CAKG,QAAQ,KAAK,SACZ,oBAAC,eAAD,EAAsC,KAAO,GAAzB,KAAK,KAAoB,CAC9C,GACA,SAAS,SAAS,KACjB,qBAAC,OAAD;GAAa;GAAM,cAAc;GAAjC,UAAA,CACE,qBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,eAAa,iBAAiB,SAAS,KAAA;IACvC,iBAAc;IACd,iBAAe;IACf,WAAU;IACV,eAAe,QAAQ,IAAI;IAP7B,UAAA,CASE,oBAAC,MAAD;KAAM,MAAK;KAA6C,UAAU;IAAW,CAAA,GAC7E,oBAAC,QAAD;KAAM,WAAU;KAAuB,UAAA,EAAE,QAAQ,CAAC,GAAG,MAAM;IAAQ,CAAA,CAC7D;GACR,CAAA,GAAA,qBAAC,cAAD;IAAc,MAAK;IAAS,WAAU;IAAtC,UAAA,CACE,oBAAC,aAAD;KAAa,WAAU;KACrB,UAAA,oBAAC,YAAD,EAAA,UAAa,EAAE,QAAQ,CAAC,GAAG,MAAM,EAAc,CAAA;IACpC,CAAA,GACb,oBAAC,OAAD;KACE,cAAY,EAAE,cAAc,CAAC,GAAG,YAAY;KAC5C,WAAU;KAET,UAAA,SACE,SAAS,SAAS,CAAC,MAAM,GAAI,KAAK,YAAY,CAAC,CAAE,CAAC,CAAC,CACnD,KAAK,SACJ,qBAAC,MAAD;MAEE,MAAM,kBAAkB,IAAI,KAAK;MACjC,eAAe,QAAQ,KAAK;MAC5B,eAAa,uBAAuB,IAAI,IAAI,SAAS,KAAA;MACrD,WAAW,GACT,0HACF;MAPF,UAAA,CASG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD,EAAgB,KAAO,CAAA,CACnB;KAVC,GAAA,KAAK,KAUN,CACP;IACA,CAAA,CACO;GACT,CAAA,CAAA;EAEN,CAAA,CAAA;;AAET"}
1
+ {"version":3,"file":"bottom-nav.js","names":[],"sources":["../../../src/components/panel/bottom-nav.tsx"],"sourcesContent":["import {\n PREFETCH_CACHE_FOR,\n bottomNavigationItems,\n isNavigationItemActive,\n navigationItemUrl,\n type NavigationItemSchema,\n} from '@alfakit/core'\nimport { Link } from '@inertiajs/react'\nimport { MenuIcon } from 'lucide-react'\nimport { useState } from 'react'\nimport { usePanel, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { Sheet, SheetContent, SheetHeader, SheetTitle } from '../ui/sheet'\nimport { NavLabel } from './nav-label'\n\nfunction BottomNavLink({\n item,\n onNavigate,\n}: {\n item: NavigationItemSchema\n onNavigate?: () => void\n}) {\n const active = isNavigationItemActive(item)\n const url = navigationItemUrl(item) ?? '#'\n\n if (item.openInNewTab) {\n return (\n <a\n {...extraProps(item.extraAttributes, 'min-w-0')}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-slot=\"bottom-nav-item\"\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"max-w-full truncate\" />\n </a>\n )\n }\n\n return (\n <Link\n {...extraProps(item.extraAttributes, 'min-w-0')}\n href={url}\n prefetch\n cacheFor={PREFETCH_CACHE_FOR}\n data-slot=\"bottom-nav-item\"\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n onClick={onNavigate}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} className=\"max-w-full truncate\" />\n </Link>\n )\n}\n\n/**\n * The mobile preset's bottom navigation bar (`Panel::mobile()`): the top-level items, with\n * the rest behind a \"More\" button that opens a sheet. Hidden from `md` up; replace it\n * everywhere with `createAlfakit({ components: { 'panel:bottom-nav': … } })`.\n */\nexport function BottomNav() {\n const panel = usePanel()\n const t = useT()\n const [open, setOpen] = useState(false)\n const { primary, overflow } = bottomNavigationItems(panel.navigation)\n\n if (primary.length === 0 && overflow.length === 0) {\n return null\n }\n\n const overflowActive = overflow.some(isNavigationItemActive)\n\n return (\n <nav\n data-slot=\"bottom-nav\"\n aria-label={t('navigation', {}, 'Navigation')}\n className=\"md:hidden\"\n >\n {primary.map((item) => (\n <BottomNavLink key={item.url ?? item.label} item={item} />\n ))}\n {overflow.length > 0 && (\n <Sheet open={open} onOpenChange={setOpen}>\n <button\n type=\"button\"\n data-slot=\"bottom-nav-item\"\n data-active={overflowActive ? 'true' : undefined}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n className=\"min-w-0 bg-transparent\"\n onClick={() => setOpen(true)}\n >\n <Icon name=\"alfakit::panels.topbar.open-sidebar-button\" fallback={MenuIcon} />\n <span className=\"max-w-full truncate\">{t('more', {}, 'More')}</span>\n </button>\n <SheetContent side=\"bottom\" className=\"max-h-[80dvh] overflow-y-auto rounded-t-xl p-0\">\n <SheetHeader className=\"border-b\">\n <SheetTitle>{t('more', {}, 'More')}</SheetTitle>\n </SheetHeader>\n <nav\n aria-label={t('navigation', {}, 'Navigation')}\n className=\"flex flex-col gap-1 p-3 text-sm\"\n >\n {overflow\n .flatMap((item) => [item, ...(item.children ?? [])])\n .map((item) => (\n <Link\n key={item.url ?? item.label}\n href={navigationItemUrl(item) ?? '#'}\n onClick={() => setOpen(false)}\n data-active={isNavigationItemActive(item) ? 'true' : undefined}\n className={cn(\n 'hover:bg-accent data-[active=true]:bg-accent flex min-h-11 items-center gap-3 rounded-md px-3 font-medium [&_svg]:size-4',\n )}\n >\n {item.icon && <Icon name={item.icon} />}\n <NavLabel item={item} />\n </Link>\n ))}\n </nav>\n </SheetContent>\n </Sheet>\n )}\n </nav>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAS,cAAc,EACrB,MACA,cAIC;CACD,MAAM,SAAS,uBAAuB,IAAI;CAC1C,MAAM,MAAM,kBAAkB,IAAI,KAAK;CAEvC,IAAI,KAAK,cACP,OACE,qBAAC,KAAD;EACE,GAAI,WAAW,KAAK,iBAAiB,SAAS;EAC9C,MAAM;EACN,QAAO;EACP,KAAI;EACJ,aAAU;EALZ,UAAA,CAOG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD;GAAgB;GAAM,WAAU;EAAuB,CAAA,CACtD;;CAIP,OACE,qBAAC,MAAD;EACE,GAAI,WAAW,KAAK,iBAAiB,SAAS;EAC9C,MAAM;EACN,UAAA;EACA,UAAU;EACV,aAAU;EACV,eAAa,SAAS,SAAS,KAAA;EAC/B,gBAAc,SAAS,SAAS,KAAA;EAChC,SAAS;EARX,UAAA,CAUG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD;GAAgB;GAAM,WAAU;EAAuB,CAAA,CACnD;;AAEV;;;;;;AAOA,SAAgB,YAAY;CAC1B,MAAM,QAAQ,SAAS;CACvB,MAAM,IAAI,KAAK;CACf,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,EAAE,SAAS,aAAa,sBAAsB,MAAM,UAAU;CAEpE,IAAI,QAAQ,WAAW,KAAK,SAAS,WAAW,GAC9C,OAAO;CAGT,MAAM,iBAAiB,SAAS,KAAK,sBAAsB;CAE3D,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAY,EAAE,cAAc,CAAC,GAAG,YAAY;EAC5C,WAAU;EAHZ,UAAA,CAKG,QAAQ,KAAK,SACZ,oBAAC,eAAD,EAAkD,KAAO,GAArC,KAAK,OAAO,KAAK,KAAoB,CAC1D,GACA,SAAS,SAAS,KACjB,qBAAC,OAAD;GAAa;GAAM,cAAc;GAAjC,UAAA,CACE,qBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,eAAa,iBAAiB,SAAS,KAAA;IACvC,iBAAc;IACd,iBAAe;IACf,WAAU;IACV,eAAe,QAAQ,IAAI;IAP7B,UAAA,CASE,oBAAC,MAAD;KAAM,MAAK;KAA6C,UAAU;IAAW,CAAA,GAC7E,oBAAC,QAAD;KAAM,WAAU;KAAuB,UAAA,EAAE,QAAQ,CAAC,GAAG,MAAM;IAAQ,CAAA,CAC7D;GACR,CAAA,GAAA,qBAAC,cAAD;IAAc,MAAK;IAAS,WAAU;IAAtC,UAAA,CACE,oBAAC,aAAD;KAAa,WAAU;KACrB,UAAA,oBAAC,YAAD,EAAA,UAAa,EAAE,QAAQ,CAAC,GAAG,MAAM,EAAc,CAAA;IACpC,CAAA,GACb,oBAAC,OAAD;KACE,cAAY,EAAE,cAAc,CAAC,GAAG,YAAY;KAC5C,WAAU;KAET,UAAA,SACE,SAAS,SAAS,CAAC,MAAM,GAAI,KAAK,YAAY,CAAC,CAAE,CAAC,CAAC,CACnD,KAAK,SACJ,qBAAC,MAAD;MAEE,MAAM,kBAAkB,IAAI,KAAK;MACjC,eAAe,QAAQ,KAAK;MAC5B,eAAa,uBAAuB,IAAI,IAAI,SAAS,KAAA;MACrD,WAAW,GACT,0HACF;MAPF,UAAA,CASG,KAAK,QAAQ,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,GACtC,oBAAC,UAAD,EAAgB,KAAO,CAAA,CACnB;KAVC,GAAA,KAAK,OAAO,KAAK,KAUlB,CACP;IACA,CAAA,CACO;GACT,CAAA,CAAA;EAEN,CAAA,CAAA;;AAET"}
@@ -31,7 +31,7 @@ function KeyboardShortcuts() {
31
31
  const overrides = JSON.stringify(config ? config.overrides ?? {} : {});
32
32
  const token = page?.token ?? "";
33
33
  const commands = panel.commandPalette?.commands;
34
- const home = panel.url;
34
+ const home = panel.dashboardUrl ?? panel.url;
35
35
  const overlayLabel = t("shortcuts.show_overlay", {}, "Show keyboard shortcuts");
36
36
  const searchLabel = t("shortcuts.focus_search", {}, "Search the table");
37
37
  const dashboardLabel = t("shortcuts.go_dashboard", {}, "Go to the dashboard");
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-shortcuts.js","names":[],"sources":["../../../src/components/panel/keyboard-shortcuts.tsx"],"sourcesContent":["import type { CommandSchema } from '@alfakit/core'\nimport {\n COMMANDS_COMPONENT,\n commandShortcutId,\n focusTableSearch,\n SHORTCUT_IDS,\n shortcuts,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { useEffect, useState } from 'react'\nimport { useActionRunner, usePageSchema, usePanel, useT } from '../../context'\nimport type { ShortcutsOverlay as Overlay } from './shortcuts-overlay'\n\ntype OverlayComponent = typeof Overlay\n\nlet loadedOverlay: OverlayComponent | undefined\nlet loadingOverlay: Promise<OverlayComponent> | undefined\n\n/** The `?` overlay's code: loaded the first time it opens; the shortcuts register eagerly. */\nfunction loadOverlay(): Promise<OverlayComponent> {\n loadingOverlay ??= import('./shortcuts-overlay').then(\n (module) => (loadedOverlay = module.ShortcutsOverlay),\n (error: unknown) => {\n loadingOverlay = undefined\n throw error\n },\n )\n\n return loadingOverlay\n}\n\n/**\n * Keyboard shortcuts of the panel: `/` (focus the table search), `g d` (dashboard), `?`\n * (the overlay) and the panel commands' `shortcut()`, with the user's customised keys\n * (`alfakit.keyboardShortcuts`). The overlay lists everything available on the page,\n * grouped, and lets users change keys (stored on the server).\n */\nexport function KeyboardShortcuts() {\n const t = useT()\n const panel = usePanel()\n const page = usePageSchema()\n const runner = useActionRunner()\n const config = panel.keyboardShortcuts\n const [open, setOpen] = useState(false)\n // Undefined on the server and during hydration (nothing loads there), so the markup matches.\n const [ShortcutsOverlay, setOverlay] = useState<OverlayComponent | undefined>(() => loadedOverlay)\n const overrides = JSON.stringify(config ? (config.overrides ?? {}) : {})\n const token = page?.token ?? ''\n const commands = panel.commandPalette?.commands\n const home = panel.url\n // Strings, so the effect below doesn't re-run on every render (`t` is a new function each time).\n const overlayLabel = t('shortcuts.show_overlay', {}, 'Show keyboard shortcuts')\n const searchLabel = t('shortcuts.focus_search', {}, 'Search the table')\n const dashboardLabel = t('shortcuts.go_dashboard', {}, 'Go to the dashboard')\n\n useEffect(() => {\n shortcuts.setEnabled(config !== false)\n shortcuts.setOverrides(JSON.parse(overrides) as Record<string, string[]>)\n }, [config, overrides])\n\n useEffect(() => {\n if (!config) return\n\n const disposers = [\n shortcuts.register({\n id: SHORTCUT_IDS.overlay,\n keys: ['?'],\n label: overlayLabel,\n group: 'general',\n handler: () => setOpen(true),\n }),\n // Declined (`false`) on pages without a table search, so `/` keeps its default.\n shortcuts.register({\n id: SHORTCUT_IDS.search,\n keys: ['/'],\n label: searchLabel,\n group: 'general',\n handler: () => focusTableSearch(),\n }),\n ...(home\n ? [\n shortcuts.register({\n id: SHORTCUT_IDS.dashboard,\n keys: ['g d'],\n label: dashboardLabel,\n group: 'navigation',\n handler: () => router.visit(home),\n }),\n ]\n : []),\n ...(commands ?? [])\n .filter((command: CommandSchema) => command.keyBindings?.length && !command.disabled)\n .map((command: CommandSchema) =>\n shortcuts.register({\n id: commandShortcutId(command.name),\n keys: command.keyBindings ?? [],\n label: command.label ?? command.name,\n group: command.url ? 'navigation' : 'commands',\n handler: () => void runner.trigger(command, { token, component: COMMANDS_COMPONENT }),\n }),\n ),\n ]\n\n return () => disposers.forEach((dispose) => dispose())\n }, [config, commands, home, runner, token, overlayLabel, searchLabel, dashboardLabel])\n\n // `?` (or the palette's \"Keyboard shortcuts\") before the code has arrived keeps `open`\n // set; the overlay mounts open once it has loaded.\n useEffect(() => {\n if (!open || ShortcutsOverlay) return\n\n let active = true\n\n loadOverlay().then(\n (component) => active && setOverlay(() => component),\n () => active && setOpen(false),\n )\n\n return () => {\n active = false\n }\n }, [open, ShortcutsOverlay])\n\n if (!config || !ShortcutsOverlay) return null\n\n return (\n <ShortcutsOverlay\n open={open}\n onOpenChange={setOpen}\n customizable={Boolean(config.customizable && panel.endpoints.shortcuts)}\n endpoint={panel.endpoints.shortcuts}\n />\n )\n}\n"],"mappings":";;;;;;AAeA,IAAI;AACJ,IAAI;;AAGJ,SAAS,cAAyC;CAChD,mBAAmB,OAAO,yBAAsB,CAAC,MAC9C,WAAY,gBAAgB,OAAO,mBACnC,UAAmB;EAClB,iBAAiB,KAAA;EACjB,MAAM;CACR,CACF;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,oBAAoB;CAClC,MAAM,IAAI,KAAK;CACf,MAAM,QAAQ,SAAS;CACvB,MAAM,OAAO,cAAc;CAC3B,MAAM,SAAS,gBAAgB;CAC/B,MAAM,SAAS,MAAM;CACrB,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,MAAM,CAAC,kBAAkB,cAAc,eAA6C,aAAa;CACjG,MAAM,YAAY,KAAK,UAAU,SAAU,OAAO,aAAa,CAAC,IAAK,CAAC,CAAC;CACvE,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,WAAW,MAAM,gBAAgB;CACvC,MAAM,OAAO,MAAM;CAEnB,MAAM,eAAe,EAAE,0BAA0B,CAAC,GAAG,yBAAyB;CAC9E,MAAM,cAAc,EAAE,0BAA0B,CAAC,GAAG,kBAAkB;CACtE,MAAM,iBAAiB,EAAE,0BAA0B,CAAC,GAAG,qBAAqB;CAE5E,gBAAgB;EACd,UAAU,WAAW,WAAW,KAAK;EACrC,UAAU,aAAa,KAAK,MAAM,SAAS,CAA6B;CAC1E,GAAG,CAAC,QAAQ,SAAS,CAAC;CAEtB,gBAAgB;EACd,IAAI,CAAC,QAAQ;EAEb,MAAM,YAAY;GAChB,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,GAAG;IACV,OAAO;IACP,OAAO;IACP,eAAe,QAAQ,IAAI;GAC7B,CAAC;GAED,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,GAAG;IACV,OAAO;IACP,OAAO;IACP,eAAe,iBAAiB;GAClC,CAAC;GACD,GAAI,OACA,CACE,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,KAAK;IACZ,OAAO;IACP,OAAO;IACP,eAAe,OAAO,MAAM,IAAI;GAClC,CAAC,CACH,IACA,CAAC;GACL,IAAI,YAAY,CAAC,EAAA,CACd,QAAQ,YAA2B,QAAQ,aAAa,UAAU,CAAC,QAAQ,QAAQ,CAAC,CACpF,KAAK,YACJ,UAAU,SAAS;IACjB,IAAI,kBAAkB,QAAQ,IAAI;IAClC,MAAM,QAAQ,eAAe,CAAC;IAC9B,OAAO,QAAQ,SAAS,QAAQ;IAChC,OAAO,QAAQ,MAAM,eAAe;IACpC,eAAe,KAAK,OAAO,QAAQ,SAAS;KAAE;KAAO,WAAW;IAAmB,CAAC;GACtF,CAAC,CACH;EACJ;EAEA,aAAa,UAAU,SAAS,YAAY,QAAQ,CAAC;CACvD,GAAG;EAAC;EAAQ;EAAU;EAAM;EAAQ;EAAO;EAAc;EAAa;CAAc,CAAC;CAIrF,gBAAgB;EACd,IAAI,CAAC,QAAQ,kBAAkB;EAE/B,IAAI,SAAS;EAEb,YAAY,CAAC,CAAC,MACX,cAAc,UAAU,iBAAiB,SAAS,SAC7C,UAAU,QAAQ,KAAK,CAC/B;EAEA,aAAa;GACX,SAAS;EACX;CACF,GAAG,CAAC,MAAM,gBAAgB,CAAC;CAE3B,IAAI,CAAC,UAAU,CAAC,kBAAkB,OAAO;CAEzC,OACE,oBAAC,kBAAD;EACQ;EACN,cAAc;EACd,cAAc,QAAQ,OAAO,gBAAgB,MAAM,UAAU,SAAS;EACtE,UAAU,MAAM,UAAU;CAC3B,CAAA;AAEL"}
1
+ {"version":3,"file":"keyboard-shortcuts.js","names":[],"sources":["../../../src/components/panel/keyboard-shortcuts.tsx"],"sourcesContent":["import type { CommandSchema } from '@alfakit/core'\nimport {\n COMMANDS_COMPONENT,\n commandShortcutId,\n focusTableSearch,\n SHORTCUT_IDS,\n shortcuts,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { useEffect, useState } from 'react'\nimport { useActionRunner, usePageSchema, usePanel, useT } from '../../context'\nimport type { ShortcutsOverlay as Overlay } from './shortcuts-overlay'\n\ntype OverlayComponent = typeof Overlay\n\nlet loadedOverlay: OverlayComponent | undefined\nlet loadingOverlay: Promise<OverlayComponent> | undefined\n\n/** The `?` overlay's code: loaded the first time it opens; the shortcuts register eagerly. */\nfunction loadOverlay(): Promise<OverlayComponent> {\n loadingOverlay ??= import('./shortcuts-overlay').then(\n (module) => (loadedOverlay = module.ShortcutsOverlay),\n (error: unknown) => {\n loadingOverlay = undefined\n throw error\n },\n )\n\n return loadingOverlay\n}\n\n/**\n * Keyboard shortcuts of the panel: `/` (focus the table search), `g d` (dashboard), `?`\n * (the overlay) and the panel commands' `shortcut()`, with the user's customised keys\n * (`alfakit.keyboardShortcuts`). The overlay lists everything available on the page,\n * grouped, and lets users change keys (stored on the server).\n */\nexport function KeyboardShortcuts() {\n const t = useT()\n const panel = usePanel()\n const page = usePageSchema()\n const runner = useActionRunner()\n const config = panel.keyboardShortcuts\n const [open, setOpen] = useState(false)\n // Undefined on the server and during hydration (nothing loads there), so the markup matches.\n const [ShortcutsOverlay, setOverlay] = useState<OverlayComponent | undefined>(() => loadedOverlay)\n const overrides = JSON.stringify(config ? (config.overrides ?? {}) : {})\n const token = page?.token ?? ''\n const commands = panel.commandPalette?.commands\n const home = panel.dashboardUrl ?? panel.url\n // Strings, so the effect below doesn't re-run on every render (`t` is a new function each time).\n const overlayLabel = t('shortcuts.show_overlay', {}, 'Show keyboard shortcuts')\n const searchLabel = t('shortcuts.focus_search', {}, 'Search the table')\n const dashboardLabel = t('shortcuts.go_dashboard', {}, 'Go to the dashboard')\n\n useEffect(() => {\n shortcuts.setEnabled(config !== false)\n shortcuts.setOverrides(JSON.parse(overrides) as Record<string, string[]>)\n }, [config, overrides])\n\n useEffect(() => {\n if (!config) return\n\n const disposers = [\n shortcuts.register({\n id: SHORTCUT_IDS.overlay,\n keys: ['?'],\n label: overlayLabel,\n group: 'general',\n handler: () => setOpen(true),\n }),\n // Declined (`false`) on pages without a table search, so `/` keeps its default.\n shortcuts.register({\n id: SHORTCUT_IDS.search,\n keys: ['/'],\n label: searchLabel,\n group: 'general',\n handler: () => focusTableSearch(),\n }),\n ...(home\n ? [\n shortcuts.register({\n id: SHORTCUT_IDS.dashboard,\n keys: ['g d'],\n label: dashboardLabel,\n group: 'navigation',\n handler: () => router.visit(home),\n }),\n ]\n : []),\n ...(commands ?? [])\n .filter((command: CommandSchema) => command.keyBindings?.length && !command.disabled)\n .map((command: CommandSchema) =>\n shortcuts.register({\n id: commandShortcutId(command.name),\n keys: command.keyBindings ?? [],\n label: command.label ?? command.name,\n group: command.url ? 'navigation' : 'commands',\n handler: () => void runner.trigger(command, { token, component: COMMANDS_COMPONENT }),\n }),\n ),\n ]\n\n return () => disposers.forEach((dispose) => dispose())\n }, [config, commands, home, runner, token, overlayLabel, searchLabel, dashboardLabel])\n\n // `?` (or the palette's \"Keyboard shortcuts\") before the code has arrived keeps `open`\n // set; the overlay mounts open once it has loaded.\n useEffect(() => {\n if (!open || ShortcutsOverlay) return\n\n let active = true\n\n loadOverlay().then(\n (component) => active && setOverlay(() => component),\n () => active && setOpen(false),\n )\n\n return () => {\n active = false\n }\n }, [open, ShortcutsOverlay])\n\n if (!config || !ShortcutsOverlay) return null\n\n return (\n <ShortcutsOverlay\n open={open}\n onOpenChange={setOpen}\n customizable={Boolean(config.customizable && panel.endpoints.shortcuts)}\n endpoint={panel.endpoints.shortcuts}\n />\n )\n}\n"],"mappings":";;;;;;AAeA,IAAI;AACJ,IAAI;;AAGJ,SAAS,cAAyC;CAChD,mBAAmB,OAAO,yBAAsB,CAAC,MAC9C,WAAY,gBAAgB,OAAO,mBACnC,UAAmB;EAClB,iBAAiB,KAAA;EACjB,MAAM;CACR,CACF;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,oBAAoB;CAClC,MAAM,IAAI,KAAK;CACf,MAAM,QAAQ,SAAS;CACvB,MAAM,OAAO,cAAc;CAC3B,MAAM,SAAS,gBAAgB;CAC/B,MAAM,SAAS,MAAM;CACrB,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,MAAM,CAAC,kBAAkB,cAAc,eAA6C,aAAa;CACjG,MAAM,YAAY,KAAK,UAAU,SAAU,OAAO,aAAa,CAAC,IAAK,CAAC,CAAC;CACvE,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,WAAW,MAAM,gBAAgB;CACvC,MAAM,OAAO,MAAM,gBAAgB,MAAM;CAEzC,MAAM,eAAe,EAAE,0BAA0B,CAAC,GAAG,yBAAyB;CAC9E,MAAM,cAAc,EAAE,0BAA0B,CAAC,GAAG,kBAAkB;CACtE,MAAM,iBAAiB,EAAE,0BAA0B,CAAC,GAAG,qBAAqB;CAE5E,gBAAgB;EACd,UAAU,WAAW,WAAW,KAAK;EACrC,UAAU,aAAa,KAAK,MAAM,SAAS,CAA6B;CAC1E,GAAG,CAAC,QAAQ,SAAS,CAAC;CAEtB,gBAAgB;EACd,IAAI,CAAC,QAAQ;EAEb,MAAM,YAAY;GAChB,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,GAAG;IACV,OAAO;IACP,OAAO;IACP,eAAe,QAAQ,IAAI;GAC7B,CAAC;GAED,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,GAAG;IACV,OAAO;IACP,OAAO;IACP,eAAe,iBAAiB;GAClC,CAAC;GACD,GAAI,OACA,CACE,UAAU,SAAS;IACjB,IAAI,aAAa;IACjB,MAAM,CAAC,KAAK;IACZ,OAAO;IACP,OAAO;IACP,eAAe,OAAO,MAAM,IAAI;GAClC,CAAC,CACH,IACA,CAAC;GACL,IAAI,YAAY,CAAC,EAAA,CACd,QAAQ,YAA2B,QAAQ,aAAa,UAAU,CAAC,QAAQ,QAAQ,CAAC,CACpF,KAAK,YACJ,UAAU,SAAS;IACjB,IAAI,kBAAkB,QAAQ,IAAI;IAClC,MAAM,QAAQ,eAAe,CAAC;IAC9B,OAAO,QAAQ,SAAS,QAAQ;IAChC,OAAO,QAAQ,MAAM,eAAe;IACpC,eAAe,KAAK,OAAO,QAAQ,SAAS;KAAE;KAAO,WAAW;IAAmB,CAAC;GACtF,CAAC,CACH;EACJ;EAEA,aAAa,UAAU,SAAS,YAAY,QAAQ,CAAC;CACvD,GAAG;EAAC;EAAQ;EAAU;EAAM;EAAQ;EAAO;EAAc;EAAa;CAAc,CAAC;CAIrF,gBAAgB;EACd,IAAI,CAAC,QAAQ,kBAAkB;EAE/B,IAAI,SAAS;EAEb,YAAY,CAAC,CAAC,MACX,cAAc,UAAU,iBAAiB,SAAS,SAC7C,UAAU,QAAQ,KAAK,CAC/B;EAEA,aAAa;GACX,SAAS;EACX;CACF,GAAG,CAAC,MAAM,gBAAgB,CAAC;CAE3B,IAAI,CAAC,UAAU,CAAC,kBAAkB,OAAO;CAEzC,OACE,oBAAC,kBAAD;EACQ;EACN,cAAc;EACd,cAAc,QAAQ,OAAO,gBAAgB,MAAM,UAAU,SAAS;EACtE,UAAU,MAAM,UAAU;CAC3B,CAAA;AAEL"}
@@ -1 +1 @@
1
- {"version":3,"file":"locale-switcher.d.ts","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"mappings":";;;;;wBAqDgB,kCAAc,IAAA;;;;;;wBA0Cd,wCAAoB,IAAA;;;;;wBAwBpB,iCAAa,IAAA"}
1
+ {"version":3,"file":"locale-switcher.d.ts","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"mappings":";;;;;wBAsDgB,kCAAc,IAAA;;;;;;wBA0Cd,wCAAoB,IAAA;;;;;wBAwBpB,iCAAa,IAAA"}
@@ -12,6 +12,7 @@ function useSwitch() {
12
12
  if (panel.endpoints.locale && code !== panel.localization.code) switchLocale(panel.endpoints.locale, code);
13
13
  };
14
14
  }
15
+ /** The panel's languages as radio items, each in its own name: picking one switches to it. */
15
16
  function LocaleOptions() {
16
17
  const localization = useLocalization();
17
18
  const change = useSwitch();
@@ -108,6 +109,6 @@ function LocaleSubMenu() {
108
109
  })] });
109
110
  }
110
111
  //#endregion
111
- export { LocaleSubMenu, LocaleSwitcher, TopbarLocaleSwitcher };
112
+ export { LocaleOptions, LocaleSubMenu, LocaleSwitcher, TopbarLocaleSwitcher };
112
113
 
113
114
  //# sourceMappingURL=locale-switcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"locale-switcher.js","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"sourcesContent":["import { collapsesLanguageSwitcher, showsLanguageSwitcher, switchLocale } from '@alfakit/core'\nimport { LanguagesIcon } from 'lucide-react'\nimport { createElement } from 'react'\nimport { useLocalization, useOptionalRegistry, usePanel, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\n\nfunction useSwitch() {\n const panel = usePanel()\n\n return (code: string) => {\n if (panel.endpoints.locale && code !== panel.localization.code) {\n switchLocale(panel.endpoints.locale, code)\n }\n }\n}\n\nfunction LocaleOptions() {\n const localization = useLocalization()\n const change = useSwitch()\n\n return (\n <DropdownMenuRadioGroup value={localization.code} onValueChange={change}>\n {localization.locales.map((locale) => (\n <DropdownMenuRadioItem\n key={locale.code}\n value={locale.code}\n lang={locale.tag}\n data-current={locale.code === localization.code ? 'true' : undefined}\n className=\"data-[current=true]:font-medium\"\n >\n <span lang={locale.tag} dir={locale.direction}>\n {locale.label}\n </span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n )\n}\n\n/**\n * Stand-alone language menu: a languages button listing each language in its own name\n * (auth pages, top bar). Renders nothing without a switcher.\n */\nexport function LocaleSwitcher() {\n const t = useT()\n const localization = useLocalization()\n\n if (!showsLanguageSwitcher(localization, 'auth')) {\n return null\n }\n\n return (\n <div data-slot=\"language-switcher\" className=\"flex\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={t('change_language', {}, 'Change language')}\n title={t('change_language', {}, 'Change language')}\n >\n <LanguagesIcon />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\n/** Whether the panel has a user menu that can hold the language entry on phones. */\nfunction useHasUserMenu(): boolean {\n const panel = usePanel()\n\n return Boolean(panel?.user && panel.userMenu)\n}\n\n/**\n * The top bars' language button (`languageSwitcher('topbar' | 'both')`): the registered\n * `panel:language-switcher` component or the built-in menu. Below `sm` it moves into the\n * user menu when there is one (`collapsesLanguageSwitcher()`).\n */\nexport function TopbarLocaleSwitcher() {\n const localization = usePanel()?.localization\n const registry = useOptionalRegistry()\n const collapses = collapsesLanguageSwitcher(localization, useHasUserMenu())\n\n if (!showsLanguageSwitcher(localization, 'topbar')) {\n return null\n }\n\n const button = createElement(registry?.resolve('panel:language-switcher') ?? LocaleSwitcher)\n\n return collapses ? (\n <div className=\"hidden sm:contents\" data-slot=\"topbar-language\">\n {button}\n </div>\n ) : (\n button\n )\n}\n\n/**\n * \"Language\" submenu for the user menu (`languageSwitcher('user-menu' | 'both')`, and\n * below `sm` for `'topbar'`).\n */\nexport function LocaleSubMenu() {\n const t = useT()\n const localization = useLocalization()\n const hasUserMenu = useHasUserMenu()\n const inMenu = showsLanguageSwitcher(localization, 'user-menu')\n // `languageSwitcher('topbar')`: the top bar's button is hidden on phones, so it's here.\n const onPhones = !inMenu && collapsesLanguageSwitcher(localization, hasUserMenu)\n\n if (!inMenu && !onPhones) {\n return null\n }\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className={onPhones ? 'sm:hidden' : undefined}>\n <LanguagesIcon />\n <span>{t('language', {}, 'Language')}</span>\n <span className=\"text-muted-foreground ms-auto ps-2 text-xs\">{localization.label}</span>\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n}\n"],"mappings":";;;;;;;;AAgBA,SAAS,YAAY;CACnB,MAAM,QAAQ,SAAS;CAEvB,QAAQ,SAAiB;EACvB,IAAI,MAAM,UAAU,UAAU,SAAS,MAAM,aAAa,MACxD,aAAa,MAAM,UAAU,QAAQ,IAAI;CAE7C;AACF;AAEA,SAAS,gBAAgB;CACvB,MAAM,eAAe,gBAAgB;CACrC,MAAM,SAAS,UAAU;CAEzB,OACE,oBAAC,wBAAD;EAAwB,OAAO,aAAa;EAAM,eAAe;EAC9D,UAAA,aAAa,QAAQ,KAAK,WACzB,oBAAC,uBAAD;GAEE,OAAO,OAAO;GACd,MAAM,OAAO;GACb,gBAAc,OAAO,SAAS,aAAa,OAAO,SAAS,KAAA;GAC3D,WAAU;GAEV,UAAA,oBAAC,QAAD;IAAM,MAAM,OAAO;IAAK,KAAK,OAAO;IACjC,UAAA,OAAO;GACJ,CAAA;EACe,GAThB,OAAO,IASS,CACxB;CACqB,CAAA;AAE5B;;;;;AAMA,SAAgB,iBAAiB;CAC/B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CAErC,IAAI,CAAC,sBAAsB,cAAc,MAAM,GAC7C,OAAO;CAGT,OACE,oBAAC,OAAD;EAAK,aAAU;EAAoB,WAAU;EAC3C,UAAA,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;GACnB,UAAA,oBAAC,QAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,cAAY,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IACtD,OAAO,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IAEjD,UAAA,oBAAC,eAAD,CAAgB,CAAA;GACV,CAAA;EACW,CAAA,GACrB,oBAAC,qBAAD;GAAqB,OAAM;GAAM,WAAU;GACzC,UAAA,oBAAC,eAAD,CAAgB,CAAA;EACG,CAAA,CACT,EAAA,CAAA;CACX,CAAA;AAET;;AAGA,SAAS,iBAA0B;CACjC,MAAM,QAAQ,SAAS;CAEvB,OAAO,QAAQ,OAAO,QAAQ,MAAM,QAAQ;AAC9C;;;;;;AAOA,SAAgB,uBAAuB;CACrC,MAAM,eAAe,SAAS,CAAC,EAAE;CACjC,MAAM,WAAW,oBAAoB;CACrC,MAAM,YAAY,0BAA0B,cAAc,eAAe,CAAC;CAE1E,IAAI,CAAC,sBAAsB,cAAc,QAAQ,GAC/C,OAAO;CAGT,MAAM,SAAS,cAAc,UAAU,QAAQ,yBAAyB,KAAK,cAAc;CAE3F,OAAO,YACL,oBAAC,OAAD;EAAK,WAAU;EAAqB,aAAU;EAC3C,UAAA;CACE,CAAA,IAEL;AAEJ;;;;;AAMA,SAAgB,gBAAgB;CAC9B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CACrC,MAAM,cAAc,eAAe;CACnC,MAAM,SAAS,sBAAsB,cAAc,WAAW;CAE9D,MAAM,WAAW,CAAC,UAAU,0BAA0B,cAAc,WAAW;CAE/E,IAAI,CAAC,UAAU,CAAC,UACd,OAAO;CAGT,OACE,qBAAC,iBAAD,EAAA,UAAA,CACE,qBAAC,wBAAD;EAAwB,WAAW,WAAW,cAAc,KAAA;EAA5D,UAAA;GACE,oBAAC,eAAD,CAAgB,CAAA;GAChB,oBAAC,QAAD,EAAA,UAAO,EAAE,YAAY,CAAC,GAAG,UAAU,EAAQ,CAAA;GAC3C,oBAAC,QAAD;IAAM,WAAU;IAA8C,UAAA,aAAa;GAAY,CAAA;EACjE;CACxB,CAAA,GAAA,oBAAC,wBAAD;EAAwB,WAAU;EAChC,UAAA,oBAAC,eAAD,CAAgB,CAAA;CACM,CAAA,CACT,EAAA,CAAA;AAErB"}
1
+ {"version":3,"file":"locale-switcher.js","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"sourcesContent":["import { collapsesLanguageSwitcher, showsLanguageSwitcher, switchLocale } from '@alfakit/core'\nimport { LanguagesIcon } from 'lucide-react'\nimport { createElement } from 'react'\nimport { useLocalization, useOptionalRegistry, usePanel, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\n\nfunction useSwitch() {\n const panel = usePanel()\n\n return (code: string) => {\n if (panel.endpoints.locale && code !== panel.localization.code) {\n switchLocale(panel.endpoints.locale, code)\n }\n }\n}\n\n/** The panel's languages as radio items, each in its own name: picking one switches to it. */\nexport function LocaleOptions() {\n const localization = useLocalization()\n const change = useSwitch()\n\n return (\n <DropdownMenuRadioGroup value={localization.code} onValueChange={change}>\n {localization.locales.map((locale) => (\n <DropdownMenuRadioItem\n key={locale.code}\n value={locale.code}\n lang={locale.tag}\n data-current={locale.code === localization.code ? 'true' : undefined}\n className=\"data-[current=true]:font-medium\"\n >\n <span lang={locale.tag} dir={locale.direction}>\n {locale.label}\n </span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n )\n}\n\n/**\n * Stand-alone language menu: a languages button listing each language in its own name\n * (auth pages, top bar). Renders nothing without a switcher.\n */\nexport function LocaleSwitcher() {\n const t = useT()\n const localization = useLocalization()\n\n if (!showsLanguageSwitcher(localization, 'auth')) {\n return null\n }\n\n return (\n <div data-slot=\"language-switcher\" className=\"flex\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={t('change_language', {}, 'Change language')}\n title={t('change_language', {}, 'Change language')}\n >\n <LanguagesIcon />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\n/** Whether the panel has a user menu that can hold the language entry on phones. */\nfunction useHasUserMenu(): boolean {\n const panel = usePanel()\n\n return Boolean(panel?.user && panel.userMenu)\n}\n\n/**\n * The top bars' language button (`languageSwitcher('topbar' | 'both')`): the registered\n * `panel:language-switcher` component or the built-in menu. Below `sm` it moves into the\n * user menu when there is one (`collapsesLanguageSwitcher()`).\n */\nexport function TopbarLocaleSwitcher() {\n const localization = usePanel()?.localization\n const registry = useOptionalRegistry()\n const collapses = collapsesLanguageSwitcher(localization, useHasUserMenu())\n\n if (!showsLanguageSwitcher(localization, 'topbar')) {\n return null\n }\n\n const button = createElement(registry?.resolve('panel:language-switcher') ?? LocaleSwitcher)\n\n return collapses ? (\n <div className=\"hidden sm:contents\" data-slot=\"topbar-language\">\n {button}\n </div>\n ) : (\n button\n )\n}\n\n/**\n * \"Language\" submenu for the user menu (`languageSwitcher('user-menu' | 'both')`, and\n * below `sm` for `'topbar'`).\n */\nexport function LocaleSubMenu() {\n const t = useT()\n const localization = useLocalization()\n const hasUserMenu = useHasUserMenu()\n const inMenu = showsLanguageSwitcher(localization, 'user-menu')\n // `languageSwitcher('topbar')`: the top bar's button is hidden on phones, so it's here.\n const onPhones = !inMenu && collapsesLanguageSwitcher(localization, hasUserMenu)\n\n if (!inMenu && !onPhones) {\n return null\n }\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className={onPhones ? 'sm:hidden' : undefined}>\n <LanguagesIcon />\n <span>{t('language', {}, 'Language')}</span>\n <span className=\"text-muted-foreground ms-auto ps-2 text-xs\">{localization.label}</span>\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n}\n"],"mappings":";;;;;;;;AAgBA,SAAS,YAAY;CACnB,MAAM,QAAQ,SAAS;CAEvB,QAAQ,SAAiB;EACvB,IAAI,MAAM,UAAU,UAAU,SAAS,MAAM,aAAa,MACxD,aAAa,MAAM,UAAU,QAAQ,IAAI;CAE7C;AACF;;AAGA,SAAgB,gBAAgB;CAC9B,MAAM,eAAe,gBAAgB;CACrC,MAAM,SAAS,UAAU;CAEzB,OACE,oBAAC,wBAAD;EAAwB,OAAO,aAAa;EAAM,eAAe;EAC9D,UAAA,aAAa,QAAQ,KAAK,WACzB,oBAAC,uBAAD;GAEE,OAAO,OAAO;GACd,MAAM,OAAO;GACb,gBAAc,OAAO,SAAS,aAAa,OAAO,SAAS,KAAA;GAC3D,WAAU;GAEV,UAAA,oBAAC,QAAD;IAAM,MAAM,OAAO;IAAK,KAAK,OAAO;IACjC,UAAA,OAAO;GACJ,CAAA;EACe,GAThB,OAAO,IASS,CACxB;CACqB,CAAA;AAE5B;;;;;AAMA,SAAgB,iBAAiB;CAC/B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CAErC,IAAI,CAAC,sBAAsB,cAAc,MAAM,GAC7C,OAAO;CAGT,OACE,oBAAC,OAAD;EAAK,aAAU;EAAoB,WAAU;EAC3C,UAAA,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;GACnB,UAAA,oBAAC,QAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,cAAY,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IACtD,OAAO,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IAEjD,UAAA,oBAAC,eAAD,CAAgB,CAAA;GACV,CAAA;EACW,CAAA,GACrB,oBAAC,qBAAD;GAAqB,OAAM;GAAM,WAAU;GACzC,UAAA,oBAAC,eAAD,CAAgB,CAAA;EACG,CAAA,CACT,EAAA,CAAA;CACX,CAAA;AAET;;AAGA,SAAS,iBAA0B;CACjC,MAAM,QAAQ,SAAS;CAEvB,OAAO,QAAQ,OAAO,QAAQ,MAAM,QAAQ;AAC9C;;;;;;AAOA,SAAgB,uBAAuB;CACrC,MAAM,eAAe,SAAS,CAAC,EAAE;CACjC,MAAM,WAAW,oBAAoB;CACrC,MAAM,YAAY,0BAA0B,cAAc,eAAe,CAAC;CAE1E,IAAI,CAAC,sBAAsB,cAAc,QAAQ,GAC/C,OAAO;CAGT,MAAM,SAAS,cAAc,UAAU,QAAQ,yBAAyB,KAAK,cAAc;CAE3F,OAAO,YACL,oBAAC,OAAD;EAAK,WAAU;EAAqB,aAAU;EAC3C,UAAA;CACE,CAAA,IAEL;AAEJ;;;;;AAMA,SAAgB,gBAAgB;CAC9B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CACrC,MAAM,cAAc,eAAe;CACnC,MAAM,SAAS,sBAAsB,cAAc,WAAW;CAE9D,MAAM,WAAW,CAAC,UAAU,0BAA0B,cAAc,WAAW;CAE/E,IAAI,CAAC,UAAU,CAAC,UACd,OAAO;CAGT,OACE,qBAAC,iBAAD,EAAA,UAAA,CACE,qBAAC,wBAAD;EAAwB,WAAW,WAAW,cAAc,KAAA;EAA5D,UAAA;GACE,oBAAC,eAAD,CAAgB,CAAA;GAChB,oBAAC,QAAD,EAAA,UAAO,EAAE,YAAY,CAAC,GAAG,UAAU,EAAQ,CAAA;GAC3C,oBAAC,QAAD;IAAM,WAAU;IAA8C,UAAA,aAAa;GAAY,CAAA;EACjE;CACxB,CAAA,GAAA,oBAAC,wBAAD;EAAwB,WAAU;EAChC,UAAA,oBAAC,eAAD,CAAgB,CAAA;CACM,CAAA,CACT,EAAA,CAAA;AAErB"}
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","names":["TooltipPrimitive","Tooltip"],"sources":["../../../src/components/ui/tooltip.tsx"],"mappings":";;;iBAIS,kBACP,kBACG,SACF,sBAAsBA,QAAiB,4BAAS,IAAA;iBAU1CC,eAAa,SAAS,sBAAsBD,QAAiB,wBAAK,IAAA;iBAIlE,oBAAoB,SAAS,sBAAsBA,QAAiB,2BAAQ,IAAA;iBAI5E,iBACP,WACA,YACA,aACG,SACF,sBAAsBA,QAAiB,2BAAQ,IAAA"}
1
+ {"version":3,"file":"tooltip.d.ts","names":["TooltipPrimitive","Tooltip"],"sources":["../../../src/components/ui/tooltip.tsx"],"mappings":";;;iBAIS,kBACP,kBACG,SACF,sBAAsBA,QAAiB,4BAAS,IAAA;iBAU1CC,eAAa,SAAS,sBAAsBD,QAAiB,wBAAK,IAAA;iBAIlE,oBAAoB,SAAS,sBAAsBA,QAAiB,2BAAQ,IAAA;iBAa5E,iBACP,WACA,YACA,aACG,SACF,sBAAsBA,QAAiB,2BAAQ,IAAA"}
@@ -17,7 +17,7 @@ function Tooltip$1({ ...props }) {
17
17
  }
18
18
  function TooltipTrigger({ ...props }) {
19
19
  return /* @__PURE__ */ jsx(Tooltip.Trigger, {
20
- "data-slot": "tooltip-trigger",
20
+ ...props.asChild ? {} : { "data-slot": "tooltip-trigger" },
21
21
  ...props
22
22
  });
23
23
  }