@alfakit/core 0.1.2 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +6 -0
- package/dist/actions.js.map +1 -1
- package/dist/advanced-fields.js +2 -2
- package/dist/advanced-fields.js.map +1 -1
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +2 -1
- package/dist/assets.js.map +1 -1
- package/dist/csp.d.ts +11 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +16 -0
- package/dist/csp.js.map +1 -0
- package/dist/form.d.ts +20 -0
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +49 -4
- package/dist/form.js.map +1 -1
- package/dist/icons.d.ts +6 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +9 -1
- package/dist/icons.js.map +1 -1
- package/dist/image-editor.d.ts +2 -2
- package/dist/image-editor.js +2 -2
- package/dist/image-files.d.ts +8 -0
- package/dist/image-files.d.ts.map +1 -1
- package/dist/image-files.js +11 -1
- package/dist/image-files.js.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.js +8 -7
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +1 -1
- package/dist/kanban.js.map +1 -1
- package/dist/page-actions.d.ts +2 -2
- package/dist/page-actions.d.ts.map +1 -1
- package/dist/page-actions.js +4 -4
- package/dist/page-actions.js.map +1 -1
- package/dist/schema/advanced-fields.d.ts +5 -0
- package/dist/schema/advanced-fields.d.ts.map +1 -1
- package/dist/schema/cms.d.ts +2 -0
- package/dist/schema/cms.d.ts.map +1 -1
- package/dist/schema/index.d.ts +3 -3
- package/dist/schema/panel.d.ts +25 -0
- package/dist/schema/panel.d.ts.map +1 -1
- package/dist/schema/panel.js.map +1 -1
- package/dist/standalone.d.ts +10 -3
- package/dist/standalone.d.ts.map +1 -1
- package/dist/standalone.js +12 -2
- package/dist/standalone.js.map +1 -1
- package/dist/starter-kit-widgets.d.ts +40 -7
- package/dist/starter-kit-widgets.d.ts.map +1 -1
- package/dist/starter-kit-widgets.js +71 -7
- package/dist/starter-kit-widgets.js.map +1 -1
- package/dist/starter-kit.d.ts +24 -1
- package/dist/starter-kit.d.ts.map +1 -1
- package/dist/starter-kit.js +33 -3
- package/dist/starter-kit.js.map +1 -1
- package/dist/utils/debounce.d.ts +2 -0
- package/dist/utils/debounce.d.ts.map +1 -1
- package/dist/utils/debounce.js +1 -0
- package/dist/utils/debounce.js.map +1 -1
- package/dist/utils/path.d.ts +11 -1
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +30 -3
- package/dist/utils/path.js.map +1 -1
- package/package.json +1 -1
- package/themes.css +6 -1
package/dist/schema/panel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel.js","names":[],"sources":["../../src/schema/panel.ts"],"sourcesContent":["import type { ActionSchema, AnyActionSchema } from './actions'\nimport type { CommandPaletteSchema, KeyboardShortcutsSchema } from './command-palette'\nimport type { Color, IconName, RecordKey, Width } from './common'\nimport type { SchemaComponent } from './components'\nimport type { DebugSchema } from './debug'\nimport type { DevToolsSchema } from './dev-tools'\nimport type { FormBlock, InfolistBlock, LinksBlock, SchemaBlock } from './form'\nimport type { NotificationSchema } from './notifications'\nimport type { PwaSchema } from './pwa'\nimport type { ConflictsSchema, PresenceSchema, RealtimeSchema } from './realtime'\nimport type { DeferredTabBadges } from './resources'\nimport type { RelationManagerSchema, RelationManagersBlock, TableBlock } from './table'\nimport type { WidgetSchema, WidgetsBlock } from './widgets'\n\nexport interface NavigationItemSchema {\n /** Text, or sanitised HTML when `labelHtml` is true (an `Htmlable` label). */\n label: string\n /** `label` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n labelHtml?: boolean\n icon?: IconName\n /**\n * Split navigation (`alfakit.navigation` is an Inertia once prop): the icon of the item\n * while active, swapped in by `resolvePanelNavigation()`.\n */\n activeIcon?: IconName\n url?: string\n openInNewTab?: boolean\n active?: boolean\n badge?: string | number\n badgeColor?: Color\n badgeTooltip?: string\n /** `Panel::deferNavigationBadges()`: the item has a badge that loads after the page. */\n deferredBadge?: boolean\n /** Set by `resolvePanelNavigation()` while a deferred badge is loading (show a placeholder). */\n badgePending?: boolean\n /** `NavigationItem::extraAttributes()`: HTML attributes for the item's link (`class` merges). */\n extraAttributes?: Record<string, string | number | boolean | null>\n children?: NavigationItemSchema[]\n}\n\nexport interface NavigationGroupSchema {\n label?: string | null\n icon?: IconName\n collapsible: boolean\n collapsed?: boolean\n items: NavigationItemSchema[]\n /** `NavigationGroup::extraSidebarAttributes()`: HTML attributes for the group in the sidebar. */\n extraSidebarAttributes?: Record<string, string | number | boolean | null>\n /** `NavigationGroup::extraTopbarAttributes()`: HTML attributes for the group in the top navigation. */\n extraTopbarAttributes?: Record<string, string | number | boolean | null>\n}\n\n/** A navigation item's badge, from `navigationState` or the deferred `alfakitNavigationBadges` prop. */\nexport interface NavigationBadgeSchema {\n badge: string | number\n badgeColor?: Color\n badgeTooltip?: string\n}\n\n/**\n * The per-request part of the panel navigation, whose structure (`navigation`) is an\n * Inertia once prop. Items are addressed by position: `group.item[.child…]` (`0.2.1`).\n */\nexport interface NavigationStateSchema {\n /** The active items (an active child's ancestors included). */\n active?: string[]\n /** Badges, unless `Panel::deferNavigationBadges()` defers them. */\n badges?: Record<string, NavigationBadgeSchema>\n}\n\n/** One `starterKit()` panel's navigation, as shared with the host application's pages. */\nexport interface StarterKitNavigationSchema {\n /** The panel id. */\n id: string\n /** The panel's home URL. */\n url: string\n brandName: string\n /** Empty for tenancy panels outside a tenant (their items link to tenant routes). */\n groups: NavigationGroupSchema[]\n /** What the drop-in header components render before they load. */\n widgets?: StarterKitWidgetsSchema\n}\n\n/**\n * The `widgets` of a `starterKit()` panel's shared navigation: what `<AlfakitSearch />`,\n * `<AlfakitNotifications />` and `<AlfakitTenantSwitcher />` show on any host page before\n * their code and the panel data (`endpoint`) have loaded.\n */\nexport interface StarterKitWidgetsSchema {\n /** GET: the panel data the components render with (the `alfakit` prop of the panel's pages). */\n endpoint: string\n /** Global search or the command palette (tenancy panels: only inside a tenant). */\n search?: { keyBindings: string[]; placeholder: string }\n /** `databaseNotifications()`: the bell's label and the user's unread count. */\n notifications?: { label: string; unreadCount: number }\n /** Tenancy panels: the user's tenants; `current` is null on the host's own pages. */\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`). */\n registrationLabel?: string\n searchable?: true\n }\n}\n\n/** A page's secondary navigation: cluster pages or a record's pages. */\nexport interface SubNavigationSchema {\n position: 'start' | 'end' | 'top'\n groups: NavigationGroupSchema[]\n}\n\n/**\n * An entry of the user or tenant menu: a link (`url`, `method: 'post'` for POST links) or\n * an `Action` with server logic or a modal (`action`), run through the action endpoint\n * with the component `userMenu` / `tenantMenu` and the page's token.\n */\nexport interface MenuItemSchema {\n label?: string\n icon?: IconName\n url?: string\n openInNewTab?: boolean\n color?: Color\n method?: 'post'\n action?: ActionSchema\n /** Group index (nested arrays in `userMenuItems()`); a separator divides groups. */\n group?: number\n}\n\n/** Action endpoint components of the menus (`Panel::USER_MENU_COMPONENT`, …). */\nexport const USER_MENU_COMPONENT = 'userMenu'\nexport const TENANT_MENU_COMPONENT = 'tenantMenu'\n\nexport interface PanelUserSchema {\n id: RecordKey\n name: string\n email?: string | null\n avatar?: string | null\n}\n\nexport interface TenantSchema {\n id: RecordKey\n /** Text, or sanitised HTML when `nameHtml` is true (an `Htmlable` `getAlfakitName()`). */\n name: string\n /** `name` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n nameHtml?: boolean\n avatar?: string | null\n url: string\n /** `HasCurrentTenantLabel`: a caption shown with the current tenant (\"Active team\"). */\n label?: string | null\n}\n\n/** The panel's design, see `Panel::theme()`. */\nexport interface PanelThemeSchema {\n name: string\n /** Value of `data-alfakit-theme`, base theme first: `\"alfakit acme\"`. */\n attribute: string\n sidebar: 'inset' | 'floating' | 'flush'\n density: 'comfortable' | 'compact'\n radius?: string | null\n font?: string | null\n /** CSS value for `--alfakit-font`. */\n fontCss?: string | null\n /** CSS value for `--alfakit-heading-font`. */\n headingFontCss?: string | null\n /** Stylesheet that loads the font, if any. */\n fontUrl?: string | null\n /** Every font stylesheet to load (body and heading fonts). */\n fontUrls?: string[]\n /** Structure and component styles; mirrored as `data-*` attributes on `<html>`. */\n design: PanelDesignSchema\n /**\n * Icon aliases replaced with `Icons::register()` (`alfakit::panels.sidebar.collapse-button`\n * → a Lucide name, image URL or sanitised SVG). The chrome falls back to its own icon.\n */\n icons?: Record<string, string> | null\n}\n\nexport interface PanelDesignSchema {\n layout: 'sidebar' | 'topbar' | 'rail'\n nav: 'soft' | 'pill' | 'bar' | 'underline'\n header: 'plain' | 'banner' | 'band' | 'compact'\n surfaces: 'bordered' | 'elevated' | 'flat' | 'ring'\n inputs: 'outline' | 'filled' | 'underline'\n buttons: 'rounded' | 'pill' | 'square'\n tables: 'lined' | 'striped' | 'minimal'\n /**\n * Below the `md` breakpoint: `drawer` (default) or the `bottom` mobile preset (bottom\n * navigation bar, tables as stacked cards, slide-over modals, 44px touch targets and\n * sticky form actions). See `Panel::mobile()`.\n */\n mobile?: 'drawer' | 'bottom'\n}\n\n/**\n * One piece of slot content: server-rendered HTML or a schema component. `for` lists the\n * components (`relationManager:<class>`, `widget:<id>`) it is scoped to: such items only\n * render in a `<Slot scope=\"…\">` of a matching component. `when` (`Panel::slot(…, when:)`)\n * limits it to renders whose slot data matches: `{ column: 'title' }` only renders in the\n * `table.header-cell` slot of the `title` column; a list value matches any of its entries.\n */\nexport type SlotItemSchema = (\n { type: 'html'; html: string } | { type: 'component'; component: SchemaComponent }\n) & { for?: string[]; when?: Record<string, SlotDataValue | SlotDataValue[]> }\n\n/** A value in a slot's render data (`<Slot data={{ column: 'title' }}>`). */\nexport type SlotDataValue = string | number | boolean | null\n\n/** A language the panel can be displayed in. */\nexport interface LocaleSchema {\n /** Laravel-style code: `en`, `pt_BR`, `zh_Hant`. */\n code: string\n /** BCP 47 tag for `Intl` and the `lang` attribute: `pt-BR`. */\n tag: string\n /** The language's own name: `Deutsch`, `العربية`. */\n label: string\n direction: 'ltr' | 'rtl'\n}\n\n/** The current request's language, formats and the panel's language list. */\nexport interface LocalizationSchema extends LocaleSchema {\n fallbackLocale: string\n /** 0 = Sunday … 6 = Saturday. */\n firstDayOfWeek: number\n /** Default ISO 4217 currency. */\n currency: string\n /** IANA timezone dates are displayed in. */\n timezone: string\n /** Whether to show a language menu. */\n switcher: boolean\n /**\n * Where panel pages offer the menu (`Panel::languageSwitcher()`): the user menu (default\n * when absent), a top bar button, both, or neither. Auth pages keep their switcher.\n */\n switcherPosition?: LanguageSwitcherPosition\n /** Languages to offer in the menu (empty when the switcher is off). */\n locales: LocaleSchema[]\n}\n\nexport type LanguageSwitcherPosition = 'user-menu' | 'topbar' | 'both' | 'hidden'\n\nexport interface PanelEndpoints {\n action: string\n modal: string\n schemaRefresh: string\n schemaOptions: string\n /** Validates one wizard step's fields (`step` = the Step component's key). */\n schemaValidate?: string\n /** Loads `deferLoading()` children and `Tab::deferBadge()` badges (`{schema}::{key}`). */\n schemaDeferred?: string\n tableCell: string\n tableReorder: string\n /** Stores a user's column order (`reorderableColumns()`). */\n tableColumns?: string\n /** `Table::deferTotals()`: the record count and summaries, loaded after the rows. */\n tableTotals?: string\n /** `Table::savedViews()`: save, share, rename, delete and pick a default view. */\n tableViews?: string\n /** `Table::spreadsheet()`: batch save (or validate) grid edits. */\n tableCells?: string\n /** Fresh schemas of lazy and polled components by token (components outside panels). */\n components?: string\n locale: string\n /** Calls `#[Expose]` methods of custom pages (`usePageMethod()`). */\n pageCall?: string\n /** Edit pages: what changed since the form was loaded (`detectConflicts()`). */\n conflicts?: string\n upload: string\n uploadDestroy: string\n /** Starts a resumable chunked upload (`{uploadChunks}/{id}` takes chunks, `/complete` finishes). */\n uploadChunks?: string\n globalSearch: string\n /** Command palette: \"create\" links and recent records (GET). */\n commandPalette?: string\n /** Stores the user's customised keyboard shortcuts (POST `{shortcuts}`). */\n shortcuts?: string\n /** `Action::undoable()`: undoes an action from its toast (POST `{undo}`). */\n undo?: string\n /** `Dashboard::customizable()`: saves or resets the user's layout (POST `{token, layout}`). */\n dashboardLayout?: string\n notifications: string\n notificationsReadAll: string\n notificationsDestroyAll: string\n logout: string\n}\n\n/** Where toasts appear (`Panel::toastPosition()`); `start`/`end` follow the reading direction. */\nexport type ToastPosition =\n 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'\n\n/** Shown while impersonating another user (`ImpersonateAction`). */\nexport interface ImpersonationSchema {\n /** \"You are signed in as …\", translated on the server. */\n label: string\n leaveLabel: string\n /** POST here to return to the real user. */\n leaveUrl: string\n}\n\n/** The `alfakit` prop shared with every page of a panel. */\n/**\n * One `errorNotifications` entry: the text to show, or `hidden` (caught, nothing shown,\n * `hiddenErrorNotification()`) or `disabled` (default handling, `disabledErrorNotification()`).\n */\nexport interface ErrorNotificationEntrySchema {\n title?: string\n body?: string | null\n icon?: IconName\n hidden?: boolean\n disabled?: boolean\n}\n\n/** `errorNotifications`, keyed by HTTP status plus `default`. */\nexport type ErrorNotificationsSchema = Record<string, ErrorNotificationEntrySchema>\n\nexport interface PanelSchema {\n id: string\n /**\n * The schema contract version the server serialised (`SchemaContract::VERSION`). Renderers\n * compare its major part with `SCHEMA_CONTRACT_VERSION`; absent on servers before 1.0.\n */\n schemaContract?: string\n path: string\n url?: string\n brandName: string\n brandLogo?: string\n darkModeBrandLogo?: string\n brandLogoHeight?: string\n favicon?: string\n /**\n * Colour custom properties without the `--alfakit-` prefix: `primary`, its readable\n * `primary-foreground` and, for palettes (`Color::Indigo`), `primary-50` … `primary-950`.\n */\n colors?: Record<string, string>\n /**\n * CSS for custom colour names (`colors(['brand' => …])`): `.alfakit-color-{name}` rules\n * that point the generic colour utilities of themes.css at `--alfakit-{name}`.\n */\n colorCss?: string\n darkMode: {\n enabled: boolean\n forced: boolean\n /** `defaultThemeMode()`; absent for `system`. */\n default?: 'light' | 'dark'\n /** `false` when `themeSwitcher(false)`: no switcher, always the default mode. */\n switcher?: false\n }\n /** `false` when `topbar(false)`: sidebar layouts render no top bar (a slim bar on phones). */\n topbar?: false\n sidebarCollapsibleOnDesktop: boolean\n sidebarFullyCollapsibleOnDesktop?: boolean\n topNavigation?: boolean\n maxContentWidth?: Width | string\n breadcrumbs: boolean\n unsavedChangesAlerts?: boolean\n /** `false` when `collapsibleNavigationGroups(false)`: groups never collapse. */\n collapsibleNavigationGroups?: false\n /** CSS lengths for the open and icon-only sidebar (`--sidebar-width`, `--sidebar-width-icon`). */\n sidebarWidth?: string\n collapsedSidebarWidth?: string\n /** Max width of `auth` layout pages (login, registration…). */\n simplePageMaxContentWidth?: Width | string\n /** `false` when `spa(false)`: every link loads a full page. */\n spa?: false\n /** URL patterns (`*` wildcard) that load with a full page request. */\n spaUrlExceptions?: string[]\n /**\n * Notifications shown instead of Inertia's error modal, keyed by HTTP status plus\n * `default`. Absent when disabled or in debug mode. A page's own `errorNotifications`\n * replaces it while that page is open.\n */\n errorNotifications?: ErrorNotificationsSchema\n theme: PanelThemeSchema\n /** Content inserted with `Panel::slot()`, keyed by slot name. */\n slots?: Record<string, SlotItemSchema[]>\n /**\n * Empty for `starterKit()` panels: their navigation is the `alfakitNavigation` page prop,\n * which every page of the host application carries (not only the panel's).\n */\n navigation: NavigationGroupSchema[]\n /**\n * The active items and badges of this request; `navigation` holds only the structure\n * (sent once). Read the navigation through `usePanel()`, which merges the two.\n */\n navigationState?: NavigationStateSchema\n /**\n * `Panel::starterKit()`: the panel runs inside a Laravel starter kit app. Its pages render\n * in the host's own layout (sidebar, header, appearance) and the host's shadcn tokens;\n * the panel's own chrome, theme attributes and colour scheme are not applied.\n */\n starterKit?: true\n user?: PanelUserSchema\n userMenu?: {\n items: MenuItemSchema[]\n profileUrl?: string\n logoutUrl: string\n /** The `profile` entry as customised in `userMenuItems()` (replaces the built-in link). */\n profile?: MenuItemSchema\n /** The customised `logout` entry, or `false` when it is hidden. */\n logout?: MenuItemSchema | false\n /**\n * `topbar` when `userMenu(position: UserMenuPosition::Topbar)`: sidebar layouts show the\n * menu in the top bar instead of the sidebar footer. Omitted for the sidebar.\n */\n position?: 'topbar'\n }\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n profileUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`); else \"New\". */\n registrationLabel?: string\n /** The profile page's own label (`EditTenantProfile::getLabel()`); else \"Settings\". */\n profileLabel?: string\n /** The customised `register` entry of `tenantMenuItems()`. */\n registration?: MenuItemSchema\n /** The customised `profile` entry of `tenantMenuItems()`. */\n profile?: MenuItemSchema\n menu?: MenuItemSchema[]\n /** `false` when `tenantSwitcher(false)`: `tenants` is empty and the menu lists no others. */\n switcher?: false\n /** `searchableTenantMenu()`, or more than 10 tenants: show a search field in the menu. */\n searchable?: boolean\n }\n databaseNotifications?: {\n pollingInterval?: string\n unreadCount: number\n /**\n * `sidebar` when `databaseNotifications(position: 'sidebar')` or `topbar(false)`: sidebar\n * layouts show the bell in the sidebar instead of the top bar. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n globalSearch?: {\n keyBindings: string[]\n placeholder: string\n /** `globalSearchDebounce()` in milliseconds; renderers default to 250. */\n debounce?: number\n /** `globalSearchFieldSuffix()`: text shown at the end of the field instead of the key hint. */\n suffix?: string\n /** `false` when `globalSearchFieldKeyBindingSuffix(false)`: no key binding hint. */\n keyBindingSuffix?: false\n /**\n * `sidebar` when `globalSearch(position: GlobalSearchPosition::Sidebar)` or\n * `topbar(false)`: sidebar layouts show the field in the sidebar header. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n endpoints: Partial<PanelEndpoints> & { logout: string }\n /** `alfakit.uploads.chunk_size` in bytes: larger files are uploaded in chunks. */\n uploadChunkSize?: number\n auth: { login?: string; register?: string; passwordReset?: string }\n flashKey: string\n /** Present while impersonating: render a banner with a way back. */\n impersonation?: ImpersonationSchema\n /**\n * `Panel::realtimeNotifications()`: the private channel to listen on with Laravel Echo;\n * with `Panel::realtime()` also the connection (`live`, `authEndpoint`, `echo`).\n */\n realtime?: RealtimeSchema\n /** Omitted for the default (`top-end`). */\n toastPosition?: ToastPosition\n /** The command palette's extras: page actions, panel commands, navigation, create links, recent records. */\n commandPalette?: CommandPaletteSchema\n /** Keyboard shortcuts (`false` with `keyboardShortcuts(false)`); absent for guests. */\n keyboardShortcuts?: KeyboardShortcutsSchema | false\n /** `Panel::pwa()`: service worker, install prompt and Web Push; absent when off. */\n pwa?: PwaSchema\n localization: LocalizationSchema\n csrf: string\n translations: Record<string, unknown>\n}\n\nexport interface BreadcrumbSchema {\n label: string\n url?: string\n}\n\nexport type ContentBlock =\n | FormBlock\n | InfolistBlock\n | SchemaBlock\n | TableBlock\n | RelationManagersBlock\n | WidgetsBlock\n | LinksBlock\n | { type: string; key: string; [key: string]: unknown }\n\nexport type PageLayout = 'app' | 'auth' | 'blank' | 'fullscreen'\n\n/** The `page` prop rendered by `Alfakit/Page`. */\nexport interface PageSchema {\n class: string\n layout: PageLayout\n title: string\n heading?: string\n subheading?: string\n breadcrumbs?: BreadcrumbSchema[]\n maxContentWidth?: Width | string\n /** `fullscreen` layout: whether to show the slim header, and where its exit link goes. */\n fullscreen?: { header: boolean; exitUrl?: string }\n /** Cluster or record sub-navigation. */\n subNavigation?: SubNavigationSchema\n headerActions?: AnyActionSchema[]\n /** `$headerActionsAlignment`; absent for `end` (the default). */\n headerActionsAlignment?: 'start' | 'center' | 'end' | 'between' | 'justify'\n /** `$extraBodyAttributes` / `getExtraBodyAttributes()`: set on `<body>` while the page is open. */\n extraBodyAttributes?: Record<string, string>\n content: ContentBlock[]\n /** Encrypted context token for internal endpoints. */\n token: string\n record?: RecordKey\n view?: { component: string; props: Record<string, unknown> }\n /**\n * An action whose modal the renderer opens once the page hydrated (`$defaultAction`,\n * `?defaultAction=`, `?tableAction=&tableActionRecord=`).\n */\n defaultAction?: DefaultActionSchema\n /**\n * Page override of the panel's `errorNotifications` (`$hasErrorNotifications`,\n * `setUpErrorNotifications()`): `false` turns them off on this page. Absent when the\n * page follows the panel.\n */\n errorNotifications?: ErrorNotificationsSchema | false\n /** Record pages with `Panel::realtime()`: who else is on the record. */\n presence?: PresenceSchema\n /** Edit pages with `detectConflicts()`: the version the form was loaded from. */\n conflicts?: ConflictsSchema\n}\n\n/** `PageSchema::defaultAction`: what to open and in which context. */\nexport interface DefaultActionSchema {\n action: ActionSchema\n /** Context component: `page`, `table`, `relationManager:…`. */\n component: string\n record?: RecordKey\n arguments?: Record<string, unknown>\n}\n\n/**\n * Fresh schemas returned by a poll, keyed by poll target: `table`, `relationManager:Class`\n * or `widget:<id or class>`.\n */\nexport type PolledComponents = Record<string, ContentBlock | WidgetSchema | RelationManagerSchema>\n\n/** Shape of `usePage().props` on an Alfakit page. */\nexport interface AlfakitPageProps {\n alfakit: PanelSchema\n page?: PageSchema\n /** Components refreshed by a poll (only present after one); merged into `page`. */\n alfakitPoll?: PolledComponents\n /**\n * Lazy widgets and relation managers (`Page::LAZY_PROP`), an Inertia deferred prop that\n * arrives right after the first response; `resolveLazyComponents()` puts them in `page`.\n */\n alfakitLazy?: PolledComponents\n /** Badges of `Tab::deferBadge()` list tabs, a deferred prop loaded after the page. */\n alfakitTabBadges?: DeferredTabBadges\n /**\n * `Panel::deferNavigationBadges()`: the navigation badges keyed by item id\n * (`navigationState`), a deferred prop loaded after the page.\n */\n alfakitNavigationBadges?: Record<string, NavigationBadgeSchema>\n alfakitAppearance?: 'light' | 'dark' | 'system'\n /** The stored sidebar state (`alfakit_sidebar_state` cookie); null when never toggled. */\n alfakitSidebarOpen?: boolean | null\n /**\n * The navigation of the application's `starterKit()` panels, shared with every Inertia\n * page (the host's own pages too) for the host sidebar. Null for guests.\n */\n alfakitNavigation?: StarterKitNavigationSchema[] | null\n /**\n * The developer debug panel's data (`Page::DEBUG_PROP`): schema tree with visibility and\n * authorisation, evaluated closures and server timings. Only present while the panel's\n * debug panel is active; never in production.\n */\n alfakitDebug?: DebugSchema\n /**\n * The schema inspector's data (context identity, class links). Only present while the\n * panel's dev tools are on (`Panel::devTools()` with `app.debug`); never in production.\n */\n alfakitDevTools?: DevToolsSchema\n flash?: {\n alfakitNotifications?: NotificationSchema[]\n alfakitAction?: {\n name: string\n status: 'success' | 'halted' | 'cancelled'\n closeModal?: boolean\n deselectRecords?: boolean | null\n }\n [key: string]: unknown\n }\n errors?: Record<string, string>\n [key: string]: unknown\n}\n\nexport type { ActionSchema }\n"],"mappings":";;AAkIA,MAAa,sBAAsB;AACnC,MAAa,wBAAwB"}
|
|
1
|
+
{"version":3,"file":"panel.js","names":[],"sources":["../../src/schema/panel.ts"],"sourcesContent":["import type { ActionSchema, AnyActionSchema } from './actions'\nimport type { CommandPaletteSchema, KeyboardShortcutsSchema } from './command-palette'\nimport type { Color, IconName, RecordKey, Width } from './common'\nimport type { SchemaComponent } from './components'\nimport type { DebugSchema } from './debug'\nimport type { DevToolsSchema } from './dev-tools'\nimport type { FormBlock, InfolistBlock, LinksBlock, SchemaBlock } from './form'\nimport type { NotificationSchema } from './notifications'\nimport type { PwaSchema } from './pwa'\nimport type { ConflictsSchema, PresenceSchema, RealtimeSchema } from './realtime'\nimport type { DeferredTabBadges } from './resources'\nimport type { StandaloneComponentSchema } from './standalone'\nimport type { RelationManagerSchema, RelationManagersBlock, TableBlock } from './table'\nimport type { WidgetSchema, WidgetsBlock } from './widgets'\n\nexport interface NavigationItemSchema {\n /** Text, or sanitised HTML when `labelHtml` is true (an `Htmlable` label). */\n label: string\n /** `label` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n labelHtml?: boolean\n icon?: IconName\n /**\n * Split navigation (`alfakit.navigation` is an Inertia once prop): the icon of the item\n * while active, swapped in by `resolvePanelNavigation()`.\n */\n activeIcon?: IconName\n url?: string\n openInNewTab?: boolean\n active?: boolean\n badge?: string | number\n badgeColor?: Color\n badgeTooltip?: string\n /** `Panel::deferNavigationBadges()`: the item has a badge that loads after the page. */\n deferredBadge?: boolean\n /** Set by `resolvePanelNavigation()` while a deferred badge is loading (show a placeholder). */\n badgePending?: boolean\n /** `NavigationItem::extraAttributes()`: HTML attributes for the item's link (`class` merges). */\n extraAttributes?: Record<string, string | number | boolean | null>\n children?: NavigationItemSchema[]\n}\n\nexport interface NavigationGroupSchema {\n label?: string | null\n icon?: IconName\n collapsible: boolean\n collapsed?: boolean\n items: NavigationItemSchema[]\n /** `NavigationGroup::extraSidebarAttributes()`: HTML attributes for the group in the sidebar. */\n extraSidebarAttributes?: Record<string, string | number | boolean | null>\n /** `NavigationGroup::extraTopbarAttributes()`: HTML attributes for the group in the top navigation. */\n extraTopbarAttributes?: Record<string, string | number | boolean | null>\n}\n\n/** A navigation item's badge, from `navigationState` or the deferred `alfakitNavigationBadges` prop. */\nexport interface NavigationBadgeSchema {\n badge: string | number\n badgeColor?: Color\n badgeTooltip?: string\n}\n\n/**\n * The per-request part of the panel navigation, whose structure (`navigation`) is an\n * Inertia once prop. Items are addressed by position: `group.item[.child…]` (`0.2.1`).\n */\nexport interface NavigationStateSchema {\n /** The active items (an active child's ancestors included). */\n active?: string[]\n /** Badges, unless `Panel::deferNavigationBadges()` defers them. */\n badges?: Record<string, NavigationBadgeSchema>\n}\n\n/** One `starterKit()` panel's navigation, as shared with the host application's pages. */\n/** One `starterKit()` panel's dashboard widgets on a host page (`alfakitWidgets` prop). */\nexport interface StarterKitDashboardWidgetsSchema {\n /** The panel id. */\n id: string\n /** A standalone widgets payload bound to the panel (lazy loading and polling included). */\n component: StandaloneComponentSchema\n}\n\nexport interface StarterKitNavigationSchema {\n /** The panel id. */\n id: string\n /** The panel's home URL. */\n url: string\n brandName: string\n /** Empty for tenancy panels outside a tenant (their items link to tenant routes). */\n groups: NavigationGroupSchema[]\n /** What the drop-in header components render before they load. */\n widgets?: StarterKitWidgetsSchema\n}\n\n/**\n * The `widgets` of a `starterKit()` panel's shared navigation: what `<AlfakitSearch />`,\n * `<AlfakitNotifications />` and `<AlfakitTenantSwitcher />` show on any host page before\n * their code and the panel data (`endpoint`) have loaded.\n */\nexport interface StarterKitWidgetsSchema {\n /** GET: the panel data the components render with (the `alfakit` prop of the panel's pages). */\n endpoint: string\n /** Global search or the command palette (tenancy panels: only inside a tenant). */\n search?: { keyBindings: string[]; placeholder: string }\n /** `databaseNotifications()`: the bell's label and the user's unread count. */\n notifications?: { label: string; unreadCount: number }\n /** Tenancy panels: the user's tenants; `current` is null on the host's own pages. */\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`). */\n registrationLabel?: string\n searchable?: true\n }\n /** While impersonating: the banner the header components show on the host's own pages. */\n impersonation?: ImpersonationSchema\n /**\n * `AiPlugin::copilot()` (alfasix/alfakit-ai, `$panel->starterKitWidget('copilot')`): the\n * label and shortcuts of `<AlfakitCopilot />` before its code loads. Tenancy panels: only\n * inside a tenant.\n */\n copilot?: { label: string; keyBindings: string[] }\n}\n\n/** A page's secondary navigation: cluster pages or a record's pages. */\nexport interface SubNavigationSchema {\n position: 'start' | 'end' | 'top'\n groups: NavigationGroupSchema[]\n}\n\n/**\n * An entry of the user or tenant menu: a link (`url`, `method: 'post'` for POST links) or\n * an `Action` with server logic or a modal (`action`), run through the action endpoint\n * with the component `userMenu` / `tenantMenu` and the page's token.\n */\nexport interface MenuItemSchema {\n label?: string\n icon?: IconName\n url?: string\n openInNewTab?: boolean\n color?: Color\n method?: 'post'\n action?: ActionSchema\n /** Group index (nested arrays in `userMenuItems()`); a separator divides groups. */\n group?: number\n}\n\n/** Action endpoint components of the menus (`Panel::USER_MENU_COMPONENT`, …). */\nexport const USER_MENU_COMPONENT = 'userMenu'\nexport const TENANT_MENU_COMPONENT = 'tenantMenu'\n\nexport interface PanelUserSchema {\n id: RecordKey\n name: string\n email?: string | null\n avatar?: string | null\n}\n\nexport interface TenantSchema {\n id: RecordKey\n /** Text, or sanitised HTML when `nameHtml` is true (an `Htmlable` `getAlfakitName()`). */\n name: string\n /** `name` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n nameHtml?: boolean\n avatar?: string | null\n url: string\n /** `HasCurrentTenantLabel`: a caption shown with the current tenant (\"Active team\"). */\n label?: string | null\n}\n\n/** The panel's design, see `Panel::theme()`. */\nexport interface PanelThemeSchema {\n name: string\n /** Value of `data-alfakit-theme`, base theme first: `\"alfakit acme\"`. */\n attribute: string\n sidebar: 'inset' | 'floating' | 'flush'\n density: 'comfortable' | 'compact'\n radius?: string | null\n font?: string | null\n /** CSS value for `--alfakit-font`. */\n fontCss?: string | null\n /** CSS value for `--alfakit-heading-font`. */\n headingFontCss?: string | null\n /** Stylesheet that loads the font, if any. */\n fontUrl?: string | null\n /** Every font stylesheet to load (body and heading fonts). */\n fontUrls?: string[]\n /** Structure and component styles; mirrored as `data-*` attributes on `<html>`. */\n design: PanelDesignSchema\n /**\n * Icon aliases replaced with `Icons::register()` (`alfakit::panels.sidebar.collapse-button`\n * → a Lucide name, image URL or sanitised SVG). The chrome falls back to its own icon.\n */\n icons?: Record<string, string> | null\n}\n\nexport interface PanelDesignSchema {\n layout: 'sidebar' | 'topbar' | 'rail'\n nav: 'soft' | 'pill' | 'bar' | 'underline'\n header: 'plain' | 'banner' | 'band' | 'compact'\n surfaces: 'bordered' | 'elevated' | 'flat' | 'ring'\n inputs: 'outline' | 'filled' | 'underline'\n buttons: 'rounded' | 'pill' | 'square'\n tables: 'lined' | 'striped' | 'minimal'\n /**\n * Below the `md` breakpoint: `drawer` (default) or the `bottom` mobile preset (bottom\n * navigation bar, tables as stacked cards, slide-over modals, 44px touch targets and\n * sticky form actions). See `Panel::mobile()`.\n */\n mobile?: 'drawer' | 'bottom'\n}\n\n/**\n * One piece of slot content: server-rendered HTML or a schema component. `for` lists the\n * components (`relationManager:<class>`, `widget:<id>`) it is scoped to: such items only\n * render in a `<Slot scope=\"…\">` of a matching component. `when` (`Panel::slot(…, when:)`)\n * limits it to renders whose slot data matches: `{ column: 'title' }` only renders in the\n * `table.header-cell` slot of the `title` column; a list value matches any of its entries.\n */\nexport type SlotItemSchema = (\n { type: 'html'; html: string } | { type: 'component'; component: SchemaComponent }\n) & { for?: string[]; when?: Record<string, SlotDataValue | SlotDataValue[]> }\n\n/** A value in a slot's render data (`<Slot data={{ column: 'title' }}>`). */\nexport type SlotDataValue = string | number | boolean | null\n\n/** A language the panel can be displayed in. */\nexport interface LocaleSchema {\n /** Laravel-style code: `en`, `pt_BR`, `zh_Hant`. */\n code: string\n /** BCP 47 tag for `Intl` and the `lang` attribute: `pt-BR`. */\n tag: string\n /** The language's own name: `Deutsch`, `العربية`. */\n label: string\n direction: 'ltr' | 'rtl'\n}\n\n/** The current request's language, formats and the panel's language list. */\nexport interface LocalizationSchema extends LocaleSchema {\n fallbackLocale: string\n /** 0 = Sunday … 6 = Saturday. */\n firstDayOfWeek: number\n /** Default ISO 4217 currency. */\n currency: string\n /** IANA timezone dates are displayed in. */\n timezone: string\n /** Whether to show a language menu. */\n switcher: boolean\n /**\n * Where panel pages offer the menu (`Panel::languageSwitcher()`): the user menu (default\n * when absent), a top bar button, both, or neither. Auth pages keep their switcher.\n */\n switcherPosition?: LanguageSwitcherPosition\n /** Languages to offer in the menu (empty when the switcher is off). */\n locales: LocaleSchema[]\n}\n\nexport type LanguageSwitcherPosition = 'user-menu' | 'topbar' | 'both' | 'hidden'\n\nexport interface PanelEndpoints {\n action: string\n modal: string\n schemaRefresh: string\n schemaOptions: string\n /** Validates one wizard step's fields (`step` = the Step component's key). */\n schemaValidate?: string\n /** Loads `deferLoading()` children and `Tab::deferBadge()` badges (`{schema}::{key}`). */\n schemaDeferred?: string\n tableCell: string\n tableReorder: string\n /** Stores a user's column order (`reorderableColumns()`). */\n tableColumns?: string\n /** `Table::deferTotals()`: the record count and summaries, loaded after the rows. */\n tableTotals?: string\n /** `Table::savedViews()`: save, share, rename, delete and pick a default view. */\n tableViews?: string\n /** `Table::spreadsheet()`: batch save (or validate) grid edits. */\n tableCells?: string\n /** Fresh schemas of lazy and polled components by token (components outside panels). */\n components?: string\n locale: string\n /** Calls `#[Expose]` methods of custom pages (`usePageMethod()`). */\n pageCall?: string\n /** Edit pages: what changed since the form was loaded (`detectConflicts()`). */\n conflicts?: string\n upload: string\n uploadDestroy: string\n /** Starts a resumable chunked upload (`{uploadChunks}/{id}` takes chunks, `/complete` finishes). */\n uploadChunks?: string\n globalSearch: string\n /** Command palette: \"create\" links and recent records (GET). */\n commandPalette?: string\n /** Stores the user's customised keyboard shortcuts (POST `{shortcuts}`). */\n shortcuts?: string\n /** `Action::undoable()`: undoes an action from its toast (POST `{undo}`). */\n undo?: string\n /** `Dashboard::customizable()`: saves or resets the user's layout (POST `{token, layout}`). */\n dashboardLayout?: string\n notifications: string\n notificationsReadAll: string\n notificationsDestroyAll: string\n logout: string\n}\n\n/** Where toasts appear (`Panel::toastPosition()`); `start`/`end` follow the reading direction. */\nexport type ToastPosition =\n 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'\n\n/** Shown while impersonating another user (`ImpersonateAction`). */\nexport interface ImpersonationSchema {\n /** \"You are signed in as …\", translated on the server. */\n label: string\n leaveLabel: string\n /** POST here to return to the real user. */\n leaveUrl: string\n}\n\n/** The `alfakit` prop shared with every page of a panel. */\n/**\n * One `errorNotifications` entry: the text to show, or `hidden` (caught, nothing shown,\n * `hiddenErrorNotification()`) or `disabled` (default handling, `disabledErrorNotification()`).\n */\nexport interface ErrorNotificationEntrySchema {\n title?: string\n body?: string | null\n icon?: IconName\n hidden?: boolean\n disabled?: boolean\n}\n\n/** `errorNotifications`, keyed by HTTP status plus `default`. */\nexport type ErrorNotificationsSchema = Record<string, ErrorNotificationEntrySchema>\n\nexport interface PanelSchema {\n id: string\n /**\n * The schema contract version the server serialised (`SchemaContract::VERSION`). Renderers\n * compare its major part with `SCHEMA_CONTRACT_VERSION`; absent on servers before 1.0.\n */\n schemaContract?: string\n path: string\n url?: string\n brandName: string\n brandLogo?: string\n darkModeBrandLogo?: string\n brandLogoHeight?: string\n favicon?: string\n /**\n * Colour custom properties without the `--alfakit-` prefix: `primary`, its readable\n * `primary-foreground` and, for palettes (`Color::Indigo`), `primary-50` … `primary-950`.\n */\n colors?: Record<string, string>\n /**\n * CSS for custom colour names (`colors(['brand' => …])`): `.alfakit-color-{name}` rules\n * that point the generic colour utilities of themes.css at `--alfakit-{name}`.\n */\n colorCss?: string\n darkMode: {\n enabled: boolean\n forced: boolean\n /** `defaultThemeMode()`; absent for `system`. */\n default?: 'light' | 'dark'\n /** `false` when `themeSwitcher(false)`: no switcher, always the default mode. */\n switcher?: false\n }\n /** `false` when `topbar(false)`: sidebar layouts render no top bar (a slim bar on phones). */\n topbar?: false\n sidebarCollapsibleOnDesktop: boolean\n sidebarFullyCollapsibleOnDesktop?: boolean\n topNavigation?: boolean\n maxContentWidth?: Width | string\n breadcrumbs: boolean\n unsavedChangesAlerts?: boolean\n /** `false` when `collapsibleNavigationGroups(false)`: groups never collapse. */\n collapsibleNavigationGroups?: false\n /** CSS lengths for the open and icon-only sidebar (`--sidebar-width`, `--sidebar-width-icon`). */\n sidebarWidth?: string\n collapsedSidebarWidth?: string\n /** Max width of `auth` layout pages (login, registration…). */\n simplePageMaxContentWidth?: Width | string\n /** `false` when `spa(false)`: every link loads a full page. */\n spa?: false\n /** URL patterns (`*` wildcard) that load with a full page request. */\n spaUrlExceptions?: string[]\n /**\n * Notifications shown instead of Inertia's error modal, keyed by HTTP status plus\n * `default`. Absent when disabled or in debug mode. A page's own `errorNotifications`\n * replaces it while that page is open.\n */\n errorNotifications?: ErrorNotificationsSchema\n theme: PanelThemeSchema\n /** Content inserted with `Panel::slot()`, keyed by slot name. */\n slots?: Record<string, SlotItemSchema[]>\n /**\n * Empty for `starterKit()` panels: their navigation is the `alfakitNavigation` page prop,\n * which every page of the host application carries (not only the panel's).\n */\n navigation: NavigationGroupSchema[]\n /**\n * The active items and badges of this request; `navigation` holds only the structure\n * (sent once). Read the navigation through `usePanel()`, which merges the two.\n */\n navigationState?: NavigationStateSchema\n /**\n * `Panel::starterKit()`: the panel runs inside a Laravel starter kit app. Its pages render\n * in the host's own layout (sidebar, header, appearance) and the host's shadcn tokens;\n * the panel's own chrome, theme attributes and colour scheme are not applied.\n */\n starterKit?: true\n user?: PanelUserSchema\n userMenu?: {\n items: MenuItemSchema[]\n profileUrl?: string\n logoutUrl: string\n /** The `profile` entry as customised in `userMenuItems()` (replaces the built-in link). */\n profile?: MenuItemSchema\n /** The customised `logout` entry, or `false` when it is hidden. */\n logout?: MenuItemSchema | false\n /**\n * `topbar` when `userMenu(position: UserMenuPosition::Topbar)`: sidebar layouts show the\n * menu in the top bar instead of the sidebar footer. Omitted for the sidebar.\n */\n position?: 'topbar'\n }\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n profileUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`); else \"New\". */\n registrationLabel?: string\n /** The profile page's own label (`EditTenantProfile::getLabel()`); else \"Settings\". */\n profileLabel?: string\n /** The customised `register` entry of `tenantMenuItems()`. */\n registration?: MenuItemSchema\n /** The customised `profile` entry of `tenantMenuItems()`. */\n profile?: MenuItemSchema\n menu?: MenuItemSchema[]\n /** `false` when `tenantSwitcher(false)`: `tenants` is empty and the menu lists no others. */\n switcher?: false\n /** `searchableTenantMenu()`, or more than 10 tenants: show a search field in the menu. */\n searchable?: boolean\n }\n databaseNotifications?: {\n pollingInterval?: string\n unreadCount: number\n /**\n * `sidebar` when `databaseNotifications(position: 'sidebar')` or `topbar(false)`: sidebar\n * layouts show the bell in the sidebar instead of the top bar. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n globalSearch?: {\n keyBindings: string[]\n placeholder: string\n /** `globalSearchDebounce()` in milliseconds; renderers default to 250. */\n debounce?: number\n /** `globalSearchFieldSuffix()`: text shown at the end of the field instead of the key hint. */\n suffix?: string\n /** `false` when `globalSearchFieldKeyBindingSuffix(false)`: no key binding hint. */\n keyBindingSuffix?: false\n /**\n * `sidebar` when `globalSearch(position: GlobalSearchPosition::Sidebar)` or\n * `topbar(false)`: sidebar layouts show the field in the sidebar header. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n endpoints: Partial<PanelEndpoints> & { logout: string }\n /** `alfakit.uploads.chunk_size` in bytes: larger files are uploaded in chunks. */\n uploadChunkSize?: number\n auth: { login?: string; register?: string; passwordReset?: string }\n flashKey: string\n /** Present while impersonating: render a banner with a way back. */\n impersonation?: ImpersonationSchema\n /**\n * `Panel::realtimeNotifications()`: the private channel to listen on with Laravel Echo;\n * with `Panel::realtime()` also the connection (`live`, `authEndpoint`, `echo`).\n */\n realtime?: RealtimeSchema\n /** Omitted for the default (`top-end`). */\n toastPosition?: ToastPosition\n /** The command palette's extras: page actions, panel commands, navigation, create links, recent records. */\n commandPalette?: CommandPaletteSchema\n /** Keyboard shortcuts (`false` with `keyboardShortcuts(false)`); absent for guests. */\n keyboardShortcuts?: KeyboardShortcutsSchema | false\n /** `Panel::pwa()`: service worker, install prompt and Web Push; absent when off. */\n pwa?: PwaSchema\n localization: LocalizationSchema\n csrf: string\n translations: Record<string, unknown>\n}\n\nexport interface BreadcrumbSchema {\n label: string\n url?: string\n}\n\nexport type ContentBlock =\n | FormBlock\n | InfolistBlock\n | SchemaBlock\n | TableBlock\n | RelationManagersBlock\n | WidgetsBlock\n | LinksBlock\n | { type: string; key: string; [key: string]: unknown }\n\nexport type PageLayout = 'app' | 'auth' | 'blank' | 'fullscreen'\n\n/** The `page` prop rendered by `Alfakit/Page`. */\nexport interface PageSchema {\n class: string\n layout: PageLayout\n title: string\n heading?: string\n subheading?: string\n breadcrumbs?: BreadcrumbSchema[]\n maxContentWidth?: Width | string\n /** `fullscreen` layout: whether to show the slim header, and where its exit link goes. */\n fullscreen?: { header: boolean; exitUrl?: string }\n /** Cluster or record sub-navigation. */\n subNavigation?: SubNavigationSchema\n headerActions?: AnyActionSchema[]\n /** `$headerActionsAlignment`; absent for `end` (the default). */\n headerActionsAlignment?: 'start' | 'center' | 'end' | 'between' | 'justify'\n /** `$extraBodyAttributes` / `getExtraBodyAttributes()`: set on `<body>` while the page is open. */\n extraBodyAttributes?: Record<string, string>\n content: ContentBlock[]\n /** Encrypted context token for internal endpoints. */\n token: string\n record?: RecordKey\n view?: { component: string; props: Record<string, unknown> }\n /**\n * An action whose modal the renderer opens once the page hydrated (`$defaultAction`,\n * `?defaultAction=`, `?tableAction=&tableActionRecord=`).\n */\n defaultAction?: DefaultActionSchema\n /**\n * Page override of the panel's `errorNotifications` (`$hasErrorNotifications`,\n * `setUpErrorNotifications()`): `false` turns them off on this page. Absent when the\n * page follows the panel.\n */\n errorNotifications?: ErrorNotificationsSchema | false\n /** Record pages with `Panel::realtime()`: who else is on the record. */\n presence?: PresenceSchema\n /** Edit pages with `detectConflicts()`: the version the form was loaded from. */\n conflicts?: ConflictsSchema\n}\n\n/** `PageSchema::defaultAction`: what to open and in which context. */\nexport interface DefaultActionSchema {\n action: ActionSchema\n /** Context component: `page`, `table`, `relationManager:…`. */\n component: string\n record?: RecordKey\n arguments?: Record<string, unknown>\n}\n\n/**\n * Fresh schemas returned by a poll, keyed by poll target: `table`, `relationManager:Class`\n * or `widget:<id or class>`.\n */\nexport type PolledComponents = Record<string, ContentBlock | WidgetSchema | RelationManagerSchema>\n\n/** Shape of `usePage().props` on an Alfakit page. */\nexport interface AlfakitPageProps {\n alfakit: PanelSchema\n page?: PageSchema\n /** Components refreshed by a poll (only present after one); merged into `page`. */\n alfakitPoll?: PolledComponents\n /**\n * Lazy widgets and relation managers (`Page::LAZY_PROP`), an Inertia deferred prop that\n * arrives right after the first response; `resolveLazyComponents()` puts them in `page`.\n */\n alfakitLazy?: PolledComponents\n /** Badges of `Tab::deferBadge()` list tabs, a deferred prop loaded after the page. */\n alfakitTabBadges?: DeferredTabBadges\n /**\n * `Panel::deferNavigationBadges()`: the navigation badges keyed by item id\n * (`navigationState`), a deferred prop loaded after the page.\n */\n alfakitNavigationBadges?: Record<string, NavigationBadgeSchema>\n alfakitAppearance?: 'light' | 'dark' | 'system'\n /** The stored sidebar state (`alfakit_sidebar_state` cookie); null when never toggled. */\n alfakitSidebarOpen?: boolean | null\n /**\n * The navigation of the application's `starterKit()` panels, shared with every Inertia\n * page (the host's own pages too) for the host sidebar. Null for guests.\n */\n alfakitNavigation?: StarterKitNavigationSchema[] | null\n /**\n * `starterKit()` panels: their dashboard widgets on the host's dashboard\n * (`starterKitDashboard()`), for `<AlfakitWidgets />`. Null on other pages, for guests\n * and users who may not open the panel.\n */\n alfakitWidgets?: StarterKitDashboardWidgetsSchema[] | null\n /**\n * The developer debug panel's data (`Page::DEBUG_PROP`): schema tree with visibility and\n * authorisation, evaluated closures and server timings. Only present while the panel's\n * debug panel is active; never in production.\n */\n alfakitDebug?: DebugSchema\n /**\n * The schema inspector's data (context identity, class links). Only present while the\n * panel's dev tools are on (`Panel::devTools()` with `app.debug`); never in production.\n */\n alfakitDevTools?: DevToolsSchema\n flash?: {\n alfakitNotifications?: NotificationSchema[]\n alfakitAction?: {\n name: string\n status: 'success' | 'halted' | 'cancelled'\n closeModal?: boolean\n deselectRecords?: boolean | null\n }\n [key: string]: unknown\n }\n errors?: Record<string, string>\n [key: string]: unknown\n}\n\nexport type { ActionSchema }\n"],"mappings":";;AAmJA,MAAa,sBAAsB;AACnC,MAAa,wBAAwB"}
|
package/dist/standalone.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { LocalizationSchema, PageSchema, PanelSchema, PolledComponents } from "./schema/panel.js";
|
|
2
1
|
import { StandaloneComponentSchema } from "./schema/standalone.js";
|
|
2
|
+
import { LocalizationSchema, PageSchema, PanelSchema, PolledComponents } from "./schema/panel.js";
|
|
3
3
|
import { PollScheduler } from "./poll.js";
|
|
4
4
|
import { registerStandaloneProp, resolvePartialProps } from "./partial-props.js";
|
|
5
5
|
//#region src/standalone.d.ts
|
|
@@ -46,8 +46,15 @@ export declare class StandaloneComponentState {
|
|
|
46
46
|
/** A new payload (page reload) replaces what was polled; lazy schemas stay cached. */
|
|
47
47
|
setComponent(component: StandaloneComponentSchema): void;
|
|
48
48
|
get page(): PageSchema;
|
|
49
|
-
/**
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Load the lazy components named by the payload. Pass the new payload when it arrived
|
|
51
|
+
* (a reload after an action): the load then belongs to it, while one started for the
|
|
52
|
+
* previous payload would be dropped as stale once the page renders the new one (the
|
|
53
|
+
* starter kit dashboard's calendar kept its old events after creating one).
|
|
54
|
+
*/
|
|
55
|
+
loadLazy(component?: StandaloneComponentSchema): Promise<void>;
|
|
56
|
+
/** Fetch fresh schemas of these components now (`live()` refreshes on record changes). */
|
|
57
|
+
refresh(targets: string[]): Promise<void>;
|
|
51
58
|
/** Poll a component (`table`, `widget:<id>`); returns an unsubscribe. */
|
|
52
59
|
poll(target: string, interval: string | number | null | undefined): () => void;
|
|
53
60
|
private load;
|
package/dist/standalone.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.d.ts","names":[],"sources":["../src/standalone.ts"],"mappings":";;;;;;;;;;;iBAiBiB,mBAAmB;;EAElC,aAAa,eAAe;;;;;;wBAuBd,sBAAsB,WAAW,4BAA4B;;wBAqB7D,qBAAqB,WAAW,4BAA4B;;wBAe5D,oBACd,cAAc,KAAK;;;;;;wBAUL,uBACd,OAAO,qCACP,WAAW,KAAK;;;;;wBAmBF,0BACd,kBACA,eACA,oBACC,QAAQ;;;;;qBAYE;UAMD;mBACS;WANH,WAAW;UAEnB;EAGE,YAAA,WAAW,2BACF;;EAMnB,aAAa,WAAW;MAQpB,QAAQ
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","names":[],"sources":["../src/standalone.ts"],"mappings":";;;;;;;;;;;iBAiBiB,mBAAmB;;EAElC,aAAa,eAAe;;;;;;wBAuBd,sBAAsB,WAAW,4BAA4B;;wBAqB7D,qBAAqB,WAAW,4BAA4B;;wBAe5D,oBACd,cAAc,KAAK;;;;;;wBAUL,uBACd,OAAO,qCACP,WAAW,KAAK;;;;;wBAmBF,0BACd,kBACA,eACA,oBACC,QAAQ;;;;;qBAYE;UAMD;mBACS;WANH,WAAW;UAEnB;EAGE,YAAA,WAAW,2BACF;;EAMnB,aAAa,WAAW;MAQpB,QAAQ;;;;;;;EAaZ,SAAS,YAAY,4BAA4B;;EAOjD,QAAQ,oBAAoB;;EAK5B,KAAK,gBAAgB;UAIP;;UAuBN"}
|
package/dist/standalone.js
CHANGED
|
@@ -112,10 +112,20 @@ var StandaloneComponentState = class {
|
|
|
112
112
|
const resolved = resolveLazyComponents(base, this.fresh, lazyComponentCache, this.scope());
|
|
113
113
|
return mergePolledComponents(resolved, this.fresh);
|
|
114
114
|
}
|
|
115
|
-
/**
|
|
116
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Load the lazy components named by the payload. Pass the new payload when it arrived
|
|
117
|
+
* (a reload after an action): the load then belongs to it, while one started for the
|
|
118
|
+
* previous payload would be dropped as stale once the page renders the new one (the
|
|
119
|
+
* starter kit dashboard's calendar kept its old events after creating one).
|
|
120
|
+
*/
|
|
121
|
+
loadLazy(component) {
|
|
122
|
+
if (component) this.setComponent(component);
|
|
117
123
|
return this.load(this.component.lazy ?? []);
|
|
118
124
|
}
|
|
125
|
+
/** Fetch fresh schemas of these components now (`live()` refreshes on record changes). */
|
|
126
|
+
refresh(targets) {
|
|
127
|
+
return this.load(targets);
|
|
128
|
+
}
|
|
119
129
|
/** Poll a component (`table`, `widget:<id>`); returns an unsubscribe. */
|
|
120
130
|
poll(target, interval) {
|
|
121
131
|
return this.scheduler.subscribe(target, interval);
|
package/dist/standalone.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone.js","names":[],"sources":["../src/standalone.ts"],"sourcesContent":["import { requestJson } from './http'\nimport { lazyComponentCache, resolveLazyComponents } from './lazy'\nimport { currentPageQuery } from './page-query'\nimport { mergePolledComponents, PollScheduler } from './poll'\nimport type { LocalizationSchema, PageSchema, PanelSchema, PolledComponents } from './schema/panel'\nimport type { StandaloneComponentSchema } from './schema/standalone'\nimport { isStandaloneComponent } from './schema/standalone'\n\nexport { registerStandaloneProp, resolvePartialProps } from './partial-props'\n\n/**\n * Support for components rendered outside panels (`<AlfakitComponent>`): the panel and\n * page shapes the renderers' hooks expect, partial-reload prop resolution, and fetching\n * lazy or polled components through the standalone endpoint.\n */\n\n/** Defaults used for `AlfakitComponent`s in every renderer (`configureAlfakit()`). */\nexport interface StandaloneDefaults<TComponent> {\n /** Schema component renderers, keyed by type (`TextInput`) or View name (`map-picker`). */\n components?: Record<string, TComponent>\n}\n\nconst DEFAULT_THEME: PanelSchema['theme'] = {\n name: 'neutral',\n attribute: 'neutral',\n sidebar: 'inset',\n density: 'comfortable',\n design: {\n layout: 'sidebar',\n nav: 'soft',\n header: 'plain',\n surfaces: 'bordered',\n inputs: 'outline',\n buttons: 'rounded',\n tables: 'lined',\n },\n}\n\n/**\n * The `alfakit` panel prop a standalone payload stands for, so every renderer component\n * (`usePanel()`, `useLocalization()`, `useFormatter()`) works unchanged outside panels.\n */\nexport function standalonePanelSchema(component: StandaloneComponentSchema): PanelSchema {\n return {\n id: 'standalone',\n path: '',\n brandName: '',\n darkMode: { enabled: true, forced: false },\n sidebarCollapsibleOnDesktop: false,\n breadcrumbs: false,\n theme: DEFAULT_THEME,\n navigation: [],\n endpoints: { ...component.endpoints, logout: '' },\n uploadChunkSize: component.uploadChunkSize,\n auth: {},\n flashKey: component.flashKey,\n localization: component.localization,\n csrf: component.csrf,\n translations: component.translations,\n }\n}\n\n/** The `page` prop a standalone payload stands for (`usePageSchema()`). */\nexport function standalonePageSchema(component: StandaloneComponentSchema): PageSchema {\n return {\n class: component.class,\n layout: 'blank',\n title: component.heading ?? '',\n heading: component.heading ?? undefined,\n subheading: component.subheading ?? undefined,\n headerActions: component.headerActions ?? undefined,\n content: component.content,\n token: component.token,\n record: component.record ?? undefined,\n }\n}\n\n/** Direction of a standalone component's language. */\nexport function standaloneDirection(\n localization: Pick<LocalizationSchema, 'direction'> | undefined,\n): 'ltr' | 'rtl' {\n return localization?.direction ?? 'ltr'\n}\n\n/**\n * The name of the Inertia prop holding `component` (matched by token), so partial reloads\n * can ask for it alone. Undefined when nested in another prop: the page is then reloaded\n * in full.\n */\nexport function findStandalonePropName(\n props: Record<string, unknown> | undefined,\n component: Pick<StandaloneComponentSchema, 'token'>,\n): string | undefined {\n if (!props) return undefined\n\n for (const [name, value] of Object.entries(props)) {\n if (isStandaloneComponent(value) && value.token === component.token) {\n return name\n }\n }\n\n return undefined\n}\n\n// ---------------------------------------------------------------- Lazy and polled components\n\n/**\n * Fetch fresh schemas of components (`table`, `widget:<id>`) of a standalone component,\n * with the host page's query string so tables keep their sort, search and filters.\n */\nexport function fetchStandaloneComponents(\n endpoint: string,\n token: string,\n targets: string[],\n): Promise<PolledComponents> {\n if (targets.length === 0) return Promise.resolve({})\n\n return requestJson<PolledComponents>(endpoint, {\n query: { token, targets: targets.join(','), tableQuery: currentPageQuery() },\n })\n}\n\n/**\n * Lazy and polled component state of one `<AlfakitComponent>`: a poll scheduler bound to\n * its token and the schemas fetched so far, merged into the page it renders.\n */\nexport class StandaloneComponentState {\n public readonly scheduler: PollScheduler\n\n private fresh: PolledComponents = {}\n\n constructor(\n private component: StandaloneComponentSchema,\n private readonly onChange: () => void,\n ) {\n this.scheduler = new PollScheduler((targets) => this.load(targets))\n }\n\n /** A new payload (page reload) replaces what was polled; lazy schemas stay cached. */\n setComponent(component: StandaloneComponentSchema): void {\n if (component.token !== this.component.token) {\n this.fresh = {}\n }\n\n this.component = component\n }\n\n get page(): PageSchema {\n const base = standalonePageSchema(this.component)\n const resolved = resolveLazyComponents(base, this.fresh, lazyComponentCache, this.scope())\n\n return mergePolledComponents(resolved, this.fresh)\n }\n\n
|
|
1
|
+
{"version":3,"file":"standalone.js","names":[],"sources":["../src/standalone.ts"],"sourcesContent":["import { requestJson } from './http'\nimport { lazyComponentCache, resolveLazyComponents } from './lazy'\nimport { currentPageQuery } from './page-query'\nimport { mergePolledComponents, PollScheduler } from './poll'\nimport type { LocalizationSchema, PageSchema, PanelSchema, PolledComponents } from './schema/panel'\nimport type { StandaloneComponentSchema } from './schema/standalone'\nimport { isStandaloneComponent } from './schema/standalone'\n\nexport { registerStandaloneProp, resolvePartialProps } from './partial-props'\n\n/**\n * Support for components rendered outside panels (`<AlfakitComponent>`): the panel and\n * page shapes the renderers' hooks expect, partial-reload prop resolution, and fetching\n * lazy or polled components through the standalone endpoint.\n */\n\n/** Defaults used for `AlfakitComponent`s in every renderer (`configureAlfakit()`). */\nexport interface StandaloneDefaults<TComponent> {\n /** Schema component renderers, keyed by type (`TextInput`) or View name (`map-picker`). */\n components?: Record<string, TComponent>\n}\n\nconst DEFAULT_THEME: PanelSchema['theme'] = {\n name: 'neutral',\n attribute: 'neutral',\n sidebar: 'inset',\n density: 'comfortable',\n design: {\n layout: 'sidebar',\n nav: 'soft',\n header: 'plain',\n surfaces: 'bordered',\n inputs: 'outline',\n buttons: 'rounded',\n tables: 'lined',\n },\n}\n\n/**\n * The `alfakit` panel prop a standalone payload stands for, so every renderer component\n * (`usePanel()`, `useLocalization()`, `useFormatter()`) works unchanged outside panels.\n */\nexport function standalonePanelSchema(component: StandaloneComponentSchema): PanelSchema {\n return {\n id: 'standalone',\n path: '',\n brandName: '',\n darkMode: { enabled: true, forced: false },\n sidebarCollapsibleOnDesktop: false,\n breadcrumbs: false,\n theme: DEFAULT_THEME,\n navigation: [],\n endpoints: { ...component.endpoints, logout: '' },\n uploadChunkSize: component.uploadChunkSize,\n auth: {},\n flashKey: component.flashKey,\n localization: component.localization,\n csrf: component.csrf,\n translations: component.translations,\n }\n}\n\n/** The `page` prop a standalone payload stands for (`usePageSchema()`). */\nexport function standalonePageSchema(component: StandaloneComponentSchema): PageSchema {\n return {\n class: component.class,\n layout: 'blank',\n title: component.heading ?? '',\n heading: component.heading ?? undefined,\n subheading: component.subheading ?? undefined,\n headerActions: component.headerActions ?? undefined,\n content: component.content,\n token: component.token,\n record: component.record ?? undefined,\n }\n}\n\n/** Direction of a standalone component's language. */\nexport function standaloneDirection(\n localization: Pick<LocalizationSchema, 'direction'> | undefined,\n): 'ltr' | 'rtl' {\n return localization?.direction ?? 'ltr'\n}\n\n/**\n * The name of the Inertia prop holding `component` (matched by token), so partial reloads\n * can ask for it alone. Undefined when nested in another prop: the page is then reloaded\n * in full.\n */\nexport function findStandalonePropName(\n props: Record<string, unknown> | undefined,\n component: Pick<StandaloneComponentSchema, 'token'>,\n): string | undefined {\n if (!props) return undefined\n\n for (const [name, value] of Object.entries(props)) {\n if (isStandaloneComponent(value) && value.token === component.token) {\n return name\n }\n }\n\n return undefined\n}\n\n// ---------------------------------------------------------------- Lazy and polled components\n\n/**\n * Fetch fresh schemas of components (`table`, `widget:<id>`) of a standalone component,\n * with the host page's query string so tables keep their sort, search and filters.\n */\nexport function fetchStandaloneComponents(\n endpoint: string,\n token: string,\n targets: string[],\n): Promise<PolledComponents> {\n if (targets.length === 0) return Promise.resolve({})\n\n return requestJson<PolledComponents>(endpoint, {\n query: { token, targets: targets.join(','), tableQuery: currentPageQuery() },\n })\n}\n\n/**\n * Lazy and polled component state of one `<AlfakitComponent>`: a poll scheduler bound to\n * its token and the schemas fetched so far, merged into the page it renders.\n */\nexport class StandaloneComponentState {\n public readonly scheduler: PollScheduler\n\n private fresh: PolledComponents = {}\n\n constructor(\n private component: StandaloneComponentSchema,\n private readonly onChange: () => void,\n ) {\n this.scheduler = new PollScheduler((targets) => this.load(targets))\n }\n\n /** A new payload (page reload) replaces what was polled; lazy schemas stay cached. */\n setComponent(component: StandaloneComponentSchema): void {\n if (component.token !== this.component.token) {\n this.fresh = {}\n }\n\n this.component = component\n }\n\n get page(): PageSchema {\n const base = standalonePageSchema(this.component)\n const resolved = resolveLazyComponents(base, this.fresh, lazyComponentCache, this.scope())\n\n return mergePolledComponents(resolved, this.fresh)\n }\n\n /**\n * Load the lazy components named by the payload. Pass the new payload when it arrived\n * (a reload after an action): the load then belongs to it, while one started for the\n * previous payload would be dropped as stale once the page renders the new one (the\n * starter kit dashboard's calendar kept its old events after creating one).\n */\n loadLazy(component?: StandaloneComponentSchema): Promise<void> {\n if (component) this.setComponent(component)\n\n return this.load(this.component.lazy ?? [])\n }\n\n /** Fetch fresh schemas of these components now (`live()` refreshes on record changes). */\n refresh(targets: string[]): Promise<void> {\n return this.load(targets)\n }\n\n /** Poll a component (`table`, `widget:<id>`); returns an unsubscribe. */\n poll(target: string, interval: string | number | null | undefined): () => void {\n return this.scheduler.subscribe(target, interval)\n }\n\n private async load(targets: string[]): Promise<void> {\n if (targets.length === 0) return\n\n const token = this.component.token\n\n try {\n const polled = await fetchStandaloneComponents(\n this.component.endpoints.components,\n token,\n targets,\n )\n\n // The page re-rendered meanwhile: its new payload is fresher than this response.\n if (token !== this.component.token) return\n\n this.fresh = { ...this.fresh, ...polled }\n this.onChange()\n } catch {\n // A failed poll keeps what the page has; the next tick tries again.\n }\n }\n\n /** Lazy schemas are cached per page path and component class, like panel pages. */\n private scope(): string {\n const path = typeof window === 'undefined' ? '' : window.location.pathname\n\n return `${path}|${this.component.class}`\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { AiCopilotSchema } from "./schema/ai.js";
|
|
2
|
+
import { StandaloneComponentSchema } from "./schema/standalone.js";
|
|
1
3
|
import { PageSchema, PanelSchema, StarterKitNavigationSchema, StarterKitWidgetsSchema } from "./schema/panel.js";
|
|
2
4
|
//#region src/starter-kit-widgets.d.ts
|
|
3
5
|
/**
|
|
4
6
|
* The drop-in header components of starter kit mode: `<AlfakitSearch />` (the command
|
|
5
|
-
* palette and global search), `<AlfakitNotifications />` (the database notifications bell)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* palette and global search), `<AlfakitNotifications />` (the database notifications bell),
|
|
8
|
+
* `<AlfakitTenantSwitcher />` and `<AlfakitCopilot />` (the AI plugin's copilot). They render
|
|
9
|
+
* on every page of the host application, from the `widgets` of the shared
|
|
10
|
+
* `alfakitNavigation` prop; their code and the panel data (translations, endpoints, the
|
|
11
|
+
* palette, realtime) load the first time one of them renders.
|
|
9
12
|
* Shared by the three renderers.
|
|
10
13
|
*/
|
|
11
14
|
/** One starter kit panel's navigation with its header components' data. */
|
|
@@ -14,10 +17,13 @@ export interface StarterKitWidgets {
|
|
|
14
17
|
widgets: StarterKitWidgetsSchema;
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
|
-
* The header components' data of a `starterKit()` panel
|
|
18
|
-
* `panelId
|
|
20
|
+
* The header components' data of a `starterKit()` panel, or null for guests and pages of
|
|
21
|
+
* other panels. Without `panelId`: the open AlfaKit page's panel, else the first one with
|
|
22
|
+
* any; with `part` (`'tenancy'` for the tenant switcher), the first panel that has that
|
|
23
|
+
* part when the preferred one hasn't, so a team workspace on a second panel still gets its
|
|
24
|
+
* switcher on the host's own pages.
|
|
19
25
|
*/
|
|
20
|
-
export declare function starterKitWidgets(props: unknown, panelId?: string): StarterKitWidgets | null;
|
|
26
|
+
export declare function starterKitWidgets(props: unknown, panelId?: string, part?: keyof StarterKitWidgetsSchema): StarterKitWidgets | null;
|
|
21
27
|
/** Whether the open page is one of the panel's own (it carries the `alfakit` prop). */
|
|
22
28
|
export declare function isAlfakitPage(props: unknown): boolean;
|
|
23
29
|
/** Load (once) the panel data the header components render with. */
|
|
@@ -50,5 +56,32 @@ export declare function requestStarterKitSearch(): void;
|
|
|
50
56
|
* true when it did.
|
|
51
57
|
*/
|
|
52
58
|
export declare function openRequestedStarterKitSearch(): boolean;
|
|
59
|
+
/** The copilot's shortcut id (the loaded `ai:copilot` component registers the same one). */
|
|
60
|
+
export declare const STARTER_KIT_COPILOT_SHORTCUT = "ai.copilot";
|
|
61
|
+
/**
|
|
62
|
+
* Register the copilot's key bindings (`mod+j`) before its code has loaded: pressing them
|
|
63
|
+
* (or clicking the button) records the request, and the copilot opens once it has loaded.
|
|
64
|
+
* The loaded copilot registers the same id, so it replaces this registration.
|
|
65
|
+
*/
|
|
66
|
+
export declare function registerStarterKitCopilotShortcut(keys: string[], label: string, onRequest?: () => void): () => void;
|
|
67
|
+
/** Ask for the copilot to open as soon as it has loaded. */
|
|
68
|
+
export declare function requestStarterKitCopilot(): void;
|
|
69
|
+
/** Whether the copilot was asked for before it loaded (and forget the request). */
|
|
70
|
+
export declare function takeRequestedStarterKitCopilot(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The copilot component in the loaded panel data: the panel's own pages show it in the
|
|
73
|
+
* `topbar.end` slot, which starter kit hosts don't render. Null without the AI plugin's
|
|
74
|
+
* copilot (or when the user may not use it).
|
|
75
|
+
*/
|
|
76
|
+
export declare function starterKitCopilot(panel: PanelSchema): AiCopilotSchema | null;
|
|
77
|
+
/**
|
|
78
|
+
* The dashboard widgets of a `starterKit()` panel on the host's dashboard
|
|
79
|
+
* (`<AlfakitWidgets />`, the `alfakitWidgets` prop): a standalone widgets payload bound to
|
|
80
|
+
* the panel, the first panel's unless `panelId` is given. Null on other pages, for guests,
|
|
81
|
+
* users who may not open the panel and panels without widgets.
|
|
82
|
+
*/
|
|
83
|
+
export declare function starterKitDashboardWidgets(props: unknown, panelId?: string): StandaloneComponentSchema | null;
|
|
84
|
+
/** How many widgets a dashboard widgets payload has (placeholders before its code loads). */
|
|
85
|
+
export declare function standaloneWidgetCount(component: StandaloneComponentSchema): number;
|
|
53
86
|
//#endregion
|
|
54
87
|
//# sourceMappingURL=starter-kit-widgets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"starter-kit-widgets.d.ts","names":[],"sources":["../src/starter-kit-widgets.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"starter-kit-widgets.d.ts","names":[],"sources":["../src/starter-kit-widgets.ts"],"mappings":";;;;;;;;;;;;;;iBAyBiB;EACf,YAAY;EACZ,SAAS;;;;;;;;;wBAUK,kBACd,gBACA,kBACA,aAAa,0BACZ;;wBAaa,cAAc;;wBAYd,oBAAoB,mBAAmB,QAAQ;;wBAqC/C,sBAAsB,mBAAmB;;wBAKzC;;;;;;;;wBAYA,uBAAuB,MAAM,aAAa,QAAQ,oBAAoB;;wBAyBtE,yBAAyB;;;;;;;wBAczB,iCACd,gBACA,eACA;;wBAgBc;;;;;wBAQA;;qBAWH;;;;;;wBASG,kCACd,gBACA,eACA;;wBAkBc;;wBAKA;;;;;;wBAaA,kBAAkB,OAAO,cAAc;;;;;;;wBAkBvC,2BACd,gBACA,mBACC;;wBAaa,sBAAsB,WAAW"}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { requestJson } from "./http.js";
|
|
2
2
|
import { SHORTCUT_IDS, shortcuts } from "./shortcuts.js";
|
|
3
|
-
import { starterKitNavigations } from "./starter-kit.js";
|
|
3
|
+
import { preferCurrentStarterKitPanel, starterKitNavigations } from "./starter-kit.js";
|
|
4
4
|
//#region src/starter-kit-widgets.ts
|
|
5
5
|
/**
|
|
6
|
-
* The header components' data of a `starterKit()` panel
|
|
7
|
-
* `panelId
|
|
6
|
+
* The header components' data of a `starterKit()` panel, or null for guests and pages of
|
|
7
|
+
* other panels. Without `panelId`: the open AlfaKit page's panel, else the first one with
|
|
8
|
+
* any; with `part` (`'tenancy'` for the tenant switcher), the first panel that has that
|
|
9
|
+
* part when the preferred one hasn't, so a team workspace on a second panel still gets its
|
|
10
|
+
* switcher on the host's own pages.
|
|
8
11
|
*/
|
|
9
|
-
function starterKitWidgets(props, panelId) {
|
|
10
|
-
const
|
|
12
|
+
function starterKitWidgets(props, panelId, part) {
|
|
13
|
+
const entries = preferCurrentStarterKitPanel(starterKitNavigations(props), props).filter((entry) => entry.widgets !== void 0 && (panelId === void 0 || entry.id === panelId));
|
|
14
|
+
const navigation = (part !== void 0 && panelId === void 0 ? entries.find((entry) => entry.widgets?.[part] !== void 0) : void 0) ?? entries[0];
|
|
11
15
|
return navigation?.widgets ? {
|
|
12
16
|
navigation,
|
|
13
17
|
widgets: navigation.widgets
|
|
@@ -24,9 +28,10 @@ function loadStarterKitPanel(endpoint) {
|
|
|
24
28
|
let request = requests.get(endpoint);
|
|
25
29
|
if (!request) {
|
|
26
30
|
request = requestJson(endpoint).then((panel) => {
|
|
31
|
+
if (!isPanelData(panel)) throw new Error(`The starter kit endpoint ${endpoint} did not answer the panel data.`);
|
|
27
32
|
panels.set(endpoint, panel);
|
|
28
33
|
return panel;
|
|
29
|
-
}
|
|
34
|
+
}).catch((error) => {
|
|
30
35
|
requests.delete(endpoint);
|
|
31
36
|
throw error;
|
|
32
37
|
});
|
|
@@ -34,6 +39,9 @@ function loadStarterKitPanel(endpoint) {
|
|
|
34
39
|
}
|
|
35
40
|
return request;
|
|
36
41
|
}
|
|
42
|
+
function isPanelData(value) {
|
|
43
|
+
return typeof value === "object" && value !== null && typeof value.endpoints === "object" && value.endpoints !== null;
|
|
44
|
+
}
|
|
37
45
|
/** The panel data once loaded, or null (so remounts render straight away). */
|
|
38
46
|
function loadedStarterKitPanel(endpoint) {
|
|
39
47
|
return panels.get(endpoint) ?? null;
|
|
@@ -125,7 +133,63 @@ function openRequestedStarterKitSearch() {
|
|
|
125
133
|
searchRequested = false;
|
|
126
134
|
return shortcuts.trigger(SHORTCUT_IDS.palette);
|
|
127
135
|
}
|
|
136
|
+
/** The copilot's shortcut id (the loaded `ai:copilot` component registers the same one). */
|
|
137
|
+
const STARTER_KIT_COPILOT_SHORTCUT = "ai.copilot";
|
|
138
|
+
let copilotRequested = false;
|
|
139
|
+
/**
|
|
140
|
+
* Register the copilot's key bindings (`mod+j`) before its code has loaded: pressing them
|
|
141
|
+
* (or clicking the button) records the request, and the copilot opens once it has loaded.
|
|
142
|
+
* The loaded copilot registers the same id, so it replaces this registration.
|
|
143
|
+
*/
|
|
144
|
+
function registerStarterKitCopilotShortcut(keys, label, onRequest) {
|
|
145
|
+
if (keys.length === 0) return () => {};
|
|
146
|
+
return shortcuts.register({
|
|
147
|
+
id: STARTER_KIT_COPILOT_SHORTCUT,
|
|
148
|
+
keys,
|
|
149
|
+
label,
|
|
150
|
+
group: "general",
|
|
151
|
+
allowInInputs: true,
|
|
152
|
+
handler: () => {
|
|
153
|
+
requestStarterKitCopilot();
|
|
154
|
+
onRequest?.();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/** Ask for the copilot to open as soon as it has loaded. */
|
|
159
|
+
function requestStarterKitCopilot() {
|
|
160
|
+
copilotRequested = true;
|
|
161
|
+
}
|
|
162
|
+
/** Whether the copilot was asked for before it loaded (and forget the request). */
|
|
163
|
+
function takeRequestedStarterKitCopilot() {
|
|
164
|
+
const requested = copilotRequested;
|
|
165
|
+
copilotRequested = false;
|
|
166
|
+
return requested;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The copilot component in the loaded panel data: the panel's own pages show it in the
|
|
170
|
+
* `topbar.end` slot, which starter kit hosts don't render. Null without the AI plugin's
|
|
171
|
+
* copilot (or when the user may not use it).
|
|
172
|
+
*/
|
|
173
|
+
function starterKitCopilot(panel) {
|
|
174
|
+
for (const item of panel.slots?.["topbar.end"] ?? []) if (item.type === "component" && item.component.type === "ai:copilot") return item.component;
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The dashboard widgets of a `starterKit()` panel on the host's dashboard
|
|
179
|
+
* (`<AlfakitWidgets />`, the `alfakitWidgets` prop): a standalone widgets payload bound to
|
|
180
|
+
* the panel, the first panel's unless `panelId` is given. Null on other pages, for guests,
|
|
181
|
+
* users who may not open the panel and panels without widgets.
|
|
182
|
+
*/
|
|
183
|
+
function starterKitDashboardWidgets(props, panelId) {
|
|
184
|
+
const value = props?.alfakitWidgets;
|
|
185
|
+
if (!Array.isArray(value)) return null;
|
|
186
|
+
return value.find((item) => panelId === void 0 || item.id === panelId)?.component ?? null;
|
|
187
|
+
}
|
|
188
|
+
/** How many widgets a dashboard widgets payload has (placeholders before its code loads). */
|
|
189
|
+
function standaloneWidgetCount(component) {
|
|
190
|
+
return component.content.reduce((total, block) => total + (block.type === "Widgets" ? block.widgets.length : 0), 0);
|
|
191
|
+
}
|
|
128
192
|
//#endregion
|
|
129
|
-
export { isAlfakitPage, loadStarterKitPanel, loadedStarterKitPanel, openRequestedStarterKitSearch, registerStarterKitSearchShortcut, requestStarterKitSearch, resetStarterKitPanels, starterKitWidgets, starterKitWidgetsPage, starterKitWidgetsPanel };
|
|
193
|
+
export { STARTER_KIT_COPILOT_SHORTCUT, isAlfakitPage, loadStarterKitPanel, loadedStarterKitPanel, openRequestedStarterKitSearch, registerStarterKitCopilotShortcut, registerStarterKitSearchShortcut, requestStarterKitCopilot, requestStarterKitSearch, resetStarterKitPanels, standaloneWidgetCount, starterKitCopilot, starterKitDashboardWidgets, starterKitWidgets, starterKitWidgetsPage, starterKitWidgetsPanel, takeRequestedStarterKitCopilot };
|
|
130
194
|
|
|
131
195
|
//# sourceMappingURL=starter-kit-widgets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"starter-kit-widgets.js","names":[],"sources":["../src/starter-kit-widgets.ts"],"sourcesContent":["import { requestJson } from './http'\nimport type {\n PageSchema,\n PanelSchema,\n StarterKitNavigationSchema,\n StarterKitWidgetsSchema,\n} from './schema/panel'\nimport { SHORTCUT_IDS, shortcuts } from './shortcuts'\nimport { starterKitNavigations } from './starter-kit'\n\n/**\n * The drop-in header components of starter kit mode: `<AlfakitSearch />` (the command\n * palette and global search), `<AlfakitNotifications />` (the database notifications bell)\n * and `<AlfakitTenantSwitcher />`. They render on every page of the host application, from\n * the `widgets` of the shared `alfakitNavigation` prop; their code and the panel data\n * (translations, endpoints, the palette, realtime) load the first time one of them renders.\n * Shared by the three renderers.\n */\n\n/** One starter kit panel's navigation with its header components' data. */\nexport interface StarterKitWidgets {\n navigation: StarterKitNavigationSchema\n widgets: StarterKitWidgetsSchema\n}\n\n/**\n * The header components' data of a `starterKit()` panel (the first one with any unless\n * `panelId` is given), or null for guests and pages of other panels.\n */\nexport function starterKitWidgets(props: unknown, panelId?: string): StarterKitWidgets | null {\n const navigation = starterKitNavigations(props).find(\n (entry) => entry.widgets !== undefined && (panelId === undefined || entry.id === panelId),\n )\n\n return navigation?.widgets ? { navigation, widgets: navigation.widgets } : null\n}\n\n/** Whether the open page is one of the panel's own (it carries the `alfakit` prop). */\nexport function isAlfakitPage(props: unknown): boolean {\n return (props as { alfakit?: unknown } | null | undefined)?.alfakit !== undefined\n}\n\n// ---------------------------------------------------------------- Panel data\n\n// Loaded once per endpoint (the tenant-scoped one inside a tenant) for the browser session;\n// Inertia visits keep it, a full page load starts over.\nconst requests = new Map<string, Promise<PanelSchema>>()\nconst panels = new Map<string, PanelSchema>()\n\n/** Load (once) the panel data the header components render with. */\nexport function loadStarterKitPanel(endpoint: string): Promise<PanelSchema> {\n let request = requests.get(endpoint)\n\n if (!request) {\n request = requestJson<PanelSchema>(endpoint).then(\n (panel) => {\n panels.set(endpoint, panel)\n\n return panel\n },\n (error: unknown) => {\n // A failed request (offline, expired session) is retried by the next render.\n requests.delete(endpoint)\n throw error\n },\n )\n requests.set(endpoint, request)\n }\n\n return request\n}\n\n/** The panel data once loaded, or null (so remounts render straight away). */\nexport function loadedStarterKitPanel(endpoint: string): PanelSchema | null {\n return panels.get(endpoint) ?? null\n}\n\n/** @internal Forget the loaded panel data (tests). */\nexport function resetStarterKitPanels(): void {\n requests.clear()\n panels.clear()\n}\n\n/**\n * The panel the header components render with: the loaded panel data with the fresh\n * shared prop (navigation, unread count, tenants). Outside Alfakit's own chrome the host\n * owns the colour scheme and there is no shortcuts overlay, and panel commands and page\n * actions need an Alfakit page (its token and modals), so the palette lists search\n * results, navigation, \"create\" links and recent records.\n */\nexport function starterKitWidgetsPanel(base: PanelSchema, source: StarterKitWidgets): PanelSchema {\n const { navigation, widgets } = source\n const { databaseNotifications, tenancy, globalSearch, commandPalette } = base\n\n return {\n ...base,\n navigation: navigation.groups,\n darkMode: { enabled: false, forced: false, switcher: false },\n keyboardShortcuts: false,\n globalSearch: globalSearch && { ...globalSearch, position: undefined },\n commandPalette: commandPalette && { ...commandPalette, commands: [] },\n databaseNotifications: databaseNotifications && {\n ...databaseNotifications,\n unreadCount: widgets.notifications?.unreadCount ?? databaseNotifications.unreadCount,\n position: undefined,\n },\n tenancy: tenancy && {\n ...tenancy,\n current: widgets.tenancy ? (widgets.tenancy.current ?? null) : tenancy.current,\n tenants: widgets.tenancy?.tenants ?? tenancy.tenants,\n },\n }\n}\n\n/** The `page` the header components render with: none of the panel's (`usePageSchema()`). */\nexport function starterKitWidgetsPage(): PageSchema {\n return { class: '', layout: 'app', title: '', content: [], token: '' }\n}\n\n// ---------------------------------------------------------------- Search shortcut\n\nlet searchRequested = false\n\n/**\n * Register the palette's key bindings before its code has loaded: pressing them (or\n * clicking the field) records the request, and the palette opens once it has loaded. It\n * uses the palette's shortcut id, so the loaded palette replaces this registration and\n * the keys are bound once, however many search components the page renders.\n */\nexport function registerStarterKitSearchShortcut(\n keys: string[],\n label: string,\n onRequest: () => void,\n): () => void {\n return shortcuts.register({\n id: SHORTCUT_IDS.palette,\n keys,\n label,\n group: 'general',\n essential: true,\n handler: () => {\n requestStarterKitSearch()\n onRequest()\n },\n })\n}\n\n/** Ask for the palette to open as soon as it has loaded. */\nexport function requestStarterKitSearch(): void {\n searchRequested = true\n}\n\n/**\n * Open the loaded palette when it was asked for before it loaded (call once it mounted):\n * true when it did.\n */\nexport function openRequestedStarterKitSearch(): boolean {\n if (!searchRequested) return false\n\n searchRequested = false\n\n return shortcuts.trigger(SHORTCUT_IDS.palette)\n}\n"],"mappings":";;;;;;;;AA6BA,SAAgB,kBAAkB,OAAgB,SAA4C;CAC5F,MAAM,aAAa,sBAAsB,KAAK,CAAC,CAAC,MAC7C,UAAU,MAAM,YAAY,KAAA,MAAc,YAAY,KAAA,KAAa,MAAM,OAAO,QACnF;CAEA,OAAO,YAAY,UAAU;EAAE;EAAY,SAAS,WAAW;CAAQ,IAAI;AAC7E;;AAGA,SAAgB,cAAc,OAAyB;CACrD,OAAQ,OAAoD,YAAY,KAAA;AAC1E;AAMA,MAAM,2BAAW,IAAI,IAAkC;AACvD,MAAM,yBAAS,IAAI,IAAyB;;AAG5C,SAAgB,oBAAoB,UAAwC;CAC1E,IAAI,UAAU,SAAS,IAAI,QAAQ;CAEnC,IAAI,CAAC,SAAS;EACZ,UAAU,YAAyB,QAAQ,CAAC,CAAC,MAC1C,UAAU;GACT,OAAO,IAAI,UAAU,KAAK;GAE1B,OAAO;EACT,IACC,UAAmB;GAElB,SAAS,OAAO,QAAQ;GACxB,MAAM;EACR,CACF;EACA,SAAS,IAAI,UAAU,OAAO;CAChC;CAEA,OAAO;AACT;;AAGA,SAAgB,sBAAsB,UAAsC;CAC1E,OAAO,OAAO,IAAI,QAAQ,KAAK;AACjC;;AAGA,SAAgB,wBAA8B;CAC5C,SAAS,MAAM;CACf,OAAO,MAAM;AACf;;;;;;;;AASA,SAAgB,uBAAuB,MAAmB,QAAwC;CAChG,MAAM,EAAE,YAAY,YAAY;CAChC,MAAM,EAAE,uBAAuB,SAAS,cAAc,mBAAmB;CAEzE,OAAO;EACL,GAAG;EACH,YAAY,WAAW;EACvB,UAAU;GAAE,SAAS;GAAO,QAAQ;GAAO,UAAU;EAAM;EAC3D,mBAAmB;EACnB,cAAc,gBAAgB;GAAE,GAAG;GAAc,UAAU,KAAA;EAAU;EACrE,gBAAgB,kBAAkB;GAAE,GAAG;GAAgB,UAAU,CAAC;EAAE;EACpE,uBAAuB,yBAAyB;GAC9C,GAAG;GACH,aAAa,QAAQ,eAAe,eAAe,sBAAsB;GACzE,UAAU,KAAA;EACZ;EACA,SAAS,WAAW;GAClB,GAAG;GACH,SAAS,QAAQ,UAAW,QAAQ,QAAQ,WAAW,OAAQ,QAAQ;GACvE,SAAS,QAAQ,SAAS,WAAW,QAAQ;EAC/C;CACF;AACF;;AAGA,SAAgB,wBAAoC;CAClD,OAAO;EAAE,OAAO;EAAI,QAAQ;EAAO,OAAO;EAAI,SAAS,CAAC;EAAG,OAAO;CAAG;AACvE;AAIA,IAAI,kBAAkB;;;;;;;AAQtB,SAAgB,iCACd,MACA,OACA,WACY;CACZ,OAAO,UAAU,SAAS;EACxB,IAAI,aAAa;EACjB;EACA;EACA,OAAO;EACP,WAAW;EACX,eAAe;GACb,wBAAwB;GACxB,UAAU;EACZ;CACF,CAAC;AACH;;AAGA,SAAgB,0BAAgC;CAC9C,kBAAkB;AACpB;;;;;AAMA,SAAgB,gCAAyC;CACvD,IAAI,CAAC,iBAAiB,OAAO;CAE7B,kBAAkB;CAElB,OAAO,UAAU,QAAQ,aAAa,OAAO;AAC/C"}
|
|
1
|
+
{"version":3,"file":"starter-kit-widgets.js","names":[],"sources":["../src/starter-kit-widgets.ts"],"sourcesContent":["import { requestJson } from './http'\nimport type { AiCopilotSchema } from './schema/ai'\nimport type {\n PageSchema,\n PanelSchema,\n StarterKitDashboardWidgetsSchema,\n StarterKitNavigationSchema,\n StarterKitWidgetsSchema,\n} from './schema/panel'\nimport type { StandaloneComponentSchema } from './schema/standalone'\nimport type { WidgetsBlock } from './schema/widgets'\nimport { SHORTCUT_IDS, shortcuts } from './shortcuts'\nimport { preferCurrentStarterKitPanel, starterKitNavigations } from './starter-kit'\n\n/**\n * The drop-in header components of starter kit mode: `<AlfakitSearch />` (the command\n * palette and global search), `<AlfakitNotifications />` (the database notifications bell),\n * `<AlfakitTenantSwitcher />` and `<AlfakitCopilot />` (the AI plugin's copilot). They render\n * on every page of the host application, from the `widgets` of the shared\n * `alfakitNavigation` prop; their code and the panel data (translations, endpoints, the\n * palette, realtime) load the first time one of them renders.\n * Shared by the three renderers.\n */\n\n/** One starter kit panel's navigation with its header components' data. */\nexport interface StarterKitWidgets {\n navigation: StarterKitNavigationSchema\n widgets: StarterKitWidgetsSchema\n}\n\n/**\n * The header components' data of a `starterKit()` panel, or null for guests and pages of\n * other panels. Without `panelId`: the open AlfaKit page's panel, else the first one with\n * any; with `part` (`'tenancy'` for the tenant switcher), the first panel that has that\n * part when the preferred one hasn't, so a team workspace on a second panel still gets its\n * switcher on the host's own pages.\n */\nexport function starterKitWidgets(\n props: unknown,\n panelId?: string,\n part?: keyof StarterKitWidgetsSchema,\n): StarterKitWidgets | null {\n const entries = preferCurrentStarterKitPanel(starterKitNavigations(props), props).filter(\n (entry) => entry.widgets !== undefined && (panelId === undefined || entry.id === panelId),\n )\n const navigation =\n (part !== undefined && panelId === undefined\n ? entries.find((entry) => entry.widgets?.[part] !== undefined)\n : undefined) ?? entries[0]\n\n return navigation?.widgets ? { navigation, widgets: navigation.widgets } : null\n}\n\n/** Whether the open page is one of the panel's own (it carries the `alfakit` prop). */\nexport function isAlfakitPage(props: unknown): boolean {\n return (props as { alfakit?: unknown } | null | undefined)?.alfakit !== undefined\n}\n\n// ---------------------------------------------------------------- Panel data\n\n// Loaded once per endpoint (the tenant-scoped one inside a tenant) for the browser session;\n// Inertia visits keep it, a full page load starts over.\nconst requests = new Map<string, Promise<PanelSchema>>()\nconst panels = new Map<string, PanelSchema>()\n\n/** Load (once) the panel data the header components render with. */\nexport function loadStarterKitPanel(endpoint: string): Promise<PanelSchema> {\n let request = requests.get(endpoint)\n\n if (!request) {\n request = requestJson<unknown>(endpoint)\n .then((panel) => {\n // A redirect to the sign-in page (expired or ended session) answers HTML, not the\n // panel: refuse it, so the components keep their trigger instead of crashing.\n if (!isPanelData(panel)) {\n throw new Error(`The starter kit endpoint ${endpoint} did not answer the panel data.`)\n }\n\n panels.set(endpoint, panel)\n\n return panel\n })\n .catch((error: unknown) => {\n // A failed request (offline, expired session) is retried by the next render.\n requests.delete(endpoint)\n throw error\n })\n requests.set(endpoint, request)\n }\n\n return request\n}\n\nfunction isPanelData(value: unknown): value is PanelSchema {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { endpoints?: unknown }).endpoints === 'object' &&\n (value as { endpoints?: unknown }).endpoints !== null\n )\n}\n\n/** The panel data once loaded, or null (so remounts render straight away). */\nexport function loadedStarterKitPanel(endpoint: string): PanelSchema | null {\n return panels.get(endpoint) ?? null\n}\n\n/** @internal Forget the loaded panel data (tests). */\nexport function resetStarterKitPanels(): void {\n requests.clear()\n panels.clear()\n}\n\n/**\n * The panel the header components render with: the loaded panel data with the fresh\n * shared prop (navigation, unread count, tenants). Outside Alfakit's own chrome the host\n * owns the colour scheme and there is no shortcuts overlay, and panel commands and page\n * actions need an Alfakit page (its token and modals), so the palette lists search\n * results, navigation, \"create\" links and recent records.\n */\nexport function starterKitWidgetsPanel(base: PanelSchema, source: StarterKitWidgets): PanelSchema {\n const { navigation, widgets } = source\n const { databaseNotifications, tenancy, globalSearch, commandPalette } = base\n\n return {\n ...base,\n navigation: navigation.groups,\n darkMode: { enabled: false, forced: false, switcher: false },\n keyboardShortcuts: false,\n globalSearch: globalSearch && { ...globalSearch, position: undefined },\n commandPalette: commandPalette && { ...commandPalette, commands: [] },\n databaseNotifications: databaseNotifications && {\n ...databaseNotifications,\n unreadCount: widgets.notifications?.unreadCount ?? databaseNotifications.unreadCount,\n position: undefined,\n },\n tenancy: tenancy && {\n ...tenancy,\n current: widgets.tenancy ? (widgets.tenancy.current ?? null) : tenancy.current,\n tenants: widgets.tenancy?.tenants ?? tenancy.tenants,\n },\n }\n}\n\n/** The `page` the header components render with: none of the panel's (`usePageSchema()`). */\nexport function starterKitWidgetsPage(): PageSchema {\n return { class: '', layout: 'app', title: '', content: [], token: '' }\n}\n\n// ---------------------------------------------------------------- Search shortcut\n\nlet searchRequested = false\n\n/**\n * Register the palette's key bindings before its code has loaded: pressing them (or\n * clicking the field) records the request, and the palette opens once it has loaded. It\n * uses the palette's shortcut id, so the loaded palette replaces this registration and\n * the keys are bound once, however many search components the page renders.\n */\nexport function registerStarterKitSearchShortcut(\n keys: string[],\n label: string,\n onRequest: () => void,\n): () => void {\n return shortcuts.register({\n id: SHORTCUT_IDS.palette,\n keys,\n label,\n group: 'general',\n essential: true,\n handler: () => {\n requestStarterKitSearch()\n onRequest()\n },\n })\n}\n\n/** Ask for the palette to open as soon as it has loaded. */\nexport function requestStarterKitSearch(): void {\n searchRequested = true\n}\n\n/**\n * Open the loaded palette when it was asked for before it loaded (call once it mounted):\n * true when it did.\n */\nexport function openRequestedStarterKitSearch(): boolean {\n if (!searchRequested) return false\n\n searchRequested = false\n\n return shortcuts.trigger(SHORTCUT_IDS.palette)\n}\n\n// ---------------------------------------------------------------- Copilot\n\n/** The copilot's shortcut id (the loaded `ai:copilot` component registers the same one). */\nexport const STARTER_KIT_COPILOT_SHORTCUT = 'ai.copilot'\n\nlet copilotRequested = false\n\n/**\n * Register the copilot's key bindings (`mod+j`) before its code has loaded: pressing them\n * (or clicking the button) records the request, and the copilot opens once it has loaded.\n * The loaded copilot registers the same id, so it replaces this registration.\n */\nexport function registerStarterKitCopilotShortcut(\n keys: string[],\n label: string,\n onRequest?: () => void,\n): () => void {\n if (keys.length === 0) return () => {}\n\n return shortcuts.register({\n id: STARTER_KIT_COPILOT_SHORTCUT,\n keys,\n label,\n group: 'general',\n allowInInputs: true,\n handler: () => {\n requestStarterKitCopilot()\n onRequest?.()\n },\n })\n}\n\n/** Ask for the copilot to open as soon as it has loaded. */\nexport function requestStarterKitCopilot(): void {\n copilotRequested = true\n}\n\n/** Whether the copilot was asked for before it loaded (and forget the request). */\nexport function takeRequestedStarterKitCopilot(): boolean {\n const requested = copilotRequested\n\n copilotRequested = false\n\n return requested\n}\n\n/**\n * The copilot component in the loaded panel data: the panel's own pages show it in the\n * `topbar.end` slot, which starter kit hosts don't render. Null without the AI plugin's\n * copilot (or when the user may not use it).\n */\nexport function starterKitCopilot(panel: PanelSchema): AiCopilotSchema | null {\n for (const item of panel.slots?.['topbar.end'] ?? []) {\n if (item.type === 'component' && item.component.type === 'ai:copilot') {\n return item.component as unknown as AiCopilotSchema\n }\n }\n\n return null\n}\n\n// ---------------------------------------------------------------- Dashboard widgets\n\n/**\n * The dashboard widgets of a `starterKit()` panel on the host's dashboard\n * (`<AlfakitWidgets />`, the `alfakitWidgets` prop): a standalone widgets payload bound to\n * the panel, the first panel's unless `panelId` is given. Null on other pages, for guests,\n * users who may not open the panel and panels without widgets.\n */\nexport function starterKitDashboardWidgets(\n props: unknown,\n panelId?: string,\n): StandaloneComponentSchema | null {\n const value = (props as { alfakitWidgets?: unknown } | null | undefined)?.alfakitWidgets\n\n if (!Array.isArray(value)) return null\n\n const entry = (value as StarterKitDashboardWidgetsSchema[]).find(\n (item) => panelId === undefined || item.id === panelId,\n )\n\n return entry?.component ?? null\n}\n\n/** How many widgets a dashboard widgets payload has (placeholders before its code loads). */\nexport function standaloneWidgetCount(component: StandaloneComponentSchema): number {\n return component.content.reduce(\n (total, block) =>\n total + (block.type === 'Widgets' ? (block as WidgetsBlock).widgets.length : 0),\n 0,\n )\n}\n"],"mappings":"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"}
|
package/dist/starter-kit.d.ts
CHANGED
|
@@ -28,13 +28,36 @@ export interface StarterKitBreadcrumb {
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function toStarterKitBreadcrumbs(breadcrumbs: BreadcrumbSchema[] | null | undefined): StarterKitBreadcrumb[];
|
|
30
30
|
/** The breadcrumbs of an Alfakit page from its Inertia props (`props.page.breadcrumbs`). */
|
|
31
|
+
/**
|
|
32
|
+
* Whether an Alfakit page renders inside the host layout (`createStarterKitPage({ layout })`).
|
|
33
|
+
* Pages with the `auth` layout (sign-in, registration, password reset, multi-factor
|
|
34
|
+
* challenge, email verification, `->login()` brought back after `starterKit()`) stand on
|
|
35
|
+
* their own, like the host's auth pages: the host layout expects a signed-in user.
|
|
36
|
+
*/
|
|
37
|
+
export declare function usesStarterKitHostLayout(props: unknown): boolean;
|
|
31
38
|
export declare function starterKitBreadcrumbsFromProps(props: unknown): StarterKitBreadcrumb[];
|
|
32
39
|
/**
|
|
33
40
|
* The shared `alfakitNavigation` prop of a page: one entry per `starterKit()` panel, or none
|
|
34
41
|
* for guests and pages of other panels.
|
|
35
42
|
*/
|
|
36
43
|
export declare function starterKitNavigations(props: unknown): StarterKitNavigationSchema[];
|
|
37
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The panel of the open AlfaKit page (`props.alfakit.id`), or undefined on the host's own
|
|
46
|
+
* pages. With several `starterKit()` panels, components without a `panel` follow it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function currentStarterKitPanelId(props: unknown): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Order panel entries for components without a `panel` id: the open AlfaKit page's panel
|
|
51
|
+
* first (an `/app` page of a second `starterKit()` panel shows that panel's navigation and
|
|
52
|
+
* tenant menu), then the others in registration order.
|
|
53
|
+
*/
|
|
54
|
+
export declare function preferCurrentStarterKitPanel<T extends {
|
|
55
|
+
id: string;
|
|
56
|
+
}>(entries: T[], props: unknown): T[];
|
|
57
|
+
/**
|
|
58
|
+
* One panel's navigation, or null. Without an id: the open AlfaKit page's panel, else the
|
|
59
|
+
* first starter kit panel.
|
|
60
|
+
*/
|
|
38
61
|
export declare function starterKitNavigation(props: unknown, panelId?: string): StarterKitNavigationSchema | null;
|
|
39
62
|
/**
|
|
40
63
|
* A navigation item flattened for a starter kit sidebar: the label as text and the item's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"starter-kit.d.ts","names":[],"sources":["../src/starter-kit.ts"],"mappings":";;;;;;;;;;;;;;qBAiCa;;wBAGG,kBACd,OAAO,KAAK;;iBAMG;EACf;EACA;;;;;;;;wBASc,wBACd,aAAa,wCACZ
|
|
1
|
+
{"version":3,"file":"starter-kit.d.ts","names":[],"sources":["../src/starter-kit.ts"],"mappings":";;;;;;;;;;;;;;qBAiCa;;wBAGG,kBACd,OAAO,KAAK;;iBAMG;EACf;EACA;;;;;;;;wBASc,wBACd,aAAa,wCACZ;;;;;;;;wBAsBa,yBAAyB;wBAMzB,+BAA+B,iBAAiB;;;;;wBAUhD,sBAAsB,iBAAiB;;;;;wBAUvC,yBAAyB;;;;;;wBAWzB,6BAA6B;EAAY;GACvD,SAAS,KACT,iBACC;;;;;wBAWa,qBACd,gBACA,mBACC;;;;;iBAcc;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;;iBAGK;EACf;EACA;EACA;EACA,OAAO;;;wBAmBO,sBACd,QAAQ,6CACP;;;;;wBA8Ba,mBAAmB,OAAO,QAAQ;;wBAalC,0BAA0B;;;;;;;wBAU1B,yBACd,OAAO,cAAc,6BACrB,IAAI,aAAa,eAAe,iCAAiC"}
|
package/dist/starter-kit.js
CHANGED
|
@@ -40,6 +40,15 @@ function toStarterKitBreadcrumbs(breadcrumbs) {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
/** The breadcrumbs of an Alfakit page from its Inertia props (`props.page.breadcrumbs`). */
|
|
43
|
+
/**
|
|
44
|
+
* Whether an Alfakit page renders inside the host layout (`createStarterKitPage({ layout })`).
|
|
45
|
+
* Pages with the `auth` layout (sign-in, registration, password reset, multi-factor
|
|
46
|
+
* challenge, email verification, `->login()` brought back after `starterKit()`) stand on
|
|
47
|
+
* their own, like the host's auth pages: the host layout expects a signed-in user.
|
|
48
|
+
*/
|
|
49
|
+
function usesStarterKitHostLayout(props) {
|
|
50
|
+
return (props?.page)?.layout !== "auth";
|
|
51
|
+
}
|
|
43
52
|
function starterKitBreadcrumbsFromProps(props) {
|
|
44
53
|
const page = props?.page;
|
|
45
54
|
return toStarterKitBreadcrumbs(page?.breadcrumbs);
|
|
@@ -52,10 +61,31 @@ function starterKitNavigations(props) {
|
|
|
52
61
|
const value = props?.alfakitNavigation;
|
|
53
62
|
return Array.isArray(value) ? value : [];
|
|
54
63
|
}
|
|
55
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* The panel of the open AlfaKit page (`props.alfakit.id`), or undefined on the host's own
|
|
66
|
+
* pages. With several `starterKit()` panels, components without a `panel` follow it.
|
|
67
|
+
*/
|
|
68
|
+
function currentStarterKitPanelId(props) {
|
|
69
|
+
const id = props?.alfakit?.id;
|
|
70
|
+
return typeof id === "string" ? id : void 0;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Order panel entries for components without a `panel` id: the open AlfaKit page's panel
|
|
74
|
+
* first (an `/app` page of a second `starterKit()` panel shows that panel's navigation and
|
|
75
|
+
* tenant menu), then the others in registration order.
|
|
76
|
+
*/
|
|
77
|
+
function preferCurrentStarterKitPanel(entries, props) {
|
|
78
|
+
const current = currentStarterKitPanelId(props);
|
|
79
|
+
const index = current === void 0 ? -1 : entries.findIndex((entry) => entry.id === current);
|
|
80
|
+
return index <= 0 ? entries : [entries[index], ...entries.filter((_, i) => i !== index)];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* One panel's navigation, or null. Without an id: the open AlfaKit page's panel, else the
|
|
84
|
+
* first starter kit panel.
|
|
85
|
+
*/
|
|
56
86
|
function starterKitNavigation(props, panelId) {
|
|
57
87
|
const navigations = starterKitNavigations(props);
|
|
58
|
-
return (panelId === void 0 ? navigations[0] : navigations.find((navigation) => navigation.id === panelId)) ?? null;
|
|
88
|
+
return (panelId === void 0 ? preferCurrentStarterKitPanel(navigations, props)[0] : navigations.find((navigation) => navigation.id === panelId)) ?? null;
|
|
59
89
|
}
|
|
60
90
|
function toNavItem(item, key) {
|
|
61
91
|
return {
|
|
@@ -126,6 +156,6 @@ function installStarterKitRuntime(show, t) {
|
|
|
126
156
|
return true;
|
|
127
157
|
}
|
|
128
158
|
//#endregion
|
|
129
|
-
export { STARTER_KIT_TOASTER_ID, currentStarterKitPanel, installStarterKitRuntime, isStarterKitPanel, starterKitBreadcrumbsFromProps, starterKitNavigation, starterKitNavigations, syncStarterKitPage, toStarterKitBreadcrumbs, toStarterKitNavGroups };
|
|
159
|
+
export { STARTER_KIT_TOASTER_ID, currentStarterKitPanel, currentStarterKitPanelId, installStarterKitRuntime, isStarterKitPanel, preferCurrentStarterKitPanel, starterKitBreadcrumbsFromProps, starterKitNavigation, starterKitNavigations, syncStarterKitPage, toStarterKitBreadcrumbs, toStarterKitNavGroups, usesStarterKitHostLayout };
|
|
130
160
|
|
|
131
161
|
//# sourceMappingURL=starter-kit.js.map
|