@alfakit/react 0.1.7 → 0.1.8
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 +8 -0
- package/dist/components/panel/locale-switcher.d.ts.map +1 -1
- package/dist/components/panel/locale-switcher.js +2 -1
- package/dist/components/panel/locale-switcher.js.map +1 -1
- package/dist/eject-manifest.json +4 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/starter-kit/index.d.ts +2 -2
- package/dist/starter-kit/index.js +2 -2
- package/dist/starter-kit/widgets-loaded.js +74 -13
- package/dist/starter-kit/widgets-loaded.js.map +1 -1
- package/dist/starter-kit/widgets-triggers.js +34 -3
- package/dist/starter-kit/widgets-triggers.js.map +1 -1
- package/dist/starter-kit/widgets.d.ts +11 -2
- package/dist/starter-kit/widgets.d.ts.map +1 -1
- package/dist/starter-kit/widgets.js +20 -2
- package/dist/starter-kit/widgets.js.map +1 -1
- package/package.json +2 -2
- package/src/components/panel/locale-switcher.tsx +2 -1
- package/src/starter-kit/index.ts +1 -0
- package/src/starter-kit/widgets-loaded.tsx +86 -10
- package/src/starter-kit/widgets-triggers.tsx +41 -1
- package/src/starter-kit/widgets.tsx +33 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @alfakit/react
|
|
2
2
|
|
|
3
|
+
## 0.1.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `<AlfakitLanguageSwitcher />`: a drop-in header component for starter kits, so users can switch the panel's language from the app header. `LocaleOptions` is exported for custom switchers.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @alfakit/core@0.1.8
|
|
10
|
+
|
|
3
11
|
## 0.1.7
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locale-switcher.d.ts","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"locale-switcher.d.ts","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"mappings":";;;;;wBAsDgB,kCAAc,IAAA;;;;;;wBA0Cd,wCAAoB,IAAA;;;;;wBAwBpB,iCAAa,IAAA"}
|
|
@@ -12,6 +12,7 @@ function useSwitch() {
|
|
|
12
12
|
if (panel.endpoints.locale && code !== panel.localization.code) switchLocale(panel.endpoints.locale, code);
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
/** The panel's languages as radio items, each in its own name: picking one switches to it. */
|
|
15
16
|
function LocaleOptions() {
|
|
16
17
|
const localization = useLocalization();
|
|
17
18
|
const change = useSwitch();
|
|
@@ -108,6 +109,6 @@ function LocaleSubMenu() {
|
|
|
108
109
|
})] });
|
|
109
110
|
}
|
|
110
111
|
//#endregion
|
|
111
|
-
export { LocaleSubMenu, LocaleSwitcher, TopbarLocaleSwitcher };
|
|
112
|
+
export { LocaleOptions, LocaleSubMenu, LocaleSwitcher, TopbarLocaleSwitcher };
|
|
112
113
|
|
|
113
114
|
//# sourceMappingURL=locale-switcher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locale-switcher.js","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"sourcesContent":["import { collapsesLanguageSwitcher, showsLanguageSwitcher, switchLocale } from '@alfakit/core'\nimport { LanguagesIcon } from 'lucide-react'\nimport { createElement } from 'react'\nimport { useLocalization, useOptionalRegistry, usePanel, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\n\nfunction useSwitch() {\n const panel = usePanel()\n\n return (code: string) => {\n if (panel.endpoints.locale && code !== panel.localization.code) {\n switchLocale(panel.endpoints.locale, code)\n }\n }\n}\n\
|
|
1
|
+
{"version":3,"file":"locale-switcher.js","names":[],"sources":["../../../src/components/panel/locale-switcher.tsx"],"sourcesContent":["import { collapsesLanguageSwitcher, showsLanguageSwitcher, switchLocale } from '@alfakit/core'\nimport { LanguagesIcon } from 'lucide-react'\nimport { createElement } from 'react'\nimport { useLocalization, useOptionalRegistry, usePanel, useT } from '../../context'\nimport { Button } from '../ui/button'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\n\nfunction useSwitch() {\n const panel = usePanel()\n\n return (code: string) => {\n if (panel.endpoints.locale && code !== panel.localization.code) {\n switchLocale(panel.endpoints.locale, code)\n }\n }\n}\n\n/** The panel's languages as radio items, each in its own name: picking one switches to it. */\nexport function LocaleOptions() {\n const localization = useLocalization()\n const change = useSwitch()\n\n return (\n <DropdownMenuRadioGroup value={localization.code} onValueChange={change}>\n {localization.locales.map((locale) => (\n <DropdownMenuRadioItem\n key={locale.code}\n value={locale.code}\n lang={locale.tag}\n data-current={locale.code === localization.code ? 'true' : undefined}\n className=\"data-[current=true]:font-medium\"\n >\n <span lang={locale.tag} dir={locale.direction}>\n {locale.label}\n </span>\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n )\n}\n\n/**\n * Stand-alone language menu: a languages button listing each language in its own name\n * (auth pages, top bar). Renders nothing without a switcher.\n */\nexport function LocaleSwitcher() {\n const t = useT()\n const localization = useLocalization()\n\n if (!showsLanguageSwitcher(localization, 'auth')) {\n return null\n }\n\n return (\n <div data-slot=\"language-switcher\" className=\"flex\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={t('change_language', {}, 'Change language')}\n title={t('change_language', {}, 'Change language')}\n >\n <LanguagesIcon />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\n/** Whether the panel has a user menu that can hold the language entry on phones. */\nfunction useHasUserMenu(): boolean {\n const panel = usePanel()\n\n return Boolean(panel?.user && panel.userMenu)\n}\n\n/**\n * The top bars' language button (`languageSwitcher('topbar' | 'both')`): the registered\n * `panel:language-switcher` component or the built-in menu. Below `sm` it moves into the\n * user menu when there is one (`collapsesLanguageSwitcher()`).\n */\nexport function TopbarLocaleSwitcher() {\n const localization = usePanel()?.localization\n const registry = useOptionalRegistry()\n const collapses = collapsesLanguageSwitcher(localization, useHasUserMenu())\n\n if (!showsLanguageSwitcher(localization, 'topbar')) {\n return null\n }\n\n const button = createElement(registry?.resolve('panel:language-switcher') ?? LocaleSwitcher)\n\n return collapses ? (\n <div className=\"hidden sm:contents\" data-slot=\"topbar-language\">\n {button}\n </div>\n ) : (\n button\n )\n}\n\n/**\n * \"Language\" submenu for the user menu (`languageSwitcher('user-menu' | 'both')`, and\n * below `sm` for `'topbar'`).\n */\nexport function LocaleSubMenu() {\n const t = useT()\n const localization = useLocalization()\n const hasUserMenu = useHasUserMenu()\n const inMenu = showsLanguageSwitcher(localization, 'user-menu')\n // `languageSwitcher('topbar')`: the top bar's button is hidden on phones, so it's here.\n const onPhones = !inMenu && collapsesLanguageSwitcher(localization, hasUserMenu)\n\n if (!inMenu && !onPhones) {\n return null\n }\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className={onPhones ? 'sm:hidden' : undefined}>\n <LanguagesIcon />\n <span>{t('language', {}, 'Language')}</span>\n <span className=\"text-muted-foreground ms-auto ps-2 text-xs\">{localization.label}</span>\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n}\n"],"mappings":";;;;;;;;AAgBA,SAAS,YAAY;CACnB,MAAM,QAAQ,SAAS;CAEvB,QAAQ,SAAiB;EACvB,IAAI,MAAM,UAAU,UAAU,SAAS,MAAM,aAAa,MACxD,aAAa,MAAM,UAAU,QAAQ,IAAI;CAE7C;AACF;;AAGA,SAAgB,gBAAgB;CAC9B,MAAM,eAAe,gBAAgB;CACrC,MAAM,SAAS,UAAU;CAEzB,OACE,oBAAC,wBAAD;EAAwB,OAAO,aAAa;EAAM,eAAe;EAC9D,UAAA,aAAa,QAAQ,KAAK,WACzB,oBAAC,uBAAD;GAEE,OAAO,OAAO;GACd,MAAM,OAAO;GACb,gBAAc,OAAO,SAAS,aAAa,OAAO,SAAS,KAAA;GAC3D,WAAU;GAEV,UAAA,oBAAC,QAAD;IAAM,MAAM,OAAO;IAAK,KAAK,OAAO;IACjC,UAAA,OAAO;GACJ,CAAA;EACe,GAThB,OAAO,IASS,CACxB;CACqB,CAAA;AAE5B;;;;;AAMA,SAAgB,iBAAiB;CAC/B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CAErC,IAAI,CAAC,sBAAsB,cAAc,MAAM,GAC7C,OAAO;CAGT,OACE,oBAAC,OAAD;EAAK,aAAU;EAAoB,WAAU;EAC3C,UAAA,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;GACnB,UAAA,oBAAC,QAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,cAAY,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IACtD,OAAO,EAAE,mBAAmB,CAAC,GAAG,iBAAiB;IAEjD,UAAA,oBAAC,eAAD,CAAgB,CAAA;GACV,CAAA;EACW,CAAA,GACrB,oBAAC,qBAAD;GAAqB,OAAM;GAAM,WAAU;GACzC,UAAA,oBAAC,eAAD,CAAgB,CAAA;EACG,CAAA,CACT,EAAA,CAAA;CACX,CAAA;AAET;;AAGA,SAAS,iBAA0B;CACjC,MAAM,QAAQ,SAAS;CAEvB,OAAO,QAAQ,OAAO,QAAQ,MAAM,QAAQ;AAC9C;;;;;;AAOA,SAAgB,uBAAuB;CACrC,MAAM,eAAe,SAAS,CAAC,EAAE;CACjC,MAAM,WAAW,oBAAoB;CACrC,MAAM,YAAY,0BAA0B,cAAc,eAAe,CAAC;CAE1E,IAAI,CAAC,sBAAsB,cAAc,QAAQ,GAC/C,OAAO;CAGT,MAAM,SAAS,cAAc,UAAU,QAAQ,yBAAyB,KAAK,cAAc;CAE3F,OAAO,YACL,oBAAC,OAAD;EAAK,WAAU;EAAqB,aAAU;EAC3C,UAAA;CACE,CAAA,IAEL;AAEJ;;;;;AAMA,SAAgB,gBAAgB;CAC9B,MAAM,IAAI,KAAK;CACf,MAAM,eAAe,gBAAgB;CACrC,MAAM,cAAc,eAAe;CACnC,MAAM,SAAS,sBAAsB,cAAc,WAAW;CAE9D,MAAM,WAAW,CAAC,UAAU,0BAA0B,cAAc,WAAW;CAE/E,IAAI,CAAC,UAAU,CAAC,UACd,OAAO;CAGT,OACE,qBAAC,iBAAD,EAAA,UAAA,CACE,qBAAC,wBAAD;EAAwB,WAAW,WAAW,cAAc,KAAA;EAA5D,UAAA;GACE,oBAAC,eAAD,CAAgB,CAAA;GAChB,oBAAC,QAAD,EAAA,UAAO,EAAE,YAAY,CAAC,GAAG,UAAU,EAAQ,CAAA;GAC3C,oBAAC,QAAD;IAAM,WAAU;IAA8C,UAAA,aAAa;GAAY,CAAA;EACjE;CACxB,CAAA,GAAA,oBAAC,wBAAD;EAAwB,WAAU;EAChC,UAAA,oBAAC,eAAD,CAAgB,CAAA;CACM,CAAA,CACT,EAAA,CAAA;AAErB"}
|
package/dist/eject-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@alfakit/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"adapter": "react",
|
|
5
|
-
"generatedAt": "2026-10-
|
|
5
|
+
"generatedAt": "2026-10-22T11:23:16.880Z",
|
|
6
6
|
"components": {
|
|
7
7
|
"TextInput": {
|
|
8
8
|
"file": "src/components/fields/text-input.tsx",
|
|
@@ -921,6 +921,7 @@
|
|
|
921
921
|
},
|
|
922
922
|
"src/starter-kit/widgets.tsx": {
|
|
923
923
|
"AlfakitCopilot": "AlfakitCopilot",
|
|
924
|
+
"AlfakitLanguageSwitcher": "AlfakitLanguageSwitcher",
|
|
924
925
|
"AlfakitNotifications": "AlfakitNotifications",
|
|
925
926
|
"AlfakitSearch": "AlfakitSearch",
|
|
926
927
|
"AlfakitTenantSwitcher": "AlfakitTenantSwitcher",
|
|
@@ -937,6 +938,7 @@
|
|
|
937
938
|
"StarterKitLayout": "StarterKitLayout",
|
|
938
939
|
"StarterKitPageOptions": "StarterKitPageOptions",
|
|
939
940
|
"AlfakitCopilot": "AlfakitCopilot",
|
|
941
|
+
"AlfakitLanguageSwitcher": "AlfakitLanguageSwitcher",
|
|
940
942
|
"AlfakitNotifications": "AlfakitNotifications",
|
|
941
943
|
"AlfakitSearch": "AlfakitSearch",
|
|
942
944
|
"AlfakitTenantSwitcher": "AlfakitTenantSwitcher",
|
package/dist/index.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ import "./standalone/index.js";
|
|
|
148
148
|
import { AlfakitWidgets, AlfakitWidgetsProps } from "./starter-kit/dashboard-widgets.js";
|
|
149
149
|
import { AlfakitNavigation, useAlfakitNavigation } from "./starter-kit/navigation.js";
|
|
150
150
|
import { AlfakitStarterKitRoot, StarterKitContentLayout, StarterKitLayout, StarterKitPageOptions, createStarterKitPage } from "./starter-kit/starter-kit.js";
|
|
151
|
-
import { AlfakitCopilot, AlfakitHeaderWidgetProps, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./starter-kit/widgets.js";
|
|
151
|
+
import { AlfakitCopilot, AlfakitHeaderWidgetProps, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./starter-kit/widgets.js";
|
|
152
152
|
import "./starter-kit/index.js";
|
|
153
153
|
import { Alert, AlertDescription, AlertTitle } from "./components/ui/alert.js";
|
|
154
154
|
import { Avatar, AvatarFallback, AvatarImage } from "./components/ui/avatar.js";
|
|
@@ -180,4 +180,4 @@ import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, Tabl
|
|
|
180
180
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs.js";
|
|
181
181
|
import { Textarea } from "./components/ui/textarea.js";
|
|
182
182
|
export * from "@alfakit/core";
|
|
183
|
-
export { ActionButton, ActionGroupMenu, ActionList, ActionModals, AiCopilot, AiStream, Alert, AlertDescription, AlertTitle, AlfakitAnalytics, AlfakitComponent, type AlfakitComponentProps, type AlfakitContextValue, AlfakitCopilot, type AlfakitHeaderWidgetProps, type AlfakitNavigation, AlfakitNotifications, AlfakitPage, AlfakitProvider, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitToaster, type AlfakitToasterProps, AlfakitWidgets, type AlfakitWidgetsProps, AnalyticsBreakdown, type AnyComponent, AppLayout, AppPage, AppSidebar, AuthLayout, Avatar, AvatarFallback, AvatarImage, Badge, BlankLayout, BottomNav, Brand, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Breadcrumbs, BuilderField, Button, CMS_BLOCK_COMPONENTS, Calendar, CalendarWidget, CallToActionBlock, CalloutComponent, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChartWidget, Checkbox, CheckboxField, CheckboxListField, CmsBlockHeading, type CmsBlockProps, CmsBlocks, type CmsContextValue, CmsFooter, CmsHead, CmsHeader, CmsImage, CmsLanguageSwitcher, CmsLinkButton, CmsLivePreview, CmsMenuList, CmsNav, CmsPage, CmsProvider, CodeEditorField, CodeEntryComponent, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorEntryComponent, ColorPickerField, ColumnManager, ColumnsBlock, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConflictBanner, ContentBlockRenderer, ContentLayout, ContentLayoutSort, type CreateAlfakitOptions, CustomizableWidgetGrid, DataTable, DatePickerField, DebugPanel, DebugPanelSlot, DebugTreeNode, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type DiscoveredComponents, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmbedBlock, EmptyStateComponent, EntryPlaceholder, EntryWrapper, FaqBlock, FeaturesBlock, type FieldBinding, FieldWrapper, FieldsetComponent, FileManager, FileManagerPickerField, FileUploadField, FilterIndicators, FlashToasts, FlexComponent, FormBlock, FormProvider, FormattedText, FullscreenLayout, type FullscreenLayoutProps, FusedGroupComponent, GalleryBlock, GlobalSearch, GridComponent, HelpdeskChat, type HelpdeskChatProps, HelpdeskInbox, HeroBlock, HiddenField, HtmlComponent, Icon, IconComponent, IconEntryComponent, IconPickerField, ImageBlock, ImageComponent, ImageEntryComponent, ImpersonationBanner, Input, Kanban, KeyValueEntryComponent, KeyValueField, KeyboardShortcuts, Label, LazyPlaceholder, LiveRecordsPill, LocaleSubMenu, LocaleSwitcher, MixedContent, ModalTableSelectField, MoneyInputField, NotificationActionButton, NotificationsMenu, OneTimeCodeInputField, PageActionProvider, PageBlock, type PageBlockProps, PageContent, PageForm, PageHeader, PageInfolist, type PageMethodBinding, type PageMethodMap, type PageMethodsBinding, PageShell, PageTable, PageWidgets, PlaceholderComponent, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PwaManager, PwaMenuItems, QueryBuilderRulesField, RadioField, RadioGroup, RadioGroupItem, RealtimeNotifications, RecordCell, RecordPresence, RecordVersionsComponent, RenderComponent, RepeatableEntryComponent, RepeaterField, RichEditorField, RichTextBlock, SchemaChildren, SchemaRenderer, ScrollArea, ScrollBar, SectionBlock, SectionComponent, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, ShortcutKeys, ShortcutsOverlay, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignInLink, Skeleton, SliderField, Slot, Spinner, Spreadsheet, type StandaloneContextValue, StandaloneProvider, StarterKitContentLayout, type StarterKitLayout, type StarterKitPageOptions, StatsOverviewWidget, Switch, Table, TableBody, TableCaption, TableCell, TableCellContent, TableFilters, TableFooter, TableHead, TableHeader, TablePagination, TableRow, TableViews, Tabs, TabsComponent, TabsContent, TabsList, TabsTrigger, TagsInputField, TenantMenu, TestimonialsBlock, TextComponent, TextEntryComponent, TextInputField, Textarea, TextareaField, ThemeToggle, Toaster, ToggleButtonsField, ToggleField, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopNavLayout, Topbar, TopbarLocaleSwitcher, TranslationManager, Tree, UndoButton, UnorderedListComponent, type UsePageMethodOptions, UserAvatar, UserMenu, ViewComponent, ViewEntryComponent, type VirtualListBinding, WidgetGrid, WidgetRenderer, WizardComponent, badgeColorClasses, badgeVariants, buttonVariants, cn, colorClass, configureAlfakit, createAlfakit, createDefaultRegistry, createStarterKitPage, cssColor, discoverComponents, discoveredComponent, extraProps, ghostButtonColorClasses, iconBackgroundColorClasses, iconSizeClasses, installPwa, outlineButtonColorClasses, panelLayouts, parseStyle, showToast, solidButtonColorClasses, switchOffColorClasses, switchOnColorClasses, textColorClasses, useActionRunner, useAlfakit, useAlfakitNavigation, useAlfakitProps, useAppPage, useChoice, useCms, useDirection, useField, useFormState, useFormStore, useFormatter, useHydrated, useIsMobile, useLiveProps, useLiveUpdates, useLocalization, useOptionalAlfakit, useOptionalFormStore, usePageActionContext, usePageBlock, usePageForm, usePageMethod, usePageMethods, usePageSchema, usePanel, usePollComponent, useRegistry, useReloadProps, useSchemaComponentContext, useScrollToErroredItem, useShortcut, useSidebar, useStandalone, useStore, useStoreSelector, useT, useVirtualList };
|
|
183
|
+
export { ActionButton, ActionGroupMenu, ActionList, ActionModals, AiCopilot, AiStream, Alert, AlertDescription, AlertTitle, AlfakitAnalytics, AlfakitComponent, type AlfakitComponentProps, type AlfakitContextValue, AlfakitCopilot, type AlfakitHeaderWidgetProps, AlfakitLanguageSwitcher, type AlfakitNavigation, AlfakitNotifications, AlfakitPage, AlfakitProvider, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitToaster, type AlfakitToasterProps, AlfakitWidgets, type AlfakitWidgetsProps, AnalyticsBreakdown, type AnyComponent, AppLayout, AppPage, AppSidebar, AuthLayout, Avatar, AvatarFallback, AvatarImage, Badge, BlankLayout, BottomNav, Brand, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Breadcrumbs, BuilderField, Button, CMS_BLOCK_COMPONENTS, Calendar, CalendarWidget, CallToActionBlock, CalloutComponent, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChartWidget, Checkbox, CheckboxField, CheckboxListField, CmsBlockHeading, type CmsBlockProps, CmsBlocks, type CmsContextValue, CmsFooter, CmsHead, CmsHeader, CmsImage, CmsLanguageSwitcher, CmsLinkButton, CmsLivePreview, CmsMenuList, CmsNav, CmsPage, CmsProvider, CodeEditorField, CodeEntryComponent, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorEntryComponent, ColorPickerField, ColumnManager, ColumnsBlock, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConflictBanner, ContentBlockRenderer, ContentLayout, ContentLayoutSort, type CreateAlfakitOptions, CustomizableWidgetGrid, DataTable, DatePickerField, DebugPanel, DebugPanelSlot, DebugTreeNode, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type DiscoveredComponents, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmbedBlock, EmptyStateComponent, EntryPlaceholder, EntryWrapper, FaqBlock, FeaturesBlock, type FieldBinding, FieldWrapper, FieldsetComponent, FileManager, FileManagerPickerField, FileUploadField, FilterIndicators, FlashToasts, FlexComponent, FormBlock, FormProvider, FormattedText, FullscreenLayout, type FullscreenLayoutProps, FusedGroupComponent, GalleryBlock, GlobalSearch, GridComponent, HelpdeskChat, type HelpdeskChatProps, HelpdeskInbox, HeroBlock, HiddenField, HtmlComponent, Icon, IconComponent, IconEntryComponent, IconPickerField, ImageBlock, ImageComponent, ImageEntryComponent, ImpersonationBanner, Input, Kanban, KeyValueEntryComponent, KeyValueField, KeyboardShortcuts, Label, LazyPlaceholder, LiveRecordsPill, LocaleSubMenu, LocaleSwitcher, MixedContent, ModalTableSelectField, MoneyInputField, NotificationActionButton, NotificationsMenu, OneTimeCodeInputField, PageActionProvider, PageBlock, type PageBlockProps, PageContent, PageForm, PageHeader, PageInfolist, type PageMethodBinding, type PageMethodMap, type PageMethodsBinding, PageShell, PageTable, PageWidgets, PlaceholderComponent, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PwaManager, PwaMenuItems, QueryBuilderRulesField, RadioField, RadioGroup, RadioGroupItem, RealtimeNotifications, RecordCell, RecordPresence, RecordVersionsComponent, RenderComponent, RepeatableEntryComponent, RepeaterField, RichEditorField, RichTextBlock, SchemaChildren, SchemaRenderer, ScrollArea, ScrollBar, SectionBlock, SectionComponent, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, ShortcutKeys, ShortcutsOverlay, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignInLink, Skeleton, SliderField, Slot, Spinner, Spreadsheet, type StandaloneContextValue, StandaloneProvider, StarterKitContentLayout, type StarterKitLayout, type StarterKitPageOptions, StatsOverviewWidget, Switch, Table, TableBody, TableCaption, TableCell, TableCellContent, TableFilters, TableFooter, TableHead, TableHeader, TablePagination, TableRow, TableViews, Tabs, TabsComponent, TabsContent, TabsList, TabsTrigger, TagsInputField, TenantMenu, TestimonialsBlock, TextComponent, TextEntryComponent, TextInputField, Textarea, TextareaField, ThemeToggle, Toaster, ToggleButtonsField, ToggleField, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopNavLayout, Topbar, TopbarLocaleSwitcher, TranslationManager, Tree, UndoButton, UnorderedListComponent, type UsePageMethodOptions, UserAvatar, UserMenu, ViewComponent, ViewEntryComponent, type VirtualListBinding, WidgetGrid, WidgetRenderer, WizardComponent, badgeColorClasses, badgeVariants, buttonVariants, cn, colorClass, configureAlfakit, createAlfakit, createDefaultRegistry, createStarterKitPage, cssColor, discoverComponents, discoveredComponent, extraProps, ghostButtonColorClasses, iconBackgroundColorClasses, iconSizeClasses, installPwa, outlineButtonColorClasses, panelLayouts, parseStyle, showToast, solidButtonColorClasses, switchOffColorClasses, switchOnColorClasses, textColorClasses, useActionRunner, useAlfakit, useAlfakitNavigation, useAlfakitProps, useAppPage, useChoice, useCms, useDirection, useField, useFormState, useFormStore, useFormatter, useHydrated, useIsMobile, useLiveProps, useLiveUpdates, useLocalization, useOptionalAlfakit, useOptionalFormStore, usePageActionContext, usePageBlock, usePageForm, usePageMethod, usePageMethods, usePageSchema, usePanel, usePollComponent, useRegistry, useReloadProps, useSchemaComponentContext, useScrollToErroredItem, useShortcut, useSidebar, useStandalone, useStore, useStoreSelector, useT, useVirtualList };
|
package/dist/index.js
CHANGED
|
@@ -157,8 +157,8 @@ import { AlfakitComponent } from "./standalone/alfakit-component.js";
|
|
|
157
157
|
import { AlfakitWidgets } from "./starter-kit/dashboard-widgets.js";
|
|
158
158
|
import { useAlfakitNavigation } from "./starter-kit/navigation.js";
|
|
159
159
|
import { AlfakitStarterKitRoot, StarterKitContentLayout, createStarterKitPage } from "./starter-kit/starter-kit.js";
|
|
160
|
-
import { AlfakitCopilot, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./starter-kit/widgets.js";
|
|
160
|
+
import { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./starter-kit/widgets.js";
|
|
161
161
|
import { Alert, AlertDescription, AlertTitle } from "./components/ui/alert.js";
|
|
162
162
|
import { Calendar } from "./components/ui/calendar.js";
|
|
163
163
|
export * from "@alfakit/core";
|
|
164
|
-
export { ActionButton, ActionGroupMenu, ActionList, ActionModals, AiCopilot, AiStream, Alert, AlertDescription, AlertTitle, AlfakitAnalytics, AlfakitComponent, AlfakitCopilot, AlfakitNotifications, AlfakitPage, AlfakitProvider, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitToaster, AlfakitWidgets, AnalyticsBreakdown, AppLayout, AppPage, AppSidebar, AuthLayout, Avatar, AvatarFallback, AvatarImage, Badge, BlankLayout, BottomNav, Brand, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Breadcrumbs, BuilderField, Button, CMS_BLOCK_COMPONENTS, Calendar, CalendarWidget, CallToActionBlock, CalloutComponent, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChartWidget, Checkbox, CheckboxField, CheckboxListField, CmsBlockHeading, CmsBlocks, CmsFooter, CmsHead, CmsHeader, CmsImage, CmsLanguageSwitcher, CmsLinkButton, CmsLivePreview, CmsMenuList, CmsNav, CmsPage, CmsProvider, CodeEditorField, CodeEntryComponent, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorEntryComponent, ColorPickerField, ColumnManager, ColumnsBlock, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConflictBanner, ContentBlockRenderer, ContentLayout, ContentLayoutSort, CustomizableWidgetGrid, DataTable, DatePickerField, DebugPanel, DebugPanelSlot, DebugTreeNode, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmbedBlock, EmptyStateComponent, EntryPlaceholder, EntryWrapper, FaqBlock, FeaturesBlock, FieldWrapper, FieldsetComponent, FileManager, FileManagerPickerField, FileUploadField, FilterIndicators, FlashToasts, FlexComponent, FormBlock, FormProvider, FormattedText, FullscreenLayout, FusedGroupComponent, GalleryBlock, GlobalSearch, GridComponent, HelpdeskChat, HelpdeskInbox, HeroBlock, HiddenField, HtmlComponent, Icon, IconComponent, IconEntryComponent, IconPickerField, ImageBlock, ImageComponent, ImageEntryComponent, ImpersonationBanner, Input, Kanban, KeyValueEntryComponent, KeyValueField, KeyboardShortcuts, Label, LazyPlaceholder, LiveRecordsPill, LocaleSubMenu, LocaleSwitcher, MixedContent, ModalTableSelectField, MoneyInputField, NotificationActionButton, NotificationsMenu, OneTimeCodeInputField, PageActionProvider, PageBlock, PageContent, PageForm, PageHeader, PageInfolist, PageShell, PageTable, PageWidgets, PlaceholderComponent, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PwaManager, PwaMenuItems, QueryBuilderRulesField, RadioField, RadioGroup, RadioGroupItem, RealtimeNotifications, RecordCell, RecordPresence, RecordVersionsComponent, RenderComponent, RepeatableEntryComponent, RepeaterField, RichEditorField, RichTextBlock, SchemaChildren, SchemaRenderer, ScrollArea, ScrollBar, SectionBlock, SectionComponent, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, ShortcutKeys, ShortcutsOverlay, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignInLink, Skeleton, SliderField, Slot, Spinner, Spreadsheet, StandaloneProvider, StarterKitContentLayout, StatsOverviewWidget, Switch, Table, TableBody, TableCaption, TableCell, TableCellContent, TableFilters, TableFooter, TableHead, TableHeader, TablePagination, TableRow, TableViews, Tabs, TabsComponent, TabsContent, TabsList, TabsTrigger, TagsInputField, TenantMenu, TestimonialsBlock, TextComponent, TextEntryComponent, TextInputField, Textarea, TextareaField, ThemeToggle, Toaster, ToggleButtonsField, ToggleField, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopNavLayout, Topbar, TopbarLocaleSwitcher, TranslationManager, Tree, UndoButton, UnorderedListComponent, UserAvatar, UserMenu, ViewComponent, ViewEntryComponent, WidgetGrid, WidgetRenderer, WizardComponent, badgeColorClasses, badgeVariants, buttonVariants, cn, colorClass, configureAlfakit, createAlfakit, createDefaultRegistry, createStarterKitPage, cssColor, discoverComponents, discoveredComponent, extraProps, ghostButtonColorClasses, iconBackgroundColorClasses, iconSizeClasses, installPwa, outlineButtonColorClasses, panelLayouts, parseStyle, showToast, solidButtonColorClasses, switchOffColorClasses, switchOnColorClasses, textColorClasses, useActionRunner, useAlfakit, useAlfakitNavigation, useAlfakitProps, useAppPage, useChoice, useCms, useDirection, useField, useFormState, useFormStore, useFormatter, useHydrated, useIsMobile, useLiveProps, useLiveUpdates, useLocalization, useOptionalAlfakit, useOptionalFormStore, usePageActionContext, usePageBlock, usePageForm, usePageMethod, usePageMethods, usePageSchema, usePanel, usePollComponent, useRegistry, useReloadProps, useSchemaComponentContext, useScrollToErroredItem, useShortcut, useSidebar, useStandalone, useStore, useStoreSelector, useT, useVirtualList };
|
|
164
|
+
export { ActionButton, ActionGroupMenu, ActionList, ActionModals, AiCopilot, AiStream, Alert, AlertDescription, AlertTitle, AlfakitAnalytics, AlfakitComponent, AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitPage, AlfakitProvider, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitToaster, AlfakitWidgets, AnalyticsBreakdown, AppLayout, AppPage, AppSidebar, AuthLayout, Avatar, AvatarFallback, AvatarImage, Badge, BlankLayout, BottomNav, Brand, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Breadcrumbs, BuilderField, Button, CMS_BLOCK_COMPONENTS, Calendar, CalendarWidget, CallToActionBlock, CalloutComponent, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChartWidget, Checkbox, CheckboxField, CheckboxListField, CmsBlockHeading, CmsBlocks, CmsFooter, CmsHead, CmsHeader, CmsImage, CmsLanguageSwitcher, CmsLinkButton, CmsLivePreview, CmsMenuList, CmsNav, CmsPage, CmsProvider, CodeEditorField, CodeEntryComponent, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorEntryComponent, ColorPickerField, ColumnManager, ColumnsBlock, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConflictBanner, ContentBlockRenderer, ContentLayout, ContentLayoutSort, CustomizableWidgetGrid, DataTable, DatePickerField, DebugPanel, DebugPanelSlot, DebugTreeNode, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmbedBlock, EmptyStateComponent, EntryPlaceholder, EntryWrapper, FaqBlock, FeaturesBlock, FieldWrapper, FieldsetComponent, FileManager, FileManagerPickerField, FileUploadField, FilterIndicators, FlashToasts, FlexComponent, FormBlock, FormProvider, FormattedText, FullscreenLayout, FusedGroupComponent, GalleryBlock, GlobalSearch, GridComponent, HelpdeskChat, HelpdeskInbox, HeroBlock, HiddenField, HtmlComponent, Icon, IconComponent, IconEntryComponent, IconPickerField, ImageBlock, ImageComponent, ImageEntryComponent, ImpersonationBanner, Input, Kanban, KeyValueEntryComponent, KeyValueField, KeyboardShortcuts, Label, LazyPlaceholder, LiveRecordsPill, LocaleSubMenu, LocaleSwitcher, MixedContent, ModalTableSelectField, MoneyInputField, NotificationActionButton, NotificationsMenu, OneTimeCodeInputField, PageActionProvider, PageBlock, PageContent, PageForm, PageHeader, PageInfolist, PageShell, PageTable, PageWidgets, PlaceholderComponent, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PwaManager, PwaMenuItems, QueryBuilderRulesField, RadioField, RadioGroup, RadioGroupItem, RealtimeNotifications, RecordCell, RecordPresence, RecordVersionsComponent, RenderComponent, RepeatableEntryComponent, RepeaterField, RichEditorField, RichTextBlock, SchemaChildren, SchemaRenderer, ScrollArea, ScrollBar, SectionBlock, SectionComponent, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, ShortcutKeys, ShortcutsOverlay, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignInLink, Skeleton, SliderField, Slot, Spinner, Spreadsheet, StandaloneProvider, StarterKitContentLayout, StatsOverviewWidget, Switch, Table, TableBody, TableCaption, TableCell, TableCellContent, TableFilters, TableFooter, TableHead, TableHeader, TablePagination, TableRow, TableViews, Tabs, TabsComponent, TabsContent, TabsList, TabsTrigger, TagsInputField, TenantMenu, TestimonialsBlock, TextComponent, TextEntryComponent, TextInputField, Textarea, TextareaField, ThemeToggle, Toaster, ToggleButtonsField, ToggleField, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopNavLayout, Topbar, TopbarLocaleSwitcher, TranslationManager, Tree, UndoButton, UnorderedListComponent, UserAvatar, UserMenu, ViewComponent, ViewEntryComponent, WidgetGrid, WidgetRenderer, WizardComponent, badgeColorClasses, badgeVariants, buttonVariants, cn, colorClass, configureAlfakit, createAlfakit, createDefaultRegistry, createStarterKitPage, cssColor, discoverComponents, discoveredComponent, extraProps, ghostButtonColorClasses, iconBackgroundColorClasses, iconSizeClasses, installPwa, outlineButtonColorClasses, panelLayouts, parseStyle, showToast, solidButtonColorClasses, switchOffColorClasses, switchOnColorClasses, textColorClasses, useActionRunner, useAlfakit, useAlfakitNavigation, useAlfakitProps, useAppPage, useChoice, useCms, useDirection, useField, useFormState, useFormStore, useFormatter, useHydrated, useIsMobile, useLiveProps, useLiveUpdates, useLocalization, useOptionalAlfakit, useOptionalFormStore, usePageActionContext, usePageBlock, usePageForm, usePageMethod, usePageMethods, usePageSchema, usePanel, usePollComponent, useRegistry, useReloadProps, useSchemaComponentContext, useScrollToErroredItem, useShortcut, useSidebar, useStandalone, useStore, useStoreSelector, useT, useVirtualList };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AlfakitWidgets, AlfakitWidgetsProps } from "./dashboard-widgets.js";
|
|
2
2
|
import { AlfakitNavigation, useAlfakitNavigation } from "./navigation.js";
|
|
3
3
|
import { AlfakitStarterKitRoot, StarterKitContentLayout, StarterKitLayout, StarterKitPageOptions, createStarterKitPage } from "./starter-kit.js";
|
|
4
|
-
import { AlfakitCopilot, AlfakitHeaderWidgetProps, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./widgets.js";
|
|
5
|
-
export { AlfakitCopilot, type AlfakitHeaderWidgetProps, type AlfakitNavigation, AlfakitNotifications, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitWidgets, type AlfakitWidgetsProps, StarterKitContentLayout, type StarterKitLayout, type StarterKitPageOptions, createStarterKitPage, useAlfakitNavigation };
|
|
4
|
+
import { AlfakitCopilot, AlfakitHeaderWidgetProps, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./widgets.js";
|
|
5
|
+
export { AlfakitCopilot, type AlfakitHeaderWidgetProps, AlfakitLanguageSwitcher, type AlfakitNavigation, AlfakitNotifications, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitWidgets, type AlfakitWidgetsProps, StarterKitContentLayout, type StarterKitLayout, type StarterKitPageOptions, createStarterKitPage, useAlfakitNavigation };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AlfakitWidgets } from "./dashboard-widgets.js";
|
|
2
2
|
import { useAlfakitNavigation } from "./navigation.js";
|
|
3
3
|
import { AlfakitStarterKitRoot, StarterKitContentLayout, createStarterKitPage } from "./starter-kit.js";
|
|
4
|
-
import { AlfakitCopilot, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./widgets.js";
|
|
5
|
-
export { AlfakitCopilot, AlfakitNotifications, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitWidgets, StarterKitContentLayout, createStarterKitPage, useAlfakitNavigation };
|
|
4
|
+
import { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher } from "./widgets.js";
|
|
5
|
+
export { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitStarterKitRoot, AlfakitTenantSwitcher, AlfakitWidgets, StarterKitContentLayout, createStarterKitPage, useAlfakitNavigation };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlfakitProvider, StandaloneProvider, useT } from "../context.js";
|
|
1
|
+
import { AlfakitProvider, StandaloneProvider, useLocalization, useT } from "../context.js";
|
|
2
2
|
import { Icon } from "../lib/icons.js";
|
|
3
3
|
import { DropdownMenu as DropdownMenu$1, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "../components/ui/dropdown-menu.js";
|
|
4
4
|
import { TooltipProvider } from "../components/ui/tooltip.js";
|
|
@@ -9,18 +9,19 @@ import { NotificationsMenu } from "../components/notifications/notifications-men
|
|
|
9
9
|
import { RealtimeNotifications } from "../components/notifications/realtime-notifications.js";
|
|
10
10
|
import { GlobalSearch } from "../components/panel/global-search.js";
|
|
11
11
|
import { TenantMenu } from "../components/panel/tenant-menu.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { LocaleOptions } from "../components/panel/locale-switcher.js";
|
|
13
|
+
import { CopilotTrigger, LanguageSwitcherTrigger, NotificationsTrigger, SearchTrigger, TenantSwitcherTrigger, languageTriggerClasses } from "./widgets-triggers.js";
|
|
14
|
+
import { ActionRunner, ComponentRegistry, Translator, initials, isAlfakitPage, loadStarterKitPanel, loadedStarterKitPanel, openRequestedStarterKitSearch, showsLanguageSwitcher, starterKitCopilot, starterKitPanelLocale, starterKitWidgetsPage, starterKitWidgetsPanel, takeRequestedStarterKitCopilot, takeRequestedStarterKitLanguages, tenantNameText } from "@alfakit/core";
|
|
14
15
|
import { router, usePage } from "@inertiajs/react";
|
|
15
|
-
import { useEffect, useMemo, useState } from "react";
|
|
16
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
16
17
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
18
|
import { Direction } from "radix-ui";
|
|
18
|
-
import { ChevronsUpDownIcon, PlusIcon } from "lucide-react";
|
|
19
|
+
import { ChevronsUpDownIcon, LanguagesIcon, PlusIcon } from "lucide-react";
|
|
19
20
|
//#region src/starter-kit/widgets-loaded.tsx
|
|
20
21
|
/**
|
|
21
22
|
* The loaded header components of starter kit mode: the panel's own search palette,
|
|
22
|
-
* notifications menu and
|
|
23
|
-
* from `widgets.endpoint`. This module only loads in the browser.
|
|
23
|
+
* notifications menu, tenant menu and language menu, rendered outside Alfakit pages with
|
|
24
|
+
* the panel data from `widgets.endpoint`. This module only loads in the browser.
|
|
24
25
|
*/
|
|
25
26
|
let widgetsContext = null;
|
|
26
27
|
function useWidgetsContext(panel) {
|
|
@@ -37,18 +38,24 @@ function useWidgetsContext(panel) {
|
|
|
37
38
|
widgetsContext.translator.setLocale(panel.localization?.tag ?? "en");
|
|
38
39
|
return widgetsContext;
|
|
39
40
|
}
|
|
40
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The panel the components render with, once its data has loaded (null until then). It is
|
|
43
|
+
* loaded per endpoint (a tenant switch) and language (a language switch), so the components
|
|
44
|
+
* follow a switch instead of keeping the old language's strings.
|
|
45
|
+
*/
|
|
41
46
|
function useWidgetsPanel(source) {
|
|
42
47
|
const endpoint = source.widgets.endpoint;
|
|
43
|
-
const
|
|
44
|
-
const
|
|
48
|
+
const locale = starterKitPanelLocale(source);
|
|
49
|
+
const [loaded, setLoaded] = useState(() => loadedStarterKitPanel(endpoint, locale));
|
|
50
|
+
const current = loaded && (locale === void 0 || loaded.localization?.code === locale) ? loaded : null;
|
|
51
|
+
const base = loadedStarterKitPanel(endpoint, locale) ?? current;
|
|
45
52
|
useEffect(() => {
|
|
46
53
|
let active = true;
|
|
47
|
-
loadStarterKitPanel(endpoint).then((panel) => active && setLoaded(panel), () => {});
|
|
54
|
+
loadStarterKitPanel(endpoint, locale).then((panel) => active && setLoaded(panel), () => {});
|
|
48
55
|
return () => {
|
|
49
56
|
active = false;
|
|
50
57
|
};
|
|
51
|
-
}, [endpoint]);
|
|
58
|
+
}, [endpoint, locale]);
|
|
52
59
|
const { navigation, widgets } = source;
|
|
53
60
|
return useMemo(() => base ? starterKitWidgetsPanel(base, {
|
|
54
61
|
navigation,
|
|
@@ -125,6 +132,60 @@ function LoadedCopilot({ source }) {
|
|
|
125
132
|
})
|
|
126
133
|
});
|
|
127
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The panel's language menu (its languages in their own names, `LocaleOptions`): picking
|
|
137
|
+
* one posts to the panel's locale endpoint and reloads the page in it. Nothing without a
|
|
138
|
+
* language switcher with two or more languages.
|
|
139
|
+
*/
|
|
140
|
+
function LoadedLanguageSwitcher({ source }) {
|
|
141
|
+
const panel = useWidgetsPanel(source);
|
|
142
|
+
if (!panel) return /* @__PURE__ */ jsx(LanguageSwitcherTrigger, { source });
|
|
143
|
+
if (!showsLanguageSwitcher(panel.localization, "auth")) return null;
|
|
144
|
+
return /* @__PURE__ */ jsx(WidgetShell, {
|
|
145
|
+
panel,
|
|
146
|
+
children: /* @__PURE__ */ jsx(LanguageMenu, {})
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function LanguageMenu() {
|
|
150
|
+
const t = useT();
|
|
151
|
+
const localization = useLocalization();
|
|
152
|
+
const [open, setOpen] = useState(false);
|
|
153
|
+
const requested = useRef(null);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
requested.current ??= takeRequestedStarterKitLanguages();
|
|
156
|
+
if (!requested.current) return;
|
|
157
|
+
const frame = requestAnimationFrame(() => setOpen(true));
|
|
158
|
+
return () => cancelAnimationFrame(frame);
|
|
159
|
+
}, []);
|
|
160
|
+
const label = t("change_language", {}, "Change language");
|
|
161
|
+
return /* @__PURE__ */ jsxs(DropdownMenu$1, {
|
|
162
|
+
open,
|
|
163
|
+
onOpenChange: setOpen,
|
|
164
|
+
children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
165
|
+
asChild: true,
|
|
166
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
167
|
+
type: "button",
|
|
168
|
+
"data-slot": "language-switcher-trigger",
|
|
169
|
+
"aria-label": label,
|
|
170
|
+
title: label,
|
|
171
|
+
className: languageTriggerClasses,
|
|
172
|
+
children: [/* @__PURE__ */ jsx(LanguagesIcon, {
|
|
173
|
+
className: "size-4",
|
|
174
|
+
"aria-hidden": "true"
|
|
175
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
176
|
+
lang: localization.tag,
|
|
177
|
+
dir: localization.direction,
|
|
178
|
+
className: "hidden sm:inline",
|
|
179
|
+
children: localization.label
|
|
180
|
+
})]
|
|
181
|
+
})
|
|
182
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
183
|
+
align: "end",
|
|
184
|
+
className: "min-w-40",
|
|
185
|
+
children: /* @__PURE__ */ jsx(LocaleOptions, {})
|
|
186
|
+
})]
|
|
187
|
+
});
|
|
188
|
+
}
|
|
128
189
|
/** The user's tenants on a page outside the panel (no current tenant). */
|
|
129
190
|
function TenantChooser({ panel }) {
|
|
130
191
|
const t = useT();
|
|
@@ -183,6 +244,6 @@ function TenantChooser({ panel }) {
|
|
|
183
244
|
})] });
|
|
184
245
|
}
|
|
185
246
|
//#endregion
|
|
186
|
-
export { LoadedCopilot, LoadedNotifications, LoadedSearch, LoadedTenantSwitcher };
|
|
247
|
+
export { LoadedCopilot, LoadedLanguageSwitcher, LoadedNotifications, LoadedSearch, LoadedTenantSwitcher };
|
|
187
248
|
|
|
188
249
|
//# sourceMappingURL=widgets-loaded.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets-loaded.js","names":["DropdownMenu","Avatar"],"sources":["../../src/starter-kit/widgets-loaded.tsx"],"sourcesContent":["import {\n ActionRunner,\n ComponentRegistry,\n Translator,\n initials,\n isAlfakitPage,\n loadStarterKitPanel,\n loadedStarterKitPanel,\n openRequestedStarterKitSearch,\n starterKitCopilot,\n starterKitWidgetsPage,\n starterKitWidgetsPanel,\n takeRequestedStarterKitCopilot,\n tenantNameText,\n type PanelSchema,\n type StarterKitWidgets,\n} from '@alfakit/core'\nimport { router, usePage } from '@inertiajs/react'\nimport { ChevronsUpDownIcon, PlusIcon } from 'lucide-react'\nimport { Direction } from 'radix-ui'\nimport { useEffect, useMemo, useState, type ReactNode } from 'react'\nimport { AiCopilot } from '../components/ai/copilot'\nimport { NotificationsMenu } from '../components/notifications/notifications-menu'\nimport { RealtimeNotifications } from '../components/notifications/realtime-notifications'\nimport { GlobalSearch } from '../components/panel/global-search'\nimport { TenantName } from '../components/panel/nav-label'\nimport { TenantMenu } from '../components/panel/tenant-menu'\nimport { Avatar, AvatarFallback, AvatarImage } from '../components/ui/avatar'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../components/ui/dropdown-menu'\nimport { TooltipProvider } from '../components/ui/tooltip'\nimport {\n AlfakitProvider,\n StandaloneProvider,\n useT,\n type AlfakitContextValue,\n type AnyComponent,\n} from '../context'\nimport { Icon } from '../lib/icons'\nimport {\n CopilotTrigger,\n NotificationsTrigger,\n SearchTrigger,\n TenantSwitcherTrigger,\n type WidgetProps,\n} from './widgets-triggers'\n\n/**\n * The loaded header components of starter kit mode: the panel's own search palette,\n * notifications menu and tenant menu, rendered outside Alfakit pages with the panel data\n * from `widgets.endpoint`. This module only loads in the browser.\n */\n\n// One context for the browser session: its own translator (the panel's strings), an empty\n// registry (only `panel:icon` overrides are looked up here) and an idle action runner.\nlet widgetsContext: AlfakitContextValue | null = null\n\nfunction useWidgetsContext(panel: PanelSchema): AlfakitContextValue {\n widgetsContext ??= {\n registry: new ComponentRegistry<AnyComponent>(),\n pages: {},\n translator: new Translator(),\n actions: new ActionRunner({ endpoints: { action: '', modal: '' } }),\n }\n\n widgetsContext.translator.setTranslations(panel.translations ?? {})\n widgetsContext.translator.setLocale(panel.localization?.tag ?? 'en')\n\n return widgetsContext\n}\n\n/** The panel the components render with, once its data has loaded (null until then). */\nfunction useWidgetsPanel(source: StarterKitWidgets): PanelSchema | null {\n const endpoint = source.widgets.endpoint\n const [loaded, setLoaded] = useState(() => loadedStarterKitPanel(endpoint))\n const base = loadedStarterKitPanel(endpoint) ?? loaded\n\n useEffect(() => {\n let active = true\n\n // Another endpoint (a tenant switch) loads its own panel data.\n void loadStarterKitPanel(endpoint).then(\n (panel) => active && setLoaded(panel),\n () => {},\n )\n\n return () => {\n active = false\n }\n }, [endpoint])\n\n const { navigation, widgets } = source\n\n return useMemo(\n () => (base ? starterKitWidgetsPanel(base, { navigation, widgets }) : null),\n [base, navigation, widgets],\n )\n}\n\nfunction WidgetShell({ panel, children }: { panel: PanelSchema; children: ReactNode }) {\n const context = useWidgetsContext(panel)\n const page = useMemo(() => starterKitWidgetsPage(), [])\n\n return (\n <AlfakitProvider value={context}>\n <StandaloneProvider value={{ panel, page }}>\n <Direction.Provider dir={panel.localization?.direction ?? 'ltr'}>\n <TooltipProvider delayDuration={0}>{children}</TooltipProvider>\n </Direction.Provider>\n </StandaloneProvider>\n </AlfakitProvider>\n )\n}\n\n/** The panel's command palette and global search. */\nexport function LoadedSearch({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n\n // A click or the key bindings before the palette loaded open it now (after its own\n // effects registered the shortcut).\n useEffect(() => {\n if (panel) openRequestedStarterKitSearch()\n }, [panel])\n\n if (!panel) return <SearchTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n <GlobalSearch />\n </WidgetShell>\n )\n}\n\n/** The notifications bell, with realtime notifications on the host's own pages. */\nexport function LoadedNotifications({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n // Alfakit pages subscribe to realtime notifications themselves.\n const alfakitPage = isAlfakitPage(usePage().props)\n\n if (!panel) return <NotificationsTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n <NotificationsMenu />\n {!alfakitPage && <RealtimeNotifications />}\n </WidgetShell>\n )\n}\n\n/** The panel's tenant menu inside a tenant, a menu to choose one elsewhere. */\nexport function LoadedTenantSwitcher({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n\n if (!panel) return <TenantSwitcherTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n {panel.tenancy?.current ? <TenantMenu variant=\"topbar\" /> : <TenantChooser panel={panel} />}\n </WidgetShell>\n )\n}\n\n/** The AI copilot (the panel pages' `topbar.end` slot component). */\nexport function LoadedCopilot({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n // A click or the key bindings before the copilot loaded open it at once.\n const [autoOpen] = useState(() => takeRequestedStarterKitCopilot())\n const component = useMemo(() => (panel ? starterKitCopilot(panel) : null), [panel])\n\n if (!panel) return <CopilotTrigger source={source} />\n\n if (!component) return null\n\n return (\n <WidgetShell panel={panel}>\n <AiCopilot component={component} autoOpen={autoOpen || takeRequestedStarterKitCopilot()} />\n </WidgetShell>\n )\n}\n\n/** The user's tenants on a page outside the panel (no current tenant). */\nfunction TenantChooser({ panel }: { panel: PanelSchema }) {\n const t = useT()\n const tenancy = panel.tenancy\n\n if (!tenancy || (tenancy.tenants.length === 0 && !tenancy.registrationUrl)) return null\n\n const label = t('starter_kit.choose_tenant', {}, 'Choose a tenant')\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"tenant-menu\"\n className=\"hover:bg-accent data-[state=open]:bg-accent inline-flex h-9 max-w-60 shrink-0 items-center gap-2 rounded-md px-2 text-sm font-medium\"\n >\n <span className=\"sr-only lg:not-sr-only lg:truncate\">{label}</span>\n <Icon\n name=\"alfakit::panels.tenant-menu.toggle-button\"\n fallback={ChevronsUpDownIcon}\n className=\"text-muted-foreground size-4 shrink-0\"\n />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"min-w-56 rounded-lg\" side=\"bottom\" align=\"end\" sideOffset={4}>\n {tenancy.tenants.length > 0 && (\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">{label}</DropdownMenuLabel>\n )}\n {tenancy.tenants.map((tenant) => (\n <DropdownMenuItem key={String(tenant.id)} onSelect={() => router.visit(tenant.url)}>\n <Avatar className=\"size-6 rounded-sm\">\n {tenant.avatar && <AvatarImage src={tenant.avatar} alt=\"\" />}\n <AvatarFallback className=\"rounded-sm text-[10px]\">\n {initials(tenantNameText(tenant))}\n </AvatarFallback>\n </Avatar>\n <TenantName tenant={tenant} className=\"flex-1 truncate\" />\n </DropdownMenuItem>\n ))}\n {tenancy.registrationUrl && (\n <>\n {tenancy.tenants.length > 0 && <DropdownMenuSeparator />}\n <DropdownMenuItem onSelect={() => router.visit(tenancy.registrationUrl!)}>\n <Icon name=\"alfakit::panels.tenant-menu.registration-button\" fallback={PlusIcon} />\n {tenancy.registrationLabel ?? t('tenancy.register', {}, 'New')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"widgets-loaded.js","names":["DropdownMenu","Avatar"],"sources":["../../src/starter-kit/widgets-loaded.tsx"],"sourcesContent":["import {\n ActionRunner,\n ComponentRegistry,\n Translator,\n initials,\n isAlfakitPage,\n loadStarterKitPanel,\n loadedStarterKitPanel,\n openRequestedStarterKitSearch,\n showsLanguageSwitcher,\n starterKitCopilot,\n starterKitPanelLocale,\n starterKitWidgetsPage,\n starterKitWidgetsPanel,\n takeRequestedStarterKitCopilot,\n takeRequestedStarterKitLanguages,\n tenantNameText,\n type PanelSchema,\n type StarterKitWidgets,\n} from '@alfakit/core'\nimport { router, usePage } from '@inertiajs/react'\nimport { ChevronsUpDownIcon, LanguagesIcon, PlusIcon } from 'lucide-react'\nimport { Direction } from 'radix-ui'\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { AiCopilot } from '../components/ai/copilot'\nimport { NotificationsMenu } from '../components/notifications/notifications-menu'\nimport { RealtimeNotifications } from '../components/notifications/realtime-notifications'\nimport { GlobalSearch } from '../components/panel/global-search'\nimport { LocaleOptions } from '../components/panel/locale-switcher'\nimport { TenantName } from '../components/panel/nav-label'\nimport { TenantMenu } from '../components/panel/tenant-menu'\nimport { Avatar, AvatarFallback, AvatarImage } from '../components/ui/avatar'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../components/ui/dropdown-menu'\nimport { TooltipProvider } from '../components/ui/tooltip'\nimport {\n AlfakitProvider,\n StandaloneProvider,\n useLocalization,\n useT,\n type AlfakitContextValue,\n type AnyComponent,\n} from '../context'\nimport { Icon } from '../lib/icons'\nimport {\n CopilotTrigger,\n LanguageSwitcherTrigger,\n NotificationsTrigger,\n SearchTrigger,\n TenantSwitcherTrigger,\n languageTriggerClasses,\n type WidgetProps,\n} from './widgets-triggers'\n\n/**\n * The loaded header components of starter kit mode: the panel's own search palette,\n * notifications menu, tenant menu and language menu, rendered outside Alfakit pages with\n * the panel data from `widgets.endpoint`. This module only loads in the browser.\n */\n\n// One context for the browser session: its own translator (the panel's strings), an empty\n// registry (only `panel:icon` overrides are looked up here) and an idle action runner.\nlet widgetsContext: AlfakitContextValue | null = null\n\nfunction useWidgetsContext(panel: PanelSchema): AlfakitContextValue {\n widgetsContext ??= {\n registry: new ComponentRegistry<AnyComponent>(),\n pages: {},\n translator: new Translator(),\n actions: new ActionRunner({ endpoints: { action: '', modal: '' } }),\n }\n\n widgetsContext.translator.setTranslations(panel.translations ?? {})\n widgetsContext.translator.setLocale(panel.localization?.tag ?? 'en')\n\n return widgetsContext\n}\n\n/**\n * The panel the components render with, once its data has loaded (null until then). It is\n * loaded per endpoint (a tenant switch) and language (a language switch), so the components\n * follow a switch instead of keeping the old language's strings.\n */\nfunction useWidgetsPanel(source: StarterKitWidgets): PanelSchema | null {\n const endpoint = source.widgets.endpoint\n const locale = starterKitPanelLocale(source)\n const [loaded, setLoaded] = useState(() => loadedStarterKitPanel(endpoint, locale))\n // Until the new language's data arrives, not the old language's.\n const current =\n loaded && (locale === undefined || loaded.localization?.code === locale) ? loaded : null\n const base = loadedStarterKitPanel(endpoint, locale) ?? current\n\n useEffect(() => {\n let active = true\n\n void loadStarterKitPanel(endpoint, locale).then(\n (panel) => active && setLoaded(panel),\n () => {},\n )\n\n return () => {\n active = false\n }\n }, [endpoint, locale])\n\n const { navigation, widgets } = source\n\n return useMemo(\n () => (base ? starterKitWidgetsPanel(base, { navigation, widgets }) : null),\n [base, navigation, widgets],\n )\n}\n\nfunction WidgetShell({ panel, children }: { panel: PanelSchema; children: ReactNode }) {\n const context = useWidgetsContext(panel)\n const page = useMemo(() => starterKitWidgetsPage(), [])\n\n return (\n <AlfakitProvider value={context}>\n <StandaloneProvider value={{ panel, page }}>\n <Direction.Provider dir={panel.localization?.direction ?? 'ltr'}>\n <TooltipProvider delayDuration={0}>{children}</TooltipProvider>\n </Direction.Provider>\n </StandaloneProvider>\n </AlfakitProvider>\n )\n}\n\n/** The panel's command palette and global search. */\nexport function LoadedSearch({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n\n // A click or the key bindings before the palette loaded open it now (after its own\n // effects registered the shortcut).\n useEffect(() => {\n if (panel) openRequestedStarterKitSearch()\n }, [panel])\n\n if (!panel) return <SearchTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n <GlobalSearch />\n </WidgetShell>\n )\n}\n\n/** The notifications bell, with realtime notifications on the host's own pages. */\nexport function LoadedNotifications({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n // Alfakit pages subscribe to realtime notifications themselves.\n const alfakitPage = isAlfakitPage(usePage().props)\n\n if (!panel) return <NotificationsTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n <NotificationsMenu />\n {!alfakitPage && <RealtimeNotifications />}\n </WidgetShell>\n )\n}\n\n/** The panel's tenant menu inside a tenant, a menu to choose one elsewhere. */\nexport function LoadedTenantSwitcher({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n\n if (!panel) return <TenantSwitcherTrigger source={source} />\n\n return (\n <WidgetShell panel={panel}>\n {panel.tenancy?.current ? <TenantMenu variant=\"topbar\" /> : <TenantChooser panel={panel} />}\n </WidgetShell>\n )\n}\n\n/** The AI copilot (the panel pages' `topbar.end` slot component). */\nexport function LoadedCopilot({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n // A click or the key bindings before the copilot loaded open it at once.\n const [autoOpen] = useState(() => takeRequestedStarterKitCopilot())\n const component = useMemo(() => (panel ? starterKitCopilot(panel) : null), [panel])\n\n if (!panel) return <CopilotTrigger source={source} />\n\n if (!component) return null\n\n return (\n <WidgetShell panel={panel}>\n <AiCopilot component={component} autoOpen={autoOpen || takeRequestedStarterKitCopilot()} />\n </WidgetShell>\n )\n}\n\n/**\n * The panel's language menu (its languages in their own names, `LocaleOptions`): picking\n * one posts to the panel's locale endpoint and reloads the page in it. Nothing without a\n * language switcher with two or more languages.\n */\nexport function LoadedLanguageSwitcher({ source }: WidgetProps) {\n const panel = useWidgetsPanel(source)\n\n if (!panel) return <LanguageSwitcherTrigger source={source} />\n\n if (!showsLanguageSwitcher(panel.localization, 'auth')) return null\n\n return (\n <WidgetShell panel={panel}>\n <LanguageMenu />\n </WidgetShell>\n )\n}\n\nfunction LanguageMenu() {\n const t = useT()\n const localization = useLocalization()\n const [open, setOpen] = useState(false)\n // Taken once per mount (effects run twice in StrictMode).\n const requested = useRef<boolean | null>(null)\n\n // A click on the trigger before the menu loaded opens it once mounted: a frame later, as\n // the menu closes again when it opens while focus is still on the replaced button.\n useEffect(() => {\n requested.current ??= takeRequestedStarterKitLanguages()\n\n if (!requested.current) return\n\n const frame = requestAnimationFrame(() => setOpen(true))\n\n return () => cancelAnimationFrame(frame)\n }, [])\n const label = t('change_language', {}, 'Change language')\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"language-switcher-trigger\"\n aria-label={label}\n title={label}\n className={languageTriggerClasses}\n >\n <LanguagesIcon className=\"size-4\" aria-hidden=\"true\" />\n <span lang={localization.tag} dir={localization.direction} className=\"hidden sm:inline\">\n {localization.label}\n </span>\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-40\">\n <LocaleOptions />\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\n/** The user's tenants on a page outside the panel (no current tenant). */\nfunction TenantChooser({ panel }: { panel: PanelSchema }) {\n const t = useT()\n const tenancy = panel.tenancy\n\n if (!tenancy || (tenancy.tenants.length === 0 && !tenancy.registrationUrl)) return null\n\n const label = t('starter_kit.choose_tenant', {}, 'Choose a tenant')\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n data-slot=\"tenant-menu\"\n className=\"hover:bg-accent data-[state=open]:bg-accent inline-flex h-9 max-w-60 shrink-0 items-center gap-2 rounded-md px-2 text-sm font-medium\"\n >\n <span className=\"sr-only lg:not-sr-only lg:truncate\">{label}</span>\n <Icon\n name=\"alfakit::panels.tenant-menu.toggle-button\"\n fallback={ChevronsUpDownIcon}\n className=\"text-muted-foreground size-4 shrink-0\"\n />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"min-w-56 rounded-lg\" side=\"bottom\" align=\"end\" sideOffset={4}>\n {tenancy.tenants.length > 0 && (\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">{label}</DropdownMenuLabel>\n )}\n {tenancy.tenants.map((tenant) => (\n <DropdownMenuItem key={String(tenant.id)} onSelect={() => router.visit(tenant.url)}>\n <Avatar className=\"size-6 rounded-sm\">\n {tenant.avatar && <AvatarImage src={tenant.avatar} alt=\"\" />}\n <AvatarFallback className=\"rounded-sm text-[10px]\">\n {initials(tenantNameText(tenant))}\n </AvatarFallback>\n </Avatar>\n <TenantName tenant={tenant} className=\"flex-1 truncate\" />\n </DropdownMenuItem>\n ))}\n {tenancy.registrationUrl && (\n <>\n {tenancy.tenants.length > 0 && <DropdownMenuSeparator />}\n <DropdownMenuItem onSelect={() => router.visit(tenancy.registrationUrl!)}>\n <Icon name=\"alfakit::panels.tenant-menu.registration-button\" fallback={PlusIcon} />\n {tenancy.registrationLabel ?? t('tenancy.register', {}, 'New')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useHydrated } from "../hooks/use-hydrated.js";
|
|
2
|
-
import { formatKeyBinding, initials, registerStarterKitCopilotShortcut, registerStarterKitSearchShortcut, requestStarterKitCopilot, requestStarterKitSearch, tenantNameText } from "@alfakit/core";
|
|
2
|
+
import { formatKeyBinding, initials, registerStarterKitCopilotShortcut, registerStarterKitSearchShortcut, requestStarterKitCopilot, requestStarterKitLanguages, requestStarterKitSearch, tenantNameText } from "@alfakit/core";
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import { BellIcon, ChevronsUpDownIcon, SearchIcon, SparklesIcon } from "lucide-react";
|
|
5
|
+
import { BellIcon, ChevronsUpDownIcon, LanguagesIcon, SearchIcon, SparklesIcon } from "lucide-react";
|
|
6
6
|
//#region src/starter-kit/widgets-triggers.tsx
|
|
7
7
|
const triggerClasses = "border-input bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 inline-flex h-8 shrink-0 items-center rounded-md border text-sm shadow-xs outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50";
|
|
8
8
|
/** The search field until the palette has loaded: the same look, and it remembers a click. */
|
|
@@ -116,7 +116,38 @@ function CopilotTrigger({ source, onLoad }) {
|
|
|
116
116
|
})
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
|
+
/** The language button's look, before and after its menu has loaded. */
|
|
120
|
+
const languageTriggerClasses = "hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1.5 rounded-md px-2 text-sm font-medium outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50";
|
|
121
|
+
/**
|
|
122
|
+
* The language button until its menu has loaded: the languages icon and the current
|
|
123
|
+
* language in its own name (from `sm`). A click is remembered and opens the menu once loaded.
|
|
124
|
+
*/
|
|
125
|
+
function LanguageSwitcherTrigger({ source, onLoad }) {
|
|
126
|
+
const language = source.widgets.language;
|
|
127
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
128
|
+
type: "button",
|
|
129
|
+
"data-slot": "language-switcher-trigger",
|
|
130
|
+
"aria-label": language?.label,
|
|
131
|
+
"aria-haspopup": "menu",
|
|
132
|
+
title: language?.label,
|
|
133
|
+
className: languageTriggerClasses,
|
|
134
|
+
onPointerEnter: () => onLoad?.(),
|
|
135
|
+
onClick: () => {
|
|
136
|
+
requestStarterKitLanguages();
|
|
137
|
+
onLoad?.();
|
|
138
|
+
},
|
|
139
|
+
children: [/* @__PURE__ */ jsx(LanguagesIcon, {
|
|
140
|
+
className: "size-4",
|
|
141
|
+
"aria-hidden": "true"
|
|
142
|
+
}), language && /* @__PURE__ */ jsx("span", {
|
|
143
|
+
lang: language.current.tag,
|
|
144
|
+
dir: language.current.direction,
|
|
145
|
+
className: "hidden sm:inline",
|
|
146
|
+
children: language.current.label
|
|
147
|
+
})]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
119
150
|
//#endregion
|
|
120
|
-
export { CopilotTrigger, NotificationsTrigger, SearchTrigger, TenantSwitcherTrigger };
|
|
151
|
+
export { CopilotTrigger, LanguageSwitcherTrigger, NotificationsTrigger, SearchTrigger, TenantSwitcherTrigger, languageTriggerClasses };
|
|
121
152
|
|
|
122
153
|
//# sourceMappingURL=widgets-triggers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets-triggers.js","names":[],"sources":["../../src/starter-kit/widgets-triggers.tsx"],"sourcesContent":["import {\n formatKeyBinding,\n initials,\n registerStarterKitCopilotShortcut,\n registerStarterKitSearchShortcut,\n requestStarterKitCopilot,\n requestStarterKitSearch,\n tenantNameText,\n type StarterKitWidgets,\n} from '@alfakit/core'\nimport { BellIcon, ChevronsUpDownIcon, SearchIcon, SparklesIcon } from 'lucide-react'\nimport { useEffect } from 'react'\nimport { useHydrated } from '../hooks/use-hydrated'\n\n/**\n * What the starter kit header components show until their code and the panel data have\n * loaded: the same look as the loaded triggers, from the shared prop alone.\n */\nexport interface WidgetProps {\n source: StarterKitWidgets\n}\n\nconst triggerClasses =\n 'border-input bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 inline-flex h-8 shrink-0 items-center rounded-md border text-sm shadow-xs outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50'\n\n/** The search field until the palette has loaded: the same look, and it remembers a click. */\nexport function SearchTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {\n const hydrated = useHydrated()\n const search = source.widgets.search\n const keys = JSON.stringify(search?.keyBindings ?? ['mod+k'])\n const placeholder = search?.placeholder ?? ''\n // `onLoad` is a stable function (it starts loading the palette's code).\n useEffect(\n () =>\n registerStarterKitSearchShortcut(JSON.parse(keys) as string[], placeholder, () => onLoad?.()),\n [keys, placeholder, onLoad],\n )\n\n const binding = search?.keyBindings[0]\n\n return (\n <button\n type=\"button\"\n className={`${triggerClasses} text-muted-foreground size-8 justify-center gap-2 px-0 font-normal sm:w-56 sm:justify-start sm:px-2.5`}\n onPointerEnter={() => onLoad?.()}\n onClick={() => {\n requestStarterKitSearch()\n onLoad?.()\n }}\n >\n <SearchIcon className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"sr-only sm:not-sr-only sm:flex-1 sm:text-start\">{placeholder}</span>\n {hydrated && binding && (\n <kbd dir=\"ltr\" className=\"bg-muted hidden rounded px-1 font-mono text-[10px] sm:inline\">\n {formatKeyBinding(binding)}\n </kbd>\n )}\n </button>\n )\n}\n\n/** The bell and its unread count until the menu has loaded. */\nexport function NotificationsTrigger({ source }: WidgetProps) {\n const notifications = source.widgets.notifications\n const count = notifications?.unreadCount ?? 0\n\n return (\n <button\n type=\"button\"\n data-slot=\"notifications-trigger\"\n aria-label={notifications?.label}\n className=\"hover:bg-accent hover:text-accent-foreground relative inline-flex size-9 items-center justify-center rounded-md\"\n >\n <BellIcon className=\"size-4\" aria-hidden=\"true\" />\n {count > 0 && (\n <span className=\"bg-primary text-primary-foreground absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium\">\n {count > 99 ? '99+' : count}\n </span>\n )}\n </button>\n )\n}\n\n/** The current tenant (or an empty avatar) until the switcher has loaded: a placeholder. */\nexport function TenantSwitcherTrigger({ source }: WidgetProps) {\n const current = source.widgets.tenancy?.current\n\n return (\n <button\n type=\"button\"\n data-slot=\"tenant-menu\"\n aria-hidden=\"true\"\n tabIndex={-1}\n className=\"hover:bg-accent inline-flex h-9 max-w-60 shrink-0 items-center gap-2 rounded-md px-2 text-sm font-medium\"\n >\n <span className=\"bg-muted flex size-6 items-center justify-center rounded-md text-[10px]\">\n {current ? initials(tenantNameText(current)) : ''}\n </span>\n {current && (\n <span className=\"sr-only min-w-0 truncate lg:not-sr-only\">{tenantNameText(current)}</span>\n )}\n <ChevronsUpDownIcon className=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n </button>\n )\n}\n\n/**\n * The AI copilot's button until its code has loaded: the same look, and it remembers a\n * click or its key bindings (`mod+j`), so the copilot opens once loaded.\n */\nexport function CopilotTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {\n const copilot = source.widgets.copilot\n const keys = JSON.stringify(copilot?.keyBindings ?? [])\n const label = copilot?.label ?? ''\n // `onLoad` is a stable function (it starts loading the copilot's code).\n useEffect(\n () => registerStarterKitCopilotShortcut(JSON.parse(keys) as string[], label, () => onLoad?.()),\n [keys, label, onLoad],\n )\n\n return (\n <button\n type=\"button\"\n data-slot=\"ai-copilot-trigger\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n title={label}\n className=\"hover:bg-accent hover:text-accent-foreground inline-flex size-9 items-center justify-center rounded-md\"\n onPointerEnter={() => onLoad?.()}\n onClick={() => {\n requestStarterKitCopilot()\n onLoad?.()\n }}\n >\n <SparklesIcon className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )\n}\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"widgets-triggers.js","names":[],"sources":["../../src/starter-kit/widgets-triggers.tsx"],"sourcesContent":["import {\n formatKeyBinding,\n initials,\n registerStarterKitCopilotShortcut,\n registerStarterKitSearchShortcut,\n requestStarterKitCopilot,\n requestStarterKitLanguages,\n requestStarterKitSearch,\n tenantNameText,\n type StarterKitWidgets,\n} from '@alfakit/core'\nimport { BellIcon, ChevronsUpDownIcon, LanguagesIcon, SearchIcon, SparklesIcon } from 'lucide-react'\nimport { useEffect } from 'react'\nimport { useHydrated } from '../hooks/use-hydrated'\n\n/**\n * What the starter kit header components show until their code and the panel data have\n * loaded: the same look as the loaded triggers, from the shared prop alone.\n */\nexport interface WidgetProps {\n source: StarterKitWidgets\n}\n\nconst triggerClasses =\n 'border-input bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 inline-flex h-8 shrink-0 items-center rounded-md border text-sm shadow-xs outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50'\n\n/** The search field until the palette has loaded: the same look, and it remembers a click. */\nexport function SearchTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {\n const hydrated = useHydrated()\n const search = source.widgets.search\n const keys = JSON.stringify(search?.keyBindings ?? ['mod+k'])\n const placeholder = search?.placeholder ?? ''\n // `onLoad` is a stable function (it starts loading the palette's code).\n useEffect(\n () =>\n registerStarterKitSearchShortcut(JSON.parse(keys) as string[], placeholder, () => onLoad?.()),\n [keys, placeholder, onLoad],\n )\n\n const binding = search?.keyBindings[0]\n\n return (\n <button\n type=\"button\"\n className={`${triggerClasses} text-muted-foreground size-8 justify-center gap-2 px-0 font-normal sm:w-56 sm:justify-start sm:px-2.5`}\n onPointerEnter={() => onLoad?.()}\n onClick={() => {\n requestStarterKitSearch()\n onLoad?.()\n }}\n >\n <SearchIcon className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"sr-only sm:not-sr-only sm:flex-1 sm:text-start\">{placeholder}</span>\n {hydrated && binding && (\n <kbd dir=\"ltr\" className=\"bg-muted hidden rounded px-1 font-mono text-[10px] sm:inline\">\n {formatKeyBinding(binding)}\n </kbd>\n )}\n </button>\n )\n}\n\n/** The bell and its unread count until the menu has loaded. */\nexport function NotificationsTrigger({ source }: WidgetProps) {\n const notifications = source.widgets.notifications\n const count = notifications?.unreadCount ?? 0\n\n return (\n <button\n type=\"button\"\n data-slot=\"notifications-trigger\"\n aria-label={notifications?.label}\n className=\"hover:bg-accent hover:text-accent-foreground relative inline-flex size-9 items-center justify-center rounded-md\"\n >\n <BellIcon className=\"size-4\" aria-hidden=\"true\" />\n {count > 0 && (\n <span className=\"bg-primary text-primary-foreground absolute -end-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium\">\n {count > 99 ? '99+' : count}\n </span>\n )}\n </button>\n )\n}\n\n/** The current tenant (or an empty avatar) until the switcher has loaded: a placeholder. */\nexport function TenantSwitcherTrigger({ source }: WidgetProps) {\n const current = source.widgets.tenancy?.current\n\n return (\n <button\n type=\"button\"\n data-slot=\"tenant-menu\"\n aria-hidden=\"true\"\n tabIndex={-1}\n className=\"hover:bg-accent inline-flex h-9 max-w-60 shrink-0 items-center gap-2 rounded-md px-2 text-sm font-medium\"\n >\n <span className=\"bg-muted flex size-6 items-center justify-center rounded-md text-[10px]\">\n {current ? initials(tenantNameText(current)) : ''}\n </span>\n {current && (\n <span className=\"sr-only min-w-0 truncate lg:not-sr-only\">{tenantNameText(current)}</span>\n )}\n <ChevronsUpDownIcon className=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n </button>\n )\n}\n\n/**\n * The AI copilot's button until its code has loaded: the same look, and it remembers a\n * click or its key bindings (`mod+j`), so the copilot opens once loaded.\n */\nexport function CopilotTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {\n const copilot = source.widgets.copilot\n const keys = JSON.stringify(copilot?.keyBindings ?? [])\n const label = copilot?.label ?? ''\n // `onLoad` is a stable function (it starts loading the copilot's code).\n useEffect(\n () => registerStarterKitCopilotShortcut(JSON.parse(keys) as string[], label, () => onLoad?.()),\n [keys, label, onLoad],\n )\n\n return (\n <button\n type=\"button\"\n data-slot=\"ai-copilot-trigger\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n title={label}\n className=\"hover:bg-accent hover:text-accent-foreground inline-flex size-9 items-center justify-center rounded-md\"\n onPointerEnter={() => onLoad?.()}\n onClick={() => {\n requestStarterKitCopilot()\n onLoad?.()\n }}\n >\n <SparklesIcon className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )\n}\n\n/** The language button's look, before and after its menu has loaded. */\nexport const languageTriggerClasses =\n 'hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1.5 rounded-md px-2 text-sm font-medium outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50'\n\n/**\n * The language button until its menu has loaded: the languages icon and the current\n * language in its own name (from `sm`). A click is remembered and opens the menu once loaded.\n */\nexport function LanguageSwitcherTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {\n const language = source.widgets.language\n\n return (\n <button\n type=\"button\"\n data-slot=\"language-switcher-trigger\"\n aria-label={language?.label}\n aria-haspopup=\"menu\"\n title={language?.label}\n className={languageTriggerClasses}\n onPointerEnter={() => onLoad?.()}\n onClick={() => {\n requestStarterKitLanguages()\n onLoad?.()\n }}\n >\n <LanguagesIcon className=\"size-4\" aria-hidden=\"true\" />\n {language && (\n <span\n lang={language.current.tag}\n dir={language.current.direction}\n className=\"hidden sm:inline\"\n >\n {language.current.label}\n </span>\n )}\n </button>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
//#region src/starter-kit/widgets.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* Drop-in header components of starter kit mode (`Panel::starterKit()`): AlfaKit's search,
|
|
4
|
-
* notifications bell and
|
|
4
|
+
* notifications bell, tenant switcher and language switcher in the host's own header, on every page of the
|
|
5
5
|
* application. They read the shared `alfakitNavigation` prop, render a lightweight trigger
|
|
6
6
|
* straight away, and load their code and the panel data the first time they render
|
|
7
7
|
* (in the browser, never during SSR).
|
|
8
8
|
*
|
|
9
9
|
* ```tsx
|
|
10
|
-
* import { AlfakitCopilot, AlfakitNotifications, AlfakitSearch } from '@alfakit/react'
|
|
10
|
+
* import { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch } from '@alfakit/react'
|
|
11
11
|
*
|
|
12
12
|
* <AlfakitSearch />
|
|
13
13
|
* <AlfakitNotifications />
|
|
14
14
|
* <AlfakitCopilot />
|
|
15
|
+
* <AlfakitLanguageSwitcher />
|
|
15
16
|
* ```
|
|
16
17
|
*/
|
|
17
18
|
export interface AlfakitHeaderWidgetProps {
|
|
@@ -42,5 +43,13 @@ export declare function AlfakitTenantSwitcher({ panel }?: AlfakitHeaderWidgetPro
|
|
|
42
43
|
* outside a tenant.
|
|
43
44
|
*/
|
|
44
45
|
export declare function AlfakitCopilot({ panel }?: AlfakitHeaderWidgetProps): import("react").JSX.Element;
|
|
46
|
+
/**
|
|
47
|
+
* The panel's language menu (`locales()`): the current language straight away, and each
|
|
48
|
+
* language in its own name once loaded. Picking one switches the panel's language (the
|
|
49
|
+
* session, the cookie and `persistLocaleUsing()`) and reloads the page, so AlfaKit pages and
|
|
50
|
+
* the header components show it; the host's own pages and `<html lang dir>` are the app's.
|
|
51
|
+
* Renders nothing for guests or with fewer than two languages.
|
|
52
|
+
*/
|
|
53
|
+
export declare function AlfakitLanguageSwitcher({ panel }?: AlfakitHeaderWidgetProps): import("react").JSX.Element;
|
|
45
54
|
//#endregion
|
|
46
55
|
//# sourceMappingURL=widgets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets.d.ts","names":[],"sources":["../../src/starter-kit/widgets.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"widgets.d.ts","names":[],"sources":["../../src/starter-kit/widgets.tsx"],"mappings":";;;;;;;;;;;;;;;;;iBAkCiB;;EAEf;;;;;;;wBAmEc,gBAAgB,UAAS,2CAA6B,IAAA;;;;;;wBAsBtD,uBAAuB,UAAS,2CAA6B,IAAA;;;;;wBAqB7D,wBAAwB,UAAS,2CAA6B,IAAA;;;;;;;wBAuB9D,iBAAiB,UAAS,2CAA6B,IAAA;;;;;;;;wBAwBvD,0BAA0B,UAAS,2CAA6B,IAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lazyComponent } from "../lib/lazy.js";
|
|
2
|
-
import { CopilotTrigger, NotificationsTrigger, SearchTrigger, TenantSwitcherTrigger } from "./widgets-triggers.js";
|
|
2
|
+
import { CopilotTrigger, LanguageSwitcherTrigger, NotificationsTrigger, SearchTrigger, TenantSwitcherTrigger } from "./widgets-triggers.js";
|
|
3
3
|
import { isAlfakitPage, starterKitWidgets } from "@alfakit/core";
|
|
4
4
|
import { usePage } from "@inertiajs/react";
|
|
5
5
|
import { Suspense, lazy } from "react";
|
|
@@ -22,6 +22,7 @@ const LoadedCopilot = lazyComponent(() => load().then((module) => module.LoadedC
|
|
|
22
22
|
function preloadCopilot() {
|
|
23
23
|
LoadedCopilot.preload();
|
|
24
24
|
}
|
|
25
|
+
const LoadedLanguageSwitcher = lazyComponent(() => load().then((module) => module.LoadedLanguageSwitcher), (props) => /* @__PURE__ */ jsx(LanguageSwitcherTrigger, { ...props }));
|
|
25
26
|
const LazyImpersonationBanner = lazy(() => import("./impersonation-banner.js"));
|
|
26
27
|
/** While impersonating, the first header component shows the banner on host pages. */
|
|
27
28
|
function StarterKitImpersonationBanner({ source }) {
|
|
@@ -96,7 +97,24 @@ function AlfakitCopilot({ panel } = {}) {
|
|
|
96
97
|
children: /* @__PURE__ */ jsx(LoadedCopilot, { source })
|
|
97
98
|
})] });
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* The panel's language menu (`locales()`): the current language straight away, and each
|
|
102
|
+
* language in its own name once loaded. Picking one switches the panel's language (the
|
|
103
|
+
* session, the cookie and `persistLocaleUsing()`) and reloads the page, so AlfaKit pages and
|
|
104
|
+
* the header components show it; the host's own pages and `<html lang dir>` are the app's.
|
|
105
|
+
* Renders nothing for guests or with fewer than two languages.
|
|
106
|
+
*/
|
|
107
|
+
function AlfakitLanguageSwitcher({ panel } = {}) {
|
|
108
|
+
const source = useWidgets(panel, "language");
|
|
109
|
+
const banner = /* @__PURE__ */ jsx(StarterKitImpersonationBanner, { source });
|
|
110
|
+
if (!source?.widgets.language) return banner;
|
|
111
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [banner, /* @__PURE__ */ jsx("span", {
|
|
112
|
+
"data-slot": "starter-kit-language-switcher",
|
|
113
|
+
className: "inline-flex",
|
|
114
|
+
children: /* @__PURE__ */ jsx(LoadedLanguageSwitcher, { source })
|
|
115
|
+
})] });
|
|
116
|
+
}
|
|
99
117
|
//#endregion
|
|
100
|
-
export { AlfakitCopilot, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher };
|
|
118
|
+
export { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch, AlfakitTenantSwitcher };
|
|
101
119
|
|
|
102
120
|
//# sourceMappingURL=widgets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widgets.js","names":[],"sources":["../../src/starter-kit/widgets.tsx"],"sourcesContent":["import {\n isAlfakitPage,\n starterKitWidgets,\n type StarterKitWidgets,\n type StarterKitWidgetsSchema,\n} from '@alfakit/core'\nimport { usePage } from '@inertiajs/react'\nimport { lazy, Suspense } from 'react'\nimport { lazyComponent } from '../lib/lazy'\nimport {\n CopilotTrigger,\n NotificationsTrigger,\n SearchTrigger,\n TenantSwitcherTrigger,\n type WidgetProps,\n} from './widgets-triggers'\n\n/**\n * Drop-in header components of starter kit mode (`Panel::starterKit()`): AlfaKit's search,\n * notifications bell and
|
|
1
|
+
{"version":3,"file":"widgets.js","names":[],"sources":["../../src/starter-kit/widgets.tsx"],"sourcesContent":["import {\n isAlfakitPage,\n starterKitWidgets,\n type StarterKitWidgets,\n type StarterKitWidgetsSchema,\n} from '@alfakit/core'\nimport { usePage } from '@inertiajs/react'\nimport { lazy, Suspense } from 'react'\nimport { lazyComponent } from '../lib/lazy'\nimport {\n CopilotTrigger,\n LanguageSwitcherTrigger,\n NotificationsTrigger,\n SearchTrigger,\n TenantSwitcherTrigger,\n type WidgetProps,\n} from './widgets-triggers'\n\n/**\n * Drop-in header components of starter kit mode (`Panel::starterKit()`): AlfaKit's search,\n * notifications bell, tenant switcher and language switcher in the host's own header, on every page of the\n * application. They read the shared `alfakitNavigation` prop, render a lightweight trigger\n * straight away, and load their code and the panel data the first time they render\n * (in the browser, never during SSR).\n *\n * ```tsx\n * import { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch } from '@alfakit/react'\n *\n * <AlfakitSearch />\n * <AlfakitNotifications />\n * <AlfakitCopilot />\n * <AlfakitLanguageSwitcher />\n * ```\n */\nexport interface AlfakitHeaderWidgetProps {\n /** The `starterKit()` panel to use (by default the open AlfaKit page's panel, else the first one that has this component). */\n panel?: string\n}\n\nconst load = () => import('./widgets-loaded')\n\nconst LoadedSearch = lazyComponent<WidgetProps>(\n () => load().then((module) => module.LoadedSearch),\n (props) => <SearchTrigger {...props} onLoad={preloadSearch} />,\n)\n\nfunction preloadSearch() {\n void LoadedSearch.preload()\n}\n\nconst LoadedNotifications = lazyComponent<WidgetProps>(\n () => load().then((module) => module.LoadedNotifications),\n (props) => <NotificationsTrigger {...props} />,\n)\n\nconst LoadedTenantSwitcher = lazyComponent<WidgetProps>(\n () => load().then((module) => module.LoadedTenantSwitcher),\n (props) => <TenantSwitcherTrigger {...props} />,\n)\n\nconst LoadedCopilot = lazyComponent<WidgetProps>(\n () => load().then((module) => module.LoadedCopilot),\n (props) => <CopilotTrigger {...props} onLoad={preloadCopilot} />,\n)\n\nfunction preloadCopilot() {\n void LoadedCopilot.preload()\n}\n\nconst LoadedLanguageSwitcher = lazyComponent<WidgetProps>(\n () => load().then((module) => module.LoadedLanguageSwitcher),\n (props) => <LanguageSwitcherTrigger {...props} />,\n)\n\n// Only needed while impersonating: its code loads then (and never on AlfaKit pages).\nconst LazyImpersonationBanner = lazy(() => import('./impersonation-banner'))\n\n/** While impersonating, the first header component shows the banner on host pages. */\nfunction StarterKitImpersonationBanner({ source }: { source: StarterKitWidgets | null }) {\n const props = usePage().props\n const impersonation = source?.widgets.impersonation\n\n if (!impersonation || isAlfakitPage(props)) return null\n\n return (\n <Suspense fallback={null}>\n <LazyImpersonationBanner impersonation={impersonation} />\n </Suspense>\n )\n}\n\nfunction useWidgets(\n panelId: string | undefined,\n part: keyof StarterKitWidgetsSchema,\n): StarterKitWidgets | null {\n return starterKitWidgets(usePage().props, panelId, part)\n}\n\n/**\n * AlfaKit's command palette and global search: the search field opens it, and so do the\n * panel's key bindings (⌘K / Ctrl+K by default), registered once for the page. Renders\n * nothing for guests or without global search and a command palette.\n */\nexport function AlfakitSearch({ panel }: AlfakitHeaderWidgetProps = {}) {\n const source = useWidgets(panel, 'search')\n\n const banner = <StarterKitImpersonationBanner source={source} />\n\n if (!source?.widgets.search) return banner\n\n return (\n <>\n {banner}\n <span data-slot=\"starter-kit-search\" className=\"inline-flex\">\n <LoadedSearch source={source} />\n </span>\n </>\n )\n}\n\n/**\n * The bell with the user's database notifications (`databaseNotifications()`), polling and\n * realtime updates as in the panel. Renders nothing for guests or without database\n * notifications.\n */\nexport function AlfakitNotifications({ panel }: AlfakitHeaderWidgetProps = {}) {\n const source = useWidgets(panel, 'notifications')\n\n const banner = <StarterKitImpersonationBanner source={source} />\n\n if (!source?.widgets.notifications) return banner\n\n return (\n <>\n {banner}\n <span data-slot=\"starter-kit-notifications\" className=\"inline-flex\">\n <LoadedNotifications source={source} />\n </span>\n </>\n )\n}\n\n/**\n * The tenant switcher of a tenancy panel: the current tenant inside the panel, or a menu\n * to choose one on the host's own pages. Renders nothing for guests or other panels.\n */\nexport function AlfakitTenantSwitcher({ panel }: AlfakitHeaderWidgetProps = {}) {\n const source = useWidgets(panel, 'tenancy')\n\n const banner = <StarterKitImpersonationBanner source={source} />\n\n if (!source?.widgets.tenancy) return banner\n\n return (\n <>\n {banner}\n <span data-slot=\"starter-kit-tenant-switcher\" className=\"inline-flex\">\n <LoadedTenantSwitcher source={source} />\n </span>\n </>\n )\n}\n\n/**\n * The AI copilot of alfasix/alfakit-ai (`AiPlugin::copilot()`): its button and chat\n * slide-over, opened with its key bindings (`mod+j`) from any page. Renders nothing for\n * guests, without the plugin's copilot, for users who may not use it and (tenancy panels)\n * outside a tenant.\n */\nexport function AlfakitCopilot({ panel }: AlfakitHeaderWidgetProps = {}) {\n const source = useWidgets(panel, 'copilot')\n\n const banner = <StarterKitImpersonationBanner source={source} />\n\n if (!source?.widgets.copilot) return banner\n\n return (\n <>\n {banner}\n <span data-slot=\"starter-kit-copilot\" className=\"inline-flex\">\n <LoadedCopilot source={source} />\n </span>\n </>\n )\n}\n\n/**\n * The panel's language menu (`locales()`): the current language straight away, and each\n * language in its own name once loaded. Picking one switches the panel's language (the\n * session, the cookie and `persistLocaleUsing()`) and reloads the page, so AlfaKit pages and\n * the header components show it; the host's own pages and `<html lang dir>` are the app's.\n * Renders nothing for guests or with fewer than two languages.\n */\nexport function AlfakitLanguageSwitcher({ panel }: AlfakitHeaderWidgetProps = {}) {\n const source = useWidgets(panel, 'language')\n\n const banner = <StarterKitImpersonationBanner source={source} />\n\n if (!source?.widgets.language) return banner\n\n return (\n <>\n {banner}\n <span data-slot=\"starter-kit-language-switcher\" className=\"inline-flex\">\n <LoadedLanguageSwitcher source={source} />\n </span>\n </>\n )\n}\n"],"mappings":";;;;;;;AAuCA,MAAM,aAAa,OAAO;AAE1B,MAAM,eAAe,oBACb,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,YAAY,IAChD,UAAU,oBAAC,eAAD;CAAe,GAAI;CAAO,QAAQ;AAAgB,CAAA,CAC/D;AAEA,SAAS,gBAAgB;CACvB,aAAkB,QAAQ;AAC5B;AAEA,MAAM,sBAAsB,oBACpB,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,mBAAmB,IACvD,UAAU,oBAAC,sBAAD,EAAsB,GAAI,MAAQ,CAAA,CAC/C;AAEA,MAAM,uBAAuB,oBACrB,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,oBAAoB,IACxD,UAAU,oBAAC,uBAAD,EAAuB,GAAI,MAAQ,CAAA,CAChD;AAEA,MAAM,gBAAgB,oBACd,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,aAAa,IACjD,UAAU,oBAAC,gBAAD;CAAgB,GAAI;CAAO,QAAQ;AAAiB,CAAA,CACjE;AAEA,SAAS,iBAAiB;CACxB,cAAmB,QAAQ;AAC7B;AAEA,MAAM,yBAAyB,oBACvB,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,sBAAsB,IAC1D,UAAU,oBAAC,yBAAD,EAAyB,GAAI,MAAQ,CAAA,CAClD;AAGA,MAAM,0BAA0B,WAAW,OAAO,4BAAyB;;AAG3E,SAAS,8BAA8B,EAAE,UAAgD;CACvF,MAAM,QAAQ,QAAQ,CAAC,CAAC;CACxB,MAAM,gBAAgB,QAAQ,QAAQ;CAEtC,IAAI,CAAC,iBAAiB,cAAc,KAAK,GAAG,OAAO;CAEnD,OACE,oBAAC,UAAD;EAAU,UAAU;EAClB,UAAA,oBAAC,yBAAD,EAAwC,cAAgB,CAAA;CAChD,CAAA;AAEd;AAEA,SAAS,WACP,SACA,MAC0B;CAC1B,OAAO,kBAAkB,QAAQ,CAAC,CAAC,OAAO,SAAS,IAAI;AACzD;;;;;;AAOA,SAAgB,cAAc,EAAE,UAAoC,CAAC,GAAG;CACtE,MAAM,SAAS,WAAW,OAAO,QAAQ;CAEzC,MAAM,SAAS,oBAAC,+BAAD,EAAuC,OAAS,CAAA;CAE/D,IAAI,CAAC,QAAQ,QAAQ,QAAQ,OAAO;CAEpC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,QACD,oBAAC,QAAD;EAAM,aAAU;EAAqB,WAAU;EAC7C,UAAA,oBAAC,cAAD,EAAsB,OAAS,CAAA;CAC3B,CAAA,CACN,EAAA,CAAA;AAEN;;;;;;AAOA,SAAgB,qBAAqB,EAAE,UAAoC,CAAC,GAAG;CAC7E,MAAM,SAAS,WAAW,OAAO,eAAe;CAEhD,MAAM,SAAS,oBAAC,+BAAD,EAAuC,OAAS,CAAA;CAE/D,IAAI,CAAC,QAAQ,QAAQ,eAAe,OAAO;CAE3C,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,QACD,oBAAC,QAAD;EAAM,aAAU;EAA4B,WAAU;EACpD,UAAA,oBAAC,qBAAD,EAA6B,OAAS,CAAA;CAClC,CAAA,CACN,EAAA,CAAA;AAEN;;;;;AAMA,SAAgB,sBAAsB,EAAE,UAAoC,CAAC,GAAG;CAC9E,MAAM,SAAS,WAAW,OAAO,SAAS;CAE1C,MAAM,SAAS,oBAAC,+BAAD,EAAuC,OAAS,CAAA;CAE/D,IAAI,CAAC,QAAQ,QAAQ,SAAS,OAAO;CAErC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,QACD,oBAAC,QAAD;EAAM,aAAU;EAA8B,WAAU;EACtD,UAAA,oBAAC,sBAAD,EAA8B,OAAS,CAAA;CACnC,CAAA,CACN,EAAA,CAAA;AAEN;;;;;;;AAQA,SAAgB,eAAe,EAAE,UAAoC,CAAC,GAAG;CACvE,MAAM,SAAS,WAAW,OAAO,SAAS;CAE1C,MAAM,SAAS,oBAAC,+BAAD,EAAuC,OAAS,CAAA;CAE/D,IAAI,CAAC,QAAQ,QAAQ,SAAS,OAAO;CAErC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,QACD,oBAAC,QAAD;EAAM,aAAU;EAAsB,WAAU;EAC9C,UAAA,oBAAC,eAAD,EAAuB,OAAS,CAAA;CAC5B,CAAA,CACN,EAAA,CAAA;AAEN;;;;;;;;AASA,SAAgB,wBAAwB,EAAE,UAAoC,CAAC,GAAG;CAChF,MAAM,SAAS,WAAW,OAAO,UAAU;CAE3C,MAAM,SAAS,oBAAC,+BAAD,EAAuC,OAAS,CAAA;CAE/D,IAAI,CAAC,QAAQ,QAAQ,UAAU,OAAO;CAEtC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,QACD,oBAAC,QAAD;EAAM,aAAU;EAAgC,WAAU;EACxD,UAAA,oBAAC,wBAAD,EAAgC,OAAS,CAAA;CACrC,CAAA,CACN,EAAA,CAAA;AAEN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfakit/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "React + shadcn/ui renderer for Alfakit, the Laravel admin panel builder by Alfa Six.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alfa Six (https://alfasixbh.com)",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"react-day-picker": "^10.0.2",
|
|
97
97
|
"sonner": "^2.0.8",
|
|
98
98
|
"tailwind-merge": "^3.7.0",
|
|
99
|
-
"@alfakit/core": "0.1.
|
|
99
|
+
"@alfakit/core": "0.1.8"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"@inertiajs/react": "^3.0.0",
|
|
@@ -24,7 +24,8 @@ function useSwitch() {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
/** The panel's languages as radio items, each in its own name: picking one switches to it. */
|
|
28
|
+
export function LocaleOptions() {
|
|
28
29
|
const localization = useLocalization()
|
|
29
30
|
const change = useSwitch()
|
|
30
31
|
|
package/src/starter-kit/index.ts
CHANGED
|
@@ -7,22 +7,26 @@ import {
|
|
|
7
7
|
loadStarterKitPanel,
|
|
8
8
|
loadedStarterKitPanel,
|
|
9
9
|
openRequestedStarterKitSearch,
|
|
10
|
+
showsLanguageSwitcher,
|
|
10
11
|
starterKitCopilot,
|
|
12
|
+
starterKitPanelLocale,
|
|
11
13
|
starterKitWidgetsPage,
|
|
12
14
|
starterKitWidgetsPanel,
|
|
13
15
|
takeRequestedStarterKitCopilot,
|
|
16
|
+
takeRequestedStarterKitLanguages,
|
|
14
17
|
tenantNameText,
|
|
15
18
|
type PanelSchema,
|
|
16
19
|
type StarterKitWidgets,
|
|
17
20
|
} from '@alfakit/core'
|
|
18
21
|
import { router, usePage } from '@inertiajs/react'
|
|
19
|
-
import { ChevronsUpDownIcon, PlusIcon } from 'lucide-react'
|
|
22
|
+
import { ChevronsUpDownIcon, LanguagesIcon, PlusIcon } from 'lucide-react'
|
|
20
23
|
import { Direction } from 'radix-ui'
|
|
21
|
-
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
|
24
|
+
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
|
|
22
25
|
import { AiCopilot } from '../components/ai/copilot'
|
|
23
26
|
import { NotificationsMenu } from '../components/notifications/notifications-menu'
|
|
24
27
|
import { RealtimeNotifications } from '../components/notifications/realtime-notifications'
|
|
25
28
|
import { GlobalSearch } from '../components/panel/global-search'
|
|
29
|
+
import { LocaleOptions } from '../components/panel/locale-switcher'
|
|
26
30
|
import { TenantName } from '../components/panel/nav-label'
|
|
27
31
|
import { TenantMenu } from '../components/panel/tenant-menu'
|
|
28
32
|
import { Avatar, AvatarFallback, AvatarImage } from '../components/ui/avatar'
|
|
@@ -38,6 +42,7 @@ import { TooltipProvider } from '../components/ui/tooltip'
|
|
|
38
42
|
import {
|
|
39
43
|
AlfakitProvider,
|
|
40
44
|
StandaloneProvider,
|
|
45
|
+
useLocalization,
|
|
41
46
|
useT,
|
|
42
47
|
type AlfakitContextValue,
|
|
43
48
|
type AnyComponent,
|
|
@@ -45,16 +50,18 @@ import {
|
|
|
45
50
|
import { Icon } from '../lib/icons'
|
|
46
51
|
import {
|
|
47
52
|
CopilotTrigger,
|
|
53
|
+
LanguageSwitcherTrigger,
|
|
48
54
|
NotificationsTrigger,
|
|
49
55
|
SearchTrigger,
|
|
50
56
|
TenantSwitcherTrigger,
|
|
57
|
+
languageTriggerClasses,
|
|
51
58
|
type WidgetProps,
|
|
52
59
|
} from './widgets-triggers'
|
|
53
60
|
|
|
54
61
|
/**
|
|
55
62
|
* The loaded header components of starter kit mode: the panel's own search palette,
|
|
56
|
-
* notifications menu and
|
|
57
|
-
* from `widgets.endpoint`. This module only loads in the browser.
|
|
63
|
+
* notifications menu, tenant menu and language menu, rendered outside Alfakit pages with
|
|
64
|
+
* the panel data from `widgets.endpoint`. This module only loads in the browser.
|
|
58
65
|
*/
|
|
59
66
|
|
|
60
67
|
// One context for the browser session: its own translator (the panel's strings), an empty
|
|
@@ -75,17 +82,24 @@ function useWidgetsContext(panel: PanelSchema): AlfakitContextValue {
|
|
|
75
82
|
return widgetsContext
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* The panel the components render with, once its data has loaded (null until then). It is
|
|
87
|
+
* loaded per endpoint (a tenant switch) and language (a language switch), so the components
|
|
88
|
+
* follow a switch instead of keeping the old language's strings.
|
|
89
|
+
*/
|
|
79
90
|
function useWidgetsPanel(source: StarterKitWidgets): PanelSchema | null {
|
|
80
91
|
const endpoint = source.widgets.endpoint
|
|
81
|
-
const
|
|
82
|
-
const
|
|
92
|
+
const locale = starterKitPanelLocale(source)
|
|
93
|
+
const [loaded, setLoaded] = useState(() => loadedStarterKitPanel(endpoint, locale))
|
|
94
|
+
// Until the new language's data arrives, not the old language's.
|
|
95
|
+
const current =
|
|
96
|
+
loaded && (locale === undefined || loaded.localization?.code === locale) ? loaded : null
|
|
97
|
+
const base = loadedStarterKitPanel(endpoint, locale) ?? current
|
|
83
98
|
|
|
84
99
|
useEffect(() => {
|
|
85
100
|
let active = true
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
void loadStarterKitPanel(endpoint).then(
|
|
102
|
+
void loadStarterKitPanel(endpoint, locale).then(
|
|
89
103
|
(panel) => active && setLoaded(panel),
|
|
90
104
|
() => {},
|
|
91
105
|
)
|
|
@@ -93,7 +107,7 @@ function useWidgetsPanel(source: StarterKitWidgets): PanelSchema | null {
|
|
|
93
107
|
return () => {
|
|
94
108
|
active = false
|
|
95
109
|
}
|
|
96
|
-
}, [endpoint])
|
|
110
|
+
}, [endpoint, locale])
|
|
97
111
|
|
|
98
112
|
const { navigation, widgets } = source
|
|
99
113
|
|
|
@@ -184,6 +198,68 @@ export function LoadedCopilot({ source }: WidgetProps) {
|
|
|
184
198
|
)
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
/**
|
|
202
|
+
* The panel's language menu (its languages in their own names, `LocaleOptions`): picking
|
|
203
|
+
* one posts to the panel's locale endpoint and reloads the page in it. Nothing without a
|
|
204
|
+
* language switcher with two or more languages.
|
|
205
|
+
*/
|
|
206
|
+
export function LoadedLanguageSwitcher({ source }: WidgetProps) {
|
|
207
|
+
const panel = useWidgetsPanel(source)
|
|
208
|
+
|
|
209
|
+
if (!panel) return <LanguageSwitcherTrigger source={source} />
|
|
210
|
+
|
|
211
|
+
if (!showsLanguageSwitcher(panel.localization, 'auth')) return null
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<WidgetShell panel={panel}>
|
|
215
|
+
<LanguageMenu />
|
|
216
|
+
</WidgetShell>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function LanguageMenu() {
|
|
221
|
+
const t = useT()
|
|
222
|
+
const localization = useLocalization()
|
|
223
|
+
const [open, setOpen] = useState(false)
|
|
224
|
+
// Taken once per mount (effects run twice in StrictMode).
|
|
225
|
+
const requested = useRef<boolean | null>(null)
|
|
226
|
+
|
|
227
|
+
// A click on the trigger before the menu loaded opens it once mounted: a frame later, as
|
|
228
|
+
// the menu closes again when it opens while focus is still on the replaced button.
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
requested.current ??= takeRequestedStarterKitLanguages()
|
|
231
|
+
|
|
232
|
+
if (!requested.current) return
|
|
233
|
+
|
|
234
|
+
const frame = requestAnimationFrame(() => setOpen(true))
|
|
235
|
+
|
|
236
|
+
return () => cancelAnimationFrame(frame)
|
|
237
|
+
}, [])
|
|
238
|
+
const label = t('change_language', {}, 'Change language')
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
242
|
+
<DropdownMenuTrigger asChild>
|
|
243
|
+
<button
|
|
244
|
+
type="button"
|
|
245
|
+
data-slot="language-switcher-trigger"
|
|
246
|
+
aria-label={label}
|
|
247
|
+
title={label}
|
|
248
|
+
className={languageTriggerClasses}
|
|
249
|
+
>
|
|
250
|
+
<LanguagesIcon className="size-4" aria-hidden="true" />
|
|
251
|
+
<span lang={localization.tag} dir={localization.direction} className="hidden sm:inline">
|
|
252
|
+
{localization.label}
|
|
253
|
+
</span>
|
|
254
|
+
</button>
|
|
255
|
+
</DropdownMenuTrigger>
|
|
256
|
+
<DropdownMenuContent align="end" className="min-w-40">
|
|
257
|
+
<LocaleOptions />
|
|
258
|
+
</DropdownMenuContent>
|
|
259
|
+
</DropdownMenu>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
187
263
|
/** The user's tenants on a page outside the panel (no current tenant). */
|
|
188
264
|
function TenantChooser({ panel }: { panel: PanelSchema }) {
|
|
189
265
|
const t = useT()
|
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
registerStarterKitCopilotShortcut,
|
|
5
5
|
registerStarterKitSearchShortcut,
|
|
6
6
|
requestStarterKitCopilot,
|
|
7
|
+
requestStarterKitLanguages,
|
|
7
8
|
requestStarterKitSearch,
|
|
8
9
|
tenantNameText,
|
|
9
10
|
type StarterKitWidgets,
|
|
10
11
|
} from '@alfakit/core'
|
|
11
|
-
import { BellIcon, ChevronsUpDownIcon, SearchIcon, SparklesIcon } from 'lucide-react'
|
|
12
|
+
import { BellIcon, ChevronsUpDownIcon, LanguagesIcon, SearchIcon, SparklesIcon } from 'lucide-react'
|
|
12
13
|
import { useEffect } from 'react'
|
|
13
14
|
import { useHydrated } from '../hooks/use-hydrated'
|
|
14
15
|
|
|
@@ -136,3 +137,42 @@ export function CopilotTrigger({ source, onLoad }: WidgetProps & { onLoad?: () =
|
|
|
136
137
|
</button>
|
|
137
138
|
)
|
|
138
139
|
}
|
|
140
|
+
|
|
141
|
+
/** The language button's look, before and after its menu has loaded. */
|
|
142
|
+
export const languageTriggerClasses =
|
|
143
|
+
'hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1.5 rounded-md px-2 text-sm font-medium outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50'
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The language button until its menu has loaded: the languages icon and the current
|
|
147
|
+
* language in its own name (from `sm`). A click is remembered and opens the menu once loaded.
|
|
148
|
+
*/
|
|
149
|
+
export function LanguageSwitcherTrigger({ source, onLoad }: WidgetProps & { onLoad?: () => void }) {
|
|
150
|
+
const language = source.widgets.language
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<button
|
|
154
|
+
type="button"
|
|
155
|
+
data-slot="language-switcher-trigger"
|
|
156
|
+
aria-label={language?.label}
|
|
157
|
+
aria-haspopup="menu"
|
|
158
|
+
title={language?.label}
|
|
159
|
+
className={languageTriggerClasses}
|
|
160
|
+
onPointerEnter={() => onLoad?.()}
|
|
161
|
+
onClick={() => {
|
|
162
|
+
requestStarterKitLanguages()
|
|
163
|
+
onLoad?.()
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<LanguagesIcon className="size-4" aria-hidden="true" />
|
|
167
|
+
{language && (
|
|
168
|
+
<span
|
|
169
|
+
lang={language.current.tag}
|
|
170
|
+
dir={language.current.direction}
|
|
171
|
+
className="hidden sm:inline"
|
|
172
|
+
>
|
|
173
|
+
{language.current.label}
|
|
174
|
+
</span>
|
|
175
|
+
)}
|
|
176
|
+
</button>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
@@ -9,6 +9,7 @@ import { lazy, Suspense } from 'react'
|
|
|
9
9
|
import { lazyComponent } from '../lib/lazy'
|
|
10
10
|
import {
|
|
11
11
|
CopilotTrigger,
|
|
12
|
+
LanguageSwitcherTrigger,
|
|
12
13
|
NotificationsTrigger,
|
|
13
14
|
SearchTrigger,
|
|
14
15
|
TenantSwitcherTrigger,
|
|
@@ -17,17 +18,18 @@ import {
|
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Drop-in header components of starter kit mode (`Panel::starterKit()`): AlfaKit's search,
|
|
20
|
-
* notifications bell and
|
|
21
|
+
* notifications bell, tenant switcher and language switcher in the host's own header, on every page of the
|
|
21
22
|
* application. They read the shared `alfakitNavigation` prop, render a lightweight trigger
|
|
22
23
|
* straight away, and load their code and the panel data the first time they render
|
|
23
24
|
* (in the browser, never during SSR).
|
|
24
25
|
*
|
|
25
26
|
* ```tsx
|
|
26
|
-
* import { AlfakitCopilot, AlfakitNotifications, AlfakitSearch } from '@alfakit/react'
|
|
27
|
+
* import { AlfakitCopilot, AlfakitLanguageSwitcher, AlfakitNotifications, AlfakitSearch } from '@alfakit/react'
|
|
27
28
|
*
|
|
28
29
|
* <AlfakitSearch />
|
|
29
30
|
* <AlfakitNotifications />
|
|
30
31
|
* <AlfakitCopilot />
|
|
32
|
+
* <AlfakitLanguageSwitcher />
|
|
31
33
|
* ```
|
|
32
34
|
*/
|
|
33
35
|
export interface AlfakitHeaderWidgetProps {
|
|
@@ -65,6 +67,11 @@ function preloadCopilot() {
|
|
|
65
67
|
void LoadedCopilot.preload()
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
const LoadedLanguageSwitcher = lazyComponent<WidgetProps>(
|
|
71
|
+
() => load().then((module) => module.LoadedLanguageSwitcher),
|
|
72
|
+
(props) => <LanguageSwitcherTrigger {...props} />,
|
|
73
|
+
)
|
|
74
|
+
|
|
68
75
|
// Only needed while impersonating: its code loads then (and never on AlfaKit pages).
|
|
69
76
|
const LazyImpersonationBanner = lazy(() => import('./impersonation-banner'))
|
|
70
77
|
|
|
@@ -176,3 +183,27 @@ export function AlfakitCopilot({ panel }: AlfakitHeaderWidgetProps = {}) {
|
|
|
176
183
|
</>
|
|
177
184
|
)
|
|
178
185
|
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The panel's language menu (`locales()`): the current language straight away, and each
|
|
189
|
+
* language in its own name once loaded. Picking one switches the panel's language (the
|
|
190
|
+
* session, the cookie and `persistLocaleUsing()`) and reloads the page, so AlfaKit pages and
|
|
191
|
+
* the header components show it; the host's own pages and `<html lang dir>` are the app's.
|
|
192
|
+
* Renders nothing for guests or with fewer than two languages.
|
|
193
|
+
*/
|
|
194
|
+
export function AlfakitLanguageSwitcher({ panel }: AlfakitHeaderWidgetProps = {}) {
|
|
195
|
+
const source = useWidgets(panel, 'language')
|
|
196
|
+
|
|
197
|
+
const banner = <StarterKitImpersonationBanner source={source} />
|
|
198
|
+
|
|
199
|
+
if (!source?.widgets.language) return banner
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<>
|
|
203
|
+
{banner}
|
|
204
|
+
<span data-slot="starter-kit-language-switcher" className="inline-flex">
|
|
205
|
+
<LoadedLanguageSwitcher source={source} />
|
|
206
|
+
</span>
|
|
207
|
+
</>
|
|
208
|
+
)
|
|
209
|
+
}
|