@adea-ai/ui 0.100.2 → 0.101.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/composites/about-dialog/about-dialog.d.ts.map +1 -1
- package/dist/components/composites/about-dialog/about-dialog.js +2 -1
- package/dist/components/composites/about-dialog/about-dialog.js.map +1 -1
- package/dist/components/composites/account-menu/account-menu.d.ts +7 -0
- package/dist/components/composites/account-menu/account-menu.d.ts.map +1 -1
- package/dist/components/composites/account-menu/account-menu.js +116 -89
- package/dist/components/composites/account-menu/account-menu.js.map +1 -1
- package/dist/components/composites/help-center/help-center.d.ts.map +1 -1
- package/dist/components/composites/help-center/help-center.js +26 -21
- package/dist/components/composites/help-center/help-center.js.map +1 -1
- package/dist/components/composites/settings/settings.d.ts.map +1 -1
- package/dist/components/composites/settings/settings.js +43 -36
- package/dist/components/composites/settings/settings.js.map +1 -1
- package/dist/components/composites/stat/stat.d.ts.map +1 -1
- package/dist/components/composites/stat/stat.js +46 -35
- package/dist/components/composites/stat/stat.js.map +1 -1
- package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
- package/dist/components/composites/update-dialog/update-dialog.js +73 -58
- package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
- package/dist/components/layout/page/page.d.ts.map +1 -1
- package/dist/components/layout/page/page.js +19 -13
- package/dist/components/layout/page/page.js.map +1 -1
- package/dist/components/theme/appearance-panel.d.ts.map +1 -1
- package/dist/components/theme/appearance-panel.js +37 -18
- package/dist/components/theme/appearance-panel.js.map +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog.js +5 -1
- package/dist/components/ui/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/components/ui/card/card.d.ts.map +1 -1
- package/dist/components/ui/card/card.js +5 -1
- package/dist/components/ui/card/card.js.map +1 -1
- package/dist/components/ui/chart/chart-options.d.ts +3 -2
- package/dist/components/ui/chart/chart-options.d.ts.map +1 -1
- package/dist/components/ui/chart/chart-options.js +30 -9
- package/dist/components/ui/chart/chart-options.js.map +1 -1
- package/dist/components/ui/chart/chart.d.ts +4 -9
- package/dist/components/ui/chart/chart.d.ts.map +1 -1
- package/dist/components/ui/chart/chart.js +65 -10
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog/dialog.js +5 -1
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/drawer/drawer.d.ts.map +1 -1
- package/dist/components/ui/drawer/drawer.js +5 -1
- package/dist/components/ui/drawer/drawer.js.map +1 -1
- package/dist/components/ui/field/field.d.ts.map +1 -1
- package/dist/components/ui/field/field.js +2 -1
- package/dist/components/ui/field/field.js.map +1 -1
- package/dist/components/ui/item/item.d.ts +1 -1
- package/dist/components/ui/navigation-menu/navigation-menu.d.ts.map +1 -1
- package/dist/components/ui/navigation-menu/navigation-menu.js +8 -7
- package/dist/components/ui/navigation-menu/navigation-menu.js.map +1 -1
- package/dist/components/ui/sheet/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet/sheet.js +5 -1
- package/dist/components/ui/sheet/sheet.js.map +1 -1
- package/dist/components/ui/tabs/tabs.d.ts +13 -0
- package/dist/components/ui/tabs/tabs.d.ts.map +1 -1
- package/dist/components/ui/tabs/tabs.js +15 -1
- package/dist/components/ui/tabs/tabs.js.map +1 -1
- package/dist/components/ui/toast/toast.d.ts.map +1 -1
- package/dist/components/ui/toast/toast.js +6 -2
- package/dist/components/ui/toast/toast.js.map +1 -1
- package/dist/components/ui/typography/index.d.ts +3 -0
- package/dist/components/ui/typography/index.d.ts.map +1 -0
- package/dist/components/ui/typography/index.js +2 -0
- package/dist/components/ui/typography/typography.d.ts +84 -0
- package/dist/components/ui/typography/typography.d.ts.map +1 -0
- package/dist/components/ui/typography/typography.js +179 -0
- package/dist/components/ui/typography/typography.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/r/account-menu.json +1 -0
- package/dist/r/alert-dialog.json +1 -0
- package/dist/r/card.json +1 -0
- package/dist/r/dialog.json +1 -0
- package/dist/r/drawer.json +1 -0
- package/dist/r/field.json +1 -0
- package/dist/r/help-center.json +1 -0
- package/dist/r/page.json +1 -0
- package/dist/r/registry.json +37 -0
- package/dist/r/settings.json +1 -0
- package/dist/r/sheet.json +1 -0
- package/dist/r/src/components/composites/about-dialog/about-dialog.tsx +3 -2
- package/dist/r/src/components/composites/account-menu/account-menu.tsx +105 -66
- package/dist/r/src/components/composites/help-center/help-center.tsx +2 -1
- package/dist/r/src/components/composites/settings/settings.tsx +4 -1
- package/dist/r/src/components/composites/stat/stat.tsx +7 -2
- package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +36 -24
- package/dist/r/src/components/layout/page/page.tsx +3 -2
- package/dist/r/src/components/theme/appearance-panel.tsx +13 -4
- package/dist/r/src/components/ui/alert-dialog/alert-dialog.tsx +2 -1
- package/dist/r/src/components/ui/card/card.tsx +2 -1
- package/dist/r/src/components/ui/chart/chart-options.ts +35 -7
- package/dist/r/src/components/ui/chart/chart.tsx +65 -12
- package/dist/r/src/components/ui/dialog/dialog.tsx +2 -1
- package/dist/r/src/components/ui/drawer/drawer.tsx +2 -1
- package/dist/r/src/components/ui/field/field.tsx +2 -3
- package/dist/r/src/components/ui/navigation-menu/navigation-menu.tsx +27 -20
- package/dist/r/src/components/ui/sheet/sheet.tsx +5 -1
- package/dist/r/src/components/ui/tabs/tabs.tsx +18 -1
- package/dist/r/src/components/ui/toast/toast.tsx +7 -6
- package/dist/r/src/components/ui/typography/index.ts +9 -0
- package/dist/r/src/components/ui/typography/typography.tsx +205 -0
- package/dist/r/stat.json +1 -0
- package/dist/r/theming.json +1 -0
- package/dist/r/typography.json +24 -0
- package/dist/r/update-dialog.json +1 -0
- package/package.json +1 -1
- package/registry.json +37 -0
- package/src/components/composites/about-dialog/about-dialog.tsx +3 -2
- package/src/components/composites/account-menu/account-menu.tsx +105 -66
- package/src/components/composites/help-center/help-center.tsx +2 -1
- package/src/components/composites/settings/settings.tsx +4 -1
- package/src/components/composites/stat/stat.tsx +7 -2
- package/src/components/composites/update-dialog/update-dialog.tsx +36 -24
- package/src/components/layout/page/page.tsx +3 -2
- package/src/components/theme/appearance-panel.tsx +13 -4
- package/src/components/ui/alert-dialog/alert-dialog.tsx +2 -1
- package/src/components/ui/card/card.tsx +2 -1
- package/src/components/ui/chart/chart-options.ts +35 -7
- package/src/components/ui/chart/chart.tsx +65 -12
- package/src/components/ui/dialog/dialog.tsx +2 -1
- package/src/components/ui/drawer/drawer.tsx +2 -1
- package/src/components/ui/field/field.tsx +2 -3
- package/src/components/ui/navigation-menu/navigation-menu.tsx +27 -20
- package/src/components/ui/sheet/sheet.tsx +5 -1
- package/src/components/ui/tabs/tabs.tsx +18 -1
- package/src/components/ui/toast/toast.tsx +7 -6
- package/src/components/ui/typography/index.ts +9 -0
- package/src/components/ui/typography/typography.tsx +205 -0
- package/src/index.ts +1 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
RadialLinearScale,
|
|
16
16
|
Tooltip as ChartTooltip,
|
|
17
17
|
} from 'chart.js'
|
|
18
|
-
import type { ChartOptions, ChartType } from 'chart.js'
|
|
18
|
+
import type { ChartOptions, ChartType, Plugin } from 'chart.js'
|
|
19
19
|
import { cn } from '../../../lib/utils'
|
|
20
20
|
import {
|
|
21
21
|
chartOptions,
|
|
@@ -62,7 +62,9 @@ import {
|
|
|
62
62
|
* A canvas cannot read a custom property, so the options and datasets carry
|
|
63
63
|
* `var(--token)` strings and each chart resolves them against its own element
|
|
64
64
|
* when it renders, and again whenever the root's attributes change — the
|
|
65
|
-
* `.dark` class, `data-accent`, an inline override.
|
|
65
|
+
* `.dark` class, `data-accent`, `data-font`, an inline override. The type is
|
|
66
|
+
* resolved the same way: `--font-sans` for the family and a type-scale rung for
|
|
67
|
+
* the size, so a chart follows the font axis too. See `createThemeResolver`.
|
|
66
68
|
*/
|
|
67
69
|
|
|
68
70
|
/**
|
|
@@ -119,6 +121,23 @@ function toCanvasColor(value: string): string {
|
|
|
119
121
|
return `rgba(${r}, ${g}, ${b}, ${Math.round(((a ?? 255) / 255) * 1000) / 1000})`
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
const length = /^(-?\d*\.?\d+)(px|rem)$/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A token's computed value as a canvas option: a colour as an sRGB colour, a
|
|
128
|
+
* `px`/`rem` length (a type-scale rung) as the number of pixels Chart.js wants,
|
|
129
|
+
* and anything else — a font stack — as written.
|
|
130
|
+
*/
|
|
131
|
+
function toCanvasValue(raw: string): string | number {
|
|
132
|
+
const match = length.exec(raw)
|
|
133
|
+
if (match) {
|
|
134
|
+
const value = Number(match[1])
|
|
135
|
+
if (match[2] === 'px') return value
|
|
136
|
+
return value * (Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16)
|
|
137
|
+
}
|
|
138
|
+
return toCanvasColor(raw)
|
|
139
|
+
}
|
|
140
|
+
|
|
122
141
|
/**
|
|
123
142
|
* Resolves `var(--token)` strings against a chart's element, and re-resolves
|
|
124
143
|
* them when the theme changes.
|
|
@@ -127,16 +146,25 @@ function toCanvasColor(value: string): string {
|
|
|
127
146
|
* inline tokens takes those. The observer watches every attribute on `<html>`,
|
|
128
147
|
* because that is where both applications switch appearance, accent and theme
|
|
129
148
|
* (`.dark`, `data-accent`, `data-theme`, inline variables); a theme axis added
|
|
130
|
-
* later is covered without touching this. It
|
|
149
|
+
* later is covered without touching this. It also re-resolves when a web font
|
|
150
|
+
* finishes loading: a canvas draws text once, so a chart rendered before the
|
|
151
|
+
* face selected by `data-font` arrived would otherwise keep the fallback face
|
|
152
|
+
* until something else redrew it. Both are released with the chart.
|
|
131
153
|
*/
|
|
132
154
|
function createThemeResolver() {
|
|
133
155
|
let element: HTMLElement | undefined
|
|
134
156
|
const [revision, invalidate] = createSignal(undefined, { equals: false })
|
|
157
|
+
const onFontsLoaded = () => invalidate()
|
|
135
158
|
|
|
136
159
|
onMount(() => {
|
|
137
160
|
const observer = new MutationObserver(() => invalidate())
|
|
138
161
|
observer.observe(document.documentElement, { attributes: true })
|
|
139
|
-
|
|
162
|
+
const fonts = typeof document.fonts === 'undefined' ? undefined : document.fonts
|
|
163
|
+
fonts?.addEventListener('loadingdone', onFontsLoaded)
|
|
164
|
+
onCleanup(() => {
|
|
165
|
+
observer.disconnect()
|
|
166
|
+
fonts?.removeEventListener('loadingdone', onFontsLoaded)
|
|
167
|
+
})
|
|
140
168
|
})
|
|
141
169
|
|
|
142
170
|
return {
|
|
@@ -147,11 +175,11 @@ function createThemeResolver() {
|
|
|
147
175
|
revision()
|
|
148
176
|
if (!element || typeof getComputedStyle === 'undefined') return value
|
|
149
177
|
const style = getComputedStyle(element)
|
|
150
|
-
const resolved = new Map<string, string | undefined>()
|
|
178
|
+
const resolved = new Map<string, string | number | undefined>()
|
|
151
179
|
return resolveCssVariables(value, (name) => {
|
|
152
180
|
if (!resolved.has(name)) {
|
|
153
181
|
const raw = style.getPropertyValue(name).trim()
|
|
154
|
-
resolved.set(name, raw ?
|
|
182
|
+
resolved.set(name, raw ? toCanvasValue(raw) : undefined)
|
|
155
183
|
}
|
|
156
184
|
return resolved.get(name)
|
|
157
185
|
})
|
|
@@ -343,13 +371,36 @@ export function RadarChart(props: SeriesChartProps) {
|
|
|
343
371
|
}
|
|
344
372
|
|
|
345
373
|
/**
|
|
346
|
-
*
|
|
374
|
+
* Puts the themed radial scale back on a chart `solid-chartjs` has just created.
|
|
347
375
|
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
376
|
+
* `solid-chartjs` deletes `options.scales.r` for every chart type but radar when
|
|
377
|
+
* it first creates a chart. Today that delete lands on a throwaway copy — Solid
|
|
378
|
+
* compiles `options={…}` to a getter, so its next read rebuilds the object with
|
|
379
|
+
* `r` intact — but that is an accident of how the prop is passed: a memoised or
|
|
380
|
+
* static options object would lose the scale, and the polar area would fall back
|
|
381
|
+
* to Chart.js's defaults (grey ticks on a translucent white box, which is a white
|
|
382
|
+
* box on a dark card). Restoring it from this component's own options makes the
|
|
383
|
+
* themed scale independent of both. Later option updates (every theme change)
|
|
384
|
+
* pass `r` through untouched; only creation drops it.
|
|
385
|
+
*
|
|
386
|
+
* `afterInit` runs inside Chart.js's constructor, after the options are read and
|
|
387
|
+
* before the first update builds the scales and draws, so the themed scale is in
|
|
388
|
+
* place for the first frame — no default-styled frame, and no extra update. The
|
|
389
|
+
* plugin is per instance, passed through the chart's `plugins` prop, so nothing
|
|
390
|
+
* is registered globally.
|
|
352
391
|
*/
|
|
392
|
+
function restoreRadialScale(options: () => ChartOptions): Plugin {
|
|
393
|
+
return {
|
|
394
|
+
id: 'adea-radial-scale',
|
|
395
|
+
afterInit(chart) {
|
|
396
|
+
const scale = options().scales?.['r']
|
|
397
|
+
if (!scale) return
|
|
398
|
+
chart.options.scales = { ...chart.options.scales, r: scale }
|
|
399
|
+
},
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** A polar area chart: radial bars, for a composition. */
|
|
353
404
|
export function PolarAreaChart(props: {
|
|
354
405
|
labels: readonly string[]
|
|
355
406
|
data: readonly number[]
|
|
@@ -357,9 +408,11 @@ export function PolarAreaChart(props: {
|
|
|
357
408
|
class?: string
|
|
358
409
|
}) {
|
|
359
410
|
const theme = createThemeResolver()
|
|
411
|
+
const options = () => theme.resolve(chartOptions(props.options, 'radial'))
|
|
360
412
|
return (
|
|
361
413
|
<div ref={theme.ref} class={cn('h-64', props.class)}>
|
|
362
414
|
<PolarArea
|
|
415
|
+
plugins={[restoreRadialScale(options)]}
|
|
363
416
|
data={theme.resolve({
|
|
364
417
|
labels: [...props.labels],
|
|
365
418
|
datasets: [
|
|
@@ -371,7 +424,7 @@ export function PolarAreaChart(props: {
|
|
|
371
424
|
},
|
|
372
425
|
],
|
|
373
426
|
})}
|
|
374
|
-
options={
|
|
427
|
+
options={options()}
|
|
375
428
|
/>
|
|
376
429
|
</div>
|
|
377
430
|
)
|
|
@@ -9,6 +9,7 @@ import type { Accessor, ComponentProps, JSX } from 'solid-js'
|
|
|
9
9
|
import { createRenderEffect, onCleanup, Show, splitProps } from 'solid-js'
|
|
10
10
|
import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface } from '../../../lib/overlay'
|
|
11
11
|
import { cn } from '../../../lib/utils'
|
|
12
|
+
import { headingVariants } from '../typography'
|
|
12
13
|
|
|
13
14
|
type FocusCycle = {
|
|
14
15
|
content: HTMLElement
|
|
@@ -327,7 +328,7 @@ export function DialogTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
|
|
|
327
328
|
const [local, rest] = splitProps(props, ['class'])
|
|
328
329
|
return (
|
|
329
330
|
<KobalteDialog.Title
|
|
330
|
-
class={cn('
|
|
331
|
+
class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
|
|
331
332
|
{...rest}
|
|
332
333
|
/>
|
|
333
334
|
)
|
|
@@ -2,6 +2,7 @@ import DrawerPrimitive from '@corvu/drawer'
|
|
|
2
2
|
import type { ComponentProps } from 'solid-js'
|
|
3
3
|
import { splitProps } from 'solid-js'
|
|
4
4
|
import { cn } from '../../../lib/utils'
|
|
5
|
+
import { headingVariants } from '../typography'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Drawer.
|
|
@@ -96,7 +97,7 @@ export function DrawerTitle(props: ComponentProps<typeof DrawerPrimitive.Label>)
|
|
|
96
97
|
const [local, rest] = splitProps(props, ['class'])
|
|
97
98
|
return (
|
|
98
99
|
<DrawerPrimitive.Label
|
|
99
|
-
class={cn('
|
|
100
|
+
class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
|
|
100
101
|
{...rest}
|
|
101
102
|
/>
|
|
102
103
|
)
|
|
@@ -4,6 +4,7 @@ import { createUniqueId, Show, splitProps } from 'solid-js'
|
|
|
4
4
|
import { Label } from '../label/label'
|
|
5
5
|
import { createFormFieldContext, FormFieldContext } from '../../../lib/form-field'
|
|
6
6
|
import { cn } from '../../../lib/utils'
|
|
7
|
+
import { headingVariants } from '../typography'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Field.
|
|
@@ -199,9 +200,7 @@ export function FieldLegend(props: ComponentProps<'legend'> & { variant?: 'legen
|
|
|
199
200
|
<legend
|
|
200
201
|
class={cn(
|
|
201
202
|
'mb-1',
|
|
202
|
-
local.variant === 'label'
|
|
203
|
-
? 'text-sm font-medium'
|
|
204
|
-
: 'text-base font-semibold tracking-tight',
|
|
203
|
+
local.variant === 'label' ? 'text-sm font-medium' : headingVariants({ size: 'card' }),
|
|
205
204
|
local.class
|
|
206
205
|
)}
|
|
207
206
|
{...rest}
|
|
@@ -2,7 +2,14 @@ import { splitProps, type ComponentProps, type JSX } from 'solid-js'
|
|
|
2
2
|
import * as KobalteNavigationMenu from '@kobalte/core/navigation-menu'
|
|
3
3
|
import { ChevronDown } from 'lucide-solid'
|
|
4
4
|
import { cn } from '../../../lib/utils'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
menuContentPadding,
|
|
7
|
+
menuItem,
|
|
8
|
+
menuLabel,
|
|
9
|
+
menuSeparator,
|
|
10
|
+
overlayMotion,
|
|
11
|
+
overlaySurface,
|
|
12
|
+
} from '../../../lib/overlay'
|
|
6
13
|
|
|
7
14
|
/**
|
|
8
15
|
* NavigationMenu.
|
|
@@ -123,7 +130,11 @@ export function NavigationMenuContent(props: ComponentProps<typeof KobalteNaviga
|
|
|
123
130
|
class={cn(
|
|
124
131
|
// Absolute so the viewport sizes to the active panel's measured box and
|
|
125
132
|
// animates between panels, rather than stacking them.
|
|
126
|
-
'absolute top-0 left-0 grid w-max gap-1
|
|
133
|
+
'absolute top-0 left-0 grid w-max gap-1 outline-none',
|
|
134
|
+
// The viewport is the surface and carries no padding of its own (its
|
|
135
|
+
// measured size is the panel's), so the panel supplies the inset every
|
|
136
|
+
// menu surface shares — the one the separator's bleed is cut for.
|
|
137
|
+
menuContentPadding,
|
|
127
138
|
// The motion is the panel's, not the viewport's. Kobalte keeps an
|
|
128
139
|
// outgoing panel mounted until the viewport's own animation ends; a
|
|
129
140
|
// viewport that carries an entrance animation never fires that again
|
|
@@ -193,7 +204,11 @@ export function NavigationMenuItem(props: NavigationMenuItemProps) {
|
|
|
193
204
|
<KobalteNavigationMenu.Item
|
|
194
205
|
class={cn(
|
|
195
206
|
menuItem,
|
|
196
|
-
|
|
207
|
+
// The description sits under the label, in the label's column. Without
|
|
208
|
+
// an icon there is no leading column, and a two-column grid would set
|
|
209
|
+
// the description beside the label instead of beneath it.
|
|
210
|
+
local.icon ? 'grid grid-cols-[auto_1fr]' : 'grid grid-cols-1',
|
|
211
|
+
'items-center gap-x-2 gap-y-0.5',
|
|
197
212
|
local.description && 'grid-rows-[auto_auto] py-2',
|
|
198
213
|
local.class
|
|
199
214
|
)}
|
|
@@ -202,7 +217,13 @@ export function NavigationMenuItem(props: NavigationMenuItemProps) {
|
|
|
202
217
|
{local.icon}
|
|
203
218
|
<span class="font-medium">{local.children}</span>
|
|
204
219
|
{local.description && (
|
|
205
|
-
<span
|
|
220
|
+
<span
|
|
221
|
+
class={cn('text-xs text-muted-foreground', {
|
|
222
|
+
'col-start-2': !!local.icon,
|
|
223
|
+
})}
|
|
224
|
+
>
|
|
225
|
+
{local.description}
|
|
226
|
+
</span>
|
|
206
227
|
)}
|
|
207
228
|
</KobalteNavigationMenu.Item>
|
|
208
229
|
)
|
|
@@ -212,12 +233,7 @@ export function NavigationMenuSeparator(
|
|
|
212
233
|
props: ComponentProps<typeof KobalteNavigationMenu.Separator>
|
|
213
234
|
) {
|
|
214
235
|
const [local, rest] = splitProps(props, ['class'])
|
|
215
|
-
return (
|
|
216
|
-
<KobalteNavigationMenu.Separator
|
|
217
|
-
class={cn('-mx-1 my-1 h-px bg-border', local.class)}
|
|
218
|
-
{...rest}
|
|
219
|
-
/>
|
|
220
|
-
)
|
|
236
|
+
return <KobalteNavigationMenu.Separator class={cn(menuSeparator, local.class)} {...rest} />
|
|
221
237
|
}
|
|
222
238
|
|
|
223
239
|
/** A non-interactive caption above a group of items. */
|
|
@@ -225,16 +241,7 @@ export function NavigationMenuGroupLabel(
|
|
|
225
241
|
props: ComponentProps<typeof KobalteNavigationMenu.GroupLabel>
|
|
226
242
|
) {
|
|
227
243
|
const [local, rest] = splitProps(props, ['class'])
|
|
228
|
-
return (
|
|
229
|
-
<KobalteNavigationMenu.GroupLabel
|
|
230
|
-
class={cn(
|
|
231
|
-
menuContentPadding,
|
|
232
|
-
'text-2xs font-semibold text-muted-foreground uppercase',
|
|
233
|
-
local.class
|
|
234
|
-
)}
|
|
235
|
-
{...rest}
|
|
236
|
-
/>
|
|
237
|
-
)
|
|
244
|
+
return <KobalteNavigationMenu.GroupLabel class={cn(menuLabel, local.class)} {...rest} />
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
export function NavigationMenuGroup(props: ComponentProps<typeof KobalteNavigationMenu.Group>) {
|
|
@@ -10,6 +10,7 @@ import { Show, splitProps } from 'solid-js'
|
|
|
10
10
|
import { cva, type VariantProps } from '../../../lib/variants'
|
|
11
11
|
import { cn } from '../../../lib/utils'
|
|
12
12
|
import { createDialogFocusRestoration, DialogOverlay } from '../dialog/dialog'
|
|
13
|
+
import { headingVariants } from '../typography'
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Sheet.
|
|
@@ -152,7 +153,10 @@ export function SheetFooter(props: ComponentProps<'div'>) {
|
|
|
152
153
|
export function SheetTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
|
|
153
154
|
const [local, rest] = splitProps(props, ['class'])
|
|
154
155
|
return (
|
|
155
|
-
<KobalteDialog.Title
|
|
156
|
+
<KobalteDialog.Title
|
|
157
|
+
class={cn(headingVariants({ size: 'subsection', leading: 'none' }), local.class)}
|
|
158
|
+
{...rest}
|
|
159
|
+
/>
|
|
156
160
|
)
|
|
157
161
|
}
|
|
158
162
|
|
|
@@ -13,6 +13,7 @@ import { cn } from '../../../lib/utils'
|
|
|
13
13
|
|
|
14
14
|
type TabsIdContextValue = {
|
|
15
15
|
id: Accessor<string>
|
|
16
|
+
orientation: Accessor<'horizontal' | 'vertical'>
|
|
16
17
|
triggerIds: Accessor<ReadonlyMap<string, string>>
|
|
17
18
|
registerTriggerId: (value: string, id: string) => void
|
|
18
19
|
unregisterTriggerId: (value: string, id: string) => void
|
|
@@ -43,6 +44,7 @@ export function Tabs(props: ComponentProps<typeof KobalteTabs>) {
|
|
|
43
44
|
const [triggerIds, setTriggerIds] = createSignal<ReadonlyMap<string, string>>(new Map())
|
|
44
45
|
const tabs: TabsIdContextValue = {
|
|
45
46
|
id,
|
|
47
|
+
orientation: () => rest.orientation ?? 'horizontal',
|
|
46
48
|
triggerIds,
|
|
47
49
|
registerTriggerId: (value, triggerId) => {
|
|
48
50
|
setTriggerIds((current) => new Map(current).set(value, triggerId))
|
|
@@ -103,6 +105,19 @@ export type TabsTriggerProps = ComponentProps<typeof KobalteTabs.Trigger> & {
|
|
|
103
105
|
appearance?: 'underline' | 'segmented'
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
/**
|
|
109
|
+
* The underline appearance marks the selected trigger on the edge it shares
|
|
110
|
+
* with the list's axis. A horizontal row reads left to right along a baseline,
|
|
111
|
+
* so the mark is a bottom bar sitting on the list's rule. A vertical list stacks
|
|
112
|
+
* its triggers, and a bottom bar there underlines one row of a column — it reads
|
|
113
|
+
* as a divider, not a selection — so the mark moves to the inline-start edge,
|
|
114
|
+
* the leading bar a side navigation uses, and the label aligns to that edge.
|
|
115
|
+
*
|
|
116
|
+
* The orientation is read from the root rather than from a `data-orientation`
|
|
117
|
+
* selector so the vertical classes are unprefixed: a composite that replaces the
|
|
118
|
+
* mark (SettingsNavigation's filled row passes `border-0`) then overrides it
|
|
119
|
+
* through the ordinary class merge instead of fighting a variant-scoped border.
|
|
120
|
+
*/
|
|
106
121
|
export function TabsTrigger(props: TabsTriggerProps) {
|
|
107
122
|
const [local, rest] = splitProps(props, ['class', 'appearance', 'id'])
|
|
108
123
|
const tabs = useTabsId()
|
|
@@ -128,7 +143,9 @@ export function TabsTrigger(props: TabsTriggerProps) {
|
|
|
128
143
|
'[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4',
|
|
129
144
|
local.appearance === 'segmented'
|
|
130
145
|
? 'h-control-sm rounded-md px-control-sm data-[selected]:bg-card data-[selected]:shadow-xs'
|
|
131
|
-
:
|
|
146
|
+
: tabs.orientation() === 'vertical'
|
|
147
|
+
? 'h-control-md justify-start rounded-none border-s-2 border-transparent ps-3 pe-2 data-[selected]:border-primary'
|
|
148
|
+
: 'h-control-md -mb-px rounded-none border-b-2 border-transparent px-2 pb-2 data-[selected]:border-primary',
|
|
132
149
|
local.class
|
|
133
150
|
)}
|
|
134
151
|
{...rest}
|
|
@@ -180,6 +180,10 @@ export type ToasterProps = ComponentProps<typeof KobalteToast.Region> & {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
|
+
* `class` lands on the region too: the props describe the region, and the
|
|
184
|
+
* region is the element a caller lays out (an inset clear of a status bar, a
|
|
185
|
+
* narrower stack). The list inside it is an implementation detail.
|
|
186
|
+
*
|
|
183
187
|
* The corner is set on the region, not the list. The region is the `fixed`
|
|
184
188
|
* element: an inset on the list inside it positions nothing, and the region falls
|
|
185
189
|
* back to wherever it happens to sit in the document flow.
|
|
@@ -217,16 +221,13 @@ export function Toaster(props: ToasterProps) {
|
|
|
217
221
|
*/
|
|
218
222
|
class={cn(
|
|
219
223
|
'pointer-events-none fixed z-(--z-toast) flex max-h-screen w-full flex-col gap-2 p-4 sm:max-w-sm',
|
|
220
|
-
toasterPosition[local.position ?? 'bottom-right']
|
|
224
|
+
toasterPosition[local.position ?? 'bottom-right'],
|
|
225
|
+
local.class
|
|
221
226
|
)}
|
|
222
227
|
{...rest}
|
|
223
228
|
>
|
|
224
229
|
<KobalteToast.List
|
|
225
|
-
class={cn(
|
|
226
|
-
'flex flex-col gap-2',
|
|
227
|
-
toasterAlignment[local.position ?? 'bottom-right'],
|
|
228
|
-
local.class
|
|
229
|
-
)}
|
|
230
|
+
class={cn('flex flex-col gap-2', toasterAlignment[local.position ?? 'bottom-right'])}
|
|
230
231
|
/>
|
|
231
232
|
</KobalteToast.Region>
|
|
232
233
|
)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Polymorphic, type PolymorphicProps } from '@kobalte/core/polymorphic'
|
|
2
|
+
import { type ComponentProps, splitProps, type ValidComponent } from 'solid-js'
|
|
3
|
+
import { cva, type VariantProps } from '../../../lib/variants'
|
|
4
|
+
import { cn } from '../../../lib/utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Typography.
|
|
8
|
+
*
|
|
9
|
+
* The type ladder in `theme.css` has eight rungs, each with its own leading. These
|
|
10
|
+
* two components are where a rung is paired with its weight and tracking, so a call
|
|
11
|
+
* site names a *role* — a page title, a section heading, a caption — and never
|
|
12
|
+
* re-decides `text-xl font-semibold tracking-tight` for itself. Twenty call sites
|
|
13
|
+
* that each choose a size and a weight drift; twenty that name a role cannot.
|
|
14
|
+
*
|
|
15
|
+
* **Size is visual; the element is the document outline.** They are separate
|
|
16
|
+
* decisions and the API keeps them separate. Each size has a conventional default
|
|
17
|
+
* element (`display`, `title` and `page` render an `h1`, `section` an `h2`, `card`
|
|
18
|
+
* an `h3`, `subsection` an `h4`) so the common case needs no prop, but the default
|
|
19
|
+
* is a convenience, not a coupling: a card-sized title that is the first heading
|
|
20
|
+
* in a settings section is an `h2`, so it says `as="h2"`. Never pick a `size` to get
|
|
21
|
+
* a heading level, and never pick an element to get a size.
|
|
22
|
+
*
|
|
23
|
+
* Headings are 600, and weights stop at 600 — on a dark surface a bold word blooms.
|
|
24
|
+
* Leading comes from the rung; `leading="none"` is for a title whose surrounding
|
|
25
|
+
* stack already sets the rhythm (a card or dialog header), and is the only other
|
|
26
|
+
* option.
|
|
27
|
+
*/
|
|
28
|
+
export const headingVariants = cva('font-semibold', {
|
|
29
|
+
variants: {
|
|
30
|
+
size: {
|
|
31
|
+
/** 30px. An app-level headline — a first-run or empty-app screen. Once per app. */
|
|
32
|
+
display: 'text-3xl tracking-tight',
|
|
33
|
+
/** 24px. A landing title or a headline figure larger than a stat's. */
|
|
34
|
+
title: 'text-2xl tracking-tight',
|
|
35
|
+
/** 20px. The title of a page (`PageHeaderTitle`). */
|
|
36
|
+
page: 'text-xl tracking-tight',
|
|
37
|
+
/** 18px. A section heading inside a page (`PageSection`). */
|
|
38
|
+
section: 'text-lg tracking-tight',
|
|
39
|
+
/** 16px. A card, dialog, drawer or settings-section title. */
|
|
40
|
+
card: 'text-base tracking-tight',
|
|
41
|
+
/** 14px. A heading inside a compact surface: a sheet, a chart, a column. */
|
|
42
|
+
subsection: 'text-sm',
|
|
43
|
+
},
|
|
44
|
+
leading: {
|
|
45
|
+
/** The rung's own line-height from `theme.css`. */
|
|
46
|
+
rung: '',
|
|
47
|
+
/** Line-height 1, for a title whose container's gap sets the rhythm. */
|
|
48
|
+
none: 'leading-none',
|
|
49
|
+
},
|
|
50
|
+
/** Tabular figures, so a row of numbers can be compared without digits shifting. */
|
|
51
|
+
numeric: {
|
|
52
|
+
true: 'tabular-nums',
|
|
53
|
+
},
|
|
54
|
+
tone: {
|
|
55
|
+
/** Inherit the surrounding colour — right on every surface by construction. */
|
|
56
|
+
inherit: '',
|
|
57
|
+
foreground: 'text-foreground',
|
|
58
|
+
muted: 'text-muted-foreground',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
defaultVariants: {
|
|
62
|
+
size: 'section',
|
|
63
|
+
leading: 'rung',
|
|
64
|
+
tone: 'inherit',
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The conventional element for each heading size. Exported so a test (and a
|
|
70
|
+
* reader) can see the whole mapping in one place; pass `as` to depart from it.
|
|
71
|
+
*/
|
|
72
|
+
export const headingElement = {
|
|
73
|
+
display: 'h1',
|
|
74
|
+
title: 'h1',
|
|
75
|
+
page: 'h1',
|
|
76
|
+
section: 'h2',
|
|
77
|
+
card: 'h3',
|
|
78
|
+
subsection: 'h4',
|
|
79
|
+
} as const
|
|
80
|
+
|
|
81
|
+
type HeadingVariantProps = VariantProps<typeof headingVariants>
|
|
82
|
+
type HeadingSize = keyof typeof headingElement
|
|
83
|
+
|
|
84
|
+
export type HeadingProps<T extends ValidComponent = 'h2'> = PolymorphicProps<
|
|
85
|
+
T,
|
|
86
|
+
HeadingVariantProps & {
|
|
87
|
+
class?: string
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
|
|
91
|
+
export function Heading<T extends ValidComponent = 'h2'>(props: HeadingProps<T>) {
|
|
92
|
+
const [local, rest] = splitProps(props as HeadingProps & { as?: ValidComponent }, [
|
|
93
|
+
'as',
|
|
94
|
+
'size',
|
|
95
|
+
'leading',
|
|
96
|
+
'numeric',
|
|
97
|
+
'tone',
|
|
98
|
+
'class',
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Polymorphic
|
|
103
|
+
as={local.as ?? headingElement[(local.size ?? 'section') as HeadingSize]}
|
|
104
|
+
data-slot="heading"
|
|
105
|
+
class={cn(
|
|
106
|
+
headingVariants({
|
|
107
|
+
size: local.size,
|
|
108
|
+
leading: local.leading,
|
|
109
|
+
numeric: local.numeric,
|
|
110
|
+
tone: local.tone,
|
|
111
|
+
}),
|
|
112
|
+
local.class
|
|
113
|
+
)}
|
|
114
|
+
{...(rest as ComponentProps<'h2'>)}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Text.
|
|
121
|
+
*
|
|
122
|
+
* Everything that is not a heading. `text-sm` is the interface default, not 16px,
|
|
123
|
+
* and it carries three weights by role: 400 for body copy, 500 for a label or
|
|
124
|
+
* control text, 600 for emphasis. Below it, `caption` (12px) is metadata and
|
|
125
|
+
* `micro` (11px, 500) is the floor — keyboard keys and the status bar. `code` is
|
|
126
|
+
* the mono face at 12px, for anything a user compares character by character.
|
|
127
|
+
*
|
|
128
|
+
* `tone` is a separate axis from `variant` because "secondary" is a colour
|
|
129
|
+
* decision, not a size: a muted caption and a muted body line are both common.
|
|
130
|
+
* The default inherits, so text on a filled surface picks up that surface's
|
|
131
|
+
* foreground without the caller restating it.
|
|
132
|
+
*
|
|
133
|
+
* Defaults: `body` renders a `p`, `code` a `code`, every other variant a `span`.
|
|
134
|
+
*/
|
|
135
|
+
export const textVariants = cva('', {
|
|
136
|
+
variants: {
|
|
137
|
+
variant: {
|
|
138
|
+
/** 14px / 400. Body copy and descriptions. */
|
|
139
|
+
body: 'text-sm',
|
|
140
|
+
/** 14px / 500. Labels, control text, list-row titles. */
|
|
141
|
+
label: 'text-sm font-medium',
|
|
142
|
+
/** 14px / 600. Emphasis inside body copy. */
|
|
143
|
+
strong: 'text-sm font-semibold',
|
|
144
|
+
/** 12px / 400. Metadata, secondary rows, units. */
|
|
145
|
+
caption: 'text-xs',
|
|
146
|
+
/** 11px / 500. The floor: keyboard keys, the status bar. */
|
|
147
|
+
micro: 'text-2xs font-medium',
|
|
148
|
+
/** 12px mono. Ids, paths, hashes, inline code. */
|
|
149
|
+
code: 'font-mono text-xs',
|
|
150
|
+
},
|
|
151
|
+
tone: {
|
|
152
|
+
inherit: '',
|
|
153
|
+
foreground: 'text-foreground',
|
|
154
|
+
muted: 'text-muted-foreground',
|
|
155
|
+
},
|
|
156
|
+
numeric: {
|
|
157
|
+
true: 'tabular-nums',
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
defaultVariants: {
|
|
161
|
+
variant: 'body',
|
|
162
|
+
tone: 'inherit',
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
export const textElement = {
|
|
167
|
+
body: 'p',
|
|
168
|
+
label: 'span',
|
|
169
|
+
strong: 'span',
|
|
170
|
+
caption: 'span',
|
|
171
|
+
micro: 'span',
|
|
172
|
+
code: 'code',
|
|
173
|
+
} as const
|
|
174
|
+
|
|
175
|
+
type TextVariantProps = VariantProps<typeof textVariants>
|
|
176
|
+
type TextVariant = keyof typeof textElement
|
|
177
|
+
|
|
178
|
+
export type TextProps<T extends ValidComponent = 'p'> = PolymorphicProps<
|
|
179
|
+
T,
|
|
180
|
+
TextVariantProps & {
|
|
181
|
+
class?: string
|
|
182
|
+
}
|
|
183
|
+
>
|
|
184
|
+
|
|
185
|
+
export function Text<T extends ValidComponent = 'p'>(props: TextProps<T>) {
|
|
186
|
+
const [local, rest] = splitProps(props as TextProps & { as?: ValidComponent }, [
|
|
187
|
+
'as',
|
|
188
|
+
'variant',
|
|
189
|
+
'tone',
|
|
190
|
+
'numeric',
|
|
191
|
+
'class',
|
|
192
|
+
])
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<Polymorphic
|
|
196
|
+
as={local.as ?? textElement[(local.variant ?? 'body') as TextVariant]}
|
|
197
|
+
data-slot="text"
|
|
198
|
+
class={cn(
|
|
199
|
+
textVariants({ variant: local.variant, tone: local.tone, numeric: local.numeric }),
|
|
200
|
+
local.class
|
|
201
|
+
)}
|
|
202
|
+
{...(rest as ComponentProps<'p'>)}
|
|
203
|
+
/>
|
|
204
|
+
)
|
|
205
|
+
}
|
package/dist/r/stat.json
CHANGED
package/dist/r/theming.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "typography",
|
|
3
|
+
"type": "registry:ui",
|
|
4
|
+
"title": "Typography",
|
|
5
|
+
"description": "",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"@kobalte/core@^0.13.14"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "src/components/ui/typography/index.ts",
|
|
15
|
+
"type": "registry:ui",
|
|
16
|
+
"target": "components/ui/typography/index.ts"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "src/components/ui/typography/typography.tsx",
|
|
20
|
+
"type": "registry:ui",
|
|
21
|
+
"target": "components/ui/typography/typography.tsx"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
package/package.json
CHANGED