@adea-ai/ui 0.99.0 → 0.99.2
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/components/composites/appearance-editor/appearance-choices.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-choices.js +32 -28
- package/dist/components/composites/appearance-editor/appearance-choices.js.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-rows.d.ts.map +1 -1
- package/dist/components/composites/appearance-editor/appearance-rows.js +1 -1
- package/dist/components/composites/appearance-editor/appearance-rows.js.map +1 -1
- package/dist/components/layout/orbit-layout/orbit-layout.d.ts +5 -1
- package/dist/components/layout/orbit-layout/orbit-layout.d.ts.map +1 -1
- package/dist/components/layout/orbit-layout/orbit-layout.js.map +1 -1
- package/dist/components/layout/side-rail/side-rail.d.ts +4 -1
- package/dist/components/layout/side-rail/side-rail.d.ts.map +1 -1
- package/dist/components/layout/side-rail/side-rail.js +58 -64
- package/dist/components/layout/side-rail/side-rail.js.map +1 -1
- package/dist/components/layout/sidebar-nav/sidebar-nav.js +2 -2
- package/dist/components/layout/sidebar-nav/sidebar-nav.js.map +1 -1
- package/dist/components/motion/presence.d.ts +17 -7
- package/dist/components/motion/presence.d.ts.map +1 -1
- package/dist/components/motion/presence.js.map +1 -1
- package/dist/components/theme/theme-picker.d.ts.map +1 -1
- package/dist/components/theme/theme-picker.js +1 -1
- package/dist/components/theme/theme-picker.js.map +1 -1
- package/dist/components/theme/theme-provider.d.ts.map +1 -1
- package/dist/components/theme/theme-provider.js +15 -4
- package/dist/components/theme/theme-provider.js.map +1 -1
- package/dist/components/ui/aspect-ratio/aspect-ratio.d.ts +2 -2
- package/dist/components/ui/aspect-ratio/aspect-ratio.d.ts.map +1 -1
- package/dist/components/ui/aspect-ratio/aspect-ratio.js +15 -1
- package/dist/components/ui/aspect-ratio/aspect-ratio.js.map +1 -1
- package/dist/components/ui/badge/badge.d.ts +1 -1
- package/dist/components/ui/button/button.d.ts +1 -1
- package/dist/components/ui/calendar/calendar.d.ts.map +1 -1
- package/dist/components/ui/calendar/calendar.js +1 -1
- package/dist/components/ui/calendar/calendar.js.map +1 -1
- package/dist/components/ui/card/card.d.ts +2 -2
- package/dist/components/ui/card/card.js +2 -2
- package/dist/components/ui/card/card.js.map +1 -1
- package/dist/components/ui/chart/chart-options.d.ts +50 -0
- package/dist/components/ui/chart/chart-options.d.ts.map +1 -0
- package/dist/components/ui/chart/chart-options.js +178 -0
- package/dist/components/ui/chart/chart-options.js.map +1 -0
- package/dist/components/ui/chart/chart.d.ts +21 -18
- package/dist/components/ui/chart/chart.d.ts.map +1 -1
- package/dist/components/ui/chart/chart.js +175 -155
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart/index.js +2 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +4 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/input-otp/input-otp.d.ts.map +1 -1
- package/dist/components/ui/input-otp/input-otp.js +2 -2
- package/dist/components/ui/input-otp/input-otp.js.map +1 -1
- package/dist/components/ui/pagination/pagination.d.ts.map +1 -1
- package/dist/components/ui/pagination/pagination.js +1 -1
- package/dist/components/ui/pagination/pagination.js.map +1 -1
- package/dist/components/ui/resizable/resizable.d.ts +0 -12
- package/dist/components/ui/resizable/resizable.d.ts.map +1 -1
- package/dist/components/ui/resizable/resizable.js +43 -9
- package/dist/components/ui/resizable/resizable.js.map +1 -1
- package/dist/lib/cn-tables.generated.js +10 -10
- package/dist/lib/cn-tables.generated.js.map +1 -1
- package/dist/r/chart.json +5 -0
- package/dist/r/registry.json +5 -0
- package/dist/r/src/components/composites/appearance-editor/appearance-choices.tsx +35 -20
- package/dist/r/src/components/composites/appearance-editor/appearance-rows.tsx +6 -2
- package/dist/r/src/components/layout/orbit-layout/orbit-layout.tsx +5 -1
- package/dist/r/src/components/layout/side-rail/side-rail.tsx +29 -16
- package/dist/r/src/components/layout/sidebar-nav/sidebar-nav.tsx +2 -2
- package/dist/r/src/components/motion/presence.tsx +17 -7
- package/dist/r/src/components/theme/theme-picker.tsx +5 -1
- package/dist/r/src/components/theme/theme-provider.tsx +19 -9
- package/dist/r/src/components/ui/aspect-ratio/aspect-ratio.tsx +14 -3
- package/dist/r/src/components/ui/calendar/calendar.tsx +6 -1
- package/dist/r/src/components/ui/card/card.tsx +2 -2
- package/dist/r/src/components/ui/chart/chart-options.ts +196 -0
- package/dist/r/src/components/ui/chart/chart.tsx +149 -101
- package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +10 -2
- package/dist/r/src/components/ui/input-otp/input-otp.tsx +14 -2
- package/dist/r/src/components/ui/pagination/pagination.tsx +8 -1
- package/dist/r/src/components/ui/resizable/resizable.tsx +46 -8
- package/dist/r/src/lib/cn-tables.generated.ts +10 -10
- package/dist/r/src/styles/base.css +4 -1
- package/dist/r/src/styles/theme.css +23 -43
- package/package.json +1 -1
- package/registry.json +5 -0
- package/src/components/composites/appearance-editor/appearance-choices.tsx +35 -20
- package/src/components/composites/appearance-editor/appearance-rows.tsx +6 -2
- package/src/components/layout/orbit-layout/orbit-layout.tsx +5 -1
- package/src/components/layout/side-rail/side-rail.tsx +29 -16
- package/src/components/layout/sidebar-nav/sidebar-nav.tsx +2 -2
- package/src/components/motion/presence.tsx +17 -7
- package/src/components/theme/theme-picker.tsx +5 -1
- package/src/components/theme/theme-provider.tsx +19 -9
- package/src/components/ui/aspect-ratio/aspect-ratio.tsx +14 -3
- package/src/components/ui/calendar/calendar.tsx +6 -1
- package/src/components/ui/card/card.tsx +2 -2
- package/src/components/ui/chart/chart-options.ts +196 -0
- package/src/components/ui/chart/chart.tsx +149 -101
- package/src/components/ui/dropdown-menu/dropdown-menu.tsx +10 -2
- package/src/components/ui/input-otp/input-otp.tsx +14 -2
- package/src/components/ui/pagination/pagination.tsx +8 -1
- package/src/components/ui/resizable/resizable.tsx +46 -8
- package/src/lib/cn-tables.generated.ts +10 -10
- package/src/styles/base.css +4 -1
- package/src/styles/theme.css +23 -43
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-picker.js","names":["ComponentProps","For","Show","splitProps","cn","ThemeVariant","Button","ThemeSwatchProps","theme","size","swatchSizes","sm","md","lg","const","ThemeSwatch","props","local","rest","colors","_el$","_tmpl$","_$spread","_$mergeProps","class","_$insert","_$createComponent","each","background","card","primary","accent","destructiveAction","children","color","_el$2","_tmpl$2","_$setStyleProperty","_$effect","_$className","ThemePreviewProps","selected","ThemePreview","c","_el$3","_tmpl$3","_el$4","firstChild","_el$5","_el$6","_el$7","nextSibling","_el$8","_el$9","_el$0","_el$1","_el$10","_el$11","_el$12","_el$13","_el$14","style","_p$","_v$","sidebar","_v$2","sidebarBorder","_v$3","_v$4","sidebarAccent","_v$5","_v$6","foreground","_v$7","mutedForeground","_v$8","_v$9","_v$0","_v$1","secondary","e","t","a","o","i","n","s","h","r","d","l","undefined","ThemePickerProps","themes","selectedId","onThemeSelect","ThemePicker","_el$15","_tmpl$4","as","type","variant","role","aria-checked","id","aria-label","familyLabel","label","onClick","_el$16","_tmpl$6","_el$17","when","family","_el$18","_tmpl$5","_$mergeProps","_$createComponent","_$className","_$setStyleProperty"],"sources":["../../../src/components/theme/theme-picker.tsx"],"sourcesContent":["import type { ComponentProps } from 'solid-js'\nimport { For, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport type { ThemeVariant } from '#lib/themes'\nimport { Button } from '../ui/button/button'\n\n/**\n * ThemeSwatch.\n *\n * The colours of a theme, as a small readable strip: the canvas, a card, the\n * primary, an accent and the destructive fill. Enough to tell two dark themes\n * apart at a glance and to recognise the one you want without applying it.\n *\n * A swatch is **read from the variant object**, not from a stylesheet, so a picker\n * can show a theme that is not currently applied — which is the whole point of a\n * picker. That is also why the catalogue is data rather than CSS.\n */\nexport type ThemeSwatchProps = ComponentProps<'span'> & {\n theme: ThemeVariant\n /** Draw the swatch at the given size. */\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst swatchSizes = {\n sm: 'h-4 w-4',\n md: 'h-6 w-6',\n lg: 'h-8 w-8',\n} as const\n\nexport function ThemeSwatch(props: ThemeSwatchProps) {\n const [local, rest] = splitProps(props, ['class', 'theme', 'size'])\n const colors = () => local.theme.colors\n\n return (\n <span\n data-slot=\"theme-swatch\"\n aria-hidden=\"true\"\n class={cn(\n 'border-border inline-flex shrink-0 overflow-hidden rounded-md border',\n local.class\n )}\n {...rest}\n >\n <For\n each={[\n colors().background,\n colors().card,\n colors().primary,\n colors().accent,\n colors().destructiveAction,\n ]}\n >\n {(color) => (\n <span\n class={cn('block', swatchSizes[local.size ?? 'md'])}\n style={{ 'background-color': color }}\n />\n )}\n </For>\n </span>\n )\n}\n\n/**\n * ThemePreview.\n *\n * A miniature of the interface, painted in a theme's own colours: a rail, a panel,\n * a heading, body text, a primary button and a destructive one.\n *\n * The point is that a colour strip cannot answer the question a user actually has —\n * \"would I want to look at this all day?\" — and a real preview can. It is drawn\n * with inline colours from the variant rather than by applying the theme, so a list\n * of twenty previews costs one object read each instead of twenty theme swaps.\n */\nexport type ThemePreviewProps = ComponentProps<'div'> & {\n theme: ThemeVariant\n /** Highlight the preview as the current selection. */\n selected?: boolean\n}\n\nexport function ThemePreview(props: ThemePreviewProps) {\n const [local, rest] = splitProps(props, ['class', 'theme', 'selected'])\n const c = () => local.theme.colors\n\n return (\n <div\n data-slot=\"theme-preview\"\n class={cn(\n 'w-44 overflow-hidden rounded-lg border shadow-xs',\n local.selected ? 'border-primary ring-3 ring-primary-subtle' : 'border-border',\n local.class\n )}\n style={{ 'background-color': c().background }}\n {...rest}\n >\n <div class=\"flex h-24\">\n {/* A rail and a panel: the two surfaces a user spends the day looking at. */}\n <div\n class=\"theme-preview-edge flex w-6 flex-col items-center gap-1.5 border-e py-2\"\n style={{ 'background-color': c().sidebar, '--preview-edge': c().sidebarBorder }}\n >\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().primary }} />\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().sidebarAccent }} />\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().sidebarAccent }} />\n </div>\n <div class=\"flex flex-1 flex-col gap-1.5 p-2\">\n <div class=\"h-1.5 w-14 rounded-full\" style={{ 'background-color': c().foreground }} />\n <div class=\"h-1 w-20 rounded-full\" style={{ 'background-color': c().mutedForeground }} />\n <div class=\"h-1 w-16 rounded-full\" style={{ 'background-color': c().mutedForeground }} />\n <div class=\"mt-auto flex items-center gap-1\">\n <span class=\"h-3 w-9 rounded-sm\" style={{ 'background-color': c().primary }} />\n <span\n class=\"h-3 w-6 rounded-sm\"\n style={{ 'background-color': c().destructiveAction }}\n />\n <span class=\"h-3 w-6 rounded-sm\" style={{ 'background-color': c().secondary }} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * ThemePicker.\n *\n * The catalogue as a selectable grid. One appearance at a time — a light picker\n * lists light themes and a dark picker lists dark ones — because choosing a dark\n * theme while looking at a light interface is a choice nobody can evaluate.\n *\n * `onSelect` rather than an internal signal: the selection belongs to the\n * preference the provider owns, and a picker that kept its own copy could disagree\n * with what is applied.\n */\nexport type ThemePickerProps = ComponentProps<'div'> & {\n themes: readonly ThemeVariant[]\n selectedId: string\n onThemeSelect: (theme: ThemeVariant) => void\n}\n\nexport function ThemePicker(props: ThemePickerProps) {\n const [local, rest] = splitProps(props, ['class', 'themes', 'selectedId', 'onThemeSelect'])\n\n return (\n <div\n data-slot=\"theme-picker\"\n role=\"radiogroup\"\n aria-label=\"Theme\"\n class={cn('flex flex-wrap gap-3', local.class)}\n {...rest}\n >\n <For each={local.themes}>\n {(theme) => (\n <Button\n as=\"button\"\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"radio\"\n aria-checked={local.selectedId === theme.id}\n aria-label={`${theme.familyLabel} ${theme.label}`}\n class=\"h-auto flex-col gap-1.5 p-0 hover:bg-transparent\"\n onClick={() => local.onThemeSelect(theme)}\n >\n <ThemePreview theme={theme} selected={local.selectedId === theme.id} />\n <span class=\"flex flex-col items-center\">\n <span class=\"text-xs font-medium\">{theme.label}</span>\n <Show when={theme.family !== 'adea'}>\n <span class=\"text-muted-foreground text-2xs\">{theme.familyLabel}</span>\n </Show>\n </span>\n </Button>\n )}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme-picker.js","names":["ComponentProps","For","Show","splitProps","cn","ThemeVariant","Button","ThemeSwatchProps","theme","size","swatchSizes","sm","md","lg","const","ThemeSwatch","props","local","rest","colors","_el$","_tmpl$","_$spread","_$mergeProps","class","_$insert","_$createComponent","each","background","card","primary","accent","destructiveAction","children","color","_el$2","_tmpl$2","_$setStyleProperty","_$effect","_$className","ThemePreviewProps","selected","ThemePreview","c","_el$3","_tmpl$3","_el$4","firstChild","_el$5","_el$6","_el$7","nextSibling","_el$8","_el$9","_el$0","_el$1","_el$10","_el$11","_el$12","_el$13","_el$14","style","_p$","_v$","sidebar","_v$2","sidebarBorder","_v$3","_v$4","sidebarAccent","_v$5","_v$6","foreground","_v$7","mutedForeground","_v$8","_v$9","_v$0","_v$1","secondary","e","t","a","o","i","n","s","h","r","d","l","undefined","ThemePickerProps","themes","selectedId","onThemeSelect","ThemePicker","_el$15","_tmpl$4","as","type","variant","role","aria-checked","id","aria-label","familyLabel","label","onClick","_el$16","_tmpl$6","_el$17","when","family","_el$18","_tmpl$5","_$mergeProps","_$createComponent","_$className","_$setStyleProperty"],"sources":["../../../src/components/theme/theme-picker.tsx"],"sourcesContent":["import type { ComponentProps } from 'solid-js'\nimport { For, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport type { ThemeVariant } from '#lib/themes'\nimport { Button } from '../ui/button/button'\n\n/**\n * ThemeSwatch.\n *\n * The colours of a theme, as a small readable strip: the canvas, a card, the\n * primary, an accent and the destructive fill. Enough to tell two dark themes\n * apart at a glance and to recognise the one you want without applying it.\n *\n * A swatch is **read from the variant object**, not from a stylesheet, so a picker\n * can show a theme that is not currently applied — which is the whole point of a\n * picker. That is also why the catalogue is data rather than CSS.\n */\nexport type ThemeSwatchProps = ComponentProps<'span'> & {\n theme: ThemeVariant\n /** Draw the swatch at the given size. */\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst swatchSizes = {\n sm: 'h-4 w-4',\n md: 'h-6 w-6',\n lg: 'h-8 w-8',\n} as const\n\nexport function ThemeSwatch(props: ThemeSwatchProps) {\n const [local, rest] = splitProps(props, ['class', 'theme', 'size'])\n const colors = () => local.theme.colors\n\n return (\n <span\n data-slot=\"theme-swatch\"\n aria-hidden=\"true\"\n class={cn(\n 'border-border inline-flex shrink-0 overflow-hidden rounded-md border',\n local.class\n )}\n {...rest}\n >\n <For\n each={[\n colors().background,\n colors().card,\n colors().primary,\n colors().accent,\n colors().destructiveAction,\n ]}\n >\n {(color) => (\n <span\n class={cn('block', swatchSizes[local.size ?? 'md'])}\n style={{ 'background-color': color }}\n />\n )}\n </For>\n </span>\n )\n}\n\n/**\n * ThemePreview.\n *\n * A miniature of the interface, painted in a theme's own colours: a rail, a panel,\n * a heading, body text, a primary button and a destructive one.\n *\n * The point is that a colour strip cannot answer the question a user actually has —\n * \"would I want to look at this all day?\" — and a real preview can. It is drawn\n * with inline colours from the variant rather than by applying the theme, so a list\n * of twenty previews costs one object read each instead of twenty theme swaps.\n */\nexport type ThemePreviewProps = ComponentProps<'div'> & {\n theme: ThemeVariant\n /** Highlight the preview as the current selection. */\n selected?: boolean\n}\n\nexport function ThemePreview(props: ThemePreviewProps) {\n const [local, rest] = splitProps(props, ['class', 'theme', 'selected'])\n const c = () => local.theme.colors\n\n return (\n <div\n data-slot=\"theme-preview\"\n class={cn(\n 'w-44 overflow-hidden rounded-lg border shadow-xs',\n local.selected ? 'border-primary ring-3 ring-primary-subtle' : 'border-border',\n local.class\n )}\n style={{ 'background-color': c().background }}\n {...rest}\n >\n <div class=\"flex h-24\">\n {/* A rail and a panel: the two surfaces a user spends the day looking at. */}\n <div\n class=\"theme-preview-edge flex w-6 flex-col items-center gap-1.5 border-e py-2\"\n style={{ 'background-color': c().sidebar, '--preview-edge': c().sidebarBorder }}\n >\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().primary }} />\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().sidebarAccent }} />\n <span class=\"size-2.5 rounded-sm\" style={{ 'background-color': c().sidebarAccent }} />\n </div>\n <div class=\"flex flex-1 flex-col gap-1.5 p-2\">\n <div class=\"h-1.5 w-14 rounded-full\" style={{ 'background-color': c().foreground }} />\n <div class=\"h-1 w-20 rounded-full\" style={{ 'background-color': c().mutedForeground }} />\n <div class=\"h-1 w-16 rounded-full\" style={{ 'background-color': c().mutedForeground }} />\n <div class=\"mt-auto flex items-center gap-1\">\n <span class=\"h-3 w-9 rounded-sm\" style={{ 'background-color': c().primary }} />\n <span\n class=\"h-3 w-6 rounded-sm\"\n style={{ 'background-color': c().destructiveAction }}\n />\n <span class=\"h-3 w-6 rounded-sm\" style={{ 'background-color': c().secondary }} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * ThemePicker.\n *\n * The catalogue as a selectable grid. One appearance at a time — a light picker\n * lists light themes and a dark picker lists dark ones — because choosing a dark\n * theme while looking at a light interface is a choice nobody can evaluate.\n *\n * `onSelect` rather than an internal signal: the selection belongs to the\n * preference the provider owns, and a picker that kept its own copy could disagree\n * with what is applied.\n */\nexport type ThemePickerProps = ComponentProps<'div'> & {\n themes: readonly ThemeVariant[]\n selectedId: string\n onThemeSelect: (theme: ThemeVariant) => void\n}\n\nexport function ThemePicker(props: ThemePickerProps) {\n const [local, rest] = splitProps(props, ['class', 'themes', 'selectedId', 'onThemeSelect'])\n\n return (\n <div\n data-slot=\"theme-picker\"\n role=\"radiogroup\"\n aria-label=\"Theme\"\n class={cn('flex flex-wrap gap-3', local.class)}\n {...rest}\n >\n <For each={local.themes}>\n {(theme) => (\n <Button\n as=\"button\"\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"radio\"\n aria-checked={local.selectedId === theme.id}\n aria-label={`${theme.familyLabel} ${theme.label}`}\n // `justify-start`: the options in a wrapped row stretch to its tallest\n // member, and an option whose label has a second line (the family)\n // is that member. Centred, every shorter option's preview dropped below\n // its neighbours'; pinned to the top, the previews share one line.\n class=\"h-auto flex-col justify-start gap-1.5 p-0 hover:bg-transparent\"\n onClick={() => local.onThemeSelect(theme)}\n >\n <ThemePreview theme={theme} selected={local.selectedId === theme.id} />\n <span class=\"flex flex-col items-center\">\n <span class=\"text-xs font-medium\">{theme.label}</span>\n <Show when={theme.family !== 'adea'}>\n <span class=\"text-muted-foreground text-2xs\">{theme.familyLabel}</span>\n </Show>\n </span>\n </Button>\n )}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAWnC,OAAO,EAOL,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,2EAA2E;IAC3E,UAAU,EAAE,eAAe,GAAG,QAAQ,CAAA;IACtC,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;IACd,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,qBAAqB,EAAE,cAOnC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,CAAA;AAEpD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,cAAc,GAAG,YAAY,CAEvE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,SAAS,EAAE,MAAM,cAAc,CAAA;IAC/B,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,eAAe,CAAA;IACzC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,YAAY,CAAA;IAC3B,qCAAqC;IACrC,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IACtD,gEAAgE;IAChE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CAChC,CAAA;AAID,wBAAgB,QAAQ,IAAI,iBAAiB,CAI5C;
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/components/theme/theme-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAWnC,OAAO,EAOL,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,2EAA2E;IAC3E,UAAU,EAAE,eAAe,GAAG,QAAQ,CAAA;IACtC,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;IACd,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,qBAAqB,EAAE,cAOnC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,CAAA;AAEpD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,cAAc,GAAG,YAAY,CAEvE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,SAAS,EAAE,MAAM,cAAc,CAAA;IAC/B,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,eAAe,CAAA;IACzC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,YAAY,CAAA;IAC3B,qCAAqC;IACrC,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IACtD,gEAAgE;IAChE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CAChC,CAAA;AAID,wBAAgB,QAAQ,IAAI,iBAAiB,CAI5C;AAkDD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IACjC;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;CACpC,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,eAoGtD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,UAAU,SAAoB,GAAG,MAAM,CAElE"}
|
|
@@ -27,11 +27,22 @@ function useTheme() {
|
|
|
27
27
|
if (!value) throw new Error("useTheme must be called inside a <ThemeProvider>.");
|
|
28
28
|
return value;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* The preference store, or nothing.
|
|
32
|
+
*
|
|
33
|
+
* Only ever called inside a `try`: where storage is blocked — a sandboxed iframe, a
|
|
34
|
+
* privacy mode, a denied site-data permission — merely *reading* the `localStorage`
|
|
35
|
+
* global throws ("Access is denied", `SecurityError`), so even the `typeof` guard
|
|
36
|
+
* has to sit inside the `try` rather than in front of it. Without a store the
|
|
37
|
+
* preference lives only in the provider's signal for the session.
|
|
38
|
+
*/
|
|
39
|
+
function preferenceStore() {
|
|
40
|
+
return typeof localStorage === "undefined" ? void 0 : localStorage;
|
|
41
|
+
}
|
|
30
42
|
/** The stored preference, validated against the catalogue. */
|
|
31
43
|
function readSelection(storageKey) {
|
|
32
|
-
if (typeof localStorage === "undefined") return defaultThemeSelection;
|
|
33
44
|
try {
|
|
34
|
-
const raw =
|
|
45
|
+
const raw = preferenceStore()?.getItem(storageKey);
|
|
35
46
|
if (!raw) return defaultThemeSelection;
|
|
36
47
|
const parsed = JSON.parse(raw);
|
|
37
48
|
return {
|
|
@@ -111,8 +122,8 @@ function ThemeProvider(props) {
|
|
|
111
122
|
...patch
|
|
112
123
|
};
|
|
113
124
|
setSelectionState(next);
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
try {
|
|
126
|
+
preferenceStore()?.setItem(storageKey(), JSON.stringify(next));
|
|
116
127
|
} catch {}
|
|
117
128
|
}
|
|
118
129
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.js","names":["JSX","createContext","createEffect","createMemo","createSignal","onCleanup","splitProps","useContext","accentPresets","fontOptions","accentVariables","builtinThemes","defaultDarkThemeId","defaultLightThemeId","themeById","themeCssVariables","ThemeAppearance","ThemeVariant","ThemeSelection","appearance","lightThemeId","darkThemeId","accent","font","density","ThemeDensity","defaultThemeSelection","resolvedDensity","selection","ThemeContextValue","resolvedAppearance","variant","setSelection","patch","Partial","themes","ThemeContext","useTheme","value","Error","readSelection","storageKey","localStorage","raw","getItem","parsed","JSON","parse","some","preset","id","option","ThemeProviderProps","children","Element","initial","ThemeProvider","props","local","setSelectionState","incoming","current","systemAppearance","setSystemAppearance","window","matchMedia","query","matches","onChange","event","MediaQueryListEvent","addEventListener","removeEventListener","preference","document","root","documentElement","active","properties","Object","assign","name","entries","style","setProperty","classList","toggle","dataset","colorScheme","removeAttribute","next","setItem","stringify","_$createComponent","Provider","inlineScriptLiteral","replace","character","charCodeAt","toString","padStart","themeScript","_$createComponent"],"sources":["../../../src/components/theme/theme-provider.tsx"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport {\n createContext,\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n splitProps,\n useContext,\n} from 'solid-js'\nimport { accentPresets, fontOptions } from '#lib/tokens'\nimport {\n accentVariables,\n builtinThemes,\n defaultDarkThemeId,\n defaultLightThemeId,\n themeById,\n themeCssVariables,\n type ThemeAppearance,\n type ThemeVariant,\n} from '#lib/themes'\n\n/**\n * ThemeProvider.\n *\n * Owns the appearance preference and applies it to the document. One provider near\n * the root; everything below reads it.\n *\n * ## What a preference is\n *\n * Four independent choices, and keeping them independent is the point:\n *\n * - **appearance** — light, dark, or follow the system.\n * - **a theme per appearance** — so a user can run Catppuccin Mocha by night and\n * Solarized Light by day without re-choosing when the system flips.\n * - **an accent** — adea's six presets, which re-colour the interactive roles on\n * top of whichever theme is active.\n * - **a font** — the interface face.\n *\n * Four axes rather than one list of presets, because they compose: a user who wants\n * Nord with a violet accent and the system font should not need someone to author\n * that combination.\n *\n * ## Why the variants are applied as inline properties\n *\n * The catalogue is data, and a theme is applied by writing its roles onto\n * `<html>` as custom properties. That is what makes an imported palette work\n * without a hand-written `[data-theme]` block per theme, and it is why a settings\n * UI can show a preview by reading the same object rather than by loading a\n * stylesheet. `theme.css` keeps the default theme's values so the first paint is\n * correct before any script runs.\n */\nexport type ThemeSelection = {\n /** `system` follows `prefers-color-scheme` and updates when it changes. */\n appearance: ThemeAppearance | 'system'\n /** The variant to use when the resolved appearance is light. */\n lightThemeId: string\n /** The variant to use when the resolved appearance is dark. */\n darkThemeId: string\n /** An `accentPresets` id, or `theme` for the variant's own primary. */\n accent: string\n /** A `fontOptions` id, or `space-grotesk` for the default. */\n font: string\n /**\n * `comfortable` is the system's default geometry; `compact` moves the control\n * ladder down one rung.\n *\n * Optional, and defaulted on read, so a preference stored before density existed\n * keeps working and `ThemeSelection` stays assignable without it.\n */\n density?: ThemeDensity\n}\n\nexport const defaultThemeSelection: ThemeSelection = {\n appearance: 'system',\n lightThemeId: defaultLightThemeId,\n darkThemeId: defaultDarkThemeId,\n accent: 'theme',\n font: 'space-grotesk',\n density: 'comfortable',\n}\n\n/**\n * The two density rungs.\n *\n * `comfortable` is the geometry in `theme.css`, so it is the absence of an\n * attribute rather than a value: a document that ships no `data-density` at all\n * renders correctly, which is what keeps this additive.\n */\nexport type ThemeDensity = 'comfortable' | 'compact'\n\n/**\n * A stored selection's density, defaulted.\n *\n * The stored value is read through here rather than trusted, because a preference\n * written before density existed has no `density` key and a hand-edited store may\n * hold anything.\n */\nexport function resolvedDensity(selection: ThemeSelection): ThemeDensity {\n return selection.density === 'compact' ? 'compact' : 'comfortable'\n}\n\nexport type ThemeContextValue = {\n /** The preference as stored. */\n selection: () => ThemeSelection\n /** What the preference resolves to right now, after `system`. */\n resolvedAppearance: () => ThemeAppearance\n /** The variant in use. */\n variant: () => ThemeVariant\n /** Update part of the preference. */\n setSelection: (patch: Partial<ThemeSelection>) => void\n /** The catalogue, so a picker does not import it separately. */\n themes: readonly ThemeVariant[]\n}\n\nconst ThemeContext = createContext<ThemeContextValue>()\n\nexport function useTheme(): ThemeContextValue {\n const value = useContext(ThemeContext)\n if (!value) throw new Error('useTheme must be called inside a <ThemeProvider>.')\n return value\n}\n\n/** The stored preference, validated against the catalogue. */\nfunction readSelection(storageKey: string): ThemeSelection {\n if (typeof localStorage === 'undefined') return defaultThemeSelection\n try {\n const raw = localStorage.getItem(storageKey)\n if (!raw) return defaultThemeSelection\n const parsed = JSON.parse(raw) as Partial<ThemeSelection>\n return {\n appearance:\n parsed.appearance === 'light' ||\n parsed.appearance === 'dark' ||\n parsed.appearance === 'system'\n ? parsed.appearance\n : defaultThemeSelection.appearance,\n // An unknown id would leave the document with no theme at all, so a stale\n // preference from an older catalogue falls back rather than applying nothing.\n lightThemeId:\n themeById(parsed.lightThemeId ?? '')?.appearance === 'light'\n ? (parsed.lightThemeId as string)\n : defaultLightThemeId,\n darkThemeId:\n themeById(parsed.darkThemeId ?? '')?.appearance === 'dark'\n ? (parsed.darkThemeId as string)\n : defaultDarkThemeId,\n accent: accentPresets.some((preset) => preset.id === parsed.accent)\n ? (parsed.accent as string)\n : defaultThemeSelection.accent,\n font: fontOptions.some((option) => option.id === parsed.font)\n ? (parsed.font as string)\n : defaultThemeSelection.font,\n }\n } catch {\n return defaultThemeSelection\n }\n}\n\nexport type ThemeProviderProps = {\n children?: JSX.Element\n /** Where the preference is persisted. Distinct per application. */\n storageKey?: string\n /** Start from this instead of the stored preference. */\n initial?: Partial<ThemeSelection>\n /**\n * The selection, owned by the caller.\n *\n * For a consumer that keeps the state itself — a settings store, or a workshop\n * whose selection lives in Storybook globals — rather than handing it to this\n * provider's signal. Each change lands here and the document follows; nothing is\n * written to the store, because the caller is the one who persists. A picker\n * below the provider still works, and its change is the caller's to read back.\n *\n * Absent, the provider is the whole owner: it reads `storageKey` on mount,\n * seeds from `initial`, and persists every `setSelection`.\n */\n selection?: Partial<ThemeSelection>\n}\n\nexport function ThemeProvider(props: ThemeProviderProps) {\n const [local] = splitProps(props, ['children', 'storageKey', 'initial', 'selection'])\n const storageKey = () => local.storageKey ?? 'adea-appearance'\n\n const [selection, setSelectionState] = createSignal<ThemeSelection>({\n ...readSelection(storageKey()),\n ...local.initial,\n })\n\n // The controlled half of the contract. A patch from the owner replaces the\n // matching axes; the stored preference is untouched, since the owner is the one\n // that persists. Reading `local.selection` — a prop getter, not a destructured\n // value — is what keeps this subscribed for the provider's lifetime.\n createEffect(() => {\n const incoming = local.selection\n if (incoming) setSelectionState((current) => ({ ...current, ...incoming }))\n })\n\n const [systemAppearance, setSystemAppearance] = createSignal<ThemeAppearance>('light')\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const query = window.matchMedia('(prefers-color-scheme: dark)')\n setSystemAppearance(query.matches ? 'dark' : 'light')\n const onChange = (event: MediaQueryListEvent) =>\n setSystemAppearance(event.matches ? 'dark' : 'light')\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n }\n\n const resolvedAppearance = createMemo<ThemeAppearance>(() => {\n const preference = selection().appearance\n if (preference === 'system') return systemAppearance()\n return preference\n })\n\n const variant = createMemo<ThemeVariant>(() => {\n const id = resolvedAppearance() === 'dark' ? selection().darkThemeId : selection().lightThemeId\n return themeById(id) ?? builtinThemes[0]!\n })\n\n createEffect(() => {\n if (typeof document === 'undefined') return\n const root = document.documentElement\n const active = variant()\n\n // The variant's roles, as properties. `--primary` and `--ring` are written here\n // too, and an accent selection is layered over them below.\n //\n // The accent is applied *here* rather than left to the `[data-accent]` blocks in\n // `theme.css`, because this loop writes inline custom properties and an inline\n // declaration outranks every stylesheet selector however specific it is. The\n // blocks still exist and are still correct — they are what styles the window\n // before this runs — but they cannot win from here, so leaving the accent to\n // them meant the attribute was set, the rules were present, and nothing\n // changed on screen.\n const properties = { ...themeCssVariables(active) }\n const accent = accentVariables(selection().accent, resolvedAppearance())\n if (accent) Object.assign(properties, accent)\n\n for (const [name, value] of Object.entries(properties)) {\n root.style.setProperty(name, value)\n }\n\n root.classList.toggle('dark', resolvedAppearance() === 'dark')\n root.dataset['theme'] = active.id\n root.dataset['appearance'] = resolvedAppearance()\n root.style.colorScheme = resolvedAppearance()\n\n if (selection().accent === 'theme') root.removeAttribute('data-accent')\n else root.dataset['accent'] = selection().accent\n\n if (selection().font === 'space-grotesk') root.removeAttribute('data-font')\n else root.dataset['font'] = selection().font\n\n // Density is a pure token shift, so unlike the colour axes there is nothing to\n // compute: the attribute selects a block in `theme.css`. It is applied here for\n // the same reason as the others — this is the one place that knows what the\n // current selection is, and it is the only writer of these attributes.\n if (resolvedDensity(selection()) === 'comfortable') root.removeAttribute('data-density')\n else root.dataset['density'] = resolvedDensity(selection())\n })\n\n const value: ThemeContextValue = {\n selection,\n resolvedAppearance,\n variant,\n themes: builtinThemes,\n setSelection(patch) {\n const next = { ...selection(), ...patch }\n setSelectionState(next)\n if (typeof localStorage !== 'undefined') {\n try {\n localStorage.setItem(storageKey(), JSON.stringify(next))\n } catch {\n // A full or unavailable store must not break the switch: the preference\n // simply does not survive the reload.\n }\n }\n },\n }\n\n return <ThemeContext.Provider value={value}>{local.children}</ThemeContext.Provider>\n}\n\n/**\n * A string as a JavaScript literal that is also safe inside an inline `<script>`.\n *\n * `JSON.stringify` produces a correct JavaScript literal, and it is **not** safe\n * here: it leaves `<` and `>` alone, so a value containing `</script>` ends the tag\n * early and everything after it is parsed as HTML. That is the classic inline-script\n * injection, and it is a real one even though the value is a developer-supplied prop\n * rather than user input — a prop can come from configuration, and a key that happens\n * to contain a closing tag should not be able to execute.\n *\n * The escape is also correct JavaScript: `\\u003c` is `<` to the parser, so the value\n * is byte-identical at runtime and inert to the HTML tokenizer. `\\u2028` and `\\u2029`\n * are escaped for the neighbouring reason — they are legal inside a JSON string and\n * were, before ES2019, line terminators to a JavaScript parser.\n *\n * CodeQL flags the unescaped form as `js/bad-code-sanitization`, which is how this\n * was found; `tests/theme-script.test.ts` pins it so it cannot come back.\n */\nexport function inlineScriptLiteral(value: string): string {\n return JSON.stringify(value).replace(\n /[<>\\u2028\\u2029]/g,\n (character) => `\\\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}`\n )\n}\n\n/**\n * The no-flash script.\n *\n * Rendered inline in `<head>` before any stylesheet that matters. Without it a dark\n * preference paints the light theme first and then swaps, which is the flash every\n * themed application has to solve and the one thing a provider cannot do from\n * inside the body.\n *\n * Deliberately tiny and dependency-free: it reads the stored preference, resolves\n * `system`, and sets the class. It does not apply the variant's roles — the default\n * theme in `theme.css` covers the first paint, and the provider writes the rest on\n * mount.\n *\n * The script reads two values out of `localStorage` and writes them to `dataset`,\n * which is a *property* assignment rather than code: a stored accent or font id can\n * set an attribute and cannot execute. The one value that reaches the source as text\n * is `storageKey`, and it goes through {@link inlineScriptLiteral} for the reason\n * documented there.\n */\nexport function themeScript(storageKey = 'adea-appearance'): string {\n return `(function(){try{var s=localStorage.getItem(${inlineScriptLiteral(storageKey)});var p=s?JSON.parse(s):{};var a=p.appearance||'system';var d=a==='dark'||(a==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var r=document.documentElement;if(d)r.classList.add('dark');r.style.colorScheme=d?'dark':'light';if(p.accent&&p.accent!=='theme')r.dataset.accent=p.accent;if(p.font&&p.font!=='space-grotesk')r.dataset.font=p.font;if(p.density==='compact')r.dataset.density='compact';}catch(e){}})();`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme-provider.js","names":["JSX","createContext","createEffect","createMemo","createSignal","onCleanup","splitProps","useContext","accentPresets","fontOptions","accentVariables","builtinThemes","defaultDarkThemeId","defaultLightThemeId","themeById","themeCssVariables","ThemeAppearance","ThemeVariant","ThemeSelection","appearance","lightThemeId","darkThemeId","accent","font","density","ThemeDensity","defaultThemeSelection","resolvedDensity","selection","ThemeContextValue","resolvedAppearance","variant","setSelection","patch","Partial","themes","ThemeContext","useTheme","value","Error","preferenceStore","Storage","localStorage","undefined","readSelection","storageKey","raw","getItem","parsed","JSON","parse","some","preset","id","option","ThemeProviderProps","children","Element","initial","ThemeProvider","props","local","setSelectionState","incoming","current","systemAppearance","setSystemAppearance","window","matchMedia","query","matches","onChange","event","MediaQueryListEvent","addEventListener","removeEventListener","preference","document","root","documentElement","active","properties","Object","assign","name","entries","style","setProperty","classList","toggle","dataset","colorScheme","removeAttribute","next","setItem","stringify","_$createComponent","Provider","inlineScriptLiteral","replace","character","charCodeAt","toString","padStart","themeScript","_$createComponent"],"sources":["../../../src/components/theme/theme-provider.tsx"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport {\n createContext,\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n splitProps,\n useContext,\n} from 'solid-js'\nimport { accentPresets, fontOptions } from '#lib/tokens'\nimport {\n accentVariables,\n builtinThemes,\n defaultDarkThemeId,\n defaultLightThemeId,\n themeById,\n themeCssVariables,\n type ThemeAppearance,\n type ThemeVariant,\n} from '#lib/themes'\n\n/**\n * ThemeProvider.\n *\n * Owns the appearance preference and applies it to the document. One provider near\n * the root; everything below reads it.\n *\n * ## What a preference is\n *\n * Four independent choices, and keeping them independent is the point:\n *\n * - **appearance** — light, dark, or follow the system.\n * - **a theme per appearance** — so a user can run Catppuccin Mocha by night and\n * Solarized Light by day without re-choosing when the system flips.\n * - **an accent** — adea's six presets, which re-colour the interactive roles on\n * top of whichever theme is active.\n * - **a font** — the interface face.\n *\n * Four axes rather than one list of presets, because they compose: a user who wants\n * Nord with a violet accent and the system font should not need someone to author\n * that combination.\n *\n * ## Why the variants are applied as inline properties\n *\n * The catalogue is data, and a theme is applied by writing its roles onto\n * `<html>` as custom properties. That is what makes an imported palette work\n * without a hand-written `[data-theme]` block per theme, and it is why a settings\n * UI can show a preview by reading the same object rather than by loading a\n * stylesheet. `theme.css` keeps the default theme's values so the first paint is\n * correct before any script runs.\n */\nexport type ThemeSelection = {\n /** `system` follows `prefers-color-scheme` and updates when it changes. */\n appearance: ThemeAppearance | 'system'\n /** The variant to use when the resolved appearance is light. */\n lightThemeId: string\n /** The variant to use when the resolved appearance is dark. */\n darkThemeId: string\n /** An `accentPresets` id, or `theme` for the variant's own primary. */\n accent: string\n /** A `fontOptions` id, or `space-grotesk` for the default. */\n font: string\n /**\n * `comfortable` is the system's default geometry; `compact` moves the control\n * ladder down one rung.\n *\n * Optional, and defaulted on read, so a preference stored before density existed\n * keeps working and `ThemeSelection` stays assignable without it.\n */\n density?: ThemeDensity\n}\n\nexport const defaultThemeSelection: ThemeSelection = {\n appearance: 'system',\n lightThemeId: defaultLightThemeId,\n darkThemeId: defaultDarkThemeId,\n accent: 'theme',\n font: 'space-grotesk',\n density: 'comfortable',\n}\n\n/**\n * The two density rungs.\n *\n * `comfortable` is the geometry in `theme.css`, so it is the absence of an\n * attribute rather than a value: a document that ships no `data-density` at all\n * renders correctly, which is what keeps this additive.\n */\nexport type ThemeDensity = 'comfortable' | 'compact'\n\n/**\n * A stored selection's density, defaulted.\n *\n * The stored value is read through here rather than trusted, because a preference\n * written before density existed has no `density` key and a hand-edited store may\n * hold anything.\n */\nexport function resolvedDensity(selection: ThemeSelection): ThemeDensity {\n return selection.density === 'compact' ? 'compact' : 'comfortable'\n}\n\nexport type ThemeContextValue = {\n /** The preference as stored. */\n selection: () => ThemeSelection\n /** What the preference resolves to right now, after `system`. */\n resolvedAppearance: () => ThemeAppearance\n /** The variant in use. */\n variant: () => ThemeVariant\n /** Update part of the preference. */\n setSelection: (patch: Partial<ThemeSelection>) => void\n /** The catalogue, so a picker does not import it separately. */\n themes: readonly ThemeVariant[]\n}\n\nconst ThemeContext = createContext<ThemeContextValue>()\n\nexport function useTheme(): ThemeContextValue {\n const value = useContext(ThemeContext)\n if (!value) throw new Error('useTheme must be called inside a <ThemeProvider>.')\n return value\n}\n\n/**\n * The preference store, or nothing.\n *\n * Only ever called inside a `try`: where storage is blocked — a sandboxed iframe, a\n * privacy mode, a denied site-data permission — merely *reading* the `localStorage`\n * global throws (\"Access is denied\", `SecurityError`), so even the `typeof` guard\n * has to sit inside the `try` rather than in front of it. Without a store the\n * preference lives only in the provider's signal for the session.\n */\nfunction preferenceStore(): Storage | undefined {\n return typeof localStorage === 'undefined' ? undefined : localStorage\n}\n\n/** The stored preference, validated against the catalogue. */\nfunction readSelection(storageKey: string): ThemeSelection {\n try {\n const raw = preferenceStore()?.getItem(storageKey)\n if (!raw) return defaultThemeSelection\n const parsed = JSON.parse(raw) as Partial<ThemeSelection>\n return {\n appearance:\n parsed.appearance === 'light' ||\n parsed.appearance === 'dark' ||\n parsed.appearance === 'system'\n ? parsed.appearance\n : defaultThemeSelection.appearance,\n // An unknown id would leave the document with no theme at all, so a stale\n // preference from an older catalogue falls back rather than applying nothing.\n lightThemeId:\n themeById(parsed.lightThemeId ?? '')?.appearance === 'light'\n ? (parsed.lightThemeId as string)\n : defaultLightThemeId,\n darkThemeId:\n themeById(parsed.darkThemeId ?? '')?.appearance === 'dark'\n ? (parsed.darkThemeId as string)\n : defaultDarkThemeId,\n accent: accentPresets.some((preset) => preset.id === parsed.accent)\n ? (parsed.accent as string)\n : defaultThemeSelection.accent,\n font: fontOptions.some((option) => option.id === parsed.font)\n ? (parsed.font as string)\n : defaultThemeSelection.font,\n }\n } catch {\n return defaultThemeSelection\n }\n}\n\nexport type ThemeProviderProps = {\n children?: JSX.Element\n /** Where the preference is persisted. Distinct per application. */\n storageKey?: string\n /** Start from this instead of the stored preference. */\n initial?: Partial<ThemeSelection>\n /**\n * The selection, owned by the caller.\n *\n * For a consumer that keeps the state itself — a settings store, or a workshop\n * whose selection lives in Storybook globals — rather than handing it to this\n * provider's signal. Each change lands here and the document follows; nothing is\n * written to the store, because the caller is the one who persists. A picker\n * below the provider still works, and its change is the caller's to read back.\n *\n * Absent, the provider is the whole owner: it reads `storageKey` on mount,\n * seeds from `initial`, and persists every `setSelection`.\n */\n selection?: Partial<ThemeSelection>\n}\n\nexport function ThemeProvider(props: ThemeProviderProps) {\n const [local] = splitProps(props, ['children', 'storageKey', 'initial', 'selection'])\n const storageKey = () => local.storageKey ?? 'adea-appearance'\n\n const [selection, setSelectionState] = createSignal<ThemeSelection>({\n ...readSelection(storageKey()),\n ...local.initial,\n })\n\n // The controlled half of the contract. A patch from the owner replaces the\n // matching axes; the stored preference is untouched, since the owner is the one\n // that persists. Reading `local.selection` — a prop getter, not a destructured\n // value — is what keeps this subscribed for the provider's lifetime.\n createEffect(() => {\n const incoming = local.selection\n if (incoming) setSelectionState((current) => ({ ...current, ...incoming }))\n })\n\n const [systemAppearance, setSystemAppearance] = createSignal<ThemeAppearance>('light')\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const query = window.matchMedia('(prefers-color-scheme: dark)')\n setSystemAppearance(query.matches ? 'dark' : 'light')\n const onChange = (event: MediaQueryListEvent) =>\n setSystemAppearance(event.matches ? 'dark' : 'light')\n query.addEventListener('change', onChange)\n onCleanup(() => query.removeEventListener('change', onChange))\n }\n\n const resolvedAppearance = createMemo<ThemeAppearance>(() => {\n const preference = selection().appearance\n if (preference === 'system') return systemAppearance()\n return preference\n })\n\n const variant = createMemo<ThemeVariant>(() => {\n const id = resolvedAppearance() === 'dark' ? selection().darkThemeId : selection().lightThemeId\n return themeById(id) ?? builtinThemes[0]!\n })\n\n createEffect(() => {\n if (typeof document === 'undefined') return\n const root = document.documentElement\n const active = variant()\n\n // The variant's roles, as properties. `--primary` and `--ring` are written here\n // too, and an accent selection is layered over them below.\n //\n // The accent is applied *here* rather than left to the `[data-accent]` blocks in\n // `theme.css`, because this loop writes inline custom properties and an inline\n // declaration outranks every stylesheet selector however specific it is. The\n // blocks still exist and are still correct — they are what styles the window\n // before this runs — but they cannot win from here, so leaving the accent to\n // them meant the attribute was set, the rules were present, and nothing\n // changed on screen.\n const properties = { ...themeCssVariables(active) }\n const accent = accentVariables(selection().accent, resolvedAppearance())\n if (accent) Object.assign(properties, accent)\n\n for (const [name, value] of Object.entries(properties)) {\n root.style.setProperty(name, value)\n }\n\n root.classList.toggle('dark', resolvedAppearance() === 'dark')\n root.dataset['theme'] = active.id\n root.dataset['appearance'] = resolvedAppearance()\n root.style.colorScheme = resolvedAppearance()\n\n if (selection().accent === 'theme') root.removeAttribute('data-accent')\n else root.dataset['accent'] = selection().accent\n\n if (selection().font === 'space-grotesk') root.removeAttribute('data-font')\n else root.dataset['font'] = selection().font\n\n // Density is a pure token shift, so unlike the colour axes there is nothing to\n // compute: the attribute selects a block in `theme.css`. It is applied here for\n // the same reason as the others — this is the one place that knows what the\n // current selection is, and it is the only writer of these attributes.\n if (resolvedDensity(selection()) === 'comfortable') root.removeAttribute('data-density')\n else root.dataset['density'] = resolvedDensity(selection())\n })\n\n const value: ThemeContextValue = {\n selection,\n resolvedAppearance,\n variant,\n themes: builtinThemes,\n setSelection(patch) {\n const next = { ...selection(), ...patch }\n setSelectionState(next)\n try {\n preferenceStore()?.setItem(storageKey(), JSON.stringify(next))\n } catch {\n // A full, blocked or unavailable store must not break the switch: the\n // preference simply does not survive the reload.\n }\n },\n }\n\n return <ThemeContext.Provider value={value}>{local.children}</ThemeContext.Provider>\n}\n\n/**\n * A string as a JavaScript literal that is also safe inside an inline `<script>`.\n *\n * `JSON.stringify` produces a correct JavaScript literal, and it is **not** safe\n * here: it leaves `<` and `>` alone, so a value containing `</script>` ends the tag\n * early and everything after it is parsed as HTML. That is the classic inline-script\n * injection, and it is a real one even though the value is a developer-supplied prop\n * rather than user input — a prop can come from configuration, and a key that happens\n * to contain a closing tag should not be able to execute.\n *\n * The escape is also correct JavaScript: `\\u003c` is `<` to the parser, so the value\n * is byte-identical at runtime and inert to the HTML tokenizer. `\\u2028` and `\\u2029`\n * are escaped for the neighbouring reason — they are legal inside a JSON string and\n * were, before ES2019, line terminators to a JavaScript parser.\n *\n * CodeQL flags the unescaped form as `js/bad-code-sanitization`, which is how this\n * was found; `tests/theme-script.test.ts` pins it so it cannot come back.\n */\nexport function inlineScriptLiteral(value: string): string {\n return JSON.stringify(value).replace(\n /[<>\\u2028\\u2029]/g,\n (character) => `\\\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}`\n )\n}\n\n/**\n * The no-flash script.\n *\n * Rendered inline in `<head>` before any stylesheet that matters. Without it a dark\n * preference paints the light theme first and then swaps, which is the flash every\n * themed application has to solve and the one thing a provider cannot do from\n * inside the body.\n *\n * Deliberately tiny and dependency-free: it reads the stored preference, resolves\n * `system`, and sets the class. It does not apply the variant's roles — the default\n * theme in `theme.css` covers the first paint, and the provider writes the rest on\n * mount.\n *\n * The script reads two values out of `localStorage` and writes them to `dataset`,\n * which is a *property* assignment rather than code: a stored accent or font id can\n * set an attribute and cannot execute. The one value that reaches the source as text\n * is `storageKey`, and it goes through {@link inlineScriptLiteral} for the reason\n * documented there.\n */\nexport function themeScript(storageKey = 'adea-appearance'): string {\n return `(function(){try{var s=localStorage.getItem(${inlineScriptLiteral(storageKey)});var p=s?JSON.parse(s):{};var a=p.appearance||'system';var d=a==='dark'||(a==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var r=document.documentElement;if(d)r.classList.add('dark');r.style.colorScheme=d?'dark':'light';if(p.accent&&p.accent!=='theme')r.dataset.accent=p.accent;if(p.font&&p.font!=='space-grotesk')r.dataset.font=p.font;if(p.density==='compact')r.dataset.density='compact';}catch(e){}})();`\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentProps } from 'solid-js';
|
|
1
|
+
import type { ComponentProps, JSX } from 'solid-js';
|
|
2
2
|
/**
|
|
3
3
|
* AspectRatio.
|
|
4
4
|
*
|
|
@@ -18,5 +18,5 @@ export type AspectRatioProps = ComponentProps<'div'> & {
|
|
|
18
18
|
/** Width divided by height, e.g. `16 / 9` or `1`. */
|
|
19
19
|
ratio?: number;
|
|
20
20
|
};
|
|
21
|
-
export declare function AspectRatio(props: AspectRatioProps):
|
|
21
|
+
export declare function AspectRatio(props: AspectRatioProps): JSX.Element;
|
|
22
22
|
//# sourceMappingURL=aspect-ratio.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aspect-ratio.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/aspect-ratio/aspect-ratio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"aspect-ratio.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/aspect-ratio/aspect-ratio.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IACrD,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,eAalD"}
|
|
@@ -3,17 +3,31 @@ import { insert, mergeProps, spread, template } from "solid-js/web";
|
|
|
3
3
|
import { splitProps } from "solid-js";
|
|
4
4
|
//#region src/components/ui/aspect-ratio/aspect-ratio.tsx
|
|
5
5
|
var _tmpl$ = /*#__PURE__*/ template(`<div data-slot=aspect-ratio>`);
|
|
6
|
+
/**
|
|
7
|
+
* The caller's style with the ratio laid over it. A spread `style` replaces the
|
|
8
|
+
* element's rather than merging, so passing any style used to drop the ratio and
|
|
9
|
+
* collapse the box to zero height. `ratio` is the API for the ratio, so it wins.
|
|
10
|
+
*/
|
|
11
|
+
function withRatio(style, ratio) {
|
|
12
|
+
const value = String(ratio);
|
|
13
|
+
if (typeof style === "string") return `${style};aspect-ratio:${value}`;
|
|
14
|
+
return {
|
|
15
|
+
...style,
|
|
16
|
+
"aspect-ratio": value
|
|
17
|
+
};
|
|
18
|
+
}
|
|
6
19
|
function AspectRatio(props) {
|
|
7
20
|
const [local, rest] = splitProps(props, [
|
|
8
21
|
"class",
|
|
9
22
|
"ratio",
|
|
23
|
+
"style",
|
|
10
24
|
"children"
|
|
11
25
|
]);
|
|
12
26
|
return (() => {
|
|
13
27
|
var _el$ = _tmpl$();
|
|
14
28
|
spread(_el$, mergeProps({
|
|
15
29
|
get style() {
|
|
16
|
-
return
|
|
30
|
+
return withRatio(local.style, local.ratio ?? 16 / 9);
|
|
17
31
|
},
|
|
18
32
|
get ["class"]() {
|
|
19
33
|
return cn("relative w-full [&>*]:absolute [&>*]:inset-0 [&>*]:size-full", local.class);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aspect-ratio.js","names":["ComponentProps","splitProps","cn","AspectRatioProps","ratio","AspectRatio","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","
|
|
1
|
+
{"version":3,"file":"aspect-ratio.js","names":["ComponentProps","JSX","splitProps","cn","AspectRatioProps","ratio","withRatio","style","CSSProperties","value","String","AspectRatio","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","class","_$insert","children","_$mergeProps"],"sources":["../../../../src/components/ui/aspect-ratio/aspect-ratio.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\n/**\n * AspectRatio.\n *\n * Holds a box to a ratio so its content cannot change the layout as it loads. The\n * problem it solves is a specific one: an image or an embed whose intrinsic size is\n * unknown until it arrives pushes everything below it down when it does, and a\n * preview or a grid of cards is where that is most visible.\n *\n * CSS `aspect-ratio` rather than the padding-top trick a decade of libraries used:\n * the property is supported everywhere this system runs, it does not require a\n * wrapper element, and it does not break when the child is positioned.\n *\n * The child fills the box, so a caller passes the image, video or iframe directly\n * and never sizes it.\n */\nexport type AspectRatioProps = ComponentProps<'div'> & {\n /** Width divided by height, e.g. `16 / 9` or `1`. */\n ratio?: number\n}\n\n/**\n * The caller's style with the ratio laid over it. A spread `style` replaces the\n * element's rather than merging, so passing any style used to drop the ratio and\n * collapse the box to zero height. `ratio` is the API for the ratio, so it wins.\n */\nfunction withRatio(style: AspectRatioProps['style'], ratio: number): JSX.CSSProperties | string {\n const value = String(ratio)\n if (typeof style === 'string') return `${style};aspect-ratio:${value}`\n return { ...style, 'aspect-ratio': value }\n}\n\nexport function AspectRatio(props: AspectRatioProps) {\n const [local, rest] = splitProps(props, ['class', 'ratio', 'style', 'children'])\n\n return (\n <div\n data-slot=\"aspect-ratio\"\n style={withRatio(local.style, local.ratio ?? 16 / 9)}\n class={cn('relative w-full [&>*]:absolute [&>*]:inset-0 [&>*]:size-full', local.class)}\n {...rest}\n >\n {local.children}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;AA6BA,SAASM,UAAUC,OAAkCF,OAA2C;CAC9F,MAAMI,QAAQC,OAAOL,KAAK;CAC1B,IAAI,OAAOE,UAAU,UAAU,OAAO,GAAGA,MAAK,gBAAiBE;CAC/D,OAAO;EAAE,GAAGF;EAAO,gBAAgBE;CAAM;AAC3C;AAEA,SAAgBE,YAAYC,OAAyB;CACnD,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EAAC;EAAS;EAAS;EAAS;CAAU,CAAC;CAE/E,cAAA;EAAA,IAAAG,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,WAAA;GAAA,IAGIX,QAAK;IAAA,OAAED,UAAUO,MAAMN,OAAOM,MAAMR,SAAS,KAAK,CAAC;GAAC;GAAA,KAAA,WAAA;IAAA,OAC7CF,GAAG,gEAAgEU,MAAMM,KAAK;GAAC;EAAA,GAClFL,IAAI,GAAA,OAAA,IAAA;EAAAM,OAAAL,YAEPF,MAAMQ,QAAQ;EAAA,OAAAN;CAAA,EAAA,CAAA;AAGrB"}
|
|
@@ -10,7 +10,7 @@ import { type VariantProps } from '#lib/variants';
|
|
|
10
10
|
* action.
|
|
11
11
|
*/
|
|
12
12
|
export declare const badgeVariants: (props?: ({
|
|
13
|
-
variant?: "default" | "destructive" | "success" | "warning" | "info" | "
|
|
13
|
+
variant?: "default" | "destructive" | "success" | "warning" | "info" | "outline" | "secondary" | "subtle" | null | undefined;
|
|
14
14
|
size?: "sm" | "md" | "lg" | null | undefined;
|
|
15
15
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
16
|
type BadgeVariantProps = VariantProps<typeof badgeVariants>;
|
|
@@ -18,7 +18,7 @@ import { type VariantProps } from '#lib/variants';
|
|
|
18
18
|
* `asChild` primitive, which has no Solid equivalent that preserves props.
|
|
19
19
|
*/
|
|
20
20
|
export declare const buttonVariants: (props?: ({
|
|
21
|
-
variant?: "link" | "default" | "destructive" | "success" | "
|
|
21
|
+
variant?: "link" | "default" | "destructive" | "success" | "outline" | "secondary" | "subtle" | "ghost" | null | undefined;
|
|
22
22
|
size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "icon-2xs" | "icon-xs" | "icon-sm" | "icon-md" | "icon-lg" | "icon-xl" | "icon-2xl" | null | undefined;
|
|
23
23
|
touchTarget?: "comfortable" | null | undefined;
|
|
24
24
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/calendar/calendar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,QAAQ,EAAE,EACf,KAAK,SAAS,IAAI,kBAAkB,EACpC,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AAExB,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAK1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;GAIG;AACH,KAAK,cAAc,GAAG;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;GAKG;AACH,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAA;AAExF,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,kBAAkB,EAAE,UAAU,CAAC,GAAG,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/calendar/calendar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,QAAQ,EAAE,EACf,KAAK,SAAS,IAAI,kBAAkB,EACpC,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AAExB,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAK1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;GAIG;AACH,KAAK,cAAc,GAAG;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAA;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;GAKG;AACH,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAA;AAExF,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,kBAAkB,EAAE,UAAU,CAAC,GAAG,cAAc,CAAA;AAgJ7F;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,eAezF;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,UAAU,CAAC,GACtE,cAAc,GAAG;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACrB,CAAA;AAEH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,eAmBhD;AAED,yDAAyD;AACzD,YAAY,EAAE,aAAa,IAAI,kBAAkB,EAAE,CAAA;AACnD,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,CAAA;AACnC,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA"}
|
|
@@ -129,7 +129,7 @@ function CalendarGrid(props) {
|
|
|
129
129
|
return month.month;
|
|
130
130
|
},
|
|
131
131
|
get ["class"]() {
|
|
132
|
-
return cn("w-full rounded-md text-xs text-foreground transition-colors", "min-h-control-xs", "hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", "data-selected:bg-primary data-selected:text-primary-foreground", "data-in-range:bg-primary-subtle", "data-range-start:rounded-r-none data-range-end:rounded-l-none", "data-today:ring-1 data-today:ring-ring", "data-disabled:text-muted-foreground data-disabled:line-through", "data-disabled:hover:bg-transparent");
|
|
132
|
+
return cn("w-full rounded-md text-xs text-foreground transition-colors", "min-h-control-xs", "hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", "data-selected:bg-primary data-selected:text-primary-foreground", "data-in-range:bg-primary-subtle data-in-range:text-foreground", "data-range-start:bg-primary data-range-start:text-primary-foreground", "data-range-end:bg-primary data-range-end:text-primary-foreground", "data-range-start:rounded-r-none data-range-end:rounded-l-none", "data-today:ring-1 data-today:ring-ring", "data-disabled:text-muted-foreground data-disabled:line-through", "data-disabled:hover:bg-transparent");
|
|
133
133
|
},
|
|
134
134
|
get children() {
|
|
135
135
|
return [(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.js","names":["For","Show","splitProps","ComponentProps","JSX","Calendar","RootProps","CorvuCalendarProps","RootSingleProps","CalendarMode","ChevronLeft","ChevronRight","cn","Button","CalendarExtras","weekdayLabels","previousLabel","nextLabel","formatMonth","month","Date","class","DistributiveOmit","PropertyKey","T","Omit","K","CalendarProps","EXTRAS","const","MONTH_FORMAT","Intl","DateTimeFormatOptions","year","monthLabel","format","DateTimeFormat","undefined","dayLabel","day","defaultWeekdays","weekday","Array","from","length","_","index","CalendarGrid","props","context","useContext","weekdays","_$createComponent","each","months","children","_el$","_tmpl$3","_el$2","firstChild","_$insert","Nav","action","as","variant","size","aria-label","Label","Table","_el$3","_tmpl$","_el$4","label","HeadCell","scope","_el$5","_tmpl$2","weeks","week","_el$6","_tmpl$4","Cell","CellTrigger","_el$7","_tmpl$5","getDate","_el$8","_tmpl$6","CalendarSurface","mode","M","local","rest","_el$9","_tmpl$7","_$mergeProps","Extract","_$effect","_$className","DatePickerProps","footer","Element","DatePicker","_el$0","when","_el$1","_tmpl$8","value","onValueChange","_el$10","_tmpl$9","CalendarRoot","CalendarElementProps","_$createComponent","_$mergeProps","_$className"],"sources":["../../../../src/components/ui/calendar/calendar.tsx"],"sourcesContent":["import { For, Show, splitProps, type ComponentProps, type JSX } from 'solid-js'\nimport Calendar, {\n type RootProps as CorvuCalendarProps,\n type RootSingleProps,\n} from '@corvu/calendar'\n\n/** The three modes corvu's calendar is a union over. */\nexport type CalendarMode = 'single' | 'multiple' | 'range'\nimport { ChevronLeft, ChevronRight } from 'lucide-solid'\nimport { cn } from '#lib/utils'\nimport { Button } from '../button'\n\n/**\n * Calendar.\n *\n * A month grid, over `@corvu/calendar`. The primitive owns the hard part — the\n * roving `tabindex` that makes a grid of days one tab stop rather than 42, the\n * arrow-key navigation, the `aria-selected`/`aria-disabled` bookkeeping, and the\n * `data-*` attributes that carry the states.\n *\n * What this component owns is the *presentation* of those states, and it is worth\n * saying why they are styled from `data-*` rather than from props: a day is today,\n * selected, in range, at a range end, and disabled independently, and a range\n * start that is also today has to read as both. Boolean props cannot express that\n * without a combinatorial explosion; the data attributes can.\n *\n * `mode` is the primitive's discriminated union — `single`, `multiple`, `range` —\n * so `value` and `onValueChange` change type with it. That is why this forwards\n * the props rather than normalising them: a normalised `Date[]` would lose what a\n * range needs to know which end moved.\n *\n * Three things the primitive leaves open, and this closes:\n *\n * 1. **The month caption is a heading**, wired to the grid's `aria-labelledby`, so\n * the grid has a name and a screen reader user can hear which month they are in.\n * 2. **The nav buttons have accessible names.** A bare chevron is not a name.\n * 3. **Today is a ring, not a fill.** A fill would be indistinguishable from\n * selected, and today is often also selected.\n *\n * The sub-components come off the default export as statics (`Calendar.Cell`,\n * `Calendar.CellTrigger`, …) — corvu's shape, and the reason this file does not\n * import seven names.\n */\n\n/**\n * The props this wrapper adds on top of the primitive's own. They are separate so\n * `splitProps` has a fixed list to pull out, and so the mode-specific props stay\n * the primitive's to define.\n */\ntype CalendarExtras = {\n /** Weekday header labels, Sunday first. Defaults to the locale's short names. */\n weekdayLabels?: readonly string[]\n /** Accessible names for the month navigation. Override for a non-English app. */\n previousLabel?: string\n nextLabel?: string\n /** Format a month for the caption. Defaults to `Intl.DateTimeFormat`. */\n formatMonth?: (month: Date) => string\n class?: string\n}\n\n/**\n * `Omit` distributes over a union, which would collapse the three modes into one\n * shape and lose the discriminant. This keeps them apart. `children` is dropped\n * because the surface supplies the grid itself — the caller's job is the value,\n * not the layout.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n/** The props of any of the three modes. `mode` decides the shape of `value`. */\nexport type CalendarProps = DistributiveOmit<CorvuCalendarProps, 'children'> & CalendarExtras\n\nconst EXTRAS = ['weekdayLabels', 'previousLabel', 'nextLabel', 'formatMonth', 'class'] as const\n\nconst MONTH_FORMAT: Intl.DateTimeFormatOptions = { month: 'long', year: 'numeric' }\n\nfunction monthLabel(month: Date, format?: (month: Date) => string): string {\n return format ? format(month) : new Intl.DateTimeFormat(undefined, MONTH_FORMAT).format(month)\n}\n\n/**\n * A day's accessible name: the full date, because the visible number is only\n * meaningful next to its column heading.\n */\nfunction dayLabel(day: Date): string {\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(day)\n}\n\n/** The locale's short weekday names, Sunday first — which is the order corvu's `weeks` use. */\nfunction defaultWeekdays(): string[] {\n const format = new Intl.DateTimeFormat(undefined, { weekday: 'short' })\n // 2024-01-07 is a Sunday, so index 0 is Sunday without arithmetic.\n return Array.from({ length: 7 }, (_, index) => format.format(new Date(2024, 0, 7 + index)))\n}\n\n/**\n * The grid, inside the root's context. A separate component because\n * `Calendar.useContext` is only available to a descendant, and reading `months()`\n * in the same component that provides it renders one month behind.\n */\nfunction CalendarGrid(props: {\n weekdayLabels?: readonly string[]\n previousLabel: string\n nextLabel: string\n formatMonth?: (month: Date) => string\n}) {\n const context = Calendar.useContext()\n const weekdays = () => props.weekdayLabels ?? defaultWeekdays()\n\n return (\n <For each={context.months()}>\n {(month, index) => (\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <Calendar.Nav\n action=\"prev-month\"\n as={Button}\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={props.previousLabel}\n >\n <ChevronLeft aria-hidden=\"true\" />\n </Calendar.Nav>\n <Calendar.Label index={index()} class=\"text-sm font-semibold text-foreground\">\n {monthLabel(month.month, props.formatMonth)}\n </Calendar.Label>\n <Calendar.Nav\n action=\"next-month\"\n as={Button}\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={props.nextLabel}\n >\n <ChevronRight aria-hidden=\"true\" />\n </Calendar.Nav>\n </div>\n <Calendar.Table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <For each={weekdays()}>\n {(label) => (\n <Calendar.HeadCell\n as=\"th\"\n scope=\"col\"\n class=\"pb-1 text-2xs font-medium text-muted-foreground\"\n >\n {label}\n </Calendar.HeadCell>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={month.weeks}>\n {(week) => (\n <tr>\n <For each={week}>\n {(day) => (\n <Calendar.Cell as=\"td\" class=\"p-0.5 text-center\">\n <Calendar.CellTrigger\n as=\"button\"\n day={day}\n month={month.month}\n class={cn(\n 'w-full rounded-md text-xs text-foreground transition-colors',\n 'min-h-control-xs',\n 'hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none',\n // Styled from data states, so a day can be a range\n // end and today at once without a prop per pair.\n 'data-selected:bg-primary data-selected:text-primary-foreground',\n 'data-in-range:bg-primary-subtle',\n 'data-range-start:rounded-r-none data-range-end:rounded-l-none',\n 'data-today:ring-1 data-today:ring-ring',\n 'data-disabled:text-muted-foreground data-disabled:line-through',\n 'data-disabled:hover:bg-transparent'\n )}\n >\n {/*\n The trigger renders no children of its own, so the\n day number is the caller's — without it every cell\n is an unnamed button (`button-name`).\n\n The number is what a sighted reader needs and the\n full date is what a screen reader user needs: \"25\"\n alone is meaningless out of context. So the number\n is hidden from the accessibility tree and the date\n supplies the name.\n */}\n <span aria-hidden=\"true\">{day.getDate()}</span>\n <span class=\"visually-hidden\">{dayLabel(day)}</span>\n </Calendar.CellTrigger>\n </Calendar.Cell>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </Calendar.Table>\n </div>\n )}\n </For>\n )\n}\n\n/**\n * Generic over the mode on purpose. `splitProps` cannot carry a discriminant\n * through destructuring — the rest object widens to `mode: 'single' | 'multiple' |\n * 'range'`, which is assignable to no single member of the primitive's union. A\n * type parameter keeps `mode` a literal, so `Extract` resolves the right member\n * and the forwarding stays checked rather than cast.\n */\nexport function CalendarSurface<M extends CalendarMode>(props: CalendarProps & { mode: M }) {\n const [local, rest] = splitProps(props as CalendarProps, [...EXTRAS])\n\n return (\n <div class={cn('w-fit rounded-lg border border-border bg-card p-3', local.class)}>\n <Calendar {...(rest as Extract<CorvuCalendarProps, { mode: M }>)}>\n <CalendarGrid\n weekdayLabels={local.weekdayLabels}\n previousLabel={local.previousLabel ?? 'Previous month'}\n nextLabel={local.nextLabel ?? 'Next month'}\n formatMonth={local.formatMonth}\n />\n </Calendar>\n </div>\n )\n}\n\n/**\n * Built from the primitive's *single* props rather than from `CalendarProps` with\n * `mode` omitted. Omitting a discriminant leaves the other members of the union\n * behind — `value` stays `Date | Date[] | {from,to}` — and the picker would then\n * have to cast to prove what its own name already says.\n */\nexport type DatePickerProps = Omit<RootSingleProps, 'mode' | 'children'> &\n CalendarExtras & {\n /** A labelled heading above the grid. */\n label?: string\n /** A footer slot: the confirm and cancel row a picker usually has. */\n footer?: JSX.Element\n }\n\n/**\n * A single-day picker: the calendar with a name and a footer. Split from\n * `CalendarSurface` because a bare calendar is a control inside a form the caller\n * already owns, while a picker is a thing the caller drops in — and the two want\n * different surroundings.\n */\nexport function DatePicker(props: DatePickerProps) {\n const [local, rest] = splitProps(props, ['value', 'onValueChange', 'label', 'footer', 'class'])\n\n return (\n <div class={cn('grid w-fit gap-2', local.class)}>\n <Show when={local.label}>\n <p class=\"text-xs font-semibold text-foreground\">{local.label}</p>\n </Show>\n <CalendarSurface\n {...rest}\n mode=\"single\"\n value={local.value}\n onValueChange={local.onValueChange}\n />\n <Show when={local.footer}>\n <div class=\"flex justify-end gap-2\">{local.footer}</div>\n </Show>\n </div>\n )\n}\n\n/** Re-exported so a caller can type a value it holds. */\nexport type { CalendarProps as CorvuCalendarProps }\nexport { Calendar as CalendarRoot }\nexport type CalendarElementProps = ComponentProps<'div'>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"calendar.js","names":["For","Show","splitProps","ComponentProps","JSX","Calendar","RootProps","CorvuCalendarProps","RootSingleProps","CalendarMode","ChevronLeft","ChevronRight","cn","Button","CalendarExtras","weekdayLabels","previousLabel","nextLabel","formatMonth","month","Date","class","DistributiveOmit","PropertyKey","T","Omit","K","CalendarProps","EXTRAS","const","MONTH_FORMAT","Intl","DateTimeFormatOptions","year","monthLabel","format","DateTimeFormat","undefined","dayLabel","day","defaultWeekdays","weekday","Array","from","length","_","index","CalendarGrid","props","context","useContext","weekdays","_$createComponent","each","months","children","_el$","_tmpl$3","_el$2","firstChild","_$insert","Nav","action","as","variant","size","aria-label","Label","Table","_el$3","_tmpl$","_el$4","label","HeadCell","scope","_el$5","_tmpl$2","weeks","week","_el$6","_tmpl$4","Cell","CellTrigger","_el$7","_tmpl$5","getDate","_el$8","_tmpl$6","CalendarSurface","mode","M","local","rest","_el$9","_tmpl$7","_$mergeProps","Extract","_$effect","_$className","DatePickerProps","footer","Element","DatePicker","_el$0","when","_el$1","_tmpl$8","value","onValueChange","_el$10","_tmpl$9","CalendarRoot","CalendarElementProps","_$createComponent","_$mergeProps","_$className"],"sources":["../../../../src/components/ui/calendar/calendar.tsx"],"sourcesContent":["import { For, Show, splitProps, type ComponentProps, type JSX } from 'solid-js'\nimport Calendar, {\n type RootProps as CorvuCalendarProps,\n type RootSingleProps,\n} from '@corvu/calendar'\n\n/** The three modes corvu's calendar is a union over. */\nexport type CalendarMode = 'single' | 'multiple' | 'range'\nimport { ChevronLeft, ChevronRight } from 'lucide-solid'\nimport { cn } from '#lib/utils'\nimport { Button } from '../button'\n\n/**\n * Calendar.\n *\n * A month grid, over `@corvu/calendar`. The primitive owns the hard part — the\n * roving `tabindex` that makes a grid of days one tab stop rather than 42, the\n * arrow-key navigation, the `aria-selected`/`aria-disabled` bookkeeping, and the\n * `data-*` attributes that carry the states.\n *\n * What this component owns is the *presentation* of those states, and it is worth\n * saying why they are styled from `data-*` rather than from props: a day is today,\n * selected, in range, at a range end, and disabled independently, and a range\n * start that is also today has to read as both. Boolean props cannot express that\n * without a combinatorial explosion; the data attributes can.\n *\n * `mode` is the primitive's discriminated union — `single`, `multiple`, `range` —\n * so `value` and `onValueChange` change type with it. That is why this forwards\n * the props rather than normalising them: a normalised `Date[]` would lose what a\n * range needs to know which end moved.\n *\n * Three things the primitive leaves open, and this closes:\n *\n * 1. **The month caption is a heading**, wired to the grid's `aria-labelledby`, so\n * the grid has a name and a screen reader user can hear which month they are in.\n * 2. **The nav buttons have accessible names.** A bare chevron is not a name.\n * 3. **Today is a ring, not a fill.** A fill would be indistinguishable from\n * selected, and today is often also selected.\n *\n * The sub-components come off the default export as statics (`Calendar.Cell`,\n * `Calendar.CellTrigger`, …) — corvu's shape, and the reason this file does not\n * import seven names.\n */\n\n/**\n * The props this wrapper adds on top of the primitive's own. They are separate so\n * `splitProps` has a fixed list to pull out, and so the mode-specific props stay\n * the primitive's to define.\n */\ntype CalendarExtras = {\n /** Weekday header labels, Sunday first. Defaults to the locale's short names. */\n weekdayLabels?: readonly string[]\n /** Accessible names for the month navigation. Override for a non-English app. */\n previousLabel?: string\n nextLabel?: string\n /** Format a month for the caption. Defaults to `Intl.DateTimeFormat`. */\n formatMonth?: (month: Date) => string\n class?: string\n}\n\n/**\n * `Omit` distributes over a union, which would collapse the three modes into one\n * shape and lose the discriminant. This keeps them apart. `children` is dropped\n * because the surface supplies the grid itself — the caller's job is the value,\n * not the layout.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never\n\n/** The props of any of the three modes. `mode` decides the shape of `value`. */\nexport type CalendarProps = DistributiveOmit<CorvuCalendarProps, 'children'> & CalendarExtras\n\nconst EXTRAS = ['weekdayLabels', 'previousLabel', 'nextLabel', 'formatMonth', 'class'] as const\n\nconst MONTH_FORMAT: Intl.DateTimeFormatOptions = { month: 'long', year: 'numeric' }\n\nfunction monthLabel(month: Date, format?: (month: Date) => string): string {\n return format ? format(month) : new Intl.DateTimeFormat(undefined, MONTH_FORMAT).format(month)\n}\n\n/**\n * A day's accessible name: the full date, because the visible number is only\n * meaningful next to its column heading.\n */\nfunction dayLabel(day: Date): string {\n return new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(day)\n}\n\n/** The locale's short weekday names, Sunday first — which is the order corvu's `weeks` use. */\nfunction defaultWeekdays(): string[] {\n const format = new Intl.DateTimeFormat(undefined, { weekday: 'short' })\n // 2024-01-07 is a Sunday, so index 0 is Sunday without arithmetic.\n return Array.from({ length: 7 }, (_, index) => format.format(new Date(2024, 0, 7 + index)))\n}\n\n/**\n * The grid, inside the root's context. A separate component because\n * `Calendar.useContext` is only available to a descendant, and reading `months()`\n * in the same component that provides it renders one month behind.\n */\nfunction CalendarGrid(props: {\n weekdayLabels?: readonly string[]\n previousLabel: string\n nextLabel: string\n formatMonth?: (month: Date) => string\n}) {\n const context = Calendar.useContext()\n const weekdays = () => props.weekdayLabels ?? defaultWeekdays()\n\n return (\n <For each={context.months()}>\n {(month, index) => (\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <Calendar.Nav\n action=\"prev-month\"\n as={Button}\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={props.previousLabel}\n >\n <ChevronLeft aria-hidden=\"true\" />\n </Calendar.Nav>\n <Calendar.Label index={index()} class=\"text-sm font-semibold text-foreground\">\n {monthLabel(month.month, props.formatMonth)}\n </Calendar.Label>\n <Calendar.Nav\n action=\"next-month\"\n as={Button}\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={props.nextLabel}\n >\n <ChevronRight aria-hidden=\"true\" />\n </Calendar.Nav>\n </div>\n <Calendar.Table class=\"w-full border-collapse\">\n <thead>\n <tr>\n <For each={weekdays()}>\n {(label) => (\n <Calendar.HeadCell\n as=\"th\"\n scope=\"col\"\n class=\"pb-1 text-2xs font-medium text-muted-foreground\"\n >\n {label}\n </Calendar.HeadCell>\n )}\n </For>\n </tr>\n </thead>\n <tbody>\n <For each={month.weeks}>\n {(week) => (\n <tr>\n <For each={week}>\n {(day) => (\n <Calendar.Cell as=\"td\" class=\"p-0.5 text-center\">\n <Calendar.CellTrigger\n as=\"button\"\n day={day}\n month={month.month}\n class={cn(\n 'w-full rounded-md text-xs text-foreground transition-colors',\n 'min-h-control-xs',\n 'hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none',\n // Styled from data states, so a day can be a range\n // end and today at once without a prop per pair.\n 'data-selected:bg-primary data-selected:text-primary-foreground',\n // corvu marks every day of a range selected, so the\n // span between the ends steps back to the subtle\n // fill and the ends take the solid one again.\n 'data-in-range:bg-primary-subtle data-in-range:text-foreground',\n 'data-range-start:bg-primary data-range-start:text-primary-foreground',\n 'data-range-end:bg-primary data-range-end:text-primary-foreground',\n 'data-range-start:rounded-r-none data-range-end:rounded-l-none',\n 'data-today:ring-1 data-today:ring-ring',\n 'data-disabled:text-muted-foreground data-disabled:line-through',\n 'data-disabled:hover:bg-transparent'\n )}\n >\n {/*\n The trigger renders no children of its own, so the\n day number is the caller's — without it every cell\n is an unnamed button (`button-name`).\n\n The number is what a sighted reader needs and the\n full date is what a screen reader user needs: \"25\"\n alone is meaningless out of context. So the number\n is hidden from the accessibility tree and the date\n supplies the name.\n */}\n <span aria-hidden=\"true\">{day.getDate()}</span>\n <span class=\"visually-hidden\">{dayLabel(day)}</span>\n </Calendar.CellTrigger>\n </Calendar.Cell>\n )}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </Calendar.Table>\n </div>\n )}\n </For>\n )\n}\n\n/**\n * Generic over the mode on purpose. `splitProps` cannot carry a discriminant\n * through destructuring — the rest object widens to `mode: 'single' | 'multiple' |\n * 'range'`, which is assignable to no single member of the primitive's union. A\n * type parameter keeps `mode` a literal, so `Extract` resolves the right member\n * and the forwarding stays checked rather than cast.\n */\nexport function CalendarSurface<M extends CalendarMode>(props: CalendarProps & { mode: M }) {\n const [local, rest] = splitProps(props as CalendarProps, [...EXTRAS])\n\n return (\n <div class={cn('w-fit rounded-lg border border-border bg-card p-3', local.class)}>\n <Calendar {...(rest as Extract<CorvuCalendarProps, { mode: M }>)}>\n <CalendarGrid\n weekdayLabels={local.weekdayLabels}\n previousLabel={local.previousLabel ?? 'Previous month'}\n nextLabel={local.nextLabel ?? 'Next month'}\n formatMonth={local.formatMonth}\n />\n </Calendar>\n </div>\n )\n}\n\n/**\n * Built from the primitive's *single* props rather than from `CalendarProps` with\n * `mode` omitted. Omitting a discriminant leaves the other members of the union\n * behind — `value` stays `Date | Date[] | {from,to}` — and the picker would then\n * have to cast to prove what its own name already says.\n */\nexport type DatePickerProps = Omit<RootSingleProps, 'mode' | 'children'> &\n CalendarExtras & {\n /** A labelled heading above the grid. */\n label?: string\n /** A footer slot: the confirm and cancel row a picker usually has. */\n footer?: JSX.Element\n }\n\n/**\n * A single-day picker: the calendar with a name and a footer. Split from\n * `CalendarSurface` because a bare calendar is a control inside a form the caller\n * already owns, while a picker is a thing the caller drops in — and the two want\n * different surroundings.\n */\nexport function DatePicker(props: DatePickerProps) {\n const [local, rest] = splitProps(props, ['value', 'onValueChange', 'label', 'footer', 'class'])\n\n return (\n <div class={cn('grid w-fit gap-2', local.class)}>\n <Show when={local.label}>\n <p class=\"text-xs font-semibold text-foreground\">{local.label}</p>\n </Show>\n <CalendarSurface\n {...rest}\n mode=\"single\"\n value={local.value}\n onValueChange={local.onValueChange}\n />\n <Show when={local.footer}>\n <div class=\"flex justify-end gap-2\">{local.footer}</div>\n </Show>\n </div>\n )\n}\n\n/** Re-exported so a caller can type a value it holds. */\nexport type { CalendarProps as CorvuCalendarProps }\nexport { Calendar as CalendarRoot }\nexport type CalendarElementProps = ComponentProps<'div'>\n"],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@ import type { ComponentProps, JSX } from 'solid-js';
|
|
|
3
3
|
* Card.
|
|
4
4
|
*
|
|
5
5
|
* A surface that sits on the canvas. Every card carries its own edge — a
|
|
6
|
-
* border — because in the light theme `--card`
|
|
7
|
-
*
|
|
6
|
+
* border — because in the light theme `--card` is one faint step off a white
|
|
7
|
+
* `--background`, and a borderless card there all but disappears. Making the border
|
|
8
8
|
* structural rather than optional is what stops one app's cards from reading
|
|
9
9
|
* as floating panels and another's as nothing at all.
|
|
10
10
|
*
|
|
@@ -13,8 +13,8 @@ var _tmpl$7 = /*#__PURE__*/ template(`<div data-slot=card-footer>`);
|
|
|
13
13
|
* Card.
|
|
14
14
|
*
|
|
15
15
|
* A surface that sits on the canvas. Every card carries its own edge — a
|
|
16
|
-
* border — because in the light theme `--card`
|
|
17
|
-
*
|
|
16
|
+
* border — because in the light theme `--card` is one faint step off a white
|
|
17
|
+
* `--background`, and a borderless card there all but disappears. Making the border
|
|
18
18
|
* structural rather than optional is what stops one app's cards from reading
|
|
19
19
|
* as floating panels and another's as nothing at all.
|
|
20
20
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","names":["ComponentProps","JSX","splitProps","cn","Card","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","class","CardHeader","_el$2","_tmpl$2","CardTitle","_el$3","_tmpl$3","CardDescription","_el$4","_tmpl$4","CardAction","_el$5","_tmpl$5","CardContent","_el$6","_tmpl$6","CardFooter","_el$7","_tmpl$7","CardProps","HTMLAttributes","HTMLDivElement","_$mergeProps"],"sources":["../../../../src/components/ui/card/card.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\n/**\n * Card.\n *\n * A surface that sits on the canvas. Every card carries its own edge — a\n * border — because in the light theme `--card`
|
|
1
|
+
{"version":3,"file":"card.js","names":["ComponentProps","JSX","splitProps","cn","Card","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","class","CardHeader","_el$2","_tmpl$2","CardTitle","_el$3","_tmpl$3","CardDescription","_el$4","_tmpl$4","CardAction","_el$5","_tmpl$5","CardContent","_el$6","_tmpl$6","CardFooter","_el$7","_tmpl$7","CardProps","HTMLAttributes","HTMLDivElement","_$mergeProps"],"sources":["../../../../src/components/ui/card/card.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\n/**\n * Card.\n *\n * A surface that sits on the canvas. Every card carries its own edge — a\n * border — because in the light theme `--card` is one faint step off a white\n * `--background`, and a borderless card there all but disappears. Making the border\n * structural rather than optional is what stops one app's cards from reading\n * as floating panels and another's as nothing at all.\n *\n * The parts are separate components rather than a template because a card's\n * header, content and footer have different padding on purpose: the header and\n * footer are tight, the content breathes.\n */\nexport function Card(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"card\"\n class={cn(\n 'bg-card text-card-foreground flex flex-col gap-4 rounded-xl border border-border py-4 shadow-xs',\n local.class\n )}\n {...rest}\n />\n )\n}\n\nexport function CardHeader(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div data-slot=\"card-header\" class={cn('flex flex-col gap-1 px-4', local.class)} {...rest} />\n )\n}\n\nexport function CardTitle(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"card-title\"\n class={cn('text-base leading-none font-semibold tracking-tight', local.class)}\n {...rest}\n />\n )\n}\n\nexport function CardDescription(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"card-description\"\n class={cn('text-muted-foreground text-sm', local.class)}\n {...rest}\n />\n )\n}\n\nexport function CardAction(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"card-action\"\n class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', local.class)}\n {...rest}\n />\n )\n}\n\nexport function CardContent(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return <div data-slot=\"card-content\" class={cn('px-4', local.class)} {...rest} />\n}\n\nexport function CardFooter(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return <div data-slot=\"card-footer\" class={cn('flex items-center px-4', local.class)} {...rest} />\n}\n\nexport type CardProps = ComponentProps<'div'> & JSX.HTMLAttributes<HTMLDivElement>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAiBA,SAAgBI,KAAKC,OAA8B;CACjD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAG,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWR,GACL,mGACAG,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBK,WAAWR,OAA8B;CACvD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAS,QAAAC,QAAA;EAAAL,OAAAI,OAAAH,WAAA,EAAA,KAAA,WAAA;GAAA,OACsCR,GAAG,4BAA4BG,MAAMM,KAAK;EAAC,EAAA,GAAML,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAO;CAAA,EAAA,CAAA;AAE7F;AAEA,SAAgBE,UAAUX,OAA8B;CACtD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAY,QAAAC,QAAA;EAAAR,OAAAO,OAAAN,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWR,GAAG,uDAAuDG,MAAMM,KAAK;EAAC,EAAA,GACzEL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAU;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBE,gBAAgBd,OAA8B;CAC5D,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAe,QAAAC,QAAA;EAAAX,OAAAU,OAAAT,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWR,GAAG,iCAAiCG,MAAMM,KAAK;EAAC,EAAA,GACnDL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAa;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBE,WAAWjB,OAA8B;CACvD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAkB,QAAAC,QAAA;EAAAd,OAAAa,OAAAZ,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWR,GAAG,kEAAkEG,MAAMM,KAAK;EAAC,EAAA,GACpFL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAgB;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBE,YAAYpB,OAA8B;CACxD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAqB,QAAAC,QAAA;EAAAjB,OAAAgB,OAAAf,WAAA,EAAA,KAAA,WAAA;GAAA,OAA4CR,GAAG,QAAQG,MAAMM,KAAK;EAAC,EAAA,GAAML,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAmB;CAAA,EAAA,CAAA;AAC/E;AAEA,SAAgBE,WAAWvB,OAA8B;CACvD,MAAM,CAACC,OAAOC,QAAQL,WAAWG,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAAwB,QAAAC,QAAA;EAAApB,OAAAmB,OAAAlB,WAAA,EAAA,KAAA,WAAA;GAAA,OAA2CR,GAAG,0BAA0BG,MAAMM,KAAK;EAAC,EAAA,GAAML,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAsB;CAAA,EAAA,CAAA;AAChG"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ChartOptions } from 'chart.js';
|
|
2
|
+
/**
|
|
3
|
+
* The chart's options, as data: no Chart.js runtime and no DOM, so the theme's
|
|
4
|
+
* defaults and the merge rules are testable on their own.
|
|
5
|
+
*
|
|
6
|
+
* Every colour here is written as `var(--token)`. A canvas cannot read a custom
|
|
7
|
+
* property — `fillStyle = 'var(--chart-1)'` is an invalid colour, which the
|
|
8
|
+
* canvas ignores and paints in its default black — so these strings are the
|
|
9
|
+
* *declaration* of which token a colour comes from. The components resolve them
|
|
10
|
+
* against their own element at render time (see `resolveCssVariables` and the
|
|
11
|
+
* resolver in `chart.tsx`).
|
|
12
|
+
*/
|
|
13
|
+
/** The five series colours, in the order a multi-series chart should use them. */
|
|
14
|
+
export declare const chartSeriesColors: readonly ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
15
|
+
/**
|
|
16
|
+
* Which scales a chart type draws. Chart.js creates a scale for every key under
|
|
17
|
+
* `options.scales` whatever the chart type, so cartesian defaults handed to a
|
|
18
|
+
* doughnut draw an x and a y axis and a grid behind it. The shape keeps each
|
|
19
|
+
* type's defaults to the scales it actually has.
|
|
20
|
+
*/
|
|
21
|
+
export type ChartScaleShape = 'cartesian' | 'radial' | 'none';
|
|
22
|
+
/**
|
|
23
|
+
* Merge Chart.js options, later sources winning, object by object.
|
|
24
|
+
*
|
|
25
|
+
* Deep, because Chart.js options are deep: `{ scales: { x: { stacked: true } } }`
|
|
26
|
+
* means "also stack x", and a spread would replace the whole axis — its grid,
|
|
27
|
+
* its tick colour, its font — with that one flag. Arrays, functions (scriptable
|
|
28
|
+
* options) and class instances (gradients, patterns) are values and replace
|
|
29
|
+
* rather than merge. No source is mutated.
|
|
30
|
+
*/
|
|
31
|
+
export declare function mergeChartOptions(...sources: (ChartOptions | undefined)[]): ChartOptions;
|
|
32
|
+
/**
|
|
33
|
+
* The theme's defaults as Chart.js options.
|
|
34
|
+
*
|
|
35
|
+
* Exported so a caller can spread it and change one thing, rather than
|
|
36
|
+
* restating every colour. Everything a chart draws that is not data — grid,
|
|
37
|
+
* ticks, legend, tooltip — is decided here once, which is what makes two charts
|
|
38
|
+
* in one application look like they came from the same place. `overrides` is
|
|
39
|
+
* merged deeply; `shape` selects the scales the chart type has.
|
|
40
|
+
*/
|
|
41
|
+
export declare function chartOptions(overrides?: ChartOptions, shape?: ChartScaleShape): ChartOptions;
|
|
42
|
+
/**
|
|
43
|
+
* Replace every `var(--token)` string in `value` — options, datasets, nested
|
|
44
|
+
* arrays — with what `read` returns for the token. A token `read` cannot
|
|
45
|
+
* resolve falls back to the `var()`'s own fallback, then to the string as
|
|
46
|
+
* written. Functions and class instances pass through untouched, so scriptable
|
|
47
|
+
* options and gradients keep working.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveCssVariables<T>(value: T, read: (name: string) => string | undefined): T;
|
|
50
|
+
//# sourceMappingURL=chart-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-options.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/chart/chart-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C;;;;;;;;;;GAUG;AAEH,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,qGAMpB,CAAA;AAkBV;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAA;AAsD7D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,OAAO,EAAE,CAAC,YAAY,GAAG,SAAS,CAAC,EAAE,GAAG,YAAY,CAMxF;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,SAAS,CAAC,EAAE,YAAY,EACxB,KAAK,GAAE,eAA6B,GACnC,YAAY,CAqCd;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,CAAC,CAmB9F"}
|