@refraction-ui/astro 0.19.0 → 0.20.0
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/analytics-sink-app-insights/app-insights-sink.ts +1 -1
- package/dist/analytics-sink-posthog/client-sdk-sink.ts +1 -1
- package/dist/analytics-sink-posthog/replay.ts +1 -1
- package/dist/astro-avatar/Avatar.astro +5 -4
- package/dist/astro-data-table/DataTable.astro +7 -3
- package/dist/astro-native-select/NativeSelect.astro +26 -0
- package/dist/astro-native-select/index.ts +1 -0
- package/dist/astro-stat-grid/StatGrid.astro +23 -19
- package/dist/astro-table/Table.astro +21 -0
- package/dist/astro-table/TableCaption.astro +9 -0
- package/dist/astro-table/TableCell.astro +13 -0
- package/dist/astro-table/TableHead.astro +13 -0
- package/dist/astro-table/TableRow.astro +9 -0
- package/dist/astro-table/index.ts +1 -0
- package/dist/avatar/avatar.styles.ts +7 -2
- package/dist/avatar/avatar.ts +8 -3
- package/dist/avatar/index.ts +1 -0
- package/dist/data-table/data-table.styles.ts +18 -0
- package/dist/data-table/data-table.ts +49 -7
- package/dist/data-table/index.ts +8 -0
- package/dist/index.ts +2 -0
- package/dist/line-chart/index.ts +1 -0
- package/dist/line-chart/line-chart.ts +36 -9
- package/dist/logger/faro-engine.ts +2 -2
- package/dist/native-select/index.ts +6 -0
- package/dist/native-select/native-select.styles.ts +30 -0
- package/dist/native-select/native-select.ts +5 -0
- package/dist/stat-grid/index.ts +9 -0
- package/dist/stat-grid/stat-grid.styles.ts +73 -6
- package/dist/stat-grid/stat-grid.ts +25 -5
- package/dist/table/index.ts +16 -0
- package/dist/table/table.styles.ts +72 -0
- package/dist/table/table.ts +24 -0
- package/package.json +3 -1
|
@@ -131,7 +131,7 @@ function createClientSdkSink(opts: ClientSdkOptions): AnalyticsSink {
|
|
|
131
131
|
return instance
|
|
132
132
|
}
|
|
133
133
|
// Lazy, dynamic, optional — never a static/hard dependency.
|
|
134
|
-
const mod = (await import(appInsightsWebSpecifier)) as {
|
|
134
|
+
const mod = (await import(/* webpackIgnore: true */ /* @vite-ignore */ appInsightsWebSpecifier)) as {
|
|
135
135
|
ApplicationInsights: new (cfg: {
|
|
136
136
|
config: {
|
|
137
137
|
connectionString?: string
|
|
@@ -68,7 +68,7 @@ const posthogJsSpecifier = 'posthog-js'
|
|
|
68
68
|
|
|
69
69
|
async function defaultLoad(): Promise<PostHogJs> {
|
|
70
70
|
// Dynamic import keeps `posthog-js` out of the graph until first delivery.
|
|
71
|
-
const mod = (await import(posthogJsSpecifier)) as unknown as
|
|
71
|
+
const mod = (await import(/* webpackIgnore: true */ /* @vite-ignore */ posthogJsSpecifier)) as unknown as
|
|
72
72
|
| { default: PostHogJs }
|
|
73
73
|
| PostHogJs
|
|
74
74
|
return 'default' in mod ? mod.default : mod
|
|
@@ -78,7 +78,7 @@ export interface SessionReplayHandle {
|
|
|
78
78
|
const posthogJsSpecifier = 'posthog-js'
|
|
79
79
|
|
|
80
80
|
async function defaultLoad(): Promise<PostHogReplay> {
|
|
81
|
-
const mod = (await import(posthogJsSpecifier)) as unknown as
|
|
81
|
+
const mod = (await import(/* webpackIgnore: true */ /* @vite-ignore */ posthogJsSpecifier)) as unknown as
|
|
82
82
|
| { default: PostHogReplay }
|
|
83
83
|
| PostHogReplay
|
|
84
84
|
return 'default' in mod ? mod.default : mod
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { createAvatar, avatarVariants, type AvatarSize } from '../avatar/index.ts'
|
|
2
|
+
import { createAvatar, avatarVariants, type AvatarSize, type AvatarShape } from '../avatar/index.ts'
|
|
3
3
|
import { cn } from '../shared/index.ts'
|
|
4
4
|
|
|
5
5
|
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
6
|
size?: AvatarSize
|
|
7
|
+
shape?: AvatarShape
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const { size = 'md', class: className, ...props } = Astro.props
|
|
10
|
-
const api = createAvatar({ size })
|
|
10
|
+
const { size = 'md', shape = 'circle', class: className, ...props } = Astro.props
|
|
11
|
+
const api = createAvatar({ size, alt: props['aria-label'] as string | undefined })
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
<span
|
|
14
|
-
class={cn(avatarVariants({ size }), className)}
|
|
15
|
+
class={cn(avatarVariants({ size, shape }), className)}
|
|
15
16
|
data-rfr-avatar
|
|
16
17
|
data-rfr-avatar-size={size}
|
|
17
18
|
{...api.ariaProps}
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
headerVariants,
|
|
6
6
|
cellVariants,
|
|
7
7
|
rowVariants,
|
|
8
|
+
getColumnValue,
|
|
9
|
+
resolveColumnAlign,
|
|
10
|
+
dataTableAlignClass,
|
|
11
|
+
dataTableNumericClass,
|
|
8
12
|
type ColumnDef,
|
|
9
13
|
type SortDirection,
|
|
10
14
|
} from '../data-table/index.ts'
|
|
@@ -41,7 +45,7 @@ const hasFilterable = columns.some((c: ColumnDef) => c.filterable)
|
|
|
41
45
|
const hProps = api.getHeaderProps(col)
|
|
42
46
|
return (
|
|
43
47
|
<th
|
|
44
|
-
class={headerVariants({ sortable: col.sortable ? 'true' : 'false' })}
|
|
48
|
+
class={cn(headerVariants({ sortable: col.sortable ? 'true' : 'false' }), dataTableAlignClass[resolveColumnAlign(col)], col.headerClassName)}
|
|
45
49
|
role={hProps.role as string}
|
|
46
50
|
aria-sort={hProps['aria-sort'] as any}
|
|
47
51
|
data-rfr-data-table-header={col.id}
|
|
@@ -94,10 +98,10 @@ const hasFilterable = columns.some((c: ColumnDef) => c.filterable)
|
|
|
94
98
|
{columns.map((col: ColumnDef) => (
|
|
95
99
|
<td
|
|
96
100
|
role="cell"
|
|
97
|
-
class={cellVariants()}
|
|
101
|
+
class={cn(cellVariants(), dataTableAlignClass[resolveColumnAlign(col)], col.numeric && dataTableNumericClass, col.className)}
|
|
98
102
|
data-column={col.id}
|
|
99
103
|
>
|
|
100
|
-
{String(col
|
|
104
|
+
{String(getColumnValue(col, row) ?? '')}
|
|
101
105
|
</td>
|
|
102
106
|
))}
|
|
103
107
|
</tr>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
nativeSelectChevronPath,
|
|
4
|
+
nativeSelectIconClass,
|
|
5
|
+
nativeSelectVariants,
|
|
6
|
+
nativeSelectWrapperClass,
|
|
7
|
+
type NativeSelectSize,
|
|
8
|
+
} from '../native-select/index.ts'
|
|
9
|
+
import { cn } from '../shared/index.ts'
|
|
10
|
+
|
|
11
|
+
interface Props extends Omit<astroHTML.JSX.SelectHTMLAttributes, 'size'> {
|
|
12
|
+
size?: NativeSelectSize
|
|
13
|
+
containerClass?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { size = 'default', class: className, containerClass, ...rest } = Astro.props
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<span class={cn(nativeSelectWrapperClass, containerClass)} data-slot="native-select">
|
|
20
|
+
<select class={cn(nativeSelectVariants({ size }), className)} {...rest}>
|
|
21
|
+
<slot />
|
|
22
|
+
</select>
|
|
23
|
+
<svg class={nativeSelectIconClass} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
24
|
+
<path d={nativeSelectChevronPath} />
|
|
25
|
+
</svg>
|
|
26
|
+
</span>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -2,44 +2,48 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createStatGrid,
|
|
4
4
|
statColumns,
|
|
5
|
+
statColumnsKey,
|
|
5
6
|
statGridVariants,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
statItemVariants,
|
|
8
|
+
statValueVariants,
|
|
9
|
+
statLabelVariants,
|
|
10
|
+
statDescriptionClass,
|
|
9
11
|
type StatItem,
|
|
12
|
+
type StatGridVariant,
|
|
13
|
+
type StatGridLayout,
|
|
14
|
+
type StatGridColumns,
|
|
10
15
|
} from '../stat-grid/index.ts'
|
|
11
16
|
import { cn } from '../shared/index.ts'
|
|
12
17
|
|
|
13
18
|
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
14
|
-
/** The stat items to display in the grid
|
|
19
|
+
/** The stat items to display in the grid. */
|
|
15
20
|
items: StatItem[]
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
columns?: number
|
|
21
|
+
/** Maximum columns (reflows on narrow screens) or `auto`. Defaults to statColumns(items.length). */
|
|
22
|
+
columns?: StatGridColumns
|
|
23
|
+
variant?: StatGridVariant
|
|
24
|
+
layout?: StatGridLayout
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
const { items, columns, class: className, ...rest } = Astro.props
|
|
24
|
-
|
|
25
|
-
const cols = columns ?? statColumns(items.length)
|
|
26
|
-
const colsKey = String(cols) as '1' | '2' | '3'
|
|
27
|
+
const { items, columns, variant = 'plain', layout = 'value-first', class: className, ...rest } = Astro.props
|
|
28
|
+
const cols = statColumnsKey(columns ?? statColumns(items.length))
|
|
27
29
|
const { ariaProps, dataAttributes } = createStatGrid()
|
|
28
|
-
const gridStyle = `grid-template-columns:repeat(${cols}, 1fr)`
|
|
29
30
|
---
|
|
30
31
|
|
|
31
32
|
<div
|
|
32
|
-
class={cn(statGridVariants({ columns:
|
|
33
|
-
|
|
33
|
+
class={cn(statGridVariants({ columns: cols, variant }), className)}
|
|
34
|
+
data-variant={variant}
|
|
35
|
+
data-layout={layout}
|
|
34
36
|
{...ariaProps}
|
|
35
37
|
{...dataAttributes}
|
|
36
38
|
{...rest}
|
|
37
39
|
>
|
|
38
40
|
{
|
|
39
41
|
items.map((item) => (
|
|
40
|
-
<div role="listitem" class={
|
|
41
|
-
<span class={
|
|
42
|
-
<span class={
|
|
42
|
+
<div role="listitem" class={statItemVariants({ variant })} data-tone={item.tone ?? 'default'}>
|
|
43
|
+
{layout === 'label-first' && <span class={statLabelVariants({ layout })}>{item.label}</span>}
|
|
44
|
+
<span class={statValueVariants({ variant, tone: item.tone ?? 'default' })}>{item.value}</span>
|
|
45
|
+
{layout === 'value-first' && <span class={statLabelVariants({ layout })}>{item.label}</span>}
|
|
46
|
+
{item.description && <span class={statDescriptionClass}>{item.description}</span>}
|
|
43
47
|
</div>
|
|
44
48
|
))
|
|
45
49
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { createTable, tableContainerClass, tableRootVariants, type TableDensity, type TableHeadTone } from '../table/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.TableHTMLAttributes {
|
|
6
|
+
/** Recorded as data-density; pass the same density to TableHead/TableCell. */
|
|
7
|
+
density?: TableDensity
|
|
8
|
+
headTone?: TableHeadTone
|
|
9
|
+
fixed?: boolean
|
|
10
|
+
containerClass?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { density = 'default', headTone = 'default', fixed = false, containerClass, class: className, ...rest } = Astro.props
|
|
14
|
+
const { dataAttributes } = createTable({ density, headTone })
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<div class={cn(tableContainerClass, containerClass)} data-slot="table-container">
|
|
18
|
+
<table class={cn(tableRootVariants({ fixed: fixed ? 'true' : 'false' }), className)} {...dataAttributes} {...rest}>
|
|
19
|
+
<slot />
|
|
20
|
+
</table>
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { tableCaptionClass } from '../table/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
6
|
+
const { class: className, ...rest } = Astro.props
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<caption class={cn(tableCaptionClass, className)} data-slot="table-caption" {...rest}><slot /></caption>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { tableCellVariants, type TableAlign, type TableDensity } from '../table/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<astroHTML.JSX.TdHTMLAttributes, 'align'> {
|
|
6
|
+
align?: TableAlign
|
|
7
|
+
density?: TableDensity
|
|
8
|
+
numeric?: boolean
|
|
9
|
+
}
|
|
10
|
+
const { align, density, numeric = false, class: className, ...rest } = Astro.props
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<td class={cn(tableCellVariants({ align, density, numeric: numeric ? 'true' : 'false' }), className)} data-slot="table-cell" {...rest}><slot /></td>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { tableHeadVariants, type TableAlign, type TableDensity, type TableHeadTone } from '../table/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends Omit<astroHTML.JSX.ThHTMLAttributes, 'align'> {
|
|
6
|
+
align?: TableAlign
|
|
7
|
+
density?: TableDensity
|
|
8
|
+
tone?: TableHeadTone
|
|
9
|
+
}
|
|
10
|
+
const { align, density, tone, scope = 'col', class: className, ...rest } = Astro.props
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<th scope={scope} class={cn(tableHeadVariants({ align, density, tone }), className)} data-slot="table-head" {...rest}><slot /></th>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { tableRowClass } from '../table/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
6
|
+
const { class: className, ...rest } = Astro.props
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<tr class={cn(tableRowClass, className)} data-slot="table-row" {...rest}><slot /></tr>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -11,8 +11,12 @@ export const avatarTokens: TokenContract = {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const avatarVariants = cva({
|
|
14
|
-
base: 'relative flex shrink-0 overflow-hidden
|
|
14
|
+
base: 'relative flex shrink-0 overflow-hidden',
|
|
15
15
|
variants: {
|
|
16
|
+
shape: {
|
|
17
|
+
circle: 'rounded-full',
|
|
18
|
+
square: 'rounded-md',
|
|
19
|
+
},
|
|
16
20
|
size: {
|
|
17
21
|
xs: 'h-6 w-6',
|
|
18
22
|
sm: 'h-8 w-8',
|
|
@@ -22,6 +26,7 @@ export const avatarVariants = cva({
|
|
|
22
26
|
},
|
|
23
27
|
},
|
|
24
28
|
defaultVariants: {
|
|
29
|
+
shape: 'circle',
|
|
25
30
|
size: 'md',
|
|
26
31
|
},
|
|
27
32
|
})
|
|
@@ -31,7 +36,7 @@ export const avatarImageVariants = cva({
|
|
|
31
36
|
})
|
|
32
37
|
|
|
33
38
|
export const avatarFallbackVariants = cva({
|
|
34
|
-
base: 'flex h-full w-full items-center justify-center rounded-
|
|
39
|
+
base: 'flex h-full w-full items-center justify-center rounded-[inherit] bg-muted text-muted-foreground',
|
|
35
40
|
variants: {
|
|
36
41
|
size: {
|
|
37
42
|
xs: 'text-xs',
|
package/dist/avatar/avatar.ts
CHANGED
|
@@ -2,6 +2,9 @@ import type { AccessibilityProps } from '../shared/index.ts'
|
|
|
2
2
|
|
|
3
3
|
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
4
4
|
|
|
5
|
+
/** Outline of the avatar. */
|
|
6
|
+
export type AvatarShape = 'circle' | 'square'
|
|
7
|
+
|
|
5
8
|
export type AvatarLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error'
|
|
6
9
|
|
|
7
10
|
export interface AvatarProps {
|
|
@@ -44,10 +47,12 @@ export function getInitials(name: string): string {
|
|
|
44
47
|
export function createAvatar(props: AvatarProps = {}): AvatarAPI {
|
|
45
48
|
const { src, alt = '', fallback = '', size = 'md' } = props
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
// role="img" makes its children presentational, so it is only applied when
|
|
51
|
+
// the avatar itself has a name; otherwise the inner <img alt> / fallback
|
|
52
|
+
// text stays exposed.
|
|
53
|
+
const ariaProps: Partial<AccessibilityProps> = {}
|
|
50
54
|
if (alt) {
|
|
55
|
+
ariaProps.role = 'img'
|
|
51
56
|
ariaProps['aria-label'] = alt
|
|
52
57
|
}
|
|
53
58
|
|
package/dist/avatar/index.ts
CHANGED
|
@@ -53,3 +53,21 @@ export const rowVariants = cva({
|
|
|
53
53
|
selected: 'false',
|
|
54
54
|
},
|
|
55
55
|
})
|
|
56
|
+
|
|
57
|
+
/** Logical text alignment per column alignment. */
|
|
58
|
+
export const dataTableAlignClass = {
|
|
59
|
+
start: 'text-start',
|
|
60
|
+
center: 'text-center',
|
|
61
|
+
end: 'text-end',
|
|
62
|
+
} as const
|
|
63
|
+
|
|
64
|
+
/** Numeric cells: tabular figures, no wrapping. */
|
|
65
|
+
export const dataTableNumericClass = 'tabular-nums whitespace-nowrap'
|
|
66
|
+
|
|
67
|
+
/** Button inside a sortable header: the keyboard-reachable sort control. */
|
|
68
|
+
export const dataTableSortButtonClass =
|
|
69
|
+
'inline-flex items-center gap-1 rounded-sm font-inherit text-inherit focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'
|
|
70
|
+
|
|
71
|
+
/** Rows with a row action: pointer + keyboard focus ring. */
|
|
72
|
+
export const dataTableActionRowClass =
|
|
73
|
+
'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring'
|
|
@@ -5,8 +5,25 @@ export interface ColumnDef<T = Record<string, unknown>> {
|
|
|
5
5
|
id: string
|
|
6
6
|
/** Column header text */
|
|
7
7
|
header: string
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Value of this column for a row. Drives sorting, filtering and — when no
|
|
10
|
+
* `cell` renderer is given — the rendered text. Optional for display-only
|
|
11
|
+
* columns that set `cell`.
|
|
12
|
+
*/
|
|
13
|
+
accessor?: (row: T) => unknown
|
|
14
|
+
/**
|
|
15
|
+
* Custom cell renderer. Framework adapters narrow the return type (React:
|
|
16
|
+
* `ReactNode`). When omitted the cell renders `String(accessor(row))`.
|
|
17
|
+
*/
|
|
18
|
+
cell?: (row: T, index: number) => unknown
|
|
19
|
+
/** Horizontal alignment of header and cells. Defaults to `start`. */
|
|
20
|
+
align?: DataTableAlign
|
|
21
|
+
/** Numeric column: tabular figures, no wrapping, aligned to `end` unless `align` says otherwise. */
|
|
22
|
+
numeric?: boolean
|
|
23
|
+
/** Extra class for this column's data cells. */
|
|
24
|
+
className?: string
|
|
25
|
+
/** Extra class for this column's header cell. */
|
|
26
|
+
headerClassName?: string
|
|
10
27
|
/** Whether this column is sortable */
|
|
11
28
|
sortable?: boolean
|
|
12
29
|
/** Whether this column is filterable */
|
|
@@ -15,6 +32,31 @@ export interface ColumnDef<T = Record<string, unknown>> {
|
|
|
15
32
|
|
|
16
33
|
export type SortDirection = 'asc' | 'desc'
|
|
17
34
|
|
|
35
|
+
/** Horizontal alignment of a column (logical, RTL-safe). */
|
|
36
|
+
export type DataTableAlign = 'start' | 'center' | 'end'
|
|
37
|
+
|
|
38
|
+
/** A column's effective alignment: explicit `align`, else `end` for numeric columns. */
|
|
39
|
+
export function resolveColumnAlign(col: { align?: DataTableAlign; numeric?: boolean }): DataTableAlign {
|
|
40
|
+
return col.align ?? (col.numeric ? 'end' : 'start')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The raw value of a column for a row (`undefined` without an accessor). */
|
|
44
|
+
export function getColumnValue<T>(col: { accessor?: (row: T) => unknown }, row: T): unknown {
|
|
45
|
+
return col.accessor ? col.accessor(row) : undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Stable React/DOM key for a row: `getRowKey` when given, else the row's
|
|
50
|
+
* index. Prefer a `getRowKey` — index keys break row state on sort/filter.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveRowKey<T>(
|
|
53
|
+
row: T,
|
|
54
|
+
index: number,
|
|
55
|
+
getRowKey?: (row: T, index: number) => string | number,
|
|
56
|
+
): string | number {
|
|
57
|
+
return getRowKey ? getRowKey(row, index) : index
|
|
58
|
+
}
|
|
59
|
+
|
|
18
60
|
export interface DataTableProps<T = Record<string, unknown>> {
|
|
19
61
|
/** Column definitions */
|
|
20
62
|
columns: ColumnDef<T>[]
|
|
@@ -74,10 +116,10 @@ export function createDataTable<T = Record<string, unknown>>(
|
|
|
74
116
|
for (const [columnId, filterValue] of Object.entries(filters)) {
|
|
75
117
|
if (!filterValue) continue
|
|
76
118
|
const col = columns.find((c) => c.id === columnId)
|
|
77
|
-
if (!col) continue
|
|
119
|
+
if (!col?.accessor) continue
|
|
78
120
|
const lowerFilter = filterValue.toLowerCase()
|
|
79
121
|
result = result.filter((row) => {
|
|
80
|
-
const cellValue = col
|
|
122
|
+
const cellValue = getColumnValue(col, row)
|
|
81
123
|
return String(cellValue ?? '').toLowerCase().includes(lowerFilter)
|
|
82
124
|
})
|
|
83
125
|
}
|
|
@@ -91,11 +133,11 @@ export function createDataTable<T = Record<string, unknown>>(
|
|
|
91
133
|
if (!sortBy) return filtered
|
|
92
134
|
|
|
93
135
|
const col = columns.find((c) => c.id === sortBy)
|
|
94
|
-
if (!col) return filtered
|
|
136
|
+
if (!col?.accessor) return filtered
|
|
95
137
|
|
|
96
138
|
return [...filtered].sort((a, b) => {
|
|
97
|
-
const aVal = col
|
|
98
|
-
const bVal = col
|
|
139
|
+
const aVal = getColumnValue(col, a)
|
|
140
|
+
const bVal = getColumnValue(col, b)
|
|
99
141
|
const aStr = String(aVal ?? '')
|
|
100
142
|
const bStr = String(bVal ?? '')
|
|
101
143
|
|
package/dist/data-table/index.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export {
|
|
2
2
|
createDataTable,
|
|
3
|
+
resolveColumnAlign,
|
|
4
|
+
getColumnValue,
|
|
5
|
+
resolveRowKey,
|
|
3
6
|
type ColumnDef,
|
|
7
|
+
type DataTableAlign,
|
|
4
8
|
type SortDirection,
|
|
5
9
|
type DataTableProps,
|
|
6
10
|
type DataTableState,
|
|
@@ -12,4 +16,8 @@ export {
|
|
|
12
16
|
headerVariants,
|
|
13
17
|
cellVariants,
|
|
14
18
|
rowVariants,
|
|
19
|
+
dataTableAlignClass,
|
|
20
|
+
dataTableNumericClass,
|
|
21
|
+
dataTableSortButtonClass,
|
|
22
|
+
dataTableActionRowClass,
|
|
15
23
|
} from './data-table.styles.js'
|
package/dist/index.ts
CHANGED
|
@@ -241,3 +241,5 @@ export * from './astro-action-list/index.ts'
|
|
|
241
241
|
export * from './astro-funnel-chart/index.ts'
|
|
242
242
|
export * from './astro-line-chart/index.ts'
|
|
243
243
|
export * from './astro-page-header/index.ts'
|
|
244
|
+
export * from './astro-table/index.ts'
|
|
245
|
+
export * from './astro-native-select/index.ts'
|
package/dist/line-chart/index.ts
CHANGED
|
@@ -27,11 +27,14 @@ export interface LineChartOptions {
|
|
|
27
27
|
/** viewBox height. */
|
|
28
28
|
height: number
|
|
29
29
|
padding?: Partial<LineChartPadding>
|
|
30
|
-
/**
|
|
30
|
+
/** Target number of gridline intervals on the y-axis (the nice scale may use fewer). */
|
|
31
31
|
tickCount?: number
|
|
32
32
|
/** Headroom multiplier above the largest value. */
|
|
33
33
|
headroom?: number
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Show every n-th x label (the last label is always shown). Defaults to
|
|
36
|
+
* thinning so labels are at least ~48 viewBox units apart.
|
|
37
|
+
*/
|
|
35
38
|
labelEvery?: number
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -91,7 +94,8 @@ export const DEFAULT_LINE_CHART_WIDTH = 760
|
|
|
91
94
|
export const DEFAULT_LINE_CHART_HEIGHT = 230
|
|
92
95
|
const DEFAULT_TICK_COUNT = 4
|
|
93
96
|
const DEFAULT_HEADROOM = 1.08
|
|
94
|
-
|
|
97
|
+
/** Minimum horizontal room per x label when thinning automatically. */
|
|
98
|
+
const MIN_LABEL_SPACING = 48
|
|
95
99
|
/** Number of `--chart-N` tokens the theme defines. */
|
|
96
100
|
const CHART_TOKEN_COUNT = 5
|
|
97
101
|
|
|
@@ -100,6 +104,28 @@ export function lineChartColor(index: number): string {
|
|
|
100
104
|
return `hsl(var(--chart-${(index % CHART_TOKEN_COUNT) + 1}))`
|
|
101
105
|
}
|
|
102
106
|
|
|
107
|
+
const NICE_STEPS = [1, 2, 5, 10]
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A "nice" y-scale: a 1-2-5 × 10^k step so there are at most `tickCount`
|
|
111
|
+
* intervals up to `rawMax`, and the max rounded up to a whole step. Tick
|
|
112
|
+
* values are therefore round numbers by construction (0, 2000, 4000, …) —
|
|
113
|
+
* never 5999.9999 — so any tick formatter prints clean labels.
|
|
114
|
+
*/
|
|
115
|
+
export function niceScale(rawMax: number, tickCount: number): { max: number; step: number; ticks: number[] } {
|
|
116
|
+
const intervals = Math.max(1, Math.floor(tickCount))
|
|
117
|
+
if (!(rawMax > 0)) return { max: 1, step: 1 / intervals, ticks: Array.from({ length: intervals + 1 }, (_, i) => i / intervals) }
|
|
118
|
+
const target = rawMax / intervals
|
|
119
|
+
const magnitude = 10 ** Math.floor(Math.log10(target))
|
|
120
|
+
const step = NICE_STEPS.map((m) => m * magnitude).find((candidate) => candidate >= target) ?? 10 * magnitude
|
|
121
|
+
const count = Math.ceil(rawMax / step - 1e-9)
|
|
122
|
+
// Strip binary floating-point noise (e.g. 0.1 * 3) to the step's precision.
|
|
123
|
+
const decimals = Math.max(0, -Math.floor(Math.log10(step)))
|
|
124
|
+
const clean = (value: number) => Number(value.toFixed(decimals))
|
|
125
|
+
const ticks = Array.from({ length: count + 1 }, (_, i) => clean(i * step))
|
|
126
|
+
return { max: ticks[ticks.length - 1], step: clean(step), ticks }
|
|
127
|
+
}
|
|
128
|
+
|
|
103
129
|
function pathFrom(points: Array<[number, number]>): string {
|
|
104
130
|
return points.map(([px, py], i) => (i ? 'L' : 'M') + px.toFixed(1) + ' ' + py.toFixed(1)).join(' ')
|
|
105
131
|
}
|
|
@@ -117,25 +143,26 @@ export function computeLineChart(options: LineChartOptions): LineChartGeometry {
|
|
|
117
143
|
height,
|
|
118
144
|
tickCount = DEFAULT_TICK_COUNT,
|
|
119
145
|
headroom = DEFAULT_HEADROOM,
|
|
120
|
-
labelEvery = DEFAULT_LABEL_EVERY,
|
|
121
146
|
} = options
|
|
122
147
|
const padding = { ...DEFAULT_LINE_CHART_PADDING, ...options.padding }
|
|
123
148
|
const n = labels.length
|
|
124
149
|
const values = series.flatMap((s) => s.data)
|
|
125
150
|
const peak = values.length ? Math.max(...values) : 0
|
|
126
|
-
const
|
|
151
|
+
const scale = niceScale(peak > 0 ? peak * headroom : 0, tickCount)
|
|
152
|
+
const max = scale.max
|
|
127
153
|
const plotW = width - padding.left - padding.right
|
|
128
154
|
const plotTop = padding.top
|
|
129
155
|
const plotBottom = height - padding.bottom
|
|
130
156
|
const plotH = plotBottom - plotTop
|
|
131
157
|
|
|
158
|
+
const labelEvery =
|
|
159
|
+
options.labelEvery ??
|
|
160
|
+
Math.max(1, Math.ceil(n / Math.max(1, Math.floor(plotW / MIN_LABEL_SPACING))))
|
|
161
|
+
|
|
132
162
|
const x = (index: number) => padding.left + (n > 1 ? (index / (n - 1)) * plotW : plotW / 2)
|
|
133
163
|
const y = (value: number) => plotTop + (1 - value / max) * plotH
|
|
134
164
|
|
|
135
|
-
const ticks: LineChartTick[] =
|
|
136
|
-
const value = (max / tickCount) * t
|
|
137
|
-
return { value, y: y(value) }
|
|
138
|
-
})
|
|
165
|
+
const ticks: LineChartTick[] = scale.ticks.map((value) => ({ value, y: y(value) }))
|
|
139
166
|
|
|
140
167
|
const xLabels: LineChartXLabel[] = labels
|
|
141
168
|
.map((label, index) => ({ index, label, x: x(index) }))
|
|
@@ -74,11 +74,11 @@ async function loadFaroTransport(
|
|
|
74
74
|
// Indirected so bundlers keep these as runtime-optional dynamic imports.
|
|
75
75
|
const sdkName = '@grafana/faro-web-sdk'
|
|
76
76
|
const tracingName = '@grafana/faro-web-tracing'
|
|
77
|
-
const sdk = (await import(/* @vite-ignore */ sdkName)) as {
|
|
77
|
+
const sdk = (await import(/* webpackIgnore: true */ /* @vite-ignore */ sdkName)) as {
|
|
78
78
|
initializeFaro: (cfg: unknown) => unknown
|
|
79
79
|
getWebInstrumentations: () => unknown[]
|
|
80
80
|
}
|
|
81
|
-
const tracing = (await import(/* @vite-ignore */ tracingName)) as {
|
|
81
|
+
const tracing = (await import(/* webpackIgnore: true */ /* @vite-ignore */ tracingName)) as {
|
|
82
82
|
TracingInstrumentation: new () => unknown
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Wrapper that positions the chevron over the select. */
|
|
4
|
+
export const nativeSelectWrapperClass = 'relative inline-flex min-w-0 items-center'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `<select>`: the platform's own listbox (mobile pickers, type-to-select,
|
|
8
|
+
* form submission, `change` events) with the input look. Size-owned utilities
|
|
9
|
+
* live only in the size variants so `size` always wins.
|
|
10
|
+
*/
|
|
11
|
+
export const nativeSelectVariants = cva({
|
|
12
|
+
base:
|
|
13
|
+
'w-full cursor-pointer appearance-none rounded-md border border-input bg-background text-foreground shadow-sm ' +
|
|
14
|
+
'ring-offset-background focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring ' +
|
|
15
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: 'h-8 pl-2.5 pr-8 text-xs',
|
|
19
|
+
default: 'h-9 pl-3 pr-9 text-sm',
|
|
20
|
+
lg: 'h-10 pl-3 pr-10 text-base',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
size: 'default',
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
/** The chevron: decorative, and never eats the click meant for the select. */
|
|
29
|
+
export const nativeSelectIconClass =
|
|
30
|
+
'pointer-events-none absolute right-2.5 h-4 w-4 shrink-0 text-muted-foreground'
|
package/dist/stat-grid/index.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
export {
|
|
2
2
|
createStatGrid,
|
|
3
3
|
statColumns,
|
|
4
|
+
statColumnsKey,
|
|
4
5
|
type StatItem,
|
|
6
|
+
type StatTone,
|
|
7
|
+
type StatGridVariant,
|
|
8
|
+
type StatGridLayout,
|
|
9
|
+
type StatGridColumns,
|
|
5
10
|
type StatGridAPI,
|
|
6
11
|
} from './stat-grid.js'
|
|
7
12
|
|
|
8
13
|
export {
|
|
9
14
|
statGridVariants,
|
|
15
|
+
statItemVariants,
|
|
16
|
+
statValueVariants,
|
|
17
|
+
statLabelVariants,
|
|
18
|
+
statDescriptionClass,
|
|
10
19
|
statItemClass,
|
|
11
20
|
statValueClass,
|
|
12
21
|
statLabelClass,
|
|
@@ -1,25 +1,92 @@
|
|
|
1
1
|
import { cva } from '../shared/index.ts'
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Grid container. Column counts are responsive (one column on a phone, more
|
|
5
|
+
* as the width allows) — never a fixed inline template that cannot reflow.
|
|
6
|
+
* `auto` fits as many ~12rem items per row as the width allows.
|
|
7
|
+
*/
|
|
4
8
|
export const statGridVariants = cva({
|
|
5
|
-
base: 'grid
|
|
9
|
+
base: 'grid',
|
|
6
10
|
variants: {
|
|
7
11
|
columns: {
|
|
8
12
|
'1': 'grid-cols-1',
|
|
9
|
-
'2': 'grid-cols-2',
|
|
10
|
-
'3': 'grid-cols-3',
|
|
13
|
+
'2': 'grid-cols-1 sm:grid-cols-2',
|
|
14
|
+
'3': 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
|
|
15
|
+
'4': 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
|
|
16
|
+
auto: 'grid-cols-[repeat(auto-fill,minmax(12rem,1fr))]',
|
|
17
|
+
},
|
|
18
|
+
variant: {
|
|
19
|
+
plain: 'gap-8',
|
|
20
|
+
card: 'gap-3',
|
|
11
21
|
},
|
|
12
22
|
},
|
|
13
23
|
defaultVariants: {
|
|
14
24
|
columns: '3',
|
|
25
|
+
variant: 'plain',
|
|
15
26
|
},
|
|
16
27
|
})
|
|
17
28
|
|
|
18
29
|
/** Individual stat item container (role="listitem"). */
|
|
30
|
+
export const statItemVariants = cva({
|
|
31
|
+
base: 'flex min-w-0 flex-col',
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
plain: '',
|
|
35
|
+
card: 'gap-1 rounded-lg border border-border bg-card p-4 text-card-foreground',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: 'plain',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/** @deprecated Use `statItemVariants()`; kept for existing imports. */
|
|
19
44
|
export const statItemClass = 'flex flex-col'
|
|
20
45
|
|
|
21
|
-
/** The
|
|
46
|
+
/** The value of a stat, by variant and tone. */
|
|
47
|
+
export const statValueVariants = cva({
|
|
48
|
+
base: 'font-bold tabular-nums',
|
|
49
|
+
variants: {
|
|
50
|
+
variant: {
|
|
51
|
+
plain: 'text-xl sm:text-2xl',
|
|
52
|
+
card: 'text-2xl leading-tight',
|
|
53
|
+
},
|
|
54
|
+
tone: {
|
|
55
|
+
default: '',
|
|
56
|
+
positive: 'text-success',
|
|
57
|
+
negative: 'text-destructive',
|
|
58
|
+
caution: 'text-warning',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
compoundVariants: [
|
|
62
|
+
{ variant: 'plain', tone: 'default', class: 'text-primary' },
|
|
63
|
+
{ variant: 'card', tone: 'default', class: 'text-foreground' },
|
|
64
|
+
],
|
|
65
|
+
defaultVariants: {
|
|
66
|
+
variant: 'plain',
|
|
67
|
+
tone: 'default',
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
/** @deprecated Use `statValueVariants()`; kept for existing imports. */
|
|
22
72
|
export const statValueClass = 'text-xl sm:text-2xl font-bold text-primary'
|
|
23
73
|
|
|
24
|
-
/** The descriptive label
|
|
74
|
+
/** The descriptive label. Above the value it reads as a small caption. */
|
|
75
|
+
export const statLabelVariants = cva({
|
|
76
|
+
base: 'text-muted-foreground',
|
|
77
|
+
variants: {
|
|
78
|
+
layout: {
|
|
79
|
+
'value-first': 'mt-1 text-sm leading-snug',
|
|
80
|
+
'label-first': 'text-xs uppercase tracking-wide',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
defaultVariants: {
|
|
84
|
+
layout: 'value-first',
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
/** @deprecated Use `statLabelVariants()`; kept for existing imports. */
|
|
25
89
|
export const statLabelClass = 'mt-1 text-sm text-muted-foreground leading-snug'
|
|
90
|
+
|
|
91
|
+
/** Optional line under a stat explaining how it was computed. */
|
|
92
|
+
export const statDescriptionClass = 'text-xs leading-snug text-muted-foreground'
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
/** Semantic colour of a stat's value. */
|
|
2
|
+
export type StatTone = 'default' | 'positive' | 'negative' | 'caution'
|
|
3
|
+
/** `plain` = marketing callouts; `card` = bordered dashboard KPI cards. */
|
|
4
|
+
export type StatGridVariant = 'plain' | 'card'
|
|
5
|
+
/** Whether the label sits under the value (callout) or above it (KPI). */
|
|
6
|
+
export type StatGridLayout = 'value-first' | 'label-first'
|
|
7
|
+
/** Column count: a fixed maximum that reflows down on narrow screens, or `auto`. */
|
|
8
|
+
export type StatGridColumns = 1 | 2 | 3 | 4 | 'auto'
|
|
9
|
+
|
|
1
10
|
/** A single stat callout displayed in the grid. */
|
|
2
11
|
export type StatItem = {
|
|
3
12
|
/** The primary value displayed prominently (e.g. "10k+", "$4.2M"). */
|
|
4
13
|
value: string
|
|
5
|
-
/** The descriptive label
|
|
14
|
+
/** The descriptive label. */
|
|
6
15
|
label: string
|
|
16
|
+
/** Stable key; defaults to the index. */
|
|
17
|
+
id?: string
|
|
18
|
+
/** Optional line explaining the figure (how it was computed, a delta). */
|
|
19
|
+
description?: string
|
|
20
|
+
/** Colour of the value. Defaults to `default`. */
|
|
21
|
+
tone?: StatTone
|
|
7
22
|
}
|
|
8
23
|
|
|
9
24
|
export interface StatGridAPI {
|
|
@@ -20,11 +35,16 @@ export interface StatGridAPI {
|
|
|
20
35
|
* - 2 items → 2 columns
|
|
21
36
|
* - 3 or more items → capped at `max` (default 3)
|
|
22
37
|
*/
|
|
23
|
-
export function statColumns(count: number, max = 3):
|
|
24
|
-
if (count <=
|
|
25
|
-
if (count === 1) return 1
|
|
38
|
+
export function statColumns(count: number, max = 3): 1 | 2 | 3 | 4 {
|
|
39
|
+
if (count <= 1) return 1
|
|
26
40
|
if (count === 2) return 2
|
|
27
|
-
return Math.min(count, max)
|
|
41
|
+
return Math.min(count, max, 4) as 3 | 4
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The cva key for a column setting (clamped to the supported 1–4). */
|
|
45
|
+
export function statColumnsKey(columns: StatGridColumns): '1' | '2' | '3' | '4' | 'auto' {
|
|
46
|
+
if (columns === 'auto') return 'auto'
|
|
47
|
+
return String(Math.min(Math.max(Math.round(columns), 1), 4)) as '1' | '2' | '3' | '4'
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createTable,
|
|
3
|
+
type TableDensity,
|
|
4
|
+
type TableAlign,
|
|
5
|
+
type TableHeadTone,
|
|
6
|
+
type TableAPI,
|
|
7
|
+
} from './table.js'
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
tableContainerClass,
|
|
11
|
+
tableRootVariants,
|
|
12
|
+
tableCaptionClass,
|
|
13
|
+
tableRowClass,
|
|
14
|
+
tableHeadVariants,
|
|
15
|
+
tableCellVariants,
|
|
16
|
+
} from './table.styles.js'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Scroll container: a wide table scrolls inside it instead of the page. */
|
|
4
|
+
export const tableContainerClass = 'relative w-full overflow-x-auto'
|
|
5
|
+
|
|
6
|
+
/** The `<table>`. `fixed` pins column widths to the header row. */
|
|
7
|
+
export const tableRootVariants = cva({
|
|
8
|
+
base: 'w-full caption-bottom border-collapse text-sm',
|
|
9
|
+
variants: {
|
|
10
|
+
fixed: {
|
|
11
|
+
true: 'table-fixed',
|
|
12
|
+
false: '',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
fixed: 'false',
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
/** Caption under the table. */
|
|
21
|
+
export const tableCaptionClass = 'mt-2 text-xs text-muted-foreground'
|
|
22
|
+
|
|
23
|
+
/** Body rows: a hairline between rows, none after the last. */
|
|
24
|
+
export const tableRowClass = 'border-b border-border-subtle last:border-b-0'
|
|
25
|
+
|
|
26
|
+
const align = {
|
|
27
|
+
start: 'text-start',
|
|
28
|
+
center: 'text-center',
|
|
29
|
+
end: 'text-end',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Header cell: subtle surface, rule under it. */
|
|
33
|
+
export const tableHeadVariants = cva({
|
|
34
|
+
base: 'border-b border-border-subtle bg-surface-subtle align-middle',
|
|
35
|
+
variants: {
|
|
36
|
+
align,
|
|
37
|
+
tone: {
|
|
38
|
+
default: 'text-xs font-medium text-muted-foreground',
|
|
39
|
+
eyebrow: 'text-[11px] font-semibold uppercase tracking-wider text-muted-foreground',
|
|
40
|
+
},
|
|
41
|
+
density: {
|
|
42
|
+
compact: 'h-8 px-2',
|
|
43
|
+
default: 'h-9 px-3',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
align: 'start',
|
|
48
|
+
tone: 'default',
|
|
49
|
+
density: 'default',
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
/** Data cell. */
|
|
54
|
+
export const tableCellVariants = cva({
|
|
55
|
+
base: 'align-middle',
|
|
56
|
+
variants: {
|
|
57
|
+
align,
|
|
58
|
+
density: {
|
|
59
|
+
compact: 'px-2 py-1.5',
|
|
60
|
+
default: 'px-3 py-2.5',
|
|
61
|
+
},
|
|
62
|
+
numeric: {
|
|
63
|
+
true: 'tabular-nums whitespace-nowrap',
|
|
64
|
+
false: '',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
defaultVariants: {
|
|
68
|
+
align: 'start',
|
|
69
|
+
density: 'default',
|
|
70
|
+
numeric: 'false',
|
|
71
|
+
},
|
|
72
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Cell padding for every cell of a table. */
|
|
2
|
+
export type TableDensity = 'compact' | 'default'
|
|
3
|
+
/** Horizontal alignment of a header or data cell (logical, RTL-safe). */
|
|
4
|
+
export type TableAlign = 'start' | 'center' | 'end'
|
|
5
|
+
/** Look of header cells: `eyebrow` is small uppercase tracked text. */
|
|
6
|
+
export type TableHeadTone = 'default' | 'eyebrow'
|
|
7
|
+
|
|
8
|
+
export interface TableAPI {
|
|
9
|
+
/** Data attributes for styling hooks. */
|
|
10
|
+
dataAttributes: Record<string, string>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Framework-agnostic data attributes for the table root. */
|
|
14
|
+
export function createTable(
|
|
15
|
+
props: { density?: TableDensity; headTone?: TableHeadTone } = {},
|
|
16
|
+
): TableAPI {
|
|
17
|
+
return {
|
|
18
|
+
dataAttributes: {
|
|
19
|
+
'data-slot': 'table',
|
|
20
|
+
'data-density': props.density ?? 'default',
|
|
21
|
+
'data-head-tone': props.headTone ?? 'default',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@refraction-ui/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "All Refraction UI Astro components in one package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"@refraction-ui/astro-mastery-bar": "workspace:*",
|
|
97
97
|
"@refraction-ui/astro-mini-map": "workspace:*",
|
|
98
98
|
"@refraction-ui/astro-mobile-nav": "workspace:*",
|
|
99
|
+
"@refraction-ui/astro-native-select": "workspace:*",
|
|
99
100
|
"@refraction-ui/astro-navbar": "workspace:*",
|
|
100
101
|
"@refraction-ui/astro-numbered-steps": "workspace:*",
|
|
101
102
|
"@refraction-ui/astro-otp-input": "workspace:*",
|
|
@@ -132,6 +133,7 @@
|
|
|
132
133
|
"@refraction-ui/astro-steps": "workspace:*",
|
|
133
134
|
"@refraction-ui/astro-sticky-note": "workspace:*",
|
|
134
135
|
"@refraction-ui/astro-switch": "workspace:*",
|
|
136
|
+
"@refraction-ui/astro-table": "workspace:*",
|
|
135
137
|
"@refraction-ui/astro-table-of-contents": "workspace:*",
|
|
136
138
|
"@refraction-ui/astro-tabs": "workspace:*",
|
|
137
139
|
"@refraction-ui/astro-terminal": "workspace:*",
|