@kodffe/ui 0.0.0-stage → 0.10.1
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/dist/_virtual/_rolldown/runtime.js +11 -0
- package/dist/accordion/accordion.d.ts +44 -0
- package/dist/accordion/accordion.js +84 -0
- package/dist/accordion/accordion.js.map +1 -0
- package/dist/alert/alert.d.ts +17 -0
- package/dist/alert/alert.js +59 -0
- package/dist/alert/alert.js.map +1 -0
- package/dist/app-shell/app-footer.d.ts +29 -0
- package/dist/app-shell/app-footer.js +39 -0
- package/dist/app-shell/app-footer.js.map +1 -0
- package/dist/app-shell/app-shell-context.d.ts +10 -0
- package/dist/app-shell/app-shell-context.js +12 -0
- package/dist/app-shell/app-shell-context.js.map +1 -0
- package/dist/app-shell/app-shell.d.ts +42 -0
- package/dist/app-shell/app-shell.js +191 -0
- package/dist/app-shell/app-shell.js.map +1 -0
- package/dist/app-shell/floating-actions.d.ts +19 -0
- package/dist/app-shell/floating-actions.js +59 -0
- package/dist/app-shell/floating-actions.js.map +1 -0
- package/dist/app-shell/workspace-switcher.d.ts +25 -0
- package/dist/app-shell/workspace-switcher.js +82 -0
- package/dist/app-shell/workspace-switcher.js.map +1 -0
- package/dist/badge/badge.d.ts +19 -0
- package/dist/badge/badge.js +59 -0
- package/dist/badge/badge.js.map +1 -0
- package/dist/banner/banner.d.ts +22 -0
- package/dist/banner/banner.js +80 -0
- package/dist/banner/banner.js.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.d.ts +18 -0
- package/dist/breadcrumbs/breadcrumbs.js +45 -0
- package/dist/breadcrumbs/breadcrumbs.js.map +1 -0
- package/dist/button/button.d.ts +38 -0
- package/dist/button/button.js +83 -0
- package/dist/button/button.js.map +1 -0
- package/dist/card/card.d.ts +159 -0
- package/dist/card/card.js +390 -0
- package/dist/card/card.js.map +1 -0
- package/dist/card/insight-card.d.ts +19 -0
- package/dist/card/insight-card.js +51 -0
- package/dist/card/insight-card.js.map +1 -0
- package/dist/card/integration-card.d.ts +19 -0
- package/dist/card/integration-card.js +70 -0
- package/dist/card/integration-card.js.map +1 -0
- package/dist/charts/charts.d.ts +75 -0
- package/dist/charts/charts.js +453 -0
- package/dist/charts/charts.js.map +1 -0
- package/dist/choice/choice.d.ts +39 -0
- package/dist/choice/choice.js +143 -0
- package/dist/choice/choice.js.map +1 -0
- package/dist/combobox/combobox.d.ts +46 -0
- package/dist/combobox/combobox.js +196 -0
- package/dist/combobox/combobox.js.map +1 -0
- package/dist/command-palette/command-palette.d.ts +35 -0
- package/dist/command-palette/command-palette.js +164 -0
- package/dist/command-palette/command-palette.js.map +1 -0
- package/dist/confirm-dialog/confirm-dialog.d.ts +37 -0
- package/dist/confirm-dialog/confirm-dialog.js +106 -0
- package/dist/confirm-dialog/confirm-dialog.js.map +1 -0
- package/dist/content/assistant.d.ts +86 -0
- package/dist/content/assistant.js +213 -0
- package/dist/content/assistant.js.map +1 -0
- package/dist/content/content.d.ts +45 -0
- package/dist/content/content.js +136 -0
- package/dist/content/content.js.map +1 -0
- package/dist/data-display/avatar-sizes.d.ts +8 -0
- package/dist/data-display/avatar-sizes.js +12 -0
- package/dist/data-display/avatar-sizes.js.map +1 -0
- package/dist/data-display/avatar.d.ts +21 -0
- package/dist/data-display/avatar.js +53 -0
- package/dist/data-display/avatar.js.map +1 -0
- package/dist/data-display/chip.d.ts +20 -0
- package/dist/data-display/chip.js +64 -0
- package/dist/data-display/chip.js.map +1 -0
- package/dist/data-display/code-block.d.ts +14 -0
- package/dist/data-display/code-block.js +50 -0
- package/dist/data-display/code-block.js.map +1 -0
- package/dist/data-display/data-display.d.ts +73 -0
- package/dist/data-display/data-display.js +227 -0
- package/dist/data-display/data-display.js.map +1 -0
- package/dist/data-display/insights.d.ts +120 -0
- package/dist/data-display/insights.js +220 -0
- package/dist/data-display/insights.js.map +1 -0
- package/dist/data-table/cells.d.ts +86 -0
- package/dist/data-table/cells.js +163 -0
- package/dist/data-table/cells.js.map +1 -0
- package/dist/data-table/data-table.d.ts +112 -0
- package/dist/data-table/data-table.js +558 -0
- package/dist/data-table/data-table.js.map +1 -0
- package/dist/date-picker/date-picker.d.ts +61 -0
- package/dist/date-picker/date-picker.js +309 -0
- package/dist/date-picker/date-picker.js.map +1 -0
- package/dist/drawer/drawer.d.ts +32 -0
- package/dist/drawer/drawer.js +72 -0
- package/dist/drawer/drawer.js.map +1 -0
- package/dist/empty-state/empty-state.d.ts +25 -0
- package/dist/empty-state/empty-state.js +78 -0
- package/dist/empty-state/empty-state.js.map +1 -0
- package/dist/error-state/error-state.d.ts +19 -0
- package/dist/error-state/error-state.js +56 -0
- package/dist/error-state/error-state.js.map +1 -0
- package/dist/event-calendar/calendar-logic.test.d.ts +1 -0
- package/dist/event-calendar/dates.d.ts +35 -0
- package/dist/event-calendar/dates.js +55 -0
- package/dist/event-calendar/dates.js.map +1 -0
- package/dist/event-calendar/event-calendar.d.ts +125 -0
- package/dist/event-calendar/event-calendar.js +795 -0
- package/dist/event-calendar/event-calendar.js.map +1 -0
- package/dist/event-calendar/event-details.d.ts +50 -0
- package/dist/event-calendar/event-details.js +124 -0
- package/dist/event-calendar/event-details.js.map +1 -0
- package/dist/event-calendar/layout.d.ts +48 -0
- package/dist/event-calendar/layout.js +55 -0
- package/dist/event-calendar/layout.js.map +1 -0
- package/dist/file-upload/file-upload.d.ts +70 -0
- package/dist/file-upload/file-upload.js +307 -0
- package/dist/file-upload/file-upload.js.map +1 -0
- package/dist/filters/filters.d.ts +48 -0
- package/dist/filters/filters.js +103 -0
- package/dist/filters/filters.js.map +1 -0
- package/dist/form-field/form-field.d.ts +39 -0
- package/dist/form-field/form-field.js +88 -0
- package/dist/form-field/form-field.js.map +1 -0
- package/dist/hover-card/hover-card.d.ts +15 -0
- package/dist/hover-card/hover-card.js +27 -0
- package/dist/hover-card/hover-card.js.map +1 -0
- package/dist/hover-card/person-hover-card.d.ts +26 -0
- package/dist/hover-card/person-hover-card.js +85 -0
- package/dist/hover-card/person-hover-card.js.map +1 -0
- package/dist/icon/icon-tile.d.ts +20 -0
- package/dist/icon/icon-tile.js +55 -0
- package/dist/icon/icon-tile.js.map +1 -0
- package/dist/icon/icon.d.ts +14 -0
- package/dist/icon/icon.js +28 -0
- package/dist/icon/icon.js.map +1 -0
- package/dist/icon/icons.generated.d.ts +358 -0
- package/dist/icon/icons.generated.js +362 -0
- package/dist/icon/icons.generated.js.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +88 -0
- package/dist/input/extra-inputs.d.ts +122 -0
- package/dist/input/extra-inputs.js +351 -0
- package/dist/input/extra-inputs.js.map +1 -0
- package/dist/input/field-styles.d.ts +5 -0
- package/dist/input/field-styles.js +14 -0
- package/dist/input/field-styles.js.map +1 -0
- package/dist/input/input.d.ts +50 -0
- package/dist/input/input.js +107 -0
- package/dist/input/input.js.map +1 -0
- package/dist/layout/layout.d.ts +41 -0
- package/dist/layout/layout.js +90 -0
- package/dist/layout/layout.js.map +1 -0
- package/dist/lib/control-styles.d.ts +9 -0
- package/dist/lib/control-styles.js +16 -0
- package/dist/lib/control-styles.js.map +1 -0
- package/dist/lib/cx.d.ts +2 -0
- package/dist/lib/cx.js +8 -0
- package/dist/lib/cx.js.map +1 -0
- package/dist/lib/glyphs.d.ts +13 -0
- package/dist/lib/glyphs.js +67 -0
- package/dist/lib/glyphs.js.map +1 -0
- package/dist/lib/segmented-styles.d.ts +8 -0
- package/dist/lib/segmented-styles.js +12 -0
- package/dist/lib/segmented-styles.js.map +1 -0
- package/dist/lib/select-on-arrow.d.ts +7 -0
- package/dist/lib/select-on-arrow.js +21 -0
- package/dist/lib/select-on-arrow.js.map +1 -0
- package/dist/lib/skip-link.d.ts +5 -0
- package/dist/lib/skip-link.js +13 -0
- package/dist/lib/skip-link.js.map +1 -0
- package/dist/lib/use-media-query.d.ts +4 -0
- package/dist/lib/use-media-query.js +15 -0
- package/dist/lib/use-media-query.js.map +1 -0
- package/dist/lists/lists.d.ts +76 -0
- package/dist/lists/lists.js +133 -0
- package/dist/lists/lists.js.map +1 -0
- package/dist/locale-switch/locale-switch.d.ts +29 -0
- package/dist/locale-switch/locale-switch.js +62 -0
- package/dist/locale-switch/locale-switch.js.map +1 -0
- package/dist/logo/logo.d.ts +31 -0
- package/dist/logo/logo.js +157 -0
- package/dist/logo/logo.js.map +1 -0
- package/dist/logo/product-logo.d.ts +19 -0
- package/dist/logo/product-logo.js +46 -0
- package/dist/logo/product-logo.js.map +1 -0
- package/dist/marketing/marketing.d.ts +176 -0
- package/dist/marketing/marketing.js +451 -0
- package/dist/marketing/marketing.js.map +1 -0
- package/dist/marketing/newsletter-form.d.ts +21 -0
- package/dist/marketing/newsletter-form.js +100 -0
- package/dist/marketing/newsletter-form.js.map +1 -0
- package/dist/mascot/mascot.d.ts +28 -0
- package/dist/mascot/mascot.js +28 -0
- package/dist/mascot/mascot.js.map +1 -0
- package/dist/mascot/mascots.generated.d.ts +284 -0
- package/dist/mascot/mascots.generated.js +287 -0
- package/dist/mascot/mascots.generated.js.map +1 -0
- package/dist/master-detail/master-detail.d.ts +30 -0
- package/dist/master-detail/master-detail.js +71 -0
- package/dist/master-detail/master-detail.js.map +1 -0
- package/dist/menu/account-menu.d.ts +44 -0
- package/dist/menu/account-menu.js +78 -0
- package/dist/menu/account-menu.js.map +1 -0
- package/dist/menu/context-menu.d.ts +31 -0
- package/dist/menu/context-menu.js +111 -0
- package/dist/menu/context-menu.js.map +1 -0
- package/dist/menu/dropdown-menu.d.ts +49 -0
- package/dist/menu/dropdown-menu.js +152 -0
- package/dist/menu/dropdown-menu.js.map +1 -0
- package/dist/menu/menu-styles.d.ts +8 -0
- package/dist/menu/menu-styles.js +6 -0
- package/dist/menu/menu-styles.js.map +1 -0
- package/dist/menu/menubar.d.ts +31 -0
- package/dist/menu/menubar.js +125 -0
- package/dist/menu/menubar.js.map +1 -0
- package/dist/modal/modal.d.ts +34 -0
- package/dist/modal/modal.js +58 -0
- package/dist/modal/modal.js.map +1 -0
- package/dist/notification-center/notification-center.d.ts +51 -0
- package/dist/notification-center/notification-center.js +244 -0
- package/dist/notification-center/notification-center.js.map +1 -0
- package/dist/otp-input/otp-input.d.ts +23 -0
- package/dist/otp-input/otp-input.js +61 -0
- package/dist/otp-input/otp-input.js.map +1 -0
- package/dist/overlay/overlay.d.ts +53 -0
- package/dist/overlay/overlay.js +103 -0
- package/dist/overlay/overlay.js.map +1 -0
- package/dist/page-header/page-header.d.ts +55 -0
- package/dist/page-header/page-header.js +114 -0
- package/dist/page-header/page-header.js.map +1 -0
- package/dist/page-header/record-headers.d.ts +51 -0
- package/dist/page-header/record-headers.js +134 -0
- package/dist/page-header/record-headers.js.map +1 -0
- package/dist/page-header/section-header.d.ts +21 -0
- package/dist/page-header/section-header.js +44 -0
- package/dist/page-header/section-header.js.map +1 -0
- package/dist/page-navigation/page-navigation.d.ts +59 -0
- package/dist/page-navigation/page-navigation.js +136 -0
- package/dist/page-navigation/page-navigation.js.map +1 -0
- package/dist/pagination/pagination.d.ts +28 -0
- package/dist/pagination/pagination.js +102 -0
- package/dist/pagination/pagination.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +34 -0
- package/dist/phone-input/phone-input.js +171 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +23 -0
- package/dist/popover/popover.js +40 -0
- package/dist/popover/popover.js.map +1 -0
- package/dist/progress/progress.d.ts +21 -0
- package/dist/progress/progress.js +49 -0
- package/dist/progress/progress.js.map +1 -0
- package/dist/segmented-control/segmented-control.d.ts +32 -0
- package/dist/segmented-control/segmented-control.js +29 -0
- package/dist/segmented-control/segmented-control.js.map +1 -0
- package/dist/select/select.d.ts +48 -0
- package/dist/select/select.js +136 -0
- package/dist/select/select.js.map +1 -0
- package/dist/settings/permission-matrix.d.ts +29 -0
- package/dist/settings/permission-matrix.js +101 -0
- package/dist/settings/permission-matrix.js.map +1 -0
- package/dist/settings/settings-row.d.ts +32 -0
- package/dist/settings/settings-row.js +63 -0
- package/dist/settings/settings-row.js.map +1 -0
- package/dist/side-nav/side-nav.d.ts +36 -0
- package/dist/side-nav/side-nav.js +123 -0
- package/dist/side-nav/side-nav.js.map +1 -0
- package/dist/site-header/site-header.d.ts +32 -0
- package/dist/site-header/site-header.js +99 -0
- package/dist/site-header/site-header.js.map +1 -0
- package/dist/skeleton/skeleton.d.ts +16 -0
- package/dist/skeleton/skeleton.js +19 -0
- package/dist/skeleton/skeleton.js.map +1 -0
- package/dist/slider/slider.d.ts +15 -0
- package/dist/slider/slider.js +48 -0
- package/dist/slider/slider.js.map +1 -0
- package/dist/spinner/spinner.d.ts +15 -0
- package/dist/spinner/spinner.js +42 -0
- package/dist/spinner/spinner.js.map +1 -0
- package/dist/stepper/step-dots.d.ts +16 -0
- package/dist/stepper/step-dots.js +26 -0
- package/dist/stepper/step-dots.js.map +1 -0
- package/dist/stepper/stepper.d.ts +26 -0
- package/dist/stepper/stepper.js +113 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/tabs/tabs.d.ts +31 -0
- package/dist/tabs/tabs.js +66 -0
- package/dist/tabs/tabs.js.map +1 -0
- package/dist/templates/app-templates.d.ts +126 -0
- package/dist/templates/app-templates.js +153 -0
- package/dist/templates/app-templates.js.map +1 -0
- package/dist/templates/site-templates.d.ts +49 -0
- package/dist/templates/site-templates.js +110 -0
- package/dist/templates/site-templates.js.map +1 -0
- package/dist/toast/toast-store.d.ts +54 -0
- package/dist/toast/toast-store.js +49 -0
- package/dist/toast/toast-store.js.map +1 -0
- package/dist/toast/toaster.d.ts +15 -0
- package/dist/toast/toaster.js +124 -0
- package/dist/toast/toaster.js.map +1 -0
- package/dist/tooltip/tooltip.d.ts +29 -0
- package/dist/tooltip/tooltip.js +54 -0
- package/dist/tooltip/tooltip.js.map +1 -0
- package/package.json +88 -3
- package/styles.css +521 -0
- package/README.md +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charts.js","names":["LineChart","RLineChart","AreaChart","RAreaChart","BarChart","RBarChart","_Fragment","useAnimate","useMediaQuery","CHART_COLORS","colorOf","s","i","fmt","v","f","axis","Frame","id","useId","cx","className","title","description","actions","showLegend","series","StatusLegend","footer","ariaTitle","table","DataTableFallback","caption","index","data","indexFormatter","row","valueFormatter","TooltipCard","active","payload","label","p","plainTitle","LineChart$1","frame","animate","ResponsiveContainer","LineChart","showGrid","CartesianGrid","XAxis","YAxis","Tooltip","Line","curve","AreaChart$1","gid","AreaChart","Area","stacked","CATEGORY_CHART_COLORS","BarChart$1","horizontal","layout","dual","secondaryAxis","BarChart","Fragment","secondaryValueFormatter","Bar","colorByCategory","Cell","j","DonutChart","d","total","sum","valueKey","PieChart","Pie","colors","centerLabel","centerSubLabel","showPercent"],"sources":["../../src/charts/charts.tsx"],"sourcesContent":["'use client';\n\nimport { useId, type ReactNode } from 'react';\nimport {\n Area,\n AreaChart as RAreaChart,\n Bar,\n BarChart as RBarChart,\n CartesianGrid,\n Cell,\n Line,\n LineChart as RLineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { cx } from '../lib/cx';\nimport { StatusLegend } from '../data-display/insights';\nimport { useMediaQuery } from '../lib/use-media-query';\n\nconst useAnimate = () => !useMediaQuery('(prefers-reduced-motion: reduce)', false);\n\n/* Charts on Recharts (ADR-0005). Colors come from the --kf-chart-* tokens, so light/dark themes\n work automatically. Every chart has a visible title and a screen-reader data table. */\n\nexport const CHART_COLORS = [\n 'var(--kf-chart-1)',\n 'var(--kf-chart-2)',\n 'var(--kf-chart-3)',\n 'var(--kf-chart-4)',\n 'var(--kf-chart-5)',\n];\n\nexport interface ChartSeries {\n /** Key in each data row. */\n key: string;\n label: string;\n /** CSS color; defaults to the next --kf-chart-* token. */\n color?: string;\n}\n\ntype Row = Record<string, string | number | null | undefined>;\n\ninterface BaseChartProps {\n data: Row[];\n /** Key of the category / x-axis value (e.g. \"month\"). */\n index: string;\n series: ChartSeries[];\n /** Visible title; also names the chart for screen readers. */\n title: ReactNode;\n /** Plain-text title when `title` is not a string. */\n ariaTitle?: string;\n /** One-sentence takeaway (\"Runs grew 38% in October\"). */\n description?: ReactNode;\n height?: number;\n /** Formats values on the axis, tooltip and data table (e.g. currency, %). */\n valueFormatter?: (value: number) => string;\n /** Formats the index on the axis and tooltip (e.g. dates). */\n indexFormatter?: (value: string | number) => string;\n showLegend?: boolean;\n showGrid?: boolean;\n /** Extra content in the header (e.g. a Tabs period switch). */\n actions?: ReactNode;\n /** A summary line under the chart (\"Busiest day · Wednesday 381\"). */\n footer?: ReactNode;\n className?: string;\n}\n\nconst colorOf = (s: ChartSeries, i: number) => s.color ?? CHART_COLORS[i % CHART_COLORS.length]!;\nconst fmt = (f: ((v: number) => string) | undefined, v: unknown) =>\n typeof v === 'number' ? (f ? f(v) : v.toLocaleString()) : String(v ?? '—');\n\nconst axis = { stroke: 'var(--kf-fg-muted)', fontSize: 12, tickLine: false, axisLine: false } as const;\n\nfunction Frame({\n title,\n ariaTitle,\n description,\n actions,\n showLegend,\n series,\n className,\n children,\n table,\n footer,\n}: {\n title: ReactNode;\n ariaTitle?: string;\n description?: ReactNode;\n actions?: ReactNode;\n footer?: ReactNode;\n showLegend?: boolean;\n series: ChartSeries[];\n className?: string;\n children: ReactNode;\n table: ReactNode;\n}) {\n const id = useId();\n return (\n <figure\n aria-labelledby={`${id}-t`}\n className={cx('m-0 grid gap-4 rounded-lg border border-border bg-surface p-5 font-sans text-fg', className)}\n >\n <figcaption className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"grid gap-0.5\">\n <span id={`${id}-t`} className=\"font-display text-base font-bold\">\n {title}\n </span>\n {description && <span className=\"text-sm text-fg-muted\">{description}</span>}\n </div>\n {actions}\n </figcaption>\n {showLegend && series.length > 1 && (\n // Decorative here: the screen-reader data table already names every series.\n <div aria-hidden=\"true\">\n <StatusLegend items={series.map((s, i) => ({ label: s.label, color: colorOf(s, i) }))} />\n </div>\n )}\n <div aria-hidden=\"true\" className=\"min-w-0\">\n {children}\n </div>\n {footer && <div className=\"border-t border-border pt-3 text-sm text-fg-muted\">{footer}</div>}\n <div className=\"sr-only\">\n {ariaTitle && <span>{ariaTitle}</span>}\n {table}\n </div>\n </figure>\n );\n}\n\nfunction DataTableFallback({\n data,\n index,\n series,\n valueFormatter,\n indexFormatter,\n caption,\n}: Pick<BaseChartProps, 'data' | 'index' | 'series' | 'valueFormatter' | 'indexFormatter'> & { caption: string }) {\n return (\n <table>\n <caption>{caption}</caption>\n <thead>\n <tr>\n <th scope=\"col\">{index}</th>\n {series.map((s) => (\n <th key={s.key} scope=\"col\">\n {s.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {data.map((row, i) => (\n <tr key={i}>\n <th scope=\"row\">{indexFormatter ? indexFormatter(row[index] as string) : String(row[index])}</th>\n {series.map((s) => (\n <td key={s.key}>{fmt(valueFormatter, row[s.key])}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n );\n}\n\nfunction TooltipCard({\n active,\n payload,\n label,\n series,\n valueFormatter,\n indexFormatter,\n}: {\n active?: boolean;\n payload?: { dataKey?: string | number; value?: number; color?: string; payload?: Row }[];\n label?: string | number;\n series: ChartSeries[];\n valueFormatter?: (v: number) => string;\n indexFormatter?: (v: string | number) => string;\n}) {\n if (!active || !payload?.length) return null;\n return (\n <div className=\"grid min-w-36 gap-1.5 rounded-md border border-border bg-surface px-3 py-2 font-sans text-sm text-fg shadow-overlay\">\n {label !== undefined && <span className=\"font-semibold\">{indexFormatter ? indexFormatter(label) : label}</span>}\n {payload.map((p) => {\n const i = series.findIndex((s) => s.key === p.dataKey);\n const s = series[i];\n return (\n <span key={String(p.dataKey)} className=\"flex items-center gap-2\">\n <span className=\"size-2.5 shrink-0 rounded-full\" style={{ background: s ? colorOf(s, i) : p.color }} />\n <span className=\"flex-1 text-fg-muted\">{s?.label ?? p.dataKey}</span>\n <span className=\"font-semibold tabular-nums\">{fmt(valueFormatter, p.value)}</span>\n </span>\n );\n })}\n </div>\n );\n}\n\nfunction plainTitle(title: ReactNode, ariaTitle?: string) {\n return ariaTitle ?? (typeof title === 'string' ? title : 'Chart');\n}\n\nexport interface LineChartProps extends BaseChartProps {\n /** Smooth curves (default) or straight segments. */\n curve?: 'monotone' | 'linear';\n}\n\n/** Trends over time (runs per day, response time). */\nexport function LineChart({\n data,\n index,\n series,\n height = 260,\n valueFormatter,\n indexFormatter,\n showLegend = true,\n showGrid = true,\n curve = 'monotone',\n ...frame\n}: LineChartProps) {\n const animate = useAnimate();\n return (\n <Frame\n {...frame}\n series={series}\n showLegend={showLegend}\n table={\n <DataTableFallback\n caption={plainTitle(frame.title, frame.ariaTitle)}\n {...{ data, index, series, valueFormatter, indexFormatter }}\n />\n }\n >\n <ResponsiveContainer width=\"100%\" height={height}>\n <RLineChart accessibilityLayer={false} data={data} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>\n {showGrid && <CartesianGrid stroke=\"var(--kf-border)\" vertical={false} />}\n <XAxis dataKey={index} {...axis} tickFormatter={indexFormatter} minTickGap={16} />\n <YAxis {...axis} width={48} tickFormatter={valueFormatter} />\n <Tooltip\n cursor={{ stroke: 'var(--kf-border)' }}\n content={<TooltipCard series={series} valueFormatter={valueFormatter} indexFormatter={indexFormatter} />}\n />\n {series.map((s, i) => (\n <Line\n isAnimationActive={animate}\n key={s.key}\n type={curve}\n dataKey={s.key}\n name={s.label}\n stroke={colorOf(s, i)}\n strokeWidth={2.5}\n dot={false}\n activeDot={{ r: 5, strokeWidth: 0 }}\n />\n ))}\n </RLineChart>\n </ResponsiveContainer>\n </Frame>\n );\n}\n\nexport interface AreaChartProps extends BaseChartProps {\n stacked?: boolean;\n}\n\n/** Volumes over time, optionally stacked (runs by workflow). */\nexport function AreaChart({\n data,\n index,\n series,\n height = 260,\n valueFormatter,\n indexFormatter,\n showLegend = true,\n showGrid = true,\n stacked,\n ...frame\n}: AreaChartProps) {\n const animate = useAnimate();\n const gid = useId().replace(/:/g, '');\n return (\n <Frame\n {...frame}\n series={series}\n showLegend={showLegend}\n table={\n <DataTableFallback\n caption={plainTitle(frame.title, frame.ariaTitle)}\n {...{ data, index, series, valueFormatter, indexFormatter }}\n />\n }\n >\n <ResponsiveContainer width=\"100%\" height={height}>\n <RAreaChart accessibilityLayer={false} data={data} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.key} id={`${gid}-${i}`} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={colorOf(s, i)} stopOpacity={0.35} />\n <stop offset=\"100%\" stopColor={colorOf(s, i)} stopOpacity={0.02} />\n </linearGradient>\n ))}\n </defs>\n {showGrid && <CartesianGrid stroke=\"var(--kf-border)\" vertical={false} />}\n <XAxis dataKey={index} {...axis} tickFormatter={indexFormatter} minTickGap={16} />\n <YAxis {...axis} width={48} tickFormatter={valueFormatter} />\n <Tooltip\n cursor={{ stroke: 'var(--kf-border)' }}\n content={<TooltipCard series={series} valueFormatter={valueFormatter} indexFormatter={indexFormatter} />}\n />\n {series.map((s, i) => (\n <Area\n isAnimationActive={animate}\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stackId={stacked ? 'a' : undefined}\n stroke={colorOf(s, i)}\n strokeWidth={2}\n fill={`url(#${gid}-${i})`}\n />\n ))}\n </RAreaChart>\n </ResponsiveContainer>\n </Frame>\n );\n}\n\nexport interface BarChartProps extends BaseChartProps {\n stacked?: boolean;\n /** Horizontal bars for long category names (rankings). */\n layout?: 'vertical-bars' | 'horizontal-bars';\n /** A series plotted against a second axis on the right (tasks left, hours right). Vertical bars only. */\n secondaryAxis?: string;\n /** Formats the right axis (defaults to `valueFormatter`). */\n secondaryValueFormatter?: (value: number) => string;\n /** One category color per bar (a single series compared across categories). */\n colorByCategory?: boolean;\n}\n\nexport const CATEGORY_CHART_COLORS = Array.from({ length: 12 }, (_, i) => `var(--kf-category-${i + 1})`);\n\n/** Comparisons between categories (hours saved by workflow, runs per month). */\nexport function BarChart({\n data,\n index,\n series,\n height = 260,\n valueFormatter,\n indexFormatter,\n showLegend = true,\n showGrid = true,\n stacked,\n layout = 'vertical-bars',\n secondaryAxis,\n secondaryValueFormatter,\n colorByCategory,\n ...frame\n}: BarChartProps) {\n const animate = useAnimate();\n const horizontal = layout === 'horizontal-bars';\n const dual = !horizontal && !!secondaryAxis && series.some((s) => s.key === secondaryAxis);\n return (\n <Frame\n {...frame}\n series={series}\n showLegend={showLegend}\n table={\n <DataTableFallback\n caption={plainTitle(frame.title, frame.ariaTitle)}\n {...{ data, index, series, valueFormatter, indexFormatter }}\n />\n }\n >\n <ResponsiveContainer width=\"100%\" height={height}>\n <RBarChart\n accessibilityLayer={false}\n data={data}\n layout={horizontal ? 'vertical' : 'horizontal'}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n barCategoryGap=\"22%\"\n >\n {showGrid && (\n <CartesianGrid\n stroke=\"var(--kf-border)\"\n vertical={horizontal}\n horizontal={!horizontal}\n // The vertical layout names its value axis \"left\" (for the optional right axis): the grid follows it.\n {...(horizontal ? {} : { yAxisId: 'left' })}\n />\n )}\n {horizontal ? (\n <>\n <XAxis type=\"number\" {...axis} tickFormatter={valueFormatter} />\n <YAxis type=\"category\" dataKey={index} {...axis} width={112} tickFormatter={indexFormatter} />\n </>\n ) : (\n <>\n <XAxis dataKey={index} {...axis} tickFormatter={indexFormatter} minTickGap={8} />\n <YAxis yAxisId=\"left\" {...axis} width={48} tickFormatter={valueFormatter} />\n {dual && (\n <YAxis yAxisId=\"right\" orientation=\"right\" {...axis} width={48} tickFormatter={secondaryValueFormatter ?? valueFormatter} />\n )}\n </>\n )}\n <Tooltip\n cursor={{ fill: 'var(--kf-surface-muted)' }}\n content={<TooltipCard series={series} valueFormatter={valueFormatter} indexFormatter={indexFormatter} />}\n />\n {series.map((s, i) => (\n <Bar\n isAnimationActive={animate}\n key={s.key}\n dataKey={s.key}\n name={s.label}\n stackId={stacked ? 'a' : undefined}\n {...(horizontal ? {} : { yAxisId: dual && s.key === secondaryAxis ? 'right' : 'left' })}\n fill={colorOf(s, i)}\n radius={stacked && i < series.length - 1 ? 0 : horizontal ? [0, 6, 6, 0] : [6, 6, 0, 0]}\n maxBarSize={48}\n >\n {colorByCategory && data.map((_, j) => <Cell key={j} fill={CATEGORY_CHART_COLORS[j % CATEGORY_CHART_COLORS.length]} />)}\n </Bar>\n ))}\n </RBarChart>\n </ResponsiveContainer>\n </Frame>\n );\n}\n\nexport interface DonutChartProps extends Omit<BaseChartProps, 'series' | 'index' | 'showGrid' | 'indexFormatter'> {\n /** Key of the slice name. */\n index: string;\n /** Key of the slice value. */\n valueKey: string;\n /** Big number in the center (e.g. the total). */\n centerLabel?: ReactNode;\n /** Slice colors (default: the chart tokens). Pass `CATEGORY_CHART_COLORS` for more than five slices. */\n colors?: string[];\n /** Adds a percentage column to the legend. */\n showPercent?: boolean;\n centerSubLabel?: ReactNode;\n}\n\n/** Share of a whole with 2–5 slices (runs by status). For more categories use BarChart. */\nexport function DonutChart({\n data,\n index,\n valueKey,\n height = 220,\n valueFormatter,\n centerLabel,\n centerSubLabel,\n showLegend = true,\n colors = CHART_COLORS,\n showPercent,\n ...frame\n}: DonutChartProps) {\n const animate = useAnimate();\n const series: ChartSeries[] = data.map((d) => ({ key: String(d[index]), label: String(d[index]) }));\n const total = data.reduce((sum, d) => sum + (Number(d[valueKey]) || 0), 0);\n return (\n <Frame\n {...frame}\n series={series}\n showLegend={false}\n table={\n <table>\n <caption>{plainTitle(frame.title, frame.ariaTitle)}</caption>\n <tbody>\n {data.map((d) => (\n <tr key={String(d[index])}>\n <th scope=\"row\">{String(d[index])}</th>\n <td>{fmt(valueFormatter, d[valueKey])}</td>\n <td>{total ? `${Math.round((Number(d[valueKey]) / total) * 100)}%` : ''}</td>\n </tr>\n ))}\n </tbody>\n </table>\n }\n >\n <div className=\"grid items-center gap-6 sm:grid-cols-[minmax(0,1fr)_auto]\">\n <div className=\"relative\">\n <ResponsiveContainer width=\"100%\" height={height}>\n <PieChart accessibilityLayer={false}>\n <Tooltip\n content={({ active, payload }) =>\n active && payload?.length ? (\n <div className=\"rounded-md border border-border bg-surface px-3 py-2 font-sans text-sm text-fg shadow-overlay\">\n <span className=\"font-semibold\">{String(payload[0]!.name)}</span>:{' '}\n {fmt(valueFormatter, payload[0]!.value)}\n </div>\n ) : null\n }\n />\n <Pie\n isAnimationActive={animate}\n rootTabIndex={-1}\n data={data}\n dataKey={valueKey}\n nameKey={index}\n innerRadius=\"62%\"\n outerRadius=\"90%\"\n paddingAngle={2}\n stroke=\"var(--kf-surface)\"\n strokeWidth={2}\n >\n {data.map((_, i) => (\n <Cell key={i} fill={colors[i % colors.length]} />\n ))}\n </Pie>\n </PieChart>\n </ResponsiveContainer>\n {centerLabel !== undefined && (\n <div className=\"pointer-events-none absolute inset-0 grid place-content-center text-center\">\n <span className=\"font-display text-2xl font-extrabold tabular-nums\">{centerLabel}</span>\n {centerSubLabel && <span className=\"text-xs text-fg-muted\">{centerSubLabel}</span>}\n </div>\n )}\n </div>\n {showLegend && (\n <ul aria-hidden=\"true\" className=\"m-0 grid list-none gap-2 p-0 text-sm\">\n {data.map((d, i) => (\n <li key={String(d[index])} className=\"flex items-center gap-2\">\n <span className=\"size-2.5 rounded-full\" style={{ background: colors[i % colors.length] }} />\n <span className=\"flex-1 text-fg-muted\">{String(d[index])}</span>\n <span className=\"font-semibold tabular-nums\">{fmt(valueFormatter, d[valueKey])}</span>\n {showPercent && (\n <span className=\"w-10 text-right text-fg-muted tabular-nums\">\n {total ? `${Math.round((Number(d[valueKey]) / total) * 100)}%` : ''}\n </span>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n </Frame>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
2
|
+
import * as RadixRadio from '@radix-ui/react-radio-group';
|
|
3
|
+
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
4
|
+
import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
5
|
+
import { type IconName } from '../icon/icon';
|
|
6
|
+
export type ChoiceVariant = 'default' | 'card' | 'pill';
|
|
7
|
+
export interface CheckboxProps extends Omit<ComponentPropsWithoutRef<typeof RadixCheckbox.Root>, 'children'> {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
/** `card` for rich choices (IntentCard), `pill` for chips (RoleChip). */
|
|
11
|
+
variant?: ChoiceVariant;
|
|
12
|
+
icon?: IconName;
|
|
13
|
+
}
|
|
14
|
+
/** Checkbox with label and optional description. `checked="indeterminate"` for partial selections. */
|
|
15
|
+
export declare function Checkbox({ label, description, variant, icon, className, ...rest }: CheckboxProps): import("react").JSX.Element;
|
|
16
|
+
export interface RadioGroupProps extends ComponentPropsWithoutRef<typeof RadixRadio.Root> {
|
|
17
|
+
options: {
|
|
18
|
+
value: string;
|
|
19
|
+
label: ReactNode;
|
|
20
|
+
description?: ReactNode;
|
|
21
|
+
icon?: IconName;
|
|
22
|
+
/** `card` only: a thumbnail above the label (an `<img>` or any preview), shown at 16:9 and cropped to fill. */
|
|
23
|
+
media?: ReactNode;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}[];
|
|
26
|
+
variant?: ChoiceVariant;
|
|
27
|
+
/** Lay out horizontally (wraps). Pills are always horizontal. */
|
|
28
|
+
horizontal?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** One choice out of 2–6 visible options. Arrow keys move the selection. */
|
|
31
|
+
export declare function RadioGroup({ options, variant, horizontal, className, ...rest }: RadioGroupProps): import("react").JSX.Element;
|
|
32
|
+
export interface SwitchProps extends Omit<ComponentPropsWithoutRef<typeof RadixSwitch.Root>, 'children'> {
|
|
33
|
+
label: ReactNode;
|
|
34
|
+
description?: ReactNode;
|
|
35
|
+
/** Put the switch after the text (settings lists). */
|
|
36
|
+
labelFirst?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** On/off setting that applies immediately. For choices submitted with a form, prefer Checkbox. */
|
|
39
|
+
export declare function Switch({ label, description, labelFirst, className, ...rest }: SwitchProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx as e } from "../lib/cx.js";
|
|
3
|
+
import { Icon as t } from "../icon/icon.js";
|
|
4
|
+
import { Glyph as n } from "../lib/glyphs.js";
|
|
5
|
+
import { checkboxBox as r, focusRing as i, switchThumb as a, switchTrack as o } from "../lib/control-styles.js";
|
|
6
|
+
import { selectOnArrow as s } from "../lib/select-on-arrow.js";
|
|
7
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
|
+
import { useId as u } from "react";
|
|
9
|
+
import * as d from "@radix-ui/react-checkbox";
|
|
10
|
+
import * as f from "@radix-ui/react-radio-group";
|
|
11
|
+
import * as p from "@radix-ui/react-switch";
|
|
12
|
+
//#region src/choice/choice.tsx
|
|
13
|
+
var m = i, h = "flex cursor-pointer items-start gap-3 rounded-md border border-border bg-surface p-4 transition-colors hover:border-fg-muted/50 has-[[data-state=checked]]:border-primary has-[[data-state=checked]]:bg-info-surface/50 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 has-[:focus-visible]:outline-3 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus", g = "inline-flex min-h-10 cursor-pointer items-center gap-2 rounded-full border border-border bg-surface px-4 text-sm font-semibold transition-colors hover:border-fg-muted/50 pointer-coarse:min-h-11 has-[[data-state=checked]]:border-primary has-[[data-state=checked]]:bg-primary has-[[data-state=checked]]:text-on-primary has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 has-[:focus-visible]:outline-3 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus";
|
|
14
|
+
function _({ label: e, description: t, id: n }) {
|
|
15
|
+
return /* @__PURE__ */ l("span", {
|
|
16
|
+
className: "grid gap-0.5",
|
|
17
|
+
children: [/* @__PURE__ */ c("span", {
|
|
18
|
+
className: "text-[15px] leading-snug font-medium text-fg group-has-[:disabled]:text-fg-muted",
|
|
19
|
+
children: e
|
|
20
|
+
}), t && /* @__PURE__ */ c("span", {
|
|
21
|
+
id: n,
|
|
22
|
+
className: "text-sm text-fg-muted",
|
|
23
|
+
children: t
|
|
24
|
+
})]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function v({ label: i, description: a, variant: o = "default", icon: s, className: f, ...p }) {
|
|
28
|
+
let v = u(), y = /* @__PURE__ */ c(d.Root, {
|
|
29
|
+
...p,
|
|
30
|
+
"aria-describedby": a ? v : void 0,
|
|
31
|
+
className: e("peer", r(), o === "default" && m, o === "default" ? "mt-0.5" : o === "card" ? "mt-0.5 focus-visible:outline-none" : "sr-only"),
|
|
32
|
+
children: /* @__PURE__ */ c(d.Indicator, { children: /* @__PURE__ */ c(n, {
|
|
33
|
+
name: p.checked === "indeterminate" ? "minus" : "check",
|
|
34
|
+
size: 14,
|
|
35
|
+
strokeWidth: 3
|
|
36
|
+
}) })
|
|
37
|
+
});
|
|
38
|
+
return o === "pill" ? /* @__PURE__ */ l("label", {
|
|
39
|
+
className: e(g, f),
|
|
40
|
+
children: [
|
|
41
|
+
y,
|
|
42
|
+
s && /* @__PURE__ */ c(t, {
|
|
43
|
+
name: s,
|
|
44
|
+
size: 20
|
|
45
|
+
}),
|
|
46
|
+
i
|
|
47
|
+
]
|
|
48
|
+
}) : /* @__PURE__ */ l("label", {
|
|
49
|
+
className: e(o === "card" ? h : "group flex cursor-pointer items-start gap-3 font-sans has-[:disabled]:cursor-not-allowed", "font-sans", f),
|
|
50
|
+
children: [
|
|
51
|
+
y,
|
|
52
|
+
s && o === "card" && /* @__PURE__ */ c(t, {
|
|
53
|
+
name: s,
|
|
54
|
+
size: 28,
|
|
55
|
+
className: "shrink-0 text-primary"
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ c(_, {
|
|
58
|
+
label: i,
|
|
59
|
+
description: a,
|
|
60
|
+
id: v
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function y({ options: n, variant: r = "default", horizontal: i, className: a, ...o }) {
|
|
66
|
+
let d = u(), p = r === "card" && n.some((e) => e.media);
|
|
67
|
+
return /* @__PURE__ */ c(f.Root, {
|
|
68
|
+
...o,
|
|
69
|
+
onKeyDown: (e) => {
|
|
70
|
+
o.onKeyDown?.(e), s(e);
|
|
71
|
+
},
|
|
72
|
+
className: e("font-sans", r === "pill" || i ? "flex flex-wrap gap-2" : p ? "grid grid-cols-[repeat(auto-fill,minmax(12rem,1fr))] gap-3" : r === "card" ? "grid gap-2" : "grid gap-3", a),
|
|
73
|
+
children: n.map((n, a) => {
|
|
74
|
+
let o = `${d}-${a}`, s = /* @__PURE__ */ c(f.Item, {
|
|
75
|
+
value: n.value,
|
|
76
|
+
disabled: n.disabled,
|
|
77
|
+
"aria-describedby": n.description ? o : void 0,
|
|
78
|
+
className: e("grid size-5 shrink-0 cursor-pointer place-items-center rounded-full border-2 border-fg-muted/60 bg-bg transition-colors data-[state=checked]:border-primary disabled:cursor-not-allowed disabled:opacity-50", r === "default" && e(m, "mt-0.5"), r === "card" && "mt-0.5 focus-visible:outline-none", r === "pill" && "sr-only"),
|
|
79
|
+
children: /* @__PURE__ */ c(f.Indicator, { className: "size-2.5 rounded-full bg-primary" })
|
|
80
|
+
});
|
|
81
|
+
return r === "pill" ? /* @__PURE__ */ l("label", {
|
|
82
|
+
className: g,
|
|
83
|
+
children: [
|
|
84
|
+
s,
|
|
85
|
+
n.icon && /* @__PURE__ */ c(t, {
|
|
86
|
+
name: n.icon,
|
|
87
|
+
size: 20
|
|
88
|
+
}),
|
|
89
|
+
n.label
|
|
90
|
+
]
|
|
91
|
+
}, n.value) : p ? /* @__PURE__ */ l("label", {
|
|
92
|
+
className: e(h, "flex-col gap-3 p-3", i && "min-w-[12rem] flex-1"),
|
|
93
|
+
children: [/* @__PURE__ */ c("span", {
|
|
94
|
+
"aria-hidden": "true",
|
|
95
|
+
className: "block aspect-video w-full overflow-hidden rounded-sm bg-surface-muted [&>img]:size-full [&>img]:object-cover",
|
|
96
|
+
children: n.media
|
|
97
|
+
}), /* @__PURE__ */ l("span", {
|
|
98
|
+
className: "flex items-start gap-3",
|
|
99
|
+
children: [s, /* @__PURE__ */ c(_, {
|
|
100
|
+
label: n.label,
|
|
101
|
+
description: n.description,
|
|
102
|
+
id: o
|
|
103
|
+
})]
|
|
104
|
+
})]
|
|
105
|
+
}, n.value) : /* @__PURE__ */ l("label", {
|
|
106
|
+
className: r === "card" ? e(h, i && "min-w-[12rem] flex-1") : "group flex cursor-pointer items-start gap-3 has-[:disabled]:cursor-not-allowed",
|
|
107
|
+
children: [
|
|
108
|
+
s,
|
|
109
|
+
n.icon && r === "card" && /* @__PURE__ */ c(t, {
|
|
110
|
+
name: n.icon,
|
|
111
|
+
size: 28,
|
|
112
|
+
className: "shrink-0 text-primary"
|
|
113
|
+
}),
|
|
114
|
+
/* @__PURE__ */ c(_, {
|
|
115
|
+
label: n.label,
|
|
116
|
+
description: n.description,
|
|
117
|
+
id: o
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
}, n.value);
|
|
121
|
+
})
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function b({ label: t, description: n, labelFirst: r, className: i, ...s }) {
|
|
125
|
+
let d = u();
|
|
126
|
+
return /* @__PURE__ */ l("label", {
|
|
127
|
+
className: e("group flex cursor-pointer items-start gap-3 font-sans has-[:disabled]:cursor-not-allowed", r && "flex-row-reverse justify-between", i),
|
|
128
|
+
children: [/* @__PURE__ */ c(p.Root, {
|
|
129
|
+
...s,
|
|
130
|
+
"aria-describedby": n ? d : void 0,
|
|
131
|
+
className: e("mt-px", o(), m),
|
|
132
|
+
children: /* @__PURE__ */ c(p.Thumb, { className: a() })
|
|
133
|
+
}), /* @__PURE__ */ c(_, {
|
|
134
|
+
label: t,
|
|
135
|
+
description: n,
|
|
136
|
+
id: d
|
|
137
|
+
})]
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { v as Checkbox, y as RadioGroup, b as Switch };
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=choice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice.js","names":["RadixCheckbox","RadixRadio","RadixSwitch","focus","focusRing","cardClasses","pillClasses","ChoiceText","label","description","Checkbox","rest","descId","useId","box","cx","checkboxBox","variant","Glyph","className","icon","Icon","RadioGroup","base","withMedia","options","o","selectOnArrow","horizontal","i","dot","Switch","labelFirst","switchTrack","switchThumb"],"sources":["../../src/choice/choice.tsx"],"sourcesContent":["'use client';\n\nimport * as RadixCheckbox from '@radix-ui/react-checkbox';\nimport * as RadixRadio from '@radix-ui/react-radio-group';\nimport * as RadixSwitch from '@radix-ui/react-switch';\nimport { useId, type ComponentPropsWithoutRef, type ReactNode } from 'react';\nimport { cx } from '../lib/cx';\nimport { checkboxBox, focusRing, switchThumb, switchTrack } from '../lib/control-styles';\nimport { Glyph } from '../lib/glyphs';\nimport { Icon, type IconName } from '../icon/icon';\nimport { selectOnArrow } from '../lib/select-on-arrow';\n\nexport type ChoiceVariant = 'default' | 'card' | 'pill';\n\nconst focus = focusRing;\n\nconst cardClasses =\n 'flex cursor-pointer items-start gap-3 rounded-md border border-border bg-surface p-4 transition-colors hover:border-fg-muted/50 ' +\n 'has-[[data-state=checked]]:border-primary has-[[data-state=checked]]:bg-info-surface/50 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ' +\n 'has-[:focus-visible]:outline-3 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus';\n\nconst pillClasses =\n 'inline-flex min-h-10 cursor-pointer items-center gap-2 rounded-full border border-border bg-surface px-4 text-sm font-semibold transition-colors hover:border-fg-muted/50 pointer-coarse:min-h-11 ' +\n 'has-[[data-state=checked]]:border-primary has-[[data-state=checked]]:bg-primary has-[[data-state=checked]]:text-on-primary has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 ' +\n 'has-[:focus-visible]:outline-3 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus';\n\nfunction ChoiceText({ label, description, id }: { label: ReactNode; description?: ReactNode; id?: string }) {\n return (\n <span className=\"grid gap-0.5\">\n <span className=\"text-[15px] leading-snug font-medium text-fg group-has-[:disabled]:text-fg-muted\">{label}</span>\n {description && (\n <span id={id} className=\"text-sm text-fg-muted\">\n {description}\n </span>\n )}\n </span>\n );\n}\n\nexport interface CheckboxProps extends Omit<ComponentPropsWithoutRef<typeof RadixCheckbox.Root>, 'children'> {\n label: ReactNode;\n description?: ReactNode;\n /** `card` for rich choices (IntentCard), `pill` for chips (RoleChip). */\n variant?: ChoiceVariant;\n icon?: IconName;\n}\n\n/** Checkbox with label and optional description. `checked=\"indeterminate\"` for partial selections. */\nexport function Checkbox({ label, description, variant = 'default', icon, className, ...rest }: CheckboxProps) {\n const descId = useId();\n const box = (\n <RadixCheckbox.Root\n {...rest}\n aria-describedby={description ? descId : undefined}\n className={cx(\n 'peer',\n checkboxBox(),\n variant === 'default' && focus,\n variant === 'default' ? 'mt-0.5' : variant === 'card' ? 'mt-0.5 focus-visible:outline-none' : 'sr-only',\n )}\n >\n <RadixCheckbox.Indicator>\n <Glyph name={rest.checked === 'indeterminate' ? 'minus' : 'check'} size={14} strokeWidth={3} />\n </RadixCheckbox.Indicator>\n </RadixCheckbox.Root>\n );\n if (variant === 'pill') {\n return (\n <label className={cx(pillClasses, className)}>\n {box}\n {icon && <Icon name={icon} size={20} />}\n {label}\n </label>\n );\n }\n return (\n <label\n className={cx(\n variant === 'card'\n ? cardClasses\n : 'group flex cursor-pointer items-start gap-3 font-sans has-[:disabled]:cursor-not-allowed',\n 'font-sans',\n className,\n )}\n >\n {box}\n {icon && variant === 'card' && <Icon name={icon} size={28} className=\"shrink-0 text-primary\" />}\n <ChoiceText label={label} description={description} id={descId} />\n </label>\n );\n}\n\nexport interface RadioGroupProps extends ComponentPropsWithoutRef<typeof RadixRadio.Root> {\n options: {\n value: string;\n label: ReactNode;\n description?: ReactNode;\n icon?: IconName;\n /** `card` only: a thumbnail above the label (an `<img>` or any preview), shown at 16:9 and cropped to fill. */\n media?: ReactNode;\n disabled?: boolean;\n }[];\n variant?: ChoiceVariant;\n /** Lay out horizontally (wraps). Pills are always horizontal. */\n horizontal?: boolean;\n}\n\n/** One choice out of 2–6 visible options. Arrow keys move the selection. */\nexport function RadioGroup({ options, variant = 'default', horizontal, className, ...rest }: RadioGroupProps) {\n const base = useId();\n const withMedia = variant === 'card' && options.some((o) => o.media);\n return (\n <RadixRadio.Root\n {...rest}\n onKeyDown={(e) => {\n rest.onKeyDown?.(e);\n selectOnArrow(e);\n }}\n className={cx(\n 'font-sans',\n // One gap per layout: `cx` does not merge conflicting utilities.\n variant === 'pill' || horizontal\n ? 'flex flex-wrap gap-2'\n : withMedia\n ? 'grid grid-cols-[repeat(auto-fill,minmax(12rem,1fr))] gap-3'\n : variant === 'card'\n ? 'grid gap-2'\n : 'grid gap-3',\n className,\n )}\n >\n {options.map((o, i) => {\n const descId = `${base}-${i}`;\n const dot = (\n <RadixRadio.Item\n value={o.value}\n disabled={o.disabled}\n aria-describedby={o.description ? descId : undefined}\n className={cx(\n 'grid size-5 shrink-0 cursor-pointer place-items-center rounded-full border-2 border-fg-muted/60 bg-bg transition-colors data-[state=checked]:border-primary disabled:cursor-not-allowed disabled:opacity-50',\n variant === 'default' && cx(focus, 'mt-0.5'),\n variant === 'card' && 'mt-0.5 focus-visible:outline-none',\n variant === 'pill' && 'sr-only',\n )}\n >\n <RadixRadio.Indicator className=\"size-2.5 rounded-full bg-primary\" />\n </RadixRadio.Item>\n );\n if (variant === 'pill') {\n return (\n <label key={o.value} className={pillClasses}>\n {dot}\n {o.icon && <Icon name={o.icon} size={20} />}\n {o.label}\n </label>\n );\n }\n if (withMedia) {\n return (\n <label key={o.value} className={cx(cardClasses, 'flex-col gap-3 p-3', horizontal && 'min-w-[12rem] flex-1')}>\n <span\n aria-hidden=\"true\"\n className=\"block aspect-video w-full overflow-hidden rounded-sm bg-surface-muted [&>img]:size-full [&>img]:object-cover\"\n >\n {o.media}\n </span>\n <span className=\"flex items-start gap-3\">\n {dot}\n <ChoiceText label={o.label} description={o.description} id={descId} />\n </span>\n </label>\n );\n }\n return (\n <label\n key={o.value}\n className={\n variant === 'card'\n ? cx(cardClasses, horizontal && 'min-w-[12rem] flex-1')\n : 'group flex cursor-pointer items-start gap-3 has-[:disabled]:cursor-not-allowed'\n }\n >\n {dot}\n {o.icon && variant === 'card' && <Icon name={o.icon} size={28} className=\"shrink-0 text-primary\" />}\n <ChoiceText label={o.label} description={o.description} id={descId} />\n </label>\n );\n })}\n </RadixRadio.Root>\n );\n}\n\nexport interface SwitchProps extends Omit<ComponentPropsWithoutRef<typeof RadixSwitch.Root>, 'children'> {\n label: ReactNode;\n description?: ReactNode;\n /** Put the switch after the text (settings lists). */\n labelFirst?: boolean;\n}\n\n/** On/off setting that applies immediately. For choices submitted with a form, prefer Checkbox. */\nexport function Switch({ label, description, labelFirst, className, ...rest }: SwitchProps) {\n const descId = useId();\n return (\n <label\n className={cx(\n 'group flex cursor-pointer items-start gap-3 font-sans has-[:disabled]:cursor-not-allowed',\n labelFirst && 'flex-row-reverse justify-between',\n className,\n )}\n >\n <RadixSwitch.Root\n {...rest}\n aria-describedby={description ? descId : undefined}\n className={cx('mt-px', switchTrack(), focus)}\n >\n <RadixSwitch.Thumb className={switchThumb()} />\n </RadixSwitch.Root>\n <ChoiceText label={label} description={description} id={descId} />\n </label>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type ControlSize } from '../input/field-styles';
|
|
2
|
+
export interface ComboboxOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface CommonProps {
|
|
9
|
+
/** Static options, filtered as the user types. Omit when using `loadOptions`. */
|
|
10
|
+
options?: ComboboxOption[];
|
|
11
|
+
/** Async search (debounced 250ms): return matching options for the query. */
|
|
12
|
+
loadOptions?: (query: string) => Promise<ComboboxOption[]>;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
size?: ControlSize;
|
|
15
|
+
id?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
invalid?: boolean;
|
|
19
|
+
'aria-label'?: string;
|
|
20
|
+
labels?: Partial<typeof LABELS>;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
interface SingleProps extends CommonProps {
|
|
24
|
+
multiple?: false;
|
|
25
|
+
value: string | null;
|
|
26
|
+
onChange: (value: string | null) => void;
|
|
27
|
+
}
|
|
28
|
+
interface MultipleProps extends CommonProps {
|
|
29
|
+
multiple: true;
|
|
30
|
+
value: string[];
|
|
31
|
+
onChange: (value: string[]) => void;
|
|
32
|
+
}
|
|
33
|
+
export type ComboboxProps = SingleProps | MultipleProps;
|
|
34
|
+
declare const LABELS: {
|
|
35
|
+
empty: string;
|
|
36
|
+
loading: string;
|
|
37
|
+
remove: string;
|
|
38
|
+
clear: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Searchable select for long lists (countries, customers, apps), with async search and
|
|
42
|
+
* multi-select chips. ARIA combobox pattern: type to filter, ↑↓ to move, ↵ to pick,
|
|
43
|
+
* Backspace removes the last chip.
|
|
44
|
+
*/
|
|
45
|
+
export declare function Combobox(props: ComboboxProps): import("react").JSX.Element;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx as e } from "../lib/cx.js";
|
|
3
|
+
import { Spinner as t } from "../spinner/spinner.js";
|
|
4
|
+
import { Glyph as n } from "../lib/glyphs.js";
|
|
5
|
+
import { controlClasses as r } from "../input/field-styles.js";
|
|
6
|
+
import { useFieldProps as i } from "../input/input.js";
|
|
7
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
8
|
+
import { useEffect as s, useId as c, useMemo as l, useRef as u, useState as d } from "react";
|
|
9
|
+
import * as f from "@radix-ui/react-popover";
|
|
10
|
+
//#region src/combobox/combobox.tsx
|
|
11
|
+
var p = {
|
|
12
|
+
empty: "No matches",
|
|
13
|
+
loading: "Searching…",
|
|
14
|
+
remove: "Remove {label}",
|
|
15
|
+
clear: "Clear"
|
|
16
|
+
}, m = (e) => e.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
|
|
17
|
+
function h(h) {
|
|
18
|
+
let { options: g = [], loadOptions: _, placeholder: v = "Search…", size: y = "md", labels: b, className: x } = h, S = {
|
|
19
|
+
...p,
|
|
20
|
+
...b
|
|
21
|
+
}, { invalid: C, ...w } = i(h), T = c(), E = u(null), [D, O] = d(!1), [k, A] = d(""), [j, M] = d(0), [N, P] = d([]), [F, I] = d(!1), L = u(/* @__PURE__ */ new Map());
|
|
22
|
+
for (let e of [...g, ...N]) L.current.set(e.value, e);
|
|
23
|
+
let R = h.multiple ? h.value : h.value ? [h.value] : [], z = !h.multiple && h.value ? L.current.get(h.value)?.label ?? h.value : "";
|
|
24
|
+
s(() => {
|
|
25
|
+
if (!_ || !D) return;
|
|
26
|
+
let e = !1;
|
|
27
|
+
I(!0);
|
|
28
|
+
let t = setTimeout(() => {
|
|
29
|
+
_(k).then((t) => !e && P(t)).finally(() => !e && I(!1));
|
|
30
|
+
}, 250);
|
|
31
|
+
return () => {
|
|
32
|
+
e = !0, clearTimeout(t);
|
|
33
|
+
};
|
|
34
|
+
}, [
|
|
35
|
+
k,
|
|
36
|
+
D,
|
|
37
|
+
_
|
|
38
|
+
]);
|
|
39
|
+
let B = l(() => {
|
|
40
|
+
if (_) return N;
|
|
41
|
+
let e = m(k);
|
|
42
|
+
return e ? g.filter((t) => m(`${t.label} ${t.hint ?? ""}`).includes(e)) : g;
|
|
43
|
+
}, [
|
|
44
|
+
_,
|
|
45
|
+
N,
|
|
46
|
+
g,
|
|
47
|
+
k
|
|
48
|
+
]);
|
|
49
|
+
s(() => M(0), [k, D]);
|
|
50
|
+
let V = (e) => {
|
|
51
|
+
e && !e.disabled && (h.multiple ? (h.onChange(R.includes(e.value) ? R.filter((t) => t !== e.value) : [...R, e.value]), A("")) : (h.onChange(e.value), A(""), O(!1)), E.current?.focus());
|
|
52
|
+
}, H = (e) => {
|
|
53
|
+
e.key === "ArrowDown" ? (e.preventDefault(), D ? M((e) => Math.min(B.length - 1, e + 1)) : O(!0)) : e.key === "ArrowUp" ? (e.preventDefault(), M((e) => Math.max(0, e - 1))) : e.key === "Enter" && D ? (e.preventDefault(), V(B[j])) : e.key === "Escape" && D ? (e.preventDefault(), O(!1)) : e.key === "Backspace" && !k && h.multiple && R.length && h.onChange(R.slice(0, -1));
|
|
54
|
+
}, U = (e) => `${T}-${e}`, W = D && !w.disabled;
|
|
55
|
+
return /* @__PURE__ */ o(f.Root, {
|
|
56
|
+
open: W,
|
|
57
|
+
onOpenChange: O,
|
|
58
|
+
children: [/* @__PURE__ */ a(f.Anchor, {
|
|
59
|
+
asChild: !0,
|
|
60
|
+
children: /* @__PURE__ */ o("div", {
|
|
61
|
+
className: e(r(y, C), "flex flex-wrap items-center gap-1.5 px-2.5 py-1.5", x),
|
|
62
|
+
"aria-disabled": w.disabled || void 0,
|
|
63
|
+
onClick: () => E.current?.focus(),
|
|
64
|
+
children: [
|
|
65
|
+
h.multiple && R.map((e) => {
|
|
66
|
+
let t = L.current.get(e)?.label ?? e;
|
|
67
|
+
return /* @__PURE__ */ o("span", {
|
|
68
|
+
className: "inline-flex items-center gap-1 rounded-full bg-surface-muted py-0.5 pr-1 pl-2.5 text-sm font-medium",
|
|
69
|
+
children: [t, /* @__PURE__ */ a("button", {
|
|
70
|
+
type: "button",
|
|
71
|
+
"aria-label": S.remove.replace("{label}", t),
|
|
72
|
+
disabled: w.disabled,
|
|
73
|
+
onClick: (t) => {
|
|
74
|
+
t.stopPropagation(), h.onChange(R.filter((t) => t !== e));
|
|
75
|
+
},
|
|
76
|
+
className: "grid size-5 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-fg-muted hover:bg-bg hover:text-fg focus-visible:outline-2 focus-visible:outline-focus",
|
|
77
|
+
children: /* @__PURE__ */ a(n, {
|
|
78
|
+
name: "close",
|
|
79
|
+
size: 12
|
|
80
|
+
})
|
|
81
|
+
})]
|
|
82
|
+
}, e);
|
|
83
|
+
}),
|
|
84
|
+
/* @__PURE__ */ a("input", {
|
|
85
|
+
ref: E,
|
|
86
|
+
id: w.id,
|
|
87
|
+
role: "combobox",
|
|
88
|
+
"aria-expanded": W,
|
|
89
|
+
"aria-controls": T,
|
|
90
|
+
"aria-autocomplete": "list",
|
|
91
|
+
"aria-activedescendant": W && B.length ? U(j) : void 0,
|
|
92
|
+
"aria-invalid": w["aria-invalid"],
|
|
93
|
+
"aria-describedby": w["aria-describedby"],
|
|
94
|
+
"aria-label": h["aria-label"],
|
|
95
|
+
"aria-required": w.required,
|
|
96
|
+
disabled: w.disabled,
|
|
97
|
+
autoComplete: "off",
|
|
98
|
+
placeholder: R.length && h.multiple ? "" : z || v,
|
|
99
|
+
value: k,
|
|
100
|
+
onChange: (e) => {
|
|
101
|
+
A(e.target.value), O(!0);
|
|
102
|
+
},
|
|
103
|
+
onFocus: () => O(!0),
|
|
104
|
+
onKeyDown: H,
|
|
105
|
+
className: e("min-w-[6rem] flex-1 border-0 bg-transparent px-0.5 py-1 font-[inherit] text-[length:inherit] text-fg outline-none disabled:cursor-not-allowed", !h.multiple && z && !k ? "placeholder:text-fg" : "placeholder:text-fg-muted/80")
|
|
106
|
+
}),
|
|
107
|
+
!h.multiple && h.value && !w.disabled && /* @__PURE__ */ a("button", {
|
|
108
|
+
type: "button",
|
|
109
|
+
"aria-label": S.clear,
|
|
110
|
+
onClick: (e) => {
|
|
111
|
+
e.stopPropagation(), h.onChange(null);
|
|
112
|
+
},
|
|
113
|
+
className: "grid size-7 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-fg-muted hover:bg-surface-muted focus-visible:outline-2 focus-visible:outline-focus",
|
|
114
|
+
children: /* @__PURE__ */ a(n, {
|
|
115
|
+
name: "close",
|
|
116
|
+
size: 14
|
|
117
|
+
})
|
|
118
|
+
}),
|
|
119
|
+
/* @__PURE__ */ a(n, {
|
|
120
|
+
name: "chevron-down",
|
|
121
|
+
size: 16,
|
|
122
|
+
className: "mr-1 shrink-0 text-fg-muted"
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
})
|
|
126
|
+
}), /* @__PURE__ */ a(f.Portal, { children: /* @__PURE__ */ a(f.Content, {
|
|
127
|
+
align: "start",
|
|
128
|
+
sideOffset: 6,
|
|
129
|
+
collisionPadding: 16,
|
|
130
|
+
role: "presentation",
|
|
131
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
132
|
+
onInteractOutside: (e) => {
|
|
133
|
+
e.target instanceof Node && E.current?.parentElement?.contains(e.target) && e.preventDefault();
|
|
134
|
+
},
|
|
135
|
+
className: "kf-menu z-50 max-h-[min(20rem,var(--radix-popover-content-available-height))] w-[var(--radix-popover-trigger-width)] overflow-y-auto rounded-md border border-border bg-surface p-1 font-sans text-fg shadow-overlay",
|
|
136
|
+
children: /* @__PURE__ */ a("div", {
|
|
137
|
+
id: T,
|
|
138
|
+
role: "listbox",
|
|
139
|
+
"aria-multiselectable": h.multiple || void 0,
|
|
140
|
+
"aria-busy": F || void 0,
|
|
141
|
+
children: F && !B.length ? /* @__PURE__ */ o("div", {
|
|
142
|
+
className: "flex items-center gap-2 px-3 py-3 text-sm text-fg-muted",
|
|
143
|
+
children: [/* @__PURE__ */ a(t, {
|
|
144
|
+
size: 20,
|
|
145
|
+
className: "text-primary"
|
|
146
|
+
}), S.loading]
|
|
147
|
+
}) : B.length === 0 ? /* @__PURE__ */ a("div", {
|
|
148
|
+
className: "px-3 py-3 text-sm text-fg-muted",
|
|
149
|
+
children: S.empty
|
|
150
|
+
}) : B.map((t, r) => {
|
|
151
|
+
let i = R.includes(t.value);
|
|
152
|
+
return /* @__PURE__ */ o("div", {
|
|
153
|
+
id: U(r),
|
|
154
|
+
role: "option",
|
|
155
|
+
"aria-selected": i,
|
|
156
|
+
"aria-disabled": t.disabled || void 0,
|
|
157
|
+
tabIndex: -1,
|
|
158
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
159
|
+
onMouseMove: () => M(r),
|
|
160
|
+
onClick: () => V(t),
|
|
161
|
+
className: e("flex min-h-9 cursor-pointer items-center gap-2.5 rounded-sm px-2.5 py-1.5 text-sm pointer-coarse:min-h-11", r === j && "bg-surface-muted", t.disabled && "pointer-events-none opacity-45"),
|
|
162
|
+
children: [
|
|
163
|
+
h.multiple && /* @__PURE__ */ a("span", {
|
|
164
|
+
className: e("grid size-4 shrink-0 place-items-center rounded-[4px] border-2", i ? "border-primary bg-primary text-on-primary" : "border-fg-muted/60"),
|
|
165
|
+
children: i && /* @__PURE__ */ a(n, {
|
|
166
|
+
name: "check",
|
|
167
|
+
size: 12,
|
|
168
|
+
strokeWidth: 3
|
|
169
|
+
})
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ o("span", {
|
|
172
|
+
className: "min-w-0 flex-1",
|
|
173
|
+
children: [/* @__PURE__ */ a("span", {
|
|
174
|
+
className: e("block truncate", i && "font-semibold"),
|
|
175
|
+
children: t.label
|
|
176
|
+
}), t.hint && /* @__PURE__ */ a("span", {
|
|
177
|
+
className: "block truncate text-xs text-fg-muted",
|
|
178
|
+
children: t.hint
|
|
179
|
+
})]
|
|
180
|
+
}),
|
|
181
|
+
!h.multiple && i && /* @__PURE__ */ a(n, {
|
|
182
|
+
name: "check",
|
|
183
|
+
size: 16,
|
|
184
|
+
className: "text-link"
|
|
185
|
+
})
|
|
186
|
+
]
|
|
187
|
+
}, t.value);
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
}) })]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
export { h as Combobox };
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=combobox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox.js","names":["Popover","LABELS","normalize","s","Combobox","overrides","props","labels","field","useFieldProps","listId","useId","inputRef","useRef","open","setOpen","useState","query","setQuery","active","setActive","remote","setRemote","loading","setLoading","known","o","options","selected","selectedLabel","loadOptions","cancelled","r","results","useMemo","q","pick","v","onKeyDown","a","optionId","i","showList","cx","controlClasses","size","invalid","className","label","e","Glyph","placeholder","Spinner","isSelected"],"sources":["../../src/combobox/combobox.tsx"],"sourcesContent":["'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport { useEffect, useId, useMemo, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react';\nimport { cx } from '../lib/cx';\nimport { Glyph } from '../lib/glyphs';\nimport { Spinner } from '../spinner/spinner';\nimport { useFieldProps } from '../input/input';\nimport { controlClasses, type ControlSize } from '../input/field-styles';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n hint?: string;\n disabled?: boolean;\n}\n\ninterface CommonProps {\n /** Static options, filtered as the user types. Omit when using `loadOptions`. */\n options?: ComboboxOption[];\n /** Async search (debounced 250ms): return matching options for the query. */\n loadOptions?: (query: string) => Promise<ComboboxOption[]>;\n placeholder?: string;\n size?: ControlSize;\n id?: string;\n disabled?: boolean;\n required?: boolean;\n invalid?: boolean;\n 'aria-label'?: string;\n labels?: Partial<typeof LABELS>;\n className?: string;\n}\n\ninterface SingleProps extends CommonProps {\n multiple?: false;\n value: string | null;\n onChange: (value: string | null) => void;\n}\n\ninterface MultipleProps extends CommonProps {\n multiple: true;\n value: string[];\n onChange: (value: string[]) => void;\n}\n\nexport type ComboboxProps = SingleProps | MultipleProps;\n\nconst LABELS = {\n empty: 'No matches',\n loading: 'Searching…',\n remove: 'Remove {label}',\n clear: 'Clear',\n};\n\nconst normalize = (s: string) => s.toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');\n\n/**\n * Searchable select for long lists (countries, customers, apps), with async search and\n * multi-select chips. ARIA combobox pattern: type to filter, ↑↓ to move, ↵ to pick,\n * Backspace removes the last chip.\n */\nexport function Combobox(props: ComboboxProps) {\n const { options = [], loadOptions, placeholder = 'Search…', size = 'md', labels: overrides, className } = props;\n const labels = { ...LABELS, ...overrides };\n const { invalid, ...field } = useFieldProps(props);\n const listId = useId();\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [active, setActive] = useState(0);\n const [remote, setRemote] = useState<ComboboxOption[]>([]);\n const [loading, setLoading] = useState(false);\n // Remember labels of selected options (async results come and go).\n const known = useRef(new Map<string, ComboboxOption>());\n for (const o of [...options, ...remote]) known.current.set(o.value, o);\n\n const selected = props.multiple ? props.value : props.value ? [props.value] : [];\n const selectedLabel = !props.multiple && props.value ? (known.current.get(props.value)?.label ?? props.value) : '';\n\n useEffect(() => {\n if (!loadOptions || !open) return;\n let cancelled = false;\n setLoading(true);\n const t = setTimeout(() => {\n loadOptions(query)\n .then((r) => !cancelled && setRemote(r))\n .finally(() => !cancelled && setLoading(false));\n }, 250);\n return () => {\n cancelled = true;\n clearTimeout(t);\n };\n }, [query, open, loadOptions]);\n\n const results = useMemo(() => {\n if (loadOptions) return remote;\n const q = normalize(query);\n return q ? options.filter((o) => normalize(`${o.label} ${o.hint ?? ''}`).includes(q)) : options;\n }, [loadOptions, remote, options, query]);\n\n useEffect(() => setActive(0), [query, open]);\n\n const pick = (o: ComboboxOption | undefined) => {\n if (!o || o.disabled) return;\n if (props.multiple) {\n props.onChange(selected.includes(o.value) ? selected.filter((v) => v !== o.value) : [...selected, o.value]);\n setQuery('');\n } else {\n props.onChange(o.value);\n setQuery('');\n setOpen(false);\n }\n inputRef.current?.focus();\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n if (!open) setOpen(true);\n else setActive((a) => Math.min(results.length - 1, a + 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setActive((a) => Math.max(0, a - 1));\n } else if (e.key === 'Enter' && open) {\n e.preventDefault();\n pick(results[active]);\n } else if (e.key === 'Escape' && open) {\n e.preventDefault();\n setOpen(false);\n } else if (e.key === 'Backspace' && !query && props.multiple && selected.length) {\n props.onChange(selected.slice(0, -1));\n }\n };\n\n const optionId = (i: number) => `${listId}-${i}`;\n const showList = open && !field.disabled;\n\n return (\n <Popover.Root open={showList} onOpenChange={setOpen}>\n <Popover.Anchor asChild>\n <div\n className={cx(controlClasses(size, invalid), 'flex flex-wrap items-center gap-1.5 px-2.5 py-1.5', className)}\n aria-disabled={field.disabled || undefined}\n onClick={() => inputRef.current?.focus()}\n >\n {props.multiple &&\n selected.map((v) => {\n const label = known.current.get(v)?.label ?? v;\n return (\n <span\n key={v}\n className=\"inline-flex items-center gap-1 rounded-full bg-surface-muted py-0.5 pr-1 pl-2.5 text-sm font-medium\"\n >\n {label}\n <button\n type=\"button\"\n aria-label={labels.remove.replace('{label}', label)}\n disabled={field.disabled}\n onClick={(e) => {\n e.stopPropagation();\n props.onChange(selected.filter((s) => s !== v));\n }}\n className=\"grid size-5 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-fg-muted hover:bg-bg hover:text-fg focus-visible:outline-2 focus-visible:outline-focus\"\n >\n <Glyph name=\"close\" size={12} />\n </button>\n </span>\n );\n })}\n <input\n ref={inputRef}\n id={field.id}\n role=\"combobox\"\n aria-expanded={showList}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={showList && results.length ? optionId(active) : undefined}\n aria-invalid={field['aria-invalid']}\n aria-describedby={field['aria-describedby']}\n aria-label={props['aria-label']}\n aria-required={field.required}\n disabled={field.disabled}\n autoComplete=\"off\"\n placeholder={selected.length && props.multiple ? '' : selectedLabel || placeholder}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={onKeyDown}\n className={cx(\n 'min-w-[6rem] flex-1 border-0 bg-transparent px-0.5 py-1 font-[inherit] text-[length:inherit] text-fg outline-none disabled:cursor-not-allowed',\n !props.multiple && selectedLabel && !query ? 'placeholder:text-fg' : 'placeholder:text-fg-muted/80',\n )}\n />\n {!props.multiple && props.value && !field.disabled && (\n <button\n type=\"button\"\n aria-label={labels.clear}\n onClick={(e) => {\n e.stopPropagation();\n props.onChange(null);\n }}\n className=\"grid size-7 cursor-pointer place-items-center rounded-full border-0 bg-transparent text-fg-muted hover:bg-surface-muted focus-visible:outline-2 focus-visible:outline-focus\"\n >\n <Glyph name=\"close\" size={14} />\n </button>\n )}\n <Glyph name=\"chevron-down\" size={16} className=\"mr-1 shrink-0 text-fg-muted\" />\n </div>\n </Popover.Anchor>\n <Popover.Portal>\n <Popover.Content\n align=\"start\"\n sideOffset={6}\n collisionPadding={16}\n role=\"presentation\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n onInteractOutside={(e) => {\n if (e.target instanceof Node && inputRef.current?.parentElement?.contains(e.target)) e.preventDefault();\n }}\n className=\"kf-menu z-50 max-h-[min(20rem,var(--radix-popover-content-available-height))] w-[var(--radix-popover-trigger-width)] overflow-y-auto rounded-md border border-border bg-surface p-1 font-sans text-fg shadow-overlay\"\n >\n <div\n id={listId}\n role=\"listbox\"\n aria-multiselectable={props.multiple || undefined}\n aria-busy={loading || undefined}\n >\n {loading && !results.length ? (\n <div className=\"flex items-center gap-2 px-3 py-3 text-sm text-fg-muted\">\n <Spinner size={20} className=\"text-primary\" />\n {labels.loading}\n </div>\n ) : results.length === 0 ? (\n <div className=\"px-3 py-3 text-sm text-fg-muted\">{labels.empty}</div>\n ) : (\n results.map((o, i) => {\n const isSelected = selected.includes(o.value);\n return (\n <div\n key={o.value}\n id={optionId(i)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={o.disabled || undefined}\n tabIndex={-1}\n onMouseDown={(e) => e.preventDefault()}\n onMouseMove={() => setActive(i)}\n onClick={() => pick(o)}\n className={cx(\n 'flex min-h-9 cursor-pointer items-center gap-2.5 rounded-sm px-2.5 py-1.5 text-sm pointer-coarse:min-h-11',\n i === active && 'bg-surface-muted',\n o.disabled && 'pointer-events-none opacity-45',\n )}\n >\n {props.multiple && (\n <span\n className={cx(\n 'grid size-4 shrink-0 place-items-center rounded-[4px] border-2',\n isSelected ? 'border-primary bg-primary text-on-primary' : 'border-fg-muted/60',\n )}\n >\n {isSelected && <Glyph name=\"check\" size={12} strokeWidth={3} />}\n </span>\n )}\n <span className=\"min-w-0 flex-1\">\n <span className={cx('block truncate', isSelected && 'font-semibold')}>{o.label}</span>\n {o.hint && <span className=\"block truncate text-xs text-fg-muted\">{o.hint}</span>}\n </span>\n {!props.multiple && isSelected && <Glyph name=\"check\" size={16} className=\"text-link\" />}\n </div>\n );\n })\n )}\n </div>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type IconName } from '../icon/icon';
|
|
2
|
+
export interface CommandItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Secondary text (e.g. a path or description). */
|
|
6
|
+
hint?: string;
|
|
7
|
+
icon?: IconName;
|
|
8
|
+
shortcut?: string;
|
|
9
|
+
/** Extra search terms (synonyms, Spanish words…). */
|
|
10
|
+
keywords?: string[];
|
|
11
|
+
onSelect: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface CommandGroup {
|
|
14
|
+
heading: string;
|
|
15
|
+
items: CommandItem[];
|
|
16
|
+
}
|
|
17
|
+
export interface CommandPaletteProps {
|
|
18
|
+
open: boolean;
|
|
19
|
+
onOpenChange: (open: boolean) => void;
|
|
20
|
+
groups: CommandGroup[];
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
emptyTitle?: string;
|
|
23
|
+
emptyDescription?: string;
|
|
24
|
+
/** Accessible name of the dialog. */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** Opens with ⌘K / Ctrl+K. Set `false` to wire your own shortcut. */
|
|
27
|
+
hotkey?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Every query word must appear in the label, hint or keywords (accent-insensitive). Label-start matches rank first. */
|
|
30
|
+
export declare function filterCommands(groups: CommandGroup[], query: string): CommandGroup[];
|
|
31
|
+
/**
|
|
32
|
+
* ⌘K search across a product's actions and pages. Combobox pattern: type to filter, arrows to
|
|
33
|
+
* move, Enter to run, Escape to close. Full-screen sheet on mobile.
|
|
34
|
+
*/
|
|
35
|
+
export declare function CommandPalette({ open, onOpenChange, groups, placeholder, emptyTitle, emptyDescription, label, hotkey, }: CommandPaletteProps): import("react").JSX.Element;
|