@alfakit/react 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/components/entries/other-entries.d.ts.map +1 -1
- package/dist/components/entries/other-entries.js +1 -1
- package/dist/components/entries/other-entries.js.map +1 -1
- package/dist/components/fields/builder.d.ts.map +1 -1
- package/dist/components/fields/builder.js +3 -3
- package/dist/components/fields/builder.js.map +1 -1
- package/dist/components/fields/file-upload.d.ts.map +1 -1
- package/dist/components/fields/file-upload.js +2 -5
- package/dist/components/fields/file-upload.js.map +1 -1
- package/dist/components/fields/repeater.d.ts.map +1 -1
- package/dist/components/fields/repeater.js +3 -3
- package/dist/components/fields/repeater.js.map +1 -1
- package/dist/components/fields/rich-editor-extensions.js +5 -2
- package/dist/components/fields/rich-editor-extensions.js.map +1 -1
- package/dist/components/layout/wizard.d.ts.map +1 -1
- package/dist/components/layout/wizard.js +4 -1
- package/dist/components/layout/wizard.js.map +1 -1
- package/dist/components/notifications/flash-toasts.d.ts.map +1 -1
- package/dist/components/notifications/flash-toasts.js +11 -1
- package/dist/components/notifications/flash-toasts.js.map +1 -1
- package/dist/components/notifications/notifications-menu.js +3 -3
- package/dist/components/notifications/notifications-menu.js.map +1 -1
- package/dist/components/panel/pwa.d.ts.map +1 -1
- package/dist/components/panel/pwa.js +8 -7
- package/dist/components/panel/pwa.js.map +1 -1
- package/dist/components/panel/shortcuts-overlay.js +1 -1
- package/dist/components/panel/shortcuts-overlay.js.map +1 -1
- package/dist/components/table/cells.js +1 -1
- package/dist/components/table/cells.js.map +1 -1
- package/dist/components.css +19 -0
- package/dist/eject-manifest.json +2 -2
- package/dist/pages/page.js +1 -1
- package/dist/pages/page.js.map +1 -1
- package/package.json +2 -2
- package/src/components/entries/other-entries.tsx +2 -0
- package/src/components/fields/builder.tsx +7 -2
- package/src/components/fields/file-upload.tsx +2 -7
- package/src/components/fields/repeater.tsx +6 -3
- package/src/components/fields/rich-editor-extensions.tsx +9 -5
- package/src/components/layout/wizard.tsx +5 -1
- package/src/components/notifications/flash-toasts.tsx +8 -0
- package/src/components/notifications/notifications-menu.tsx +3 -3
- package/src/components/panel/pwa.tsx +23 -9
- package/src/components/panel/shortcuts-overlay.tsx +1 -1
- package/src/components/table/cells.tsx +3 -1
- package/src/pages/page.tsx +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pwa.js","names":[],"sources":["../../../src/components/panel/pwa.tsx"],"sourcesContent":["import {\n applyPwaUpdate,\n canInstallPwa,\n promptPwaInstall,\n pwaStore,\n startPwa,\n subscribeToPush,\n unsubscribeFromPush,\n type Translator,\n} from '@alfakit/core'\nimport { BellIcon, BellOffIcon, DownloadIcon } from 'lucide-react'\nimport { useEffect } from 'react'\nimport { toast } from 'sonner'\nimport { usePanel, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { Icon } from '../../lib/icons'\nimport { DropdownMenuItem } from '../ui/dropdown-menu'\n\nconst UPDATE_TOAST_ID = 'alfakit-pwa-update'\n\n/** Show the browser's install prompt, or the \"Add to Home Screen\" hint on iOS Safari. */\nexport async function installPwa(t: Translator['t']): Promise<void> {\n if ((await promptPwaInstall()) === 'ios') {\n toast.info(\n t(\n 'pwa.ios_hint',\n {},\n 'To install this app, tap the Share button and then “Add to Home Screen”.',\n ),\n { duration: 10000 },\n )\n }\n}\n\n/**\n * `Panel::pwa()`: registers the service worker and offers \"Reload\" when a new version\n * waits. Renders nothing; does nothing without `pwa` or during a server render.\n */\nexport function PwaManager() {\n const panel = usePanel()\n const t = useT()\n const pwa = panel?.pwa\n const { updateAvailable } = useStore(pwaStore)\n\n useEffect(() => {\n startPwa(pwa)\n }, [pwa])\n\n useEffect(() => {\n if (!updateAvailable) return\n\n toast(t('pwa.update_available', {}, 'A new version is available')
|
|
1
|
+
{"version":3,"file":"pwa.js","names":[],"sources":["../../../src/components/panel/pwa.tsx"],"sourcesContent":["import {\n applyPwaUpdate,\n canInstallPwa,\n promptPwaInstall,\n pwaStore,\n startPwa,\n subscribeToPush,\n unsubscribeFromPush,\n type Translator,\n} from '@alfakit/core'\nimport { BellIcon, BellOffIcon, DownloadIcon } from 'lucide-react'\nimport { useEffect } from 'react'\nimport { toast } from 'sonner'\nimport { usePanel, useT } from '../../context'\nimport { useStore } from '../../hooks/use-store'\nimport { Icon } from '../../lib/icons'\nimport { toastOptions } from '../notifications/flash-toasts'\nimport { DropdownMenuItem } from '../ui/dropdown-menu'\n\nconst UPDATE_TOAST_ID = 'alfakit-pwa-update'\n\n/** Show the browser's install prompt, or the \"Add to Home Screen\" hint on iOS Safari. */\nexport async function installPwa(t: Translator['t']): Promise<void> {\n if ((await promptPwaInstall()) === 'ios') {\n toast.info(\n t(\n 'pwa.ios_hint',\n {},\n 'To install this app, tap the Share button and then “Add to Home Screen”.',\n ),\n toastOptions({ duration: 10000 }),\n )\n }\n}\n\n/**\n * `Panel::pwa()`: registers the service worker and offers \"Reload\" when a new version\n * waits. Renders nothing; does nothing without `pwa` or during a server render.\n */\nexport function PwaManager() {\n const panel = usePanel()\n const t = useT()\n const pwa = panel?.pwa\n const { updateAvailable } = useStore(pwaStore)\n\n useEffect(() => {\n startPwa(pwa)\n }, [pwa])\n\n useEffect(() => {\n if (!updateAvailable) return\n\n toast(\n t('pwa.update_available', {}, 'A new version is available'),\n toastOptions({\n id: UPDATE_TOAST_ID,\n duration: Infinity,\n action: { label: t('pwa.reload', {}, 'Reload'), onClick: () => applyPwaUpdate() },\n }),\n )\n }, [updateAvailable, t])\n\n return null\n}\n\n/** User menu entries: \"Install app\" and turning Web Push on or off for this browser. */\nexport function PwaMenuItems() {\n const panel = usePanel()\n const t = useT()\n const state = useStore(pwaStore)\n\n if (!panel?.pwa) return null\n\n const push = panel.pwa.push && state.push !== 'unsupported'\n\n const togglePush = async () => {\n const outcome =\n state.push === 'subscribed' ? await unsubscribeFromPush() : await subscribeToPush()\n\n if (outcome === 'subscribed') {\n toast.success(\n t('pwa.notifications.enabled', {}, 'Notifications are on for this device'),\n toastOptions(),\n )\n } else if (outcome === 'unsubscribed') {\n toast.success(\n t('pwa.notifications.disabled', {}, 'Notifications are off for this device'),\n toastOptions(),\n )\n } else if (outcome === 'denied') {\n toast.error(\n t('pwa.notifications.blocked', {}, 'Notifications are blocked in your browser settings'),\n toastOptions(),\n )\n } else {\n toast.error(\n t('pwa.notifications.failed', {}, 'Couldn’t change notifications. Try again.'),\n toastOptions(),\n )\n }\n }\n\n return (\n <>\n {canInstallPwa(state) && (\n <DropdownMenuItem data-slot=\"pwa-install\" onSelect={() => void installPwa(t)}>\n <Icon name=\"alfakit::panels.user-menu.install-app-item\" fallback={DownloadIcon} />{' '}\n {t('pwa.install', {}, 'Install app')}\n </DropdownMenuItem>\n )}\n {push && (\n <DropdownMenuItem\n data-slot=\"pwa-push\"\n disabled={state.pushBusy || state.push === 'denied'}\n onSelect={() => void togglePush()}\n >\n {state.push === 'subscribed' ? (\n <>\n <Icon name=\"alfakit::panels.user-menu.push-off-item\" fallback={BellOffIcon} />{' '}\n {t('pwa.notifications.disable', {}, 'Turn off notifications')}\n </>\n ) : (\n <>\n <Icon name=\"alfakit::panels.user-menu.push-on-item\" fallback={BellIcon} />{' '}\n {t('pwa.notifications.enable', {}, 'Turn on notifications')}\n </>\n )}\n </DropdownMenuItem>\n )}\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;AAmBA,MAAM,kBAAkB;;AAGxB,eAAsB,WAAW,GAAmC;CAClE,IAAK,MAAM,iBAAiB,MAAO,OACjC,MAAM,KACJ,EACE,gBACA,CAAC,GACD,0EACF,GACA,aAAa,EAAE,UAAU,IAAM,CAAC,CAClC;AAEJ;;;;;AAMA,SAAgB,aAAa;CAC3B,MAAM,QAAQ,SAAS;CACvB,MAAM,IAAI,KAAK;CACf,MAAM,MAAM,OAAO;CACnB,MAAM,EAAE,oBAAoB,SAAS,QAAQ;CAE7C,gBAAgB;EACd,SAAS,GAAG;CACd,GAAG,CAAC,GAAG,CAAC;CAER,gBAAgB;EACd,IAAI,CAAC,iBAAiB;EAEtB,MACE,EAAE,wBAAwB,CAAC,GAAG,4BAA4B,GAC1D,aAAa;GACX,IAAI;GACJ,UAAU;GACV,QAAQ;IAAE,OAAO,EAAE,cAAc,CAAC,GAAG,QAAQ;IAAG,eAAe,eAAe;GAAE;EAClF,CAAC,CACH;CACF,GAAG,CAAC,iBAAiB,CAAC,CAAC;CAEvB,OAAO;AACT;;AAGA,SAAgB,eAAe;CAC7B,MAAM,QAAQ,SAAS;CACvB,MAAM,IAAI,KAAK;CACf,MAAM,QAAQ,SAAS,QAAQ;CAE/B,IAAI,CAAC,OAAO,KAAK,OAAO;CAExB,MAAM,OAAO,MAAM,IAAI,QAAQ,MAAM,SAAS;CAE9C,MAAM,aAAa,YAAY;EAC7B,MAAM,UACJ,MAAM,SAAS,eAAe,MAAM,oBAAoB,IAAI,MAAM,gBAAgB;EAEpF,IAAI,YAAY,cACd,MAAM,QACJ,EAAE,6BAA6B,CAAC,GAAG,sCAAsC,GACzE,aAAa,CACf;OACK,IAAI,YAAY,gBACrB,MAAM,QACJ,EAAE,8BAA8B,CAAC,GAAG,uCAAuC,GAC3E,aAAa,CACf;OACK,IAAI,YAAY,UACrB,MAAM,MACJ,EAAE,6BAA6B,CAAC,GAAG,oDAAoD,GACvF,aAAa,CACf;OAEA,MAAM,MACJ,EAAE,4BAA4B,CAAC,GAAG,2CAA2C,GAC7E,aAAa,CACf;CAEJ;CAEA,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,cAAc,KAAK,KAClB,qBAAC,kBAAD;EAAkB,aAAU;EAAc,gBAAgB,KAAK,WAAW,CAAC;EAA3E,UAAA;GACE,oBAAC,MAAD;IAAM,MAAK;IAA6C,UAAU;GAAe,CAAA;GAAE;GAClF,EAAE,eAAe,CAAC,GAAG,aAAa;EACnB;CAEnB,CAAA,GAAA,QACC,oBAAC,kBAAD;EACE,aAAU;EACV,UAAU,MAAM,YAAY,MAAM,SAAS;EAC3C,gBAAgB,KAAK,WAAW;EAE/B,UAAA,MAAM,SAAS,eACd,qBAAA,YAAA,EAAA,UAAA;GACE,oBAAC,MAAD;IAAM,MAAK;IAA0C,UAAU;GAAc,CAAA;GAAE;GAC9E,EAAE,6BAA6B,CAAC,GAAG,wBAAwB;EAC5D,EAAA,CAAA,IAEF,qBAAA,YAAA,EAAA,UAAA;GACE,oBAAC,MAAD;IAAM,MAAK;IAAyC,UAAU;GAAW,CAAA;GAAE;GAC1E,EAAE,4BAA4B,CAAC,GAAG,uBAAuB;EAC1D,EAAA,CAAA;CAEY,CAAA,CAEpB,EAAA,CAAA;AAEN"}
|
|
@@ -140,7 +140,7 @@ function ShortcutsOverlay({ open, onOpenChange, customizable, endpoint }) {
|
|
|
140
140
|
type: "button",
|
|
141
141
|
variant: "ghost",
|
|
142
142
|
size: "xs",
|
|
143
|
-
"aria-label": t("shortcuts.reset_label", { label: item.label }, `
|
|
143
|
+
"aria-label": t("shortcuts.reset_label", { label: item.label }, `Reset the shortcut for ${item.label}`),
|
|
144
144
|
onClick: () => void save(item.id, null),
|
|
145
145
|
children: t("shortcuts.reset", {}, "Reset")
|
|
146
146
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shortcuts-overlay.js","names":[],"sources":["../../../src/components/panel/shortcuts-overlay.tsx"],"sourcesContent":["import type { ShortcutListGroup, ShortcutListItem } from '@alfakit/core'\nimport {\n ShortcutRecorder,\n flattenActionGroup,\n saveShortcutOverrides,\n shortcutOverlayGroups,\n shortcuts,\n} from '@alfakit/core'\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport { usePageSchema, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog'\nimport { ShortcutKeys } from './shortcut-keys'\n\nconst subscribe = (listener: () => void) => shortcuts.subscribe(listener)\nconst version = () => shortcuts.getVersion()\n\n/**\n * The `?` overlay: every shortcut available on the page, grouped; users may change their\n * keys (stored on the server). `KeyboardShortcuts` loads it the first time it opens.\n */\nexport function ShortcutsOverlay({\n open,\n onOpenChange,\n customizable,\n endpoint,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n customizable: boolean\n endpoint?: string\n}) {\n const t = useT()\n const page = usePageSchema()\n useSyncExternalStore(subscribe, version, version)\n const [recording, setRecording] = useState<string | null>(null)\n const [message, setMessage] = useState('')\n\n const groups = shortcutOverlayGroups(\n shortcuts.list(),\n flattenActionGroup(page?.headerActions ?? []),\n )\n\n const save = async (id: string, keys: string[] | null) => {\n if (!endpoint) return\n\n try {\n shortcuts.setOverrides(await saveShortcutOverrides(endpoint, { [id]: keys }))\n setMessage(t('shortcuts.saved', {}, 'Shortcut saved.'))\n } catch {\n setMessage(t('shortcuts.save_failed', {}, 'The shortcut could not be saved.'))\n }\n }\n\n // While recording, the keys pressed are the new binding: one key (with modifiers) or a\n // sequence (\"g o\"), finished a second after the last key (Escape cancels). Listening in\n // the capture phase keeps the keys from closing the dialog or running another shortcut.\n useEffect(() => {\n if (!recording) return\n\n shortcuts.setPaused(true)\n\n const recorder = new ShortcutRecorder({\n onDone: (binding) => {\n setRecording(null)\n\n const conflict = shortcuts.conflict([binding], recording)\n\n if (conflict) {\n setMessage(\n t(\n 'shortcuts.conflict',\n { label: conflict.label },\n `Already used by “${conflict.label}”.`,\n ),\n )\n\n return\n }\n\n void save(recording, [binding])\n },\n })\n\n const listener = (event: KeyboardEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (event.key === 'Escape') {\n recorder.cancel()\n setRecording(null)\n setMessage(t('shortcuts.cancelled', {}, 'Change cancelled.'))\n\n return\n }\n\n recorder.press(event)\n }\n\n window.addEventListener('keydown', listener, true)\n\n return () => {\n recorder.cancel()\n window.removeEventListener('keydown', listener, true)\n shortcuts.setPaused(false)\n }\n // `save` and `t` only change with the endpoint and the locale.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [recording])\n\n const groupLabel = (group: ShortcutListGroup['group']) =>\n ({\n general: t('shortcuts.groups.general', {}, 'General'),\n navigation: t('shortcuts.groups.navigation', {}, 'Navigation'),\n commands: t('shortcuts.groups.commands', {}, 'Commands'),\n actions: t('shortcuts.groups.actions', {}, 'Actions on this page'),\n })[group]\n\n const change = (item: ShortcutListItem) => {\n setMessage(\n t(\n 'shortcuts.recording',\n { label: item.label },\n `Press the new keys for “${item.label}”. Escape cancels.`,\n ),\n )\n setRecording(item.id)\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n setRecording(null)\n setMessage('')\n onOpenChange(next)\n }}\n >\n <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n <div data-slot=\"shortcuts-overlay\" className=\"flex flex-col gap-4\">\n <DialogHeader>\n <DialogTitle>{t('shortcuts.title', {}, 'Keyboard shortcuts')}</DialogTitle>\n <DialogDescription>\n {t('shortcuts.description', {}, 'Shortcuts available on this page.')}\n </DialogDescription>\n </DialogHeader>\n <div role=\"status\" aria-live=\"polite\" className=\"text-muted-foreground min-h-5 text-sm\">\n {message}\n </div>\n <div className=\"flex flex-col gap-5\">\n {groups.map((group) => (\n <section\n key={group.group}\n data-group={group.group}\n aria-labelledby={`shortcuts-${group.group}`}\n >\n <h3\n id={`shortcuts-${group.group}`}\n className=\"text-muted-foreground mb-2 text-xs font-medium uppercase\"\n >\n {groupLabel(group.group)}\n </h3>\n <ul className=\"flex flex-col divide-y rounded-md border\">\n {group.items.map((item) => (\n <li\n key={item.id}\n data-shortcut={item.id}\n className=\"flex items-center gap-3 px-3 py-2 text-sm\"\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {recording === item.id ? (\n <span className=\"text-primary text-xs\">\n {t('shortcuts.press_keys', {}, 'Press keys…')}\n </span>\n ) : item.keys.length === 0 ? (\n <span className=\"text-muted-foreground text-xs\">\n {t('shortcuts.off', {}, 'Off')}\n </span>\n ) : (\n <span className=\"flex flex-wrap items-center justify-end gap-2\">\n {item.keys.map((key) => (\n <ShortcutKeys key={key} binding={key} />\n ))}\n </span>\n )}\n {customizable && item.customizable && (\n <span className=\"flex shrink-0 gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.change_label',\n { label: item.label },\n `Change the shortcut for ${item.label}`,\n )}\n onClick={() => change(item)}\n >\n {t('shortcuts.change', {}, 'Change')}\n </Button>\n {item.keys.length > 0 && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.turn_off_label',\n { label: item.label },\n `Turn off the shortcut for ${item.label}`,\n )}\n onClick={() => void save(item.id, [])}\n >\n {t('shortcuts.turn_off', {}, 'Turn off')}\n </Button>\n )}\n {item.customized && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.reset_label',\n { label: item.label },\n `Restore the default shortcut for ${item.label}`,\n )}\n onClick={() => void save(item.id, null)}\n >\n {t('shortcuts.reset', {}, 'Reset')}\n </Button>\n )}\n </span>\n )}\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shortcuts-overlay.js","names":[],"sources":["../../../src/components/panel/shortcuts-overlay.tsx"],"sourcesContent":["import type { ShortcutListGroup, ShortcutListItem } from '@alfakit/core'\nimport {\n ShortcutRecorder,\n flattenActionGroup,\n saveShortcutOverrides,\n shortcutOverlayGroups,\n shortcuts,\n} from '@alfakit/core'\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport { usePageSchema, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog'\nimport { ShortcutKeys } from './shortcut-keys'\n\nconst subscribe = (listener: () => void) => shortcuts.subscribe(listener)\nconst version = () => shortcuts.getVersion()\n\n/**\n * The `?` overlay: every shortcut available on the page, grouped; users may change their\n * keys (stored on the server). `KeyboardShortcuts` loads it the first time it opens.\n */\nexport function ShortcutsOverlay({\n open,\n onOpenChange,\n customizable,\n endpoint,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n customizable: boolean\n endpoint?: string\n}) {\n const t = useT()\n const page = usePageSchema()\n useSyncExternalStore(subscribe, version, version)\n const [recording, setRecording] = useState<string | null>(null)\n const [message, setMessage] = useState('')\n\n const groups = shortcutOverlayGroups(\n shortcuts.list(),\n flattenActionGroup(page?.headerActions ?? []),\n )\n\n const save = async (id: string, keys: string[] | null) => {\n if (!endpoint) return\n\n try {\n shortcuts.setOverrides(await saveShortcutOverrides(endpoint, { [id]: keys }))\n setMessage(t('shortcuts.saved', {}, 'Shortcut saved.'))\n } catch {\n setMessage(t('shortcuts.save_failed', {}, 'The shortcut could not be saved.'))\n }\n }\n\n // While recording, the keys pressed are the new binding: one key (with modifiers) or a\n // sequence (\"g o\"), finished a second after the last key (Escape cancels). Listening in\n // the capture phase keeps the keys from closing the dialog or running another shortcut.\n useEffect(() => {\n if (!recording) return\n\n shortcuts.setPaused(true)\n\n const recorder = new ShortcutRecorder({\n onDone: (binding) => {\n setRecording(null)\n\n const conflict = shortcuts.conflict([binding], recording)\n\n if (conflict) {\n setMessage(\n t(\n 'shortcuts.conflict',\n { label: conflict.label },\n `Already used by “${conflict.label}”.`,\n ),\n )\n\n return\n }\n\n void save(recording, [binding])\n },\n })\n\n const listener = (event: KeyboardEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (event.key === 'Escape') {\n recorder.cancel()\n setRecording(null)\n setMessage(t('shortcuts.cancelled', {}, 'Change cancelled.'))\n\n return\n }\n\n recorder.press(event)\n }\n\n window.addEventListener('keydown', listener, true)\n\n return () => {\n recorder.cancel()\n window.removeEventListener('keydown', listener, true)\n shortcuts.setPaused(false)\n }\n // `save` and `t` only change with the endpoint and the locale.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [recording])\n\n const groupLabel = (group: ShortcutListGroup['group']) =>\n ({\n general: t('shortcuts.groups.general', {}, 'General'),\n navigation: t('shortcuts.groups.navigation', {}, 'Navigation'),\n commands: t('shortcuts.groups.commands', {}, 'Commands'),\n actions: t('shortcuts.groups.actions', {}, 'Actions on this page'),\n })[group]\n\n const change = (item: ShortcutListItem) => {\n setMessage(\n t(\n 'shortcuts.recording',\n { label: item.label },\n `Press the new keys for “${item.label}”. Escape cancels.`,\n ),\n )\n setRecording(item.id)\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n setRecording(null)\n setMessage('')\n onOpenChange(next)\n }}\n >\n <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n <div data-slot=\"shortcuts-overlay\" className=\"flex flex-col gap-4\">\n <DialogHeader>\n <DialogTitle>{t('shortcuts.title', {}, 'Keyboard shortcuts')}</DialogTitle>\n <DialogDescription>\n {t('shortcuts.description', {}, 'Shortcuts available on this page.')}\n </DialogDescription>\n </DialogHeader>\n <div role=\"status\" aria-live=\"polite\" className=\"text-muted-foreground min-h-5 text-sm\">\n {message}\n </div>\n <div className=\"flex flex-col gap-5\">\n {groups.map((group) => (\n <section\n key={group.group}\n data-group={group.group}\n aria-labelledby={`shortcuts-${group.group}`}\n >\n <h3\n id={`shortcuts-${group.group}`}\n className=\"text-muted-foreground mb-2 text-xs font-medium uppercase\"\n >\n {groupLabel(group.group)}\n </h3>\n <ul className=\"flex flex-col divide-y rounded-md border\">\n {group.items.map((item) => (\n <li\n key={item.id}\n data-shortcut={item.id}\n className=\"flex items-center gap-3 px-3 py-2 text-sm\"\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {recording === item.id ? (\n <span className=\"text-primary text-xs\">\n {t('shortcuts.press_keys', {}, 'Press keys…')}\n </span>\n ) : item.keys.length === 0 ? (\n <span className=\"text-muted-foreground text-xs\">\n {t('shortcuts.off', {}, 'Off')}\n </span>\n ) : (\n <span className=\"flex flex-wrap items-center justify-end gap-2\">\n {item.keys.map((key) => (\n <ShortcutKeys key={key} binding={key} />\n ))}\n </span>\n )}\n {customizable && item.customizable && (\n <span className=\"flex shrink-0 gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.change_label',\n { label: item.label },\n `Change the shortcut for ${item.label}`,\n )}\n onClick={() => change(item)}\n >\n {t('shortcuts.change', {}, 'Change')}\n </Button>\n {item.keys.length > 0 && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.turn_off_label',\n { label: item.label },\n `Turn off the shortcut for ${item.label}`,\n )}\n onClick={() => void save(item.id, [])}\n >\n {t('shortcuts.turn_off', {}, 'Turn off')}\n </Button>\n )}\n {item.customized && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t(\n 'shortcuts.reset_label',\n { label: item.label },\n `Reset the shortcut for ${item.label}`,\n )}\n onClick={() => void save(item.id, null)}\n >\n {t('shortcuts.reset', {}, 'Reset')}\n </Button>\n )}\n </span>\n )}\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
|
|
@@ -126,7 +126,7 @@ function ImageCell({ column, cell }) {
|
|
|
126
126
|
...extraProps(cell.extraImgAttributes, cn("object-cover", schema.circular ? "rounded-full" : "rounded-md", schema.stacked && usesDefaultImageStack(schema) && "ring-background ring-2"))
|
|
127
127
|
}, index)), schema.limitedRemainingText && urls.length > limit && /* @__PURE__ */ jsxs("span", {
|
|
128
128
|
"data-slot": "image-remaining",
|
|
129
|
-
className: cn("text-muted-foreground ms-1", remainingTextSizeClass(schema.limitedRemainingTextSize)),
|
|
129
|
+
className: cn("text-muted-foreground", schema.stacked && usesDefaultImageStack(schema) ? "ms-3" : "ms-1", remainingTextSizeClass(schema.limitedRemainingTextSize)),
|
|
130
130
|
children: ["+", urls.length - limit]
|
|
131
131
|
})]
|
|
132
132
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cells.js","names":[],"sources":["../../../src/components/table/cells.tsx"],"sourcesContent":["import type {\n CellSchema,\n ColorColumnSchema,\n ColumnSchema,\n IconColumnSchema,\n Option,\n OptionOrGroup,\n RecordKey,\n SelectColumnSchema,\n TextInputColumnSchema,\n ToggleColumnSchema,\n ViewColumnSchema,\n} from '@alfakit/core'\nimport {\n columnRegistryKey,\n filterSelectColumnOptions,\n isOptionGroup,\n optionText,\n remainingTextSizeClass,\n resolvePartialProps,\n searchTableColumnOptions,\n selectColumnEmptyMessage,\n stackedImageStyle,\n updateTableCell,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'\nimport { createElement, useEffect, useId, useRef, useState } from 'react'\nimport { usePanel, useRegistry, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconSizeClasses,\n switchOffColorClasses,\n switchOnColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormattedText } from '../entries/text-entry'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../ui/command'\nimport { Input } from '../ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\nexport interface CellProps {\n column: ColumnSchema\n cell: CellSchema\n recordKey: RecordKey\n token: string\n component: string\n /**\n * Set when this renderer was picked from the registry (`column:<type>`): it then renders\n * the built-in cell instead of looking itself up again, so an ejected `TableCellContent`\n * registered for a column type doesn't render itself forever.\n */\n fromRegistry?: boolean\n}\n\nexport function TableCellContent(props: CellProps) {\n const { column } = props\n const registry = useRegistry()\n // A component registered as `column:<type>` replaces the built-in cell renderer.\n const Custom = props.fromRegistry ? undefined : registry.resolve(columnRegistryKey(column))\n\n if (Custom) {\n return createElement(Custom, { ...props, fromRegistry: true })\n }\n\n switch (column.type) {\n case 'IconColumn':\n return <IconCell {...props} />\n case 'ImageColumn':\n return <ImageCell {...props} />\n case 'ColorColumn':\n return <ColorCell {...props} />\n case 'ToggleColumn':\n case 'CheckboxColumn':\n return <BooleanCell {...props} />\n case 'SelectColumn':\n return <SelectCell {...props} />\n case 'TextInputColumn':\n return <TextInputCell {...props} />\n case 'ViewColumn':\n return <ViewCell {...props} />\n default:\n return <TextCell {...props} />\n }\n}\n\n/** `IconColumn`: one icon, or one per item of a list state (`wrap()` lets them wrap). */\nfunction IconCell({ column, cell }: CellProps) {\n const icons = cell.icons ?? (cell.icon ? [{ icon: cell.icon, color: cell.color }] : [])\n const sizeClass = colorClass(iconSizeClasses, cell.size, 'md')\n\n if (icons.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n if (!cell.icons) {\n return (\n <Icon\n name={icons[0]!.icon}\n className={cn(sizeClass, icons[0]!.color && textColorClasses[icons[0]!.color])}\n />\n )\n }\n\n return (\n <div\n data-slot=\"icon-column-icons\"\n className={cn(\n 'flex items-center gap-1.5',\n (column as IconColumnSchema).wrap ? 'flex-wrap' : 'flex-nowrap',\n )}\n >\n {icons.map((item, index) => (\n <Icon\n key={index}\n name={item.icon}\n className={cn(sizeClass, item.color && textColorClasses[item.color])}\n />\n ))}\n </div>\n )\n}\n\n/** `ViewColumn`: the application component registered under the column's `view()` name. */\nfunction ViewCell(props: CellProps) {\n const t = useT()\n const name = (props.column as ViewColumnSchema).component ?? ''\n const Custom = useRegistry().resolve(name)\n\n if (!name || !Custom) {\n return (\n <span className=\"text-destructive text-xs\">\n {t('component_not_registered', { component: name }, `Component ${name} is not registered.`)}\n </span>\n )\n }\n\n return createElement(Custom, { ...(props.cell.props ?? {}), state: props.cell.state, ...props })\n}\n\nfunction TextCell({ column, cell }: CellProps) {\n const text = (\n <FormattedText\n value={cell.state}\n schema={{\n ...(column as object),\n color: cell.color,\n icon: cell.icon,\n iconPosition: cell.iconPosition,\n iconColor: cell.iconColor,\n itemColors: cell.itemColors,\n itemIcons: cell.itemIcons,\n itemUrls: cell.itemUrls,\n }}\n />\n )\n const description = cell.description ? (\n <span className=\"text-muted-foreground block text-xs\">{cell.description}</span>\n ) : null\n const position =\n (column as { descriptionPosition?: 'above' | 'below' }).descriptionPosition ?? 'below'\n\n return (\n <div className=\"flex flex-col gap-0.5\">\n {position === 'above' && description}\n {text}\n {position === 'below' && description}\n </div>\n )\n}\n\nfunction ImageCell({ column, cell }: CellProps) {\n const urls = Array.isArray(cell.state) ? (cell.state as string[]) : []\n const schema = column as {\n circular?: boolean\n square?: boolean\n size?: number | string\n height?: number | string\n width?: string\n stacked?: boolean\n limit?: number\n limitedRemainingText?: boolean\n limitedRemainingTextSize?: string\n wrap?: boolean\n ring?: number\n overlap?: number\n }\n const limit = schema.limit ?? urls.length\n const size = schema.size ?? 40\n\n if (urls.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n return (\n <div\n className={cn(\n 'flex items-center',\n schema.wrap && 'flex-wrap',\n schema.stacked ? usesDefaultImageStack(schema) && '-space-x-2' : 'gap-1.5',\n schema.wrap && schema.stacked && 'gap-y-1.5',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n // Without `alt()` the first image is named after the column, so a record link that\n // wraps it has a name (W-V4-20).\n alt={cell.alt ?? (index === 0 ? (column.label ?? '') : '')}\n style={{\n height: schema.height ?? size,\n width: schema.width ?? (schema.square || schema.circular ? size : undefined),\n ...stackedImageStyle(index, schema),\n }}\n {...extraProps(\n cell.extraImgAttributes,\n cn(\n 'object-cover',\n schema.circular ? 'rounded-full' : 'rounded-md',\n schema.stacked && usesDefaultImageStack(schema) && 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {schema.limitedRemainingText && urls.length > limit && (\n <span\n data-slot=\"image-remaining\"\n className={cn(\n 'text-muted-foreground ms-1',\n remainingTextSizeClass(schema.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n )\n}\n\nfunction ColorCell({ column, cell }: CellProps) {\n const t = useT()\n const schema = column as ColorColumnSchema\n const [copied, setCopied] = useState<string | null>(null)\n const colors = Array.isArray(cell.state)\n ? (cell.state as string[])\n : cell.state\n ? [String(cell.state)]\n : []\n\n if (colors.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n const copy = (color: string) => {\n void navigator.clipboard?.writeText(color).then(() => {\n setCopied(color)\n setTimeout(() => setCopied(null), schema.copyMessageDuration ?? 1500)\n })\n }\n\n return (\n <div className={cn('flex items-center gap-1.5', schema.wrap ? 'flex-wrap' : 'flex-nowrap')}>\n {colors.map((color) =>\n schema.copyable ? (\n <button\n key={color}\n type=\"button\"\n data-slot=\"color-copy\"\n title={color}\n aria-label={`${t('copy', {}, 'Copy')} ${color}`}\n className=\"focus-visible:ring-ring/50 inline-flex rounded outline-none focus-visible:ring-[3px]\"\n onClick={(event) => {\n // The cell may sit inside a link or a clickable row.\n event.preventDefault()\n event.stopPropagation()\n copy(color)\n }}\n >\n <span\n className=\"inline-flex size-5 items-center justify-center rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n >\n {copied === color && <CheckIcon className=\"size-3 text-white mix-blend-difference\" />}\n </span>\n </button>\n ) : (\n <span\n key={color}\n title={color}\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n ),\n )}\n {schema.copyable && copied && (\n <span role=\"status\" className={cn('text-xs', !schema.copyMessage && 'sr-only')}>\n {schema.copyMessage ?? t('copied', {}, 'Copied')}\n </span>\n )}\n </div>\n )\n}\n\nfunction useCellUpdate({ recordKey, column, token, component }: CellProps) {\n const panel = usePanel()\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const update = async (state: unknown) => {\n if (!panel.endpoints.tableCell || !panel.endpoints.tableReorder) return\n\n setSaving(true)\n setError(null)\n\n try {\n await updateTableCell(\n { cell: panel.endpoints.tableCell, reorder: panel.endpoints.tableReorder },\n token,\n component,\n recordKey,\n column.name,\n state,\n )\n router.reload({ only: resolvePartialProps(['page']) })\n } catch (caught) {\n const errors = (caught as { errors?: Record<string, string[]> }).errors\n setError(errors?.state?.[0] ?? (caught as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n return { update, saving, error }\n}\n\nfunction BooleanCell(props: CellProps) {\n const { column, cell } = props\n const { update, saving } = useCellUpdate(props)\n const [checked, setChecked] = useState(Boolean(cell.state))\n const disabled = Boolean(cell.disabled) || saving\n const toggle = column as ToggleColumnSchema\n\n const onChange = (next: boolean) => {\n setChecked(next)\n void update(next)\n }\n\n if (column.type === 'CheckboxColumn') {\n return (\n <Checkbox\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={(value) => onChange(value === true)}\n onClick={(event) => event.stopPropagation()}\n />\n )\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\" onClick={(event) => event.stopPropagation()}>\n <Switch\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={onChange}\n className={cn(\n toggle.onColor && colorClass(switchOnColorClasses, toggle.onColor),\n toggle.offColor && colorClass(switchOffColorClasses, toggle.offColor),\n )}\n />\n {(checked ? toggle.onIcon : toggle.offIcon) && (\n <Icon\n name={checked ? toggle.onIcon : toggle.offIcon}\n className={cn(\n colorClass(textColorClasses, checked ? toggle.onColor : toggle.offColor, 'gray'),\n )}\n />\n )}\n </span>\n )\n}\n\nfunction flatten(options: OptionOrGroup[]) {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nfunction SelectCell(props: CellProps) {\n const schema = props.column as SelectColumnSchema\n\n return schema.native === false ? (\n <SelectComboboxCell {...props} />\n ) : (\n <NativeSelectCell {...props} />\n )\n}\n\n/**\n * An option label: sanitised HTML under `allowOptionsHtml()`, text otherwise. Long labels\n * wrap unless `wrapOptionLabels(false)` truncates them.\n */\nfunction SelectOptionLabel({\n label,\n html,\n truncate,\n}: {\n label: string\n html?: boolean\n truncate?: boolean\n}) {\n const className = truncate ? 'min-w-0 truncate' : 'min-w-0 break-words whitespace-normal'\n\n // The server sanitises HTML labels (`Support\\Html::sanitize`).\n return html ? (\n <span className={className} dangerouslySetInnerHTML={{ __html: label }} />\n ) : (\n <span className={className}>{label}</span>\n )\n}\n\n/**\n * `SelectColumn::native(false)`: a combobox, searchable with `searchableOptions()`. Options\n * come with the table, or from the server (`optionsSearchPath`) as the user types.\n */\nfunction SelectComboboxCell(props: CellProps) {\n const { column, cell, recordKey, token, component } = props\n const schema = column as SelectColumnSchema\n const t = useT()\n const panel = usePanel()\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const [label, setLabel] = useState(cell.optionLabel)\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [loading, setLoading] = useState(false)\n const [remote, setRemote] = useState<Option[]>([])\n const request = useRef(0)\n const isRemote = Boolean(schema.optionsSearchPath)\n const debounce = schema.optionsSearchDebounce ?? 300\n const staticOptions = flatten(schema.options)\n\n useEffect(() => {\n if (!open || !isRemote || !panel.endpoints.schemaOptions) return\n\n const url = panel.endpoints.schemaOptions\n const timer = setTimeout(() => {\n const current = ++request.current\n setLoading(true)\n\n void searchTableColumnOptions(url, token, component, schema, search)\n .then((results) => current === request.current && setRemote(results))\n .catch(() => current === request.current && setRemote([]))\n .finally(() => current === request.current && setLoading(false))\n }, debounce)\n\n return () => clearTimeout(timer)\n }, [open, search, isRemote, panel.endpoints.schemaOptions, token, component, schema, debounce])\n\n const options = filterSelectColumnOptions(\n isRemote ? remote : staticOptions,\n schema,\n isRemote ? '' : search,\n recordKey,\n )\n const emptyMessage = selectColumnEmptyMessage(schema, {\n loading,\n search,\n optionCount: options.length,\n })\n const currentLabel =\n staticOptions.find((option) => option.value === value)?.label ??\n remote.find((option) => option.value === value)?.label ??\n label\n\n const choose = (next: string, nextLabel?: string) => {\n setValue(next)\n setLabel(nextLabel)\n setOpen(false)\n void update(next === '' ? null : next)\n }\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next)\n\n if (!next) setSearch('')\n }}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n disabled={Boolean(cell.disabled) || saving}\n className={cn(\n 'h-8 min-w-32 justify-between font-normal',\n error && 'border-destructive',\n )}\n >\n {value !== '' && currentLabel !== undefined ? (\n <SelectOptionLabel label={currentLabel} html={schema.allowOptionsHtml} truncate />\n ) : (\n <span className=\"text-muted-foreground\">\n {schema.placeholder ??\n (value !== '' ? value : t('select_option', {}, 'Select an option'))}\n </span>\n )}\n {loading ? (\n <Spinner className=\"opacity-50\" />\n ) : (\n <ChevronsUpDownIcon className=\"size-4 shrink-0 opacity-50\" />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-0\" align=\"start\">\n <Command shouldFilter={false}>\n {schema.searchableOptions && (\n <CommandInput\n placeholder={\n schema.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n }\n value={search}\n onValueChange={setSearch}\n />\n )}\n <CommandList>\n <CommandEmpty data-slot=\"select-column-message\">\n {emptyMessage.message ?? t(emptyMessage.key, {}, emptyMessage.fallback)}\n </CommandEmpty>\n <CommandGroup>\n {schema.selectablePlaceholder && value !== '' && (\n <CommandItem value=\"\" onSelect={() => choose('')}>\n <span className=\"text-muted-foreground\">\n {schema.placeholder ?? t('clear', {}, 'Clear')}\n </span>\n </CommandItem>\n )}\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.value}\n keywords={[optionText(option.label)]}\n disabled={option.disabled}\n onSelect={() => choose(option.value, option.label)}\n >\n <CheckIcon\n className={cn('size-4', option.value === value ? 'opacity-100' : 'opacity-0')}\n />\n <SelectOptionLabel\n label={option.label}\n html={schema.allowOptionsHtml}\n truncate={schema.wrapOptionLabels === false}\n />\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n\nfunction NativeSelectCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as SelectColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n\n const errorId = useId()\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <select\n value={value}\n disabled={Boolean(cell.disabled) || saving}\n onChange={(event) => {\n setValue(event.target.value)\n void update(event.target.value === '' ? null : event.target.value)\n }}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'border-input dark:bg-input/30 focus-visible:ring-ring/50 h-8 min-w-32 rounded-md border bg-transparent px-2 text-sm shadow-xs outline-none focus-visible:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {schema.selectablePlaceholder && <option value=\"\">{schema.placeholder ?? ''}</option>}\n {flatten(schema.options)\n .filter((option) => !(schema.ignoreRecord && option.value === String(props.recordKey)))\n .map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n\nfunction TextInputCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as TextInputColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const initial = cell.state === null || cell.state === undefined ? '' : String(cell.state)\n // `prefix()` / `suffix()`: text affixes around the input, like the TextInput field.\n const grouped = Boolean(cell.prefix || cell.suffix)\n const errorId = useId()\n\n const field = (\n <span className={cn('relative block', grouped && 'min-w-0 flex-1')}>\n {schema.prefixIcon && (\n <span\n data-slot=\"input-prefix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute start-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.prefixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.prefixIcon} className=\"size-4\" />\n </span>\n )}\n <Input\n type={schema.inputType}\n value={value}\n placeholder={schema.placeholder}\n step={schema.step}\n inputMode={schema.inputMode as React.HTMLAttributes<HTMLInputElement>['inputMode']}\n disabled={Boolean(cell.disabled) || saving}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'h-8 min-w-32',\n schema.prefixIcon && 'ps-8',\n schema.suffixIcon && 'pe-8',\n grouped &&\n 'h-full rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',\n )}\n onChange={(event) => setValue(event.target.value)}\n onBlur={() => value !== initial && void update(value)}\n onKeyDown={(event) => event.key === 'Enter' && (event.target as HTMLInputElement).blur()}\n />\n {schema.suffixIcon && (\n <span\n data-slot=\"input-suffix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute end-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.suffixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.suffixIcon} className=\"size-4\" />\n </span>\n )}\n </span>\n )\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n {grouped ? (\n <span\n data-slot=\"input-group\"\n className={cn(\n 'border-input dark:bg-input/30 has-[input:focus-visible]:border-ring has-[input:focus-visible]:ring-ring/50 flex h-8 min-w-32 items-stretch overflow-hidden rounded-md border shadow-xs has-[input:focus-visible]:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {cell.prefix && (\n <span\n data-slot=\"input-prefix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-e px-2 text-sm\"\n >\n {cell.prefix}\n </span>\n )}\n {field}\n {cell.suffix && (\n <span\n data-slot=\"input-suffix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-s px-2 text-sm\"\n >\n {cell.suffix}\n </span>\n )}\n </span>\n ) : (\n field\n )}\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cells.js","names":[],"sources":["../../../src/components/table/cells.tsx"],"sourcesContent":["import type {\n CellSchema,\n ColorColumnSchema,\n ColumnSchema,\n IconColumnSchema,\n Option,\n OptionOrGroup,\n RecordKey,\n SelectColumnSchema,\n TextInputColumnSchema,\n ToggleColumnSchema,\n ViewColumnSchema,\n} from '@alfakit/core'\nimport {\n columnRegistryKey,\n filterSelectColumnOptions,\n isOptionGroup,\n optionText,\n remainingTextSizeClass,\n resolvePartialProps,\n searchTableColumnOptions,\n selectColumnEmptyMessage,\n stackedImageStyle,\n updateTableCell,\n usesDefaultImageStack,\n} from '@alfakit/core'\nimport { router } from '@inertiajs/react'\nimport { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'\nimport { createElement, useEffect, useId, useRef, useState } from 'react'\nimport { usePanel, useRegistry, useT } from '../../context'\nimport { extraProps } from '../../lib/attributes'\nimport {\n colorClass,\n iconSizeClasses,\n switchOffColorClasses,\n switchOnColorClasses,\n textColorClasses,\n} from '../../lib/colors'\nimport { Icon } from '../../lib/icons'\nimport { cn } from '../../lib/utils'\nimport { FormattedText } from '../entries/text-entry'\nimport { Button } from '../ui/button'\nimport { Checkbox } from '../ui/checkbox'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../ui/command'\nimport { Input } from '../ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\nexport interface CellProps {\n column: ColumnSchema\n cell: CellSchema\n recordKey: RecordKey\n token: string\n component: string\n /**\n * Set when this renderer was picked from the registry (`column:<type>`): it then renders\n * the built-in cell instead of looking itself up again, so an ejected `TableCellContent`\n * registered for a column type doesn't render itself forever.\n */\n fromRegistry?: boolean\n}\n\nexport function TableCellContent(props: CellProps) {\n const { column } = props\n const registry = useRegistry()\n // A component registered as `column:<type>` replaces the built-in cell renderer.\n const Custom = props.fromRegistry ? undefined : registry.resolve(columnRegistryKey(column))\n\n if (Custom) {\n return createElement(Custom, { ...props, fromRegistry: true })\n }\n\n switch (column.type) {\n case 'IconColumn':\n return <IconCell {...props} />\n case 'ImageColumn':\n return <ImageCell {...props} />\n case 'ColorColumn':\n return <ColorCell {...props} />\n case 'ToggleColumn':\n case 'CheckboxColumn':\n return <BooleanCell {...props} />\n case 'SelectColumn':\n return <SelectCell {...props} />\n case 'TextInputColumn':\n return <TextInputCell {...props} />\n case 'ViewColumn':\n return <ViewCell {...props} />\n default:\n return <TextCell {...props} />\n }\n}\n\n/** `IconColumn`: one icon, or one per item of a list state (`wrap()` lets them wrap). */\nfunction IconCell({ column, cell }: CellProps) {\n const icons = cell.icons ?? (cell.icon ? [{ icon: cell.icon, color: cell.color }] : [])\n const sizeClass = colorClass(iconSizeClasses, cell.size, 'md')\n\n if (icons.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n if (!cell.icons) {\n return (\n <Icon\n name={icons[0]!.icon}\n className={cn(sizeClass, icons[0]!.color && textColorClasses[icons[0]!.color])}\n />\n )\n }\n\n return (\n <div\n data-slot=\"icon-column-icons\"\n className={cn(\n 'flex items-center gap-1.5',\n (column as IconColumnSchema).wrap ? 'flex-wrap' : 'flex-nowrap',\n )}\n >\n {icons.map((item, index) => (\n <Icon\n key={index}\n name={item.icon}\n className={cn(sizeClass, item.color && textColorClasses[item.color])}\n />\n ))}\n </div>\n )\n}\n\n/** `ViewColumn`: the application component registered under the column's `view()` name. */\nfunction ViewCell(props: CellProps) {\n const t = useT()\n const name = (props.column as ViewColumnSchema).component ?? ''\n const Custom = useRegistry().resolve(name)\n\n if (!name || !Custom) {\n return (\n <span className=\"text-destructive text-xs\">\n {t('component_not_registered', { component: name }, `Component ${name} is not registered.`)}\n </span>\n )\n }\n\n return createElement(Custom, { ...(props.cell.props ?? {}), state: props.cell.state, ...props })\n}\n\nfunction TextCell({ column, cell }: CellProps) {\n const text = (\n <FormattedText\n value={cell.state}\n schema={{\n ...(column as object),\n color: cell.color,\n icon: cell.icon,\n iconPosition: cell.iconPosition,\n iconColor: cell.iconColor,\n itemColors: cell.itemColors,\n itemIcons: cell.itemIcons,\n itemUrls: cell.itemUrls,\n }}\n />\n )\n const description = cell.description ? (\n <span className=\"text-muted-foreground block text-xs\">{cell.description}</span>\n ) : null\n const position =\n (column as { descriptionPosition?: 'above' | 'below' }).descriptionPosition ?? 'below'\n\n return (\n <div className=\"flex flex-col gap-0.5\">\n {position === 'above' && description}\n {text}\n {position === 'below' && description}\n </div>\n )\n}\n\nfunction ImageCell({ column, cell }: CellProps) {\n const urls = Array.isArray(cell.state) ? (cell.state as string[]) : []\n const schema = column as {\n circular?: boolean\n square?: boolean\n size?: number | string\n height?: number | string\n width?: string\n stacked?: boolean\n limit?: number\n limitedRemainingText?: boolean\n limitedRemainingTextSize?: string\n wrap?: boolean\n ring?: number\n overlap?: number\n }\n const limit = schema.limit ?? urls.length\n const size = schema.size ?? 40\n\n if (urls.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n return (\n <div\n className={cn(\n 'flex items-center',\n schema.wrap && 'flex-wrap',\n schema.stacked ? usesDefaultImageStack(schema) && '-space-x-2' : 'gap-1.5',\n schema.wrap && schema.stacked && 'gap-y-1.5',\n )}\n >\n {urls.slice(0, limit).map((url, index) => (\n <img\n key={index}\n src={url}\n // Without `alt()` the first image is named after the column, so a record link that\n // wraps it has a name (W-V4-20).\n alt={cell.alt ?? (index === 0 ? (column.label ?? '') : '')}\n style={{\n height: schema.height ?? size,\n width: schema.width ?? (schema.square || schema.circular ? size : undefined),\n ...stackedImageStyle(index, schema),\n }}\n {...extraProps(\n cell.extraImgAttributes,\n cn(\n 'object-cover',\n schema.circular ? 'rounded-full' : 'rounded-md',\n schema.stacked && usesDefaultImageStack(schema) && 'ring-background ring-2',\n ),\n )}\n />\n ))}\n {schema.limitedRemainingText && urls.length > limit && (\n <span\n data-slot=\"image-remaining\"\n className={cn(\n 'text-muted-foreground',\n // The last image keeps the stack's negative end margin (W-S3-03).\n schema.stacked && usesDefaultImageStack(schema) ? 'ms-3' : 'ms-1',\n remainingTextSizeClass(schema.limitedRemainingTextSize),\n )}\n >\n +{urls.length - limit}\n </span>\n )}\n </div>\n )\n}\n\nfunction ColorCell({ column, cell }: CellProps) {\n const t = useT()\n const schema = column as ColorColumnSchema\n const [copied, setCopied] = useState<string | null>(null)\n const colors = Array.isArray(cell.state)\n ? (cell.state as string[])\n : cell.state\n ? [String(cell.state)]\n : []\n\n if (colors.length === 0) {\n return <span className=\"text-muted-foreground\">—</span>\n }\n\n const copy = (color: string) => {\n void navigator.clipboard?.writeText(color).then(() => {\n setCopied(color)\n setTimeout(() => setCopied(null), schema.copyMessageDuration ?? 1500)\n })\n }\n\n return (\n <div className={cn('flex items-center gap-1.5', schema.wrap ? 'flex-wrap' : 'flex-nowrap')}>\n {colors.map((color) =>\n schema.copyable ? (\n <button\n key={color}\n type=\"button\"\n data-slot=\"color-copy\"\n title={color}\n aria-label={`${t('copy', {}, 'Copy')} ${color}`}\n className=\"focus-visible:ring-ring/50 inline-flex rounded outline-none focus-visible:ring-[3px]\"\n onClick={(event) => {\n // The cell may sit inside a link or a clickable row.\n event.preventDefault()\n event.stopPropagation()\n copy(color)\n }}\n >\n <span\n className=\"inline-flex size-5 items-center justify-center rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n >\n {copied === color && <CheckIcon className=\"size-3 text-white mix-blend-difference\" />}\n </span>\n </button>\n ) : (\n <span\n key={color}\n title={color}\n className=\"size-5 rounded border shadow-xs\"\n style={{ backgroundColor: color }}\n />\n ),\n )}\n {schema.copyable && copied && (\n <span role=\"status\" className={cn('text-xs', !schema.copyMessage && 'sr-only')}>\n {schema.copyMessage ?? t('copied', {}, 'Copied')}\n </span>\n )}\n </div>\n )\n}\n\nfunction useCellUpdate({ recordKey, column, token, component }: CellProps) {\n const panel = usePanel()\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const update = async (state: unknown) => {\n if (!panel.endpoints.tableCell || !panel.endpoints.tableReorder) return\n\n setSaving(true)\n setError(null)\n\n try {\n await updateTableCell(\n { cell: panel.endpoints.tableCell, reorder: panel.endpoints.tableReorder },\n token,\n component,\n recordKey,\n column.name,\n state,\n )\n router.reload({ only: resolvePartialProps(['page']) })\n } catch (caught) {\n const errors = (caught as { errors?: Record<string, string[]> }).errors\n setError(errors?.state?.[0] ?? (caught as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n return { update, saving, error }\n}\n\nfunction BooleanCell(props: CellProps) {\n const { column, cell } = props\n const { update, saving } = useCellUpdate(props)\n const [checked, setChecked] = useState(Boolean(cell.state))\n const disabled = Boolean(cell.disabled) || saving\n const toggle = column as ToggleColumnSchema\n\n const onChange = (next: boolean) => {\n setChecked(next)\n void update(next)\n }\n\n if (column.type === 'CheckboxColumn') {\n return (\n <Checkbox\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={(value) => onChange(value === true)}\n onClick={(event) => event.stopPropagation()}\n />\n )\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\" onClick={(event) => event.stopPropagation()}>\n <Switch\n checked={checked}\n disabled={disabled}\n aria-label={column.label || column.name}\n onCheckedChange={onChange}\n className={cn(\n toggle.onColor && colorClass(switchOnColorClasses, toggle.onColor),\n toggle.offColor && colorClass(switchOffColorClasses, toggle.offColor),\n )}\n />\n {(checked ? toggle.onIcon : toggle.offIcon) && (\n <Icon\n name={checked ? toggle.onIcon : toggle.offIcon}\n className={cn(\n colorClass(textColorClasses, checked ? toggle.onColor : toggle.offColor, 'gray'),\n )}\n />\n )}\n </span>\n )\n}\n\nfunction flatten(options: OptionOrGroup[]) {\n return options.flatMap((option) => (isOptionGroup(option) ? option.options : [option]))\n}\n\nfunction SelectCell(props: CellProps) {\n const schema = props.column as SelectColumnSchema\n\n return schema.native === false ? (\n <SelectComboboxCell {...props} />\n ) : (\n <NativeSelectCell {...props} />\n )\n}\n\n/**\n * An option label: sanitised HTML under `allowOptionsHtml()`, text otherwise. Long labels\n * wrap unless `wrapOptionLabels(false)` truncates them.\n */\nfunction SelectOptionLabel({\n label,\n html,\n truncate,\n}: {\n label: string\n html?: boolean\n truncate?: boolean\n}) {\n const className = truncate ? 'min-w-0 truncate' : 'min-w-0 break-words whitespace-normal'\n\n // The server sanitises HTML labels (`Support\\Html::sanitize`).\n return html ? (\n <span className={className} dangerouslySetInnerHTML={{ __html: label }} />\n ) : (\n <span className={className}>{label}</span>\n )\n}\n\n/**\n * `SelectColumn::native(false)`: a combobox, searchable with `searchableOptions()`. Options\n * come with the table, or from the server (`optionsSearchPath`) as the user types.\n */\nfunction SelectComboboxCell(props: CellProps) {\n const { column, cell, recordKey, token, component } = props\n const schema = column as SelectColumnSchema\n const t = useT()\n const panel = usePanel()\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const [label, setLabel] = useState(cell.optionLabel)\n const [open, setOpen] = useState(false)\n const [search, setSearch] = useState('')\n const [loading, setLoading] = useState(false)\n const [remote, setRemote] = useState<Option[]>([])\n const request = useRef(0)\n const isRemote = Boolean(schema.optionsSearchPath)\n const debounce = schema.optionsSearchDebounce ?? 300\n const staticOptions = flatten(schema.options)\n\n useEffect(() => {\n if (!open || !isRemote || !panel.endpoints.schemaOptions) return\n\n const url = panel.endpoints.schemaOptions\n const timer = setTimeout(() => {\n const current = ++request.current\n setLoading(true)\n\n void searchTableColumnOptions(url, token, component, schema, search)\n .then((results) => current === request.current && setRemote(results))\n .catch(() => current === request.current && setRemote([]))\n .finally(() => current === request.current && setLoading(false))\n }, debounce)\n\n return () => clearTimeout(timer)\n }, [open, search, isRemote, panel.endpoints.schemaOptions, token, component, schema, debounce])\n\n const options = filterSelectColumnOptions(\n isRemote ? remote : staticOptions,\n schema,\n isRemote ? '' : search,\n recordKey,\n )\n const emptyMessage = selectColumnEmptyMessage(schema, {\n loading,\n search,\n optionCount: options.length,\n })\n const currentLabel =\n staticOptions.find((option) => option.value === value)?.label ??\n remote.find((option) => option.value === value)?.label ??\n label\n\n const choose = (next: string, nextLabel?: string) => {\n setValue(next)\n setLabel(nextLabel)\n setOpen(false)\n void update(next === '' ? null : next)\n }\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next)\n\n if (!next) setSearch('')\n }}\n >\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n role=\"combobox\"\n aria-expanded={open}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n disabled={Boolean(cell.disabled) || saving}\n className={cn(\n 'h-8 min-w-32 justify-between font-normal',\n error && 'border-destructive',\n )}\n >\n {value !== '' && currentLabel !== undefined ? (\n <SelectOptionLabel label={currentLabel} html={schema.allowOptionsHtml} truncate />\n ) : (\n <span className=\"text-muted-foreground\">\n {schema.placeholder ??\n (value !== '' ? value : t('select_option', {}, 'Select an option'))}\n </span>\n )}\n {loading ? (\n <Spinner className=\"opacity-50\" />\n ) : (\n <ChevronsUpDownIcon className=\"size-4 shrink-0 opacity-50\" />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-64 p-0\" align=\"start\">\n <Command shouldFilter={false}>\n {schema.searchableOptions && (\n <CommandInput\n placeholder={\n schema.searchPrompt ?? t('form.search_placeholder', {}, 'Type to search…')\n }\n value={search}\n onValueChange={setSearch}\n />\n )}\n <CommandList>\n <CommandEmpty data-slot=\"select-column-message\">\n {emptyMessage.message ?? t(emptyMessage.key, {}, emptyMessage.fallback)}\n </CommandEmpty>\n <CommandGroup>\n {schema.selectablePlaceholder && value !== '' && (\n <CommandItem value=\"\" onSelect={() => choose('')}>\n <span className=\"text-muted-foreground\">\n {schema.placeholder ?? t('clear', {}, 'Clear')}\n </span>\n </CommandItem>\n )}\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.value}\n keywords={[optionText(option.label)]}\n disabled={option.disabled}\n onSelect={() => choose(option.value, option.label)}\n >\n <CheckIcon\n className={cn('size-4', option.value === value ? 'opacity-100' : 'opacity-0')}\n />\n <SelectOptionLabel\n label={option.label}\n html={schema.allowOptionsHtml}\n truncate={schema.wrapOptionLabels === false}\n />\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n {error && <span className=\"text-destructive block text-xs\">{error}</span>}\n </span>\n )\n}\n\nfunction NativeSelectCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as SelectColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n\n const errorId = useId()\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n <select\n value={value}\n disabled={Boolean(cell.disabled) || saving}\n onChange={(event) => {\n setValue(event.target.value)\n void update(event.target.value === '' ? null : event.target.value)\n }}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'border-input dark:bg-input/30 focus-visible:ring-ring/50 h-8 min-w-32 rounded-md border bg-transparent px-2 text-sm shadow-xs outline-none focus-visible:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {schema.selectablePlaceholder && <option value=\"\">{schema.placeholder ?? ''}</option>}\n {flatten(schema.options)\n .filter((option) => !(schema.ignoreRecord && option.value === String(props.recordKey)))\n .map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n\nfunction TextInputCell(props: CellProps) {\n const { column, cell } = props\n const schema = column as TextInputColumnSchema\n const { update, saving, error } = useCellUpdate(props)\n const [value, setValue] = useState(\n cell.state === null || cell.state === undefined ? '' : String(cell.state),\n )\n const initial = cell.state === null || cell.state === undefined ? '' : String(cell.state)\n // `prefix()` / `suffix()`: text affixes around the input, like the TextInput field.\n const grouped = Boolean(cell.prefix || cell.suffix)\n const errorId = useId()\n\n const field = (\n <span className={cn('relative block', grouped && 'min-w-0 flex-1')}>\n {schema.prefixIcon && (\n <span\n data-slot=\"input-prefix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute start-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.prefixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.prefixIcon} className=\"size-4\" />\n </span>\n )}\n <Input\n type={schema.inputType}\n value={value}\n placeholder={schema.placeholder}\n step={schema.step}\n inputMode={schema.inputMode as React.HTMLAttributes<HTMLInputElement>['inputMode']}\n disabled={Boolean(cell.disabled) || saving}\n aria-label={column.label || column.name}\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className={cn(\n 'h-8 min-w-32',\n schema.prefixIcon && 'ps-8',\n schema.suffixIcon && 'pe-8',\n grouped &&\n 'h-full rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',\n )}\n onChange={(event) => setValue(event.target.value)}\n onBlur={() => value !== initial && void update(value)}\n onKeyDown={(event) => event.key === 'Enter' && (event.target as HTMLInputElement).blur()}\n />\n {schema.suffixIcon && (\n <span\n data-slot=\"input-suffix-icon\"\n aria-hidden=\"true\"\n className={cn(\n 'pointer-events-none absolute end-2.5 top-1/2 flex -translate-y-1/2',\n colorClass(textColorClasses, schema.suffixIconColor, 'gray'),\n )}\n >\n <Icon name={schema.suffixIcon} className=\"size-4\" />\n </span>\n )}\n </span>\n )\n\n return (\n <span onClick={(event) => event.stopPropagation()}>\n {grouped ? (\n <span\n data-slot=\"input-group\"\n className={cn(\n 'border-input dark:bg-input/30 has-[input:focus-visible]:border-ring has-[input:focus-visible]:ring-ring/50 flex h-8 min-w-32 items-stretch overflow-hidden rounded-md border shadow-xs has-[input:focus-visible]:ring-[3px]',\n error && 'border-destructive',\n )}\n >\n {cell.prefix && (\n <span\n data-slot=\"input-prefix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-e px-2 text-sm\"\n >\n {cell.prefix}\n </span>\n )}\n {field}\n {cell.suffix && (\n <span\n data-slot=\"input-suffix\"\n className=\"text-muted-foreground bg-muted/50 flex items-center border-s px-2 text-sm\"\n >\n {cell.suffix}\n </span>\n )}\n </span>\n ) : (\n field\n )}\n {error && (\n <span id={errorId} className=\"text-destructive block text-xs\">\n {error}\n </span>\n )}\n </span>\n )\n}\n"],"mappings":"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"}
|
package/dist/components.css
CHANGED
|
@@ -182,6 +182,13 @@
|
|
|
182
182
|
.fc[data-slot='calendar'] .fc-day-other .fc-daygrid-day-number {
|
|
183
183
|
color: var(--muted-foreground);
|
|
184
184
|
}
|
|
185
|
+
/*
|
|
186
|
+
* FullCalendar fades other months' dates to 30% opacity, which fails WCAG AA contrast
|
|
187
|
+
* (W-V4-20); the muted colour alone sets them apart.
|
|
188
|
+
*/
|
|
189
|
+
.fc[data-slot='calendar'] .fc-day-other .fc-daygrid-day-top {
|
|
190
|
+
opacity: 1;
|
|
191
|
+
}
|
|
185
192
|
/* Today: a tinted cell (`--fc-today-bg-color`) and the date in a primary pill. */
|
|
186
193
|
.fc[data-slot='calendar'] .fc-daygrid-day.fc-day-today .fc-daygrid-day-number {
|
|
187
194
|
display: inline-flex;
|
|
@@ -345,3 +352,15 @@
|
|
|
345
352
|
transition: none;
|
|
346
353
|
}
|
|
347
354
|
}
|
|
355
|
+
|
|
356
|
+
/* ------------------------------------------------------------------ Dev tools */
|
|
357
|
+
|
|
358
|
+
/*
|
|
359
|
+
* The floating debug-panel and schema-inspector buttons (local development) sit at the
|
|
360
|
+
* bottom end, over the helpdesk inbox's reply composer and its Send button: lift them
|
|
361
|
+
* above the composer while the inbox is on the page (W-S4-09).
|
|
362
|
+
*/
|
|
363
|
+
body:has([data-slot='helpdesk-inbox']) [data-slot='dev-tools-toggle'],
|
|
364
|
+
body:has([data-slot='helpdesk-inbox']) [data-slot='starter-kit-debug-panel'] {
|
|
365
|
+
bottom: 14rem;
|
|
366
|
+
}
|
package/dist/eject-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@alfakit/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"adapter": "react",
|
|
5
|
-
"generatedAt": "2026-10-
|
|
5
|
+
"generatedAt": "2026-10-26T09:54:26.975Z",
|
|
6
6
|
"components": {
|
|
7
7
|
"TextInput": {
|
|
8
8
|
"file": "src/components/fields/text-input.tsx",
|
package/dist/pages/page.js
CHANGED
package/dist/pages/page.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.js","names":["FormBlock"],"sources":["../../src/pages/page.tsx"],"sourcesContent":["import type {\n ContentBlock,\n FormBlock as FormBlockSchema,\n InfolistBlock,\n LinksBlock,\n PageSchema,\n RelationManagersBlock,\n SchemaBlock,\n TableBlock as TableBlockSchema,\n WidgetsBlock as WidgetsBlockSchema,\n} from '@alfakit/core'\nimport {\n applyBodyAttributes,\n customPageProps,\n hasRelationManagerTabs,\n isLazyPlaceholder,\n openDefaultAction,\n RELATION_QUERY_KEY,\n relationManagerTabs,\n relationTabFromQuery,\n relationTabSlug,\n resolveLazyComponents,\n type DefaultActionOpener,\n} from '@alfakit/core'\nimport { Head, Link, usePage } from '@inertiajs/react'\nimport {\n createElement,\n Fragment,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport { PageActionProvider } from '../components/actions/action-context'\nimport { ActionList } from '../components/actions/action-list'\nimport { FormBlock } from '../components/form/form-block'\nimport { KeyboardShortcuts } from '../components/panel/keyboard-shortcuts'\nimport { LazyPlaceholder } from '../components/panel/lazy-placeholder'\nimport { Slot } from '../components/panel/slot'\nimport { SubNavigationLayout } from '../components/panel/sub-navigation'\nimport { ConflictBanner } from '../components/realtime/conflict-banner'\nimport { RecordPresence } from '../components/realtime/record-presence'\nimport { SchemaRenderer } from '../components/schema/schema-renderer'\nimport { ListTabs, ListTabsPanel } from '../components/table/list-tabs'\nimport { DataTable } from '../components/table/table-block'\nimport { Badge } from '../components/ui/badge'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../components/ui/tabs'\nimport { WidgetGrid } from '../components/widgets/widgets-block'\nimport {\n useActionRunner,\n useAlfakit,\n useAlfakitProps,\n usePanel,\n useT,\n type AnyComponent,\n} from '../context'\nimport { BlankLayout } from '../layouts/blank-layout'\nimport { badgeColorClasses, colorClass } from '../lib/colors'\nimport { Icon } from '../lib/icons'\nimport { cn } from '../lib/utils'\n\n/**\n * `Alfakit/Page`: renders any schema-driven page. Custom pages (`Page::$component`) are\n * resolved from the `pages` map passed to `createAlfakit()`.\n */\nexport function AlfakitPage() {\n const props = useAlfakitProps()\n const panel = usePanel()\n const t = useT()\n const { pages } = useAlfakit()\n // Lazy widgets and relation managers arrive in a deferred prop after the first response.\n const page = useMemo(\n () => (props.page ? resolveLazyComponents(props.page, props.alfakitLazy) : undefined),\n [props.page, props.alfakitLazy],\n )\n\n useDefaultAction(page)\n\n if (!page) {\n return null\n }\n\n // Starter kit mode: the host's `title` callback adds the application name.\n const title = panel.starterKit ? page.title : `${page.title} - ${panel.brandName}`\n\n if (page.view) {\n const Custom = pages[page.view.component]\n\n if (!Custom) {\n return (\n <div className=\"text-destructive p-6\">\n {t(\n 'page_not_registered',\n { component: page.view.component },\n `Page component ${page.view.component} is not registered in createAlfakit({ pages }).`,\n )}\n </div>\n )\n }\n\n return (\n <PageActionProvider value={{ token: page.token, component: 'page' }}>\n <Head title={title} />\n {/* The component brings its own PageShell (as in panel mode, where it is the Inertia\n page itself) and takes the live props, so `reloadProps()` reaches it. */}\n <Custom\n {...customPageProps(page.view.props, props as Record<string, unknown>)}\n page={page}\n />\n </PageActionProvider>\n )\n }\n\n return (\n <PageActionProvider value={{ token: page.token, component: 'page' }}>\n <Head title={title} />\n <PageShell page={page}>\n <PageContent page={page} />\n </PageShell>\n </PageActionProvider>\n )\n}\n\n/**\n * Opens the page's default action (`$defaultAction`, `?tableAction=`) once the page has\n * hydrated, and once per page: re-renders after the action ran don't open it again.\n */\nexport function useDefaultAction(page: PageSchema | undefined) {\n const runner = useActionRunner()\n const opener = useRef<DefaultActionOpener>({ opened: null })\n\n useEffect(() => {\n openDefaultAction(page, opener.current, (action, context) => runner.trigger(action, context))\n }, [page, runner])\n}\n\n/** Sets the page's `$extraBodyAttributes` on `<body>` while it is open. */\nexport function useBodyAttributes(attributes: Record<string, string> | undefined) {\n const key = attributes ? JSON.stringify(attributes) : ''\n\n useEffect(\n () => applyBodyAttributes(key ? (JSON.parse(key) as Record<string, string>) : undefined),\n [key],\n )\n}\n\n/**\n * The registered layout: `createAlfakit()` registers the panel's (`panelLayouts`), the starter\n * kit its content layout. Without one the page renders bare: importing the panel layouts here\n * would put the sidebar and top bar in every starter kit page.\n */\nfunction layout(registry: { resolve(key: string): AnyComponent | undefined }, key: string) {\n return registry.resolve(key) ?? BlankLayout\n}\n\nexport function PageShell({ page, children }: { page: PageSchema; children: ReactNode }) {\n const { registry } = useAlfakit()\n\n useBodyAttributes(page.extraBodyAttributes)\n\n // Each layout can be replaced: createAlfakit({ components: { 'layout:app': MyLayout } }).\n if (page.layout === 'auth') {\n return createElement(\n layout(registry, 'layout:auth'),\n { heading: page.heading ?? page.title, subheading: page.subheading, page },\n children,\n )\n }\n\n if (page.layout === 'blank') {\n return createElement(layout(registry, 'layout:blank'), { page }, children)\n }\n\n if (page.layout === 'fullscreen') {\n return createElement(layout(registry, 'layout:fullscreen'), { page }, children)\n }\n\n return createElement(\n layout(registry, 'layout:app'),\n { breadcrumbs: page.breadcrumbs, maxContentWidth: page.maxContentWidth, page },\n <>\n {/* `?` overlay, `/`, `g d` and panel command shortcuts (`keyboardShortcuts()`). */}\n <KeyboardShortcuts />\n <Slot name=\"page.start\" />\n <PageHeader page={page} />\n {/* Edit pages: somebody else saved the record (`detectConflicts()`, `Panel::realtime()`). */}\n {page.conflicts &&\n createElement(registry.resolve('page:conflict-banner') ?? ConflictBanner, { page })}\n {page.subNavigation ? (\n <SubNavigationLayout navigation={page.subNavigation}>{children}</SubNavigationLayout>\n ) : (\n children\n )}\n <Slot name=\"page.end\" />\n </>,\n )\n}\n\nexport function PageHeader({ page }: { page: PageSchema }) {\n const { registry } = useAlfakit()\n const actions = page.headerActions ?? []\n // `$headerActionsAlignment`: the default (end) sits beside the heading; the others\n // stack the actions under it.\n const alignment = page.headerActionsAlignment\n\n return (\n <>\n <Slot name=\"page.header.before\" />\n <header\n data-slot=\"page-header\"\n data-actions-alignment={alignment}\n className={cn(\n 'flex flex-col gap-4',\n !alignment && 'sm:flex-row sm:items-start sm:justify-between',\n )}\n >\n <div className=\"min-w-0\">\n <Slot name=\"page.heading.before\" />\n <h1 className=\"text-2xl font-semibold tracking-tight\">{page.heading ?? page.title}</h1>\n {page.subheading && (\n <p className=\"text-muted-foreground mt-1 text-sm\">{page.subheading}</p>\n )}\n <Slot name=\"page.heading.after\" />\n </div>\n <div\n className={cn(\n 'flex shrink-0 flex-wrap items-center gap-2',\n alignment === 'start'\n ? 'justify-start'\n : alignment === 'center'\n ? 'justify-center'\n : alignment === 'between' || alignment === 'justify'\n ? 'justify-between'\n : 'justify-end',\n )}\n >\n {/* Record pages with `Panel::realtime()`: who else is on the record. */}\n {page.presence &&\n createElement(registry.resolve('page:presence') ?? RecordPresence, {\n presence: page.presence,\n })}\n <Slot name=\"page.actions.before\" />\n {actions.length > 0 && (\n <ActionList\n actions={actions}\n context={{ token: page.token, component: 'page' }}\n align=\"end\"\n className=\"max-w-full shrink-0\"\n />\n )}\n <Slot name=\"page.actions.after\" />\n </div>\n </header>\n <Slot name=\"page.header.after\" />\n </>\n )\n}\n\nexport function PageContent({ page }: { page: PageSchema }) {\n return (\n <div className=\"flex flex-col gap-6\">\n {page.content.map((block) => (\n <ContentBlockRenderer key={block.key} block={block} />\n ))}\n </div>\n )\n}\n\nexport function ContentBlockRenderer({ block }: { block: ContentBlock }) {\n const { registry } = useAlfakit()\n // Links list tabs to the table they control.\n const tabsIdBase = `alfakit-list-tabs-${useId().replace(/[^A-Za-z0-9_-]/g, '')}`\n\n switch (block.type) {\n case 'Form':\n return <FormBlock block={block as FormBlockSchema} />\n case 'Infolist':\n return (\n <div data-slot=\"infolist\" className=\"contents\">\n <SchemaRenderer\n components={(block as InfolistBlock).components}\n columns={{ default: 1, lg: 2 }}\n />\n </div>\n )\n case 'Schema':\n return (\n <SchemaRenderer components={(block as SchemaBlock).components} columns={{ default: 1 }} />\n )\n case 'Table': {\n const table = block as TableBlockSchema\n const content = table.tabs?.length ? (\n <div className=\"flex flex-col gap-4\">\n <ListTabs\n tabs={table.tabs}\n activeTab={table.activeTab}\n tabsQueryKey={table.tabsQueryKey}\n label={table.pluralModelLabel}\n idBase={tabsIdBase}\n />\n <ListTabsPanel idBase={tabsIdBase} activeTab={table.activeTab}>\n <DataTable table={table} component={table.component} />\n </ListTabsPanel>\n </div>\n ) : (\n <DataTable table={table} component={table.component} />\n )\n\n if (block.key !== 'table') {\n return content\n }\n\n // The page's main table: the server sends these slots on List / ManageRelatedRecords pages only.\n return (\n <>\n <Slot name=\"resource.list-records.table.before\" />\n <Slot name=\"resource.manage-related-records.table.before\" />\n {content}\n <Slot name=\"resource.manage-related-records.table.after\" />\n <Slot name=\"resource.list-records.table.after\" />\n </>\n )\n }\n case 'RelationManagers':\n return <RelationManagers block={block as RelationManagersBlock} />\n case 'Widgets':\n return block.key === 'headerWidgets' ? (\n <>\n <Slot name=\"page.header-widgets.before\" />\n <WidgetGrid block={block as WidgetsBlockSchema} />\n <Slot name=\"page.header-widgets.after\" />\n </>\n ) : block.key === 'footerWidgets' ? (\n <>\n <Slot name=\"page.footer-widgets.before\" />\n <WidgetGrid block={block as WidgetsBlockSchema} />\n <Slot name=\"page.footer-widgets.after\" />\n </>\n ) : (\n <WidgetGrid block={block as WidgetsBlockSchema} />\n )\n case 'Links':\n return (\n <div className=\"flex flex-col items-center gap-1 text-center text-sm\">\n {(block as LinksBlock).links.map((link) => (\n <Link\n key={link.url}\n href={link.url}\n className=\"text-primary underline-offset-4 hover:underline\"\n >\n {link.label}\n </Link>\n ))}\n </div>\n )\n default: {\n const Custom = registry.resolve(block.type)\n\n return Custom ? createElement(Custom, { block }) : null\n }\n }\n}\n\nfunction RelationManagers({ block }: { block: RelationManagersBlock }) {\n const tabs = useMemo(() => relationManagerTabs(block), [block])\n // `?relation=`: a key in `getRelations()` or a position. The page URL (not\n // `window.location`) so the server renders the same tab as the browser.\n const url = usePage().url\n const [active, setActive] = useState(\n () =>\n relationTabFromQuery(\n tabs,\n new URL(url, 'http://localhost').searchParams.get(RELATION_QUERY_KEY),\n )?.key ??\n tabs[0]?.key ??\n '',\n )\n\n const changeTab = (key: string) => {\n setActive(key)\n\n if (typeof window === 'undefined') return\n\n const next = new URL(window.location.href)\n const slug = relationTabSlug(tabs, key)\n\n if (slug === null) next.searchParams.delete(RELATION_QUERY_KEY)\n else next.searchParams.set(RELATION_QUERY_KEY, slug)\n\n window.history.replaceState(window.history.state, '', next)\n }\n\n if (block.managers.length === 0) {\n return null\n }\n\n if (!hasRelationManagerTabs(block)) {\n const manager = block.managers[0]!\n\n return (\n <section className=\"flex flex-col gap-3\">\n <Slot name=\"resource.relation-manager.before\" scope={manager.key} />\n {isLazyPlaceholder(manager) ? (\n <LazyPlaceholder variant=\"table\" />\n ) : (\n <DataTable\n table={{ ...manager.table, heading: manager.table.heading ?? manager.title }}\n component={manager.component}\n />\n )}\n <Slot name=\"resource.relation-manager.after\" scope={manager.key} />\n </section>\n )\n }\n\n return (\n <Tabs value={active} onValueChange={changeTab}>\n <TabsList>\n <Slot name=\"resource.tabs.start\" />\n {tabs.map((tab) => (\n <TabsTrigger key={tab.key} value={tab.key}>\n {tab.icon && <Icon name={tab.icon} />}\n {tab.title}\n {tab.badge !== undefined && tab.badge !== null && tab.badge !== '' && (\n <Badge\n variant=\"secondary\"\n title={tab.badgeTooltip}\n className={cn(\n 'ms-1 px-1.5 py-0',\n tab.badgeColor && colorClass(badgeColorClasses, tab.badgeColor, 'gray'),\n )}\n >\n {tab.badge}\n {tab.badgeTooltip && <span className=\"sr-only\">{` (${tab.badgeTooltip})`}</span>}\n </Badge>\n )}\n </TabsTrigger>\n ))}\n <Slot name=\"resource.tabs.end\" />\n </TabsList>\n {tabs.map((tab) => (\n <TabsContent\n key={tab.key}\n value={tab.key}\n className=\"flex flex-col gap-6 pt-2 data-[state=inactive]:hidden\"\n data-slot={tab.group ? 'relation-group' : tab.content ? 'relation-content' : undefined}\n // The form keeps its unsaved state while another tab is open.\n forceMount={tab.content ? true : undefined}\n >\n {/* The page's form / infolist (`hasCombinedRelationManagerTabsWithContent()`). */}\n {tab.content && <ContentBlockRenderer block={tab.content as ContentBlock} />}\n {tab.managers.map((manager) => (\n <Fragment key={manager.key}>\n <Slot name=\"resource.relation-manager.before\" scope={manager.key} />\n {isLazyPlaceholder(manager) ? (\n <LazyPlaceholder variant=\"table\" />\n ) : (\n <DataTable\n // Inside a group each table keeps its own heading; a lone manager's is the tab.\n table={{\n ...manager.table,\n heading: tab.group ? (manager.table.heading ?? manager.title) : undefined,\n }}\n component={manager.component}\n />\n )}\n <Slot name=\"resource.relation-manager.after\" scope={manager.key} />\n </Fragment>\n ))}\n </TabsContent>\n ))}\n </Tabs>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page.js","names":["FormBlock"],"sources":["../../src/pages/page.tsx"],"sourcesContent":["import type {\n ContentBlock,\n FormBlock as FormBlockSchema,\n InfolistBlock,\n LinksBlock,\n PageSchema,\n RelationManagersBlock,\n SchemaBlock,\n TableBlock as TableBlockSchema,\n WidgetsBlock as WidgetsBlockSchema,\n} from '@alfakit/core'\nimport {\n applyBodyAttributes,\n customPageProps,\n hasRelationManagerTabs,\n isLazyPlaceholder,\n openDefaultAction,\n RELATION_QUERY_KEY,\n relationManagerTabs,\n relationTabFromQuery,\n relationTabSlug,\n resolveLazyComponents,\n type DefaultActionOpener,\n} from '@alfakit/core'\nimport { Head, Link, usePage } from '@inertiajs/react'\nimport {\n createElement,\n Fragment,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport { PageActionProvider } from '../components/actions/action-context'\nimport { ActionList } from '../components/actions/action-list'\nimport { FormBlock } from '../components/form/form-block'\nimport { KeyboardShortcuts } from '../components/panel/keyboard-shortcuts'\nimport { LazyPlaceholder } from '../components/panel/lazy-placeholder'\nimport { Slot } from '../components/panel/slot'\nimport { SubNavigationLayout } from '../components/panel/sub-navigation'\nimport { ConflictBanner } from '../components/realtime/conflict-banner'\nimport { RecordPresence } from '../components/realtime/record-presence'\nimport { SchemaRenderer } from '../components/schema/schema-renderer'\nimport { ListTabs, ListTabsPanel } from '../components/table/list-tabs'\nimport { DataTable } from '../components/table/table-block'\nimport { Badge } from '../components/ui/badge'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../components/ui/tabs'\nimport { WidgetGrid } from '../components/widgets/widgets-block'\nimport {\n useActionRunner,\n useAlfakit,\n useAlfakitProps,\n usePanel,\n useT,\n type AnyComponent,\n} from '../context'\nimport { BlankLayout } from '../layouts/blank-layout'\nimport { badgeColorClasses, colorClass } from '../lib/colors'\nimport { Icon } from '../lib/icons'\nimport { cn } from '../lib/utils'\n\n/**\n * `Alfakit/Page`: renders any schema-driven page. Custom pages (`Page::$component`) are\n * resolved from the `pages` map passed to `createAlfakit()`.\n */\nexport function AlfakitPage() {\n const props = useAlfakitProps()\n const panel = usePanel()\n const t = useT()\n const { pages } = useAlfakit()\n // Lazy widgets and relation managers arrive in a deferred prop after the first response.\n const page = useMemo(\n () => (props.page ? resolveLazyComponents(props.page, props.alfakitLazy) : undefined),\n [props.page, props.alfakitLazy],\n )\n\n useDefaultAction(page)\n\n if (!page) {\n return null\n }\n\n // Starter kit mode: the host's `title` callback adds the application name.\n const title = panel.starterKit ? page.title : `${page.title} - ${panel.brandName}`\n\n if (page.view) {\n const Custom = pages[page.view.component]\n\n if (!Custom) {\n return (\n <div className=\"text-destructive p-6\">\n {t(\n 'page_not_registered',\n { component: page.view.component },\n `Page component ${page.view.component} is not registered in createAlfakit({ pages }).`,\n )}\n </div>\n )\n }\n\n return (\n <PageActionProvider value={{ token: page.token, component: 'page' }}>\n <Head title={title} />\n {/* The component brings its own PageShell (as in panel mode, where it is the Inertia\n page itself) and takes the live props, so `reloadProps()` reaches it. */}\n <Custom\n {...customPageProps(page.view.props, props as Record<string, unknown>)}\n page={page}\n />\n </PageActionProvider>\n )\n }\n\n return (\n <PageActionProvider value={{ token: page.token, component: 'page' }}>\n <Head title={title} />\n <PageShell page={page}>\n <PageContent page={page} />\n </PageShell>\n </PageActionProvider>\n )\n}\n\n/**\n * Opens the page's default action (`$defaultAction`, `?tableAction=`) once the page has\n * hydrated, and once per page: re-renders after the action ran don't open it again.\n */\nexport function useDefaultAction(page: PageSchema | undefined) {\n const runner = useActionRunner()\n const opener = useRef<DefaultActionOpener>({ opened: null })\n\n useEffect(() => {\n openDefaultAction(page, opener.current, (action, context) => runner.trigger(action, context))\n }, [page, runner])\n}\n\n/** Sets the page's `$extraBodyAttributes` on `<body>` while it is open. */\nexport function useBodyAttributes(attributes: Record<string, string> | undefined) {\n const key = attributes ? JSON.stringify(attributes) : ''\n\n useEffect(\n () => applyBodyAttributes(key ? (JSON.parse(key) as Record<string, string>) : undefined),\n [key],\n )\n}\n\n/**\n * The registered layout: `createAlfakit()` registers the panel's (`panelLayouts`), the starter\n * kit its content layout. Without one the page renders bare: importing the panel layouts here\n * would put the sidebar and top bar in every starter kit page.\n */\nfunction layout(registry: { resolve(key: string): AnyComponent | undefined }, key: string) {\n return registry.resolve(key) ?? BlankLayout\n}\n\nexport function PageShell({ page, children }: { page: PageSchema; children: ReactNode }) {\n const { registry } = useAlfakit()\n\n useBodyAttributes(page.extraBodyAttributes)\n\n // Each layout can be replaced: createAlfakit({ components: { 'layout:app': MyLayout } }).\n if (page.layout === 'auth') {\n return createElement(\n layout(registry, 'layout:auth'),\n { heading: page.heading ?? page.title, subheading: page.subheading, page },\n children,\n )\n }\n\n if (page.layout === 'blank') {\n return createElement(layout(registry, 'layout:blank'), { page }, children)\n }\n\n if (page.layout === 'fullscreen') {\n return createElement(layout(registry, 'layout:fullscreen'), { page }, children)\n }\n\n return createElement(\n layout(registry, 'layout:app'),\n { breadcrumbs: page.breadcrumbs, maxContentWidth: page.maxContentWidth, page },\n <>\n {/* `?` overlay, `/`, `g d` and panel command shortcuts (`keyboardShortcuts()`). */}\n <KeyboardShortcuts />\n <Slot name=\"page.start\" />\n <PageHeader page={page} />\n {/* Edit pages: somebody else saved the record (`detectConflicts()`, `Panel::realtime()`). */}\n {page.conflicts &&\n createElement(registry.resolve('page:conflict-banner') ?? ConflictBanner, { page })}\n {page.subNavigation ? (\n <SubNavigationLayout navigation={page.subNavigation}>{children}</SubNavigationLayout>\n ) : (\n children\n )}\n <Slot name=\"page.end\" />\n </>,\n )\n}\n\nexport function PageHeader({ page }: { page: PageSchema }) {\n const { registry } = useAlfakit()\n const actions = page.headerActions ?? []\n // `$headerActionsAlignment`: the default (end) sits beside the heading; the others\n // stack the actions under it.\n const alignment = page.headerActionsAlignment\n\n return (\n <>\n <Slot name=\"page.header.before\" />\n <header\n data-slot=\"page-header\"\n data-actions-alignment={alignment}\n className={cn(\n 'flex flex-col gap-4',\n !alignment && 'sm:flex-row sm:items-start sm:justify-between',\n )}\n >\n <div className=\"min-w-0\">\n <Slot name=\"page.heading.before\" />\n <h1 className=\"text-2xl font-semibold tracking-tight\">{page.heading ?? page.title}</h1>\n {page.subheading && (\n <p className=\"text-muted-foreground mt-1 text-sm\">{page.subheading}</p>\n )}\n <Slot name=\"page.heading.after\" />\n </div>\n <div\n className={cn(\n 'flex shrink-0 flex-wrap items-center gap-2',\n alignment === 'start'\n ? 'justify-start'\n : alignment === 'center'\n ? 'justify-center'\n : alignment === 'between' || alignment === 'justify'\n ? 'justify-between'\n : 'justify-end',\n )}\n >\n {/* Record pages with `Panel::realtime()`: who else is on the record. */}\n {page.presence &&\n createElement(registry.resolve('page:presence') ?? RecordPresence, {\n presence: page.presence,\n })}\n <Slot name=\"page.actions.before\" />\n {actions.length > 0 && (\n <ActionList\n actions={actions}\n context={{ token: page.token, component: 'page' }}\n align=\"end\"\n className=\"max-w-full shrink-0\"\n />\n )}\n <Slot name=\"page.actions.after\" />\n </div>\n </header>\n <Slot name=\"page.header.after\" />\n </>\n )\n}\n\nexport function PageContent({ page }: { page: PageSchema }) {\n return (\n <div className=\"flex flex-col gap-6\">\n {page.content.map((block) => (\n <ContentBlockRenderer key={block.key} block={block} />\n ))}\n </div>\n )\n}\n\nexport function ContentBlockRenderer({ block }: { block: ContentBlock }) {\n const { registry } = useAlfakit()\n // Links list tabs to the table they control.\n const tabsIdBase = `alfakit-list-tabs-${useId().replace(/[^A-Za-z0-9_-]/g, '')}`\n\n switch (block.type) {\n case 'Form':\n return <FormBlock block={block as FormBlockSchema} />\n case 'Infolist':\n return (\n <div data-slot=\"infolist\" className=\"contents\">\n <SchemaRenderer\n components={(block as InfolistBlock).components}\n columns={(block as InfolistBlock).columns ?? { default: 1, lg: 2 }}\n />\n </div>\n )\n case 'Schema':\n return (\n <SchemaRenderer components={(block as SchemaBlock).components} columns={{ default: 1 }} />\n )\n case 'Table': {\n const table = block as TableBlockSchema\n const content = table.tabs?.length ? (\n <div className=\"flex flex-col gap-4\">\n <ListTabs\n tabs={table.tabs}\n activeTab={table.activeTab}\n tabsQueryKey={table.tabsQueryKey}\n label={table.pluralModelLabel}\n idBase={tabsIdBase}\n />\n <ListTabsPanel idBase={tabsIdBase} activeTab={table.activeTab}>\n <DataTable table={table} component={table.component} />\n </ListTabsPanel>\n </div>\n ) : (\n <DataTable table={table} component={table.component} />\n )\n\n if (block.key !== 'table') {\n return content\n }\n\n // The page's main table: the server sends these slots on List / ManageRelatedRecords pages only.\n return (\n <>\n <Slot name=\"resource.list-records.table.before\" />\n <Slot name=\"resource.manage-related-records.table.before\" />\n {content}\n <Slot name=\"resource.manage-related-records.table.after\" />\n <Slot name=\"resource.list-records.table.after\" />\n </>\n )\n }\n case 'RelationManagers':\n return <RelationManagers block={block as RelationManagersBlock} />\n case 'Widgets':\n return block.key === 'headerWidgets' ? (\n <>\n <Slot name=\"page.header-widgets.before\" />\n <WidgetGrid block={block as WidgetsBlockSchema} />\n <Slot name=\"page.header-widgets.after\" />\n </>\n ) : block.key === 'footerWidgets' ? (\n <>\n <Slot name=\"page.footer-widgets.before\" />\n <WidgetGrid block={block as WidgetsBlockSchema} />\n <Slot name=\"page.footer-widgets.after\" />\n </>\n ) : (\n <WidgetGrid block={block as WidgetsBlockSchema} />\n )\n case 'Links':\n return (\n <div className=\"flex flex-col items-center gap-1 text-center text-sm\">\n {(block as LinksBlock).links.map((link) => (\n <Link\n key={link.url}\n href={link.url}\n className=\"text-primary underline-offset-4 hover:underline\"\n >\n {link.label}\n </Link>\n ))}\n </div>\n )\n default: {\n const Custom = registry.resolve(block.type)\n\n return Custom ? createElement(Custom, { block }) : null\n }\n }\n}\n\nfunction RelationManagers({ block }: { block: RelationManagersBlock }) {\n const tabs = useMemo(() => relationManagerTabs(block), [block])\n // `?relation=`: a key in `getRelations()` or a position. The page URL (not\n // `window.location`) so the server renders the same tab as the browser.\n const url = usePage().url\n const [active, setActive] = useState(\n () =>\n relationTabFromQuery(\n tabs,\n new URL(url, 'http://localhost').searchParams.get(RELATION_QUERY_KEY),\n )?.key ??\n tabs[0]?.key ??\n '',\n )\n\n const changeTab = (key: string) => {\n setActive(key)\n\n if (typeof window === 'undefined') return\n\n const next = new URL(window.location.href)\n const slug = relationTabSlug(tabs, key)\n\n if (slug === null) next.searchParams.delete(RELATION_QUERY_KEY)\n else next.searchParams.set(RELATION_QUERY_KEY, slug)\n\n window.history.replaceState(window.history.state, '', next)\n }\n\n if (block.managers.length === 0) {\n return null\n }\n\n if (!hasRelationManagerTabs(block)) {\n const manager = block.managers[0]!\n\n return (\n <section className=\"flex flex-col gap-3\">\n <Slot name=\"resource.relation-manager.before\" scope={manager.key} />\n {isLazyPlaceholder(manager) ? (\n <LazyPlaceholder variant=\"table\" />\n ) : (\n <DataTable\n table={{ ...manager.table, heading: manager.table.heading ?? manager.title }}\n component={manager.component}\n />\n )}\n <Slot name=\"resource.relation-manager.after\" scope={manager.key} />\n </section>\n )\n }\n\n return (\n <Tabs value={active} onValueChange={changeTab}>\n <TabsList>\n <Slot name=\"resource.tabs.start\" />\n {tabs.map((tab) => (\n <TabsTrigger key={tab.key} value={tab.key}>\n {tab.icon && <Icon name={tab.icon} />}\n {tab.title}\n {tab.badge !== undefined && tab.badge !== null && tab.badge !== '' && (\n <Badge\n variant=\"secondary\"\n title={tab.badgeTooltip}\n className={cn(\n 'ms-1 px-1.5 py-0',\n tab.badgeColor && colorClass(badgeColorClasses, tab.badgeColor, 'gray'),\n )}\n >\n {tab.badge}\n {tab.badgeTooltip && <span className=\"sr-only\">{` (${tab.badgeTooltip})`}</span>}\n </Badge>\n )}\n </TabsTrigger>\n ))}\n <Slot name=\"resource.tabs.end\" />\n </TabsList>\n {tabs.map((tab) => (\n <TabsContent\n key={tab.key}\n value={tab.key}\n className=\"flex flex-col gap-6 pt-2 data-[state=inactive]:hidden\"\n data-slot={tab.group ? 'relation-group' : tab.content ? 'relation-content' : undefined}\n // The form keeps its unsaved state while another tab is open.\n forceMount={tab.content ? true : undefined}\n >\n {/* The page's form / infolist (`hasCombinedRelationManagerTabsWithContent()`). */}\n {tab.content && <ContentBlockRenderer block={tab.content as ContentBlock} />}\n {tab.managers.map((manager) => (\n <Fragment key={manager.key}>\n <Slot name=\"resource.relation-manager.before\" scope={manager.key} />\n {isLazyPlaceholder(manager) ? (\n <LazyPlaceholder variant=\"table\" />\n ) : (\n <DataTable\n // Inside a group each table keeps its own heading; a lone manager's is the tab.\n table={{\n ...manager.table,\n heading: tab.group ? (manager.table.heading ?? manager.title) : undefined,\n }}\n component={manager.component}\n />\n )}\n <Slot name=\"resource.relation-manager.after\" scope={manager.key} />\n </Fragment>\n ))}\n </TabsContent>\n ))}\n </Tabs>\n )\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfakit/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "React + shadcn/ui renderer for Alfakit, the Laravel admin panel builder by Alfa Six.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alfa Six (https://alfasixbh.com)",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"react-day-picker": "^10.0.2",
|
|
97
97
|
"sonner": "^2.0.8",
|
|
98
98
|
"tailwind-merge": "^3.7.0",
|
|
99
|
-
"@alfakit/core": "0.1.
|
|
99
|
+
"@alfakit/core": "0.1.12"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"@inertiajs/react": "^3.0.0",
|
|
@@ -115,6 +115,8 @@ export function ImageEntryComponent({ component }: { component: ImageEntrySchema
|
|
|
115
115
|
data-slot="image-remaining"
|
|
116
116
|
className={cn(
|
|
117
117
|
'text-muted-foreground',
|
|
118
|
+
// The last image keeps the stack's negative end margin (W-S3-03).
|
|
119
|
+
component.stacked && usesDefaultImageStack(component) && 'ms-3',
|
|
118
120
|
remainingTextSizeClass(component.limitedRemainingTextSize),
|
|
119
121
|
)}
|
|
120
122
|
>
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
insertBuilderItem,
|
|
18
18
|
isDeferredPlaceholder,
|
|
19
19
|
itemActionContext,
|
|
20
|
+
itemExtraActions,
|
|
20
21
|
moveIndexedState,
|
|
21
22
|
moveRepeaterItem,
|
|
22
23
|
removeIndexedState,
|
|
@@ -316,7 +317,9 @@ function BuilderFieldContent({ component }: { component: BuilderSchema }) {
|
|
|
316
317
|
index={index}
|
|
317
318
|
count={items.length}
|
|
318
319
|
resolve={(name) => resolve(name, serverItem)}
|
|
319
|
-
extraActions={
|
|
320
|
+
extraActions={
|
|
321
|
+
menu || locked ? undefined : itemExtraActions(component, serverItem)
|
|
322
|
+
}
|
|
320
323
|
buttons={buttons && !menu}
|
|
321
324
|
canClone={canClone && !menu}
|
|
322
325
|
canDelete={canDelete && !menu}
|
|
@@ -353,7 +356,9 @@ function BuilderFieldContent({ component }: { component: BuilderSchema }) {
|
|
|
353
356
|
canMoveDown={buttons && index < items.length - 1}
|
|
354
357
|
canClone={canClone}
|
|
355
358
|
canDelete={canDelete}
|
|
356
|
-
extraActions={
|
|
359
|
+
extraActions={
|
|
360
|
+
locked ? undefined : itemExtraActions(component, serverItem)
|
|
361
|
+
}
|
|
357
362
|
onAction={(name, onClient) =>
|
|
358
363
|
runItemAction(
|
|
359
364
|
resolve(name, serverItem),
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
fileManagerSize,
|
|
10
10
|
filesFromClipboard,
|
|
11
11
|
fileUploadAccept,
|
|
12
|
+
fileUploadEntries,
|
|
12
13
|
fileUploadPaths,
|
|
13
14
|
insertUploadedFile,
|
|
14
15
|
moveRepeaterItem,
|
|
@@ -103,13 +104,7 @@ export function FileUploadField({ component }: { component: FileUploadSchema })
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
const paths = fileUploadPaths(value)
|
|
106
|
-
const files: LocalFile[] = paths.
|
|
107
|
-
(path) =>
|
|
108
|
-
[...component.files, ...uploaded].find((file) => file.path === path) ?? {
|
|
109
|
-
path,
|
|
110
|
-
name: path.replace(/^tmp:/, ''),
|
|
111
|
-
},
|
|
112
|
-
)
|
|
107
|
+
const files: LocalFile[] = fileUploadEntries(paths, component.files, uploaded)
|
|
113
108
|
const maxFiles = component.multiple ? (component.maxFiles ?? Infinity) : 1
|
|
114
109
|
const canAddMore = files.length + uploading.length < maxFiles
|
|
115
110
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RepeaterSchema, SchemaComponent } from '@alfakit/core'
|
|
2
2
|
import {
|
|
3
|
-
VIRTUAL_ESTIMATES,
|
|
4
3
|
appendRepeaterItem,
|
|
5
4
|
cloneIndexedState,
|
|
6
5
|
cloneRepeaterItem,
|
|
@@ -8,12 +7,14 @@ import {
|
|
|
8
7
|
fieldIds,
|
|
9
8
|
gridColumnsClasses,
|
|
10
9
|
isDeferredPlaceholder,
|
|
10
|
+
itemExtraActions,
|
|
11
11
|
moveIndexedState,
|
|
12
12
|
moveRepeaterItem,
|
|
13
13
|
removeIndexedState,
|
|
14
14
|
removeRepeaterItem,
|
|
15
15
|
reorderedItemKeys,
|
|
16
16
|
templateForIndex,
|
|
17
|
+
VIRTUAL_ESTIMATES,
|
|
17
18
|
virtualizeOptions,
|
|
18
19
|
} from '@alfakit/core'
|
|
19
20
|
import { useState } from 'react'
|
|
@@ -165,7 +166,7 @@ function RepeaterFieldContent({ component }: { component: RepeaterSchema }) {
|
|
|
165
166
|
virtual={virtual}
|
|
166
167
|
context={context}
|
|
167
168
|
resolve={(name, index) => resolve(name, component.items[index])}
|
|
168
|
-
extraActionsFor={(index) => component.items[index]
|
|
169
|
+
extraActionsFor={(index) => itemExtraActions(component, component.items[index])}
|
|
169
170
|
/>
|
|
170
171
|
) : (
|
|
171
172
|
<>
|
|
@@ -245,7 +246,9 @@ function RepeaterFieldContent({ component }: { component: RepeaterSchema }) {
|
|
|
245
246
|
index={index}
|
|
246
247
|
count={items.length}
|
|
247
248
|
resolve={(name) => resolve(name, serverItem)}
|
|
248
|
-
extraActions={
|
|
249
|
+
extraActions={
|
|
250
|
+
locked ? undefined : itemExtraActions(component, serverItem)
|
|
251
|
+
}
|
|
249
252
|
buttons={buttons}
|
|
250
253
|
canClone={Boolean(component.cloneable) && canAdd}
|
|
251
254
|
canDelete={canDelete}
|