@refraction-ui/astro 0.18.1 → 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.
Files changed (78) hide show
  1. package/dist/action-list/action-list.styles.ts +33 -0
  2. package/dist/action-list/action-list.ts +31 -0
  3. package/dist/action-list/index.ts +16 -0
  4. package/dist/analytics-sink-app-insights/app-insights-sink.ts +1 -1
  5. package/dist/analytics-sink-posthog/client-sdk-sink.ts +1 -1
  6. package/dist/analytics-sink-posthog/replay.ts +1 -1
  7. package/dist/astro-action-list/ActionList.astro +12 -0
  8. package/dist/astro-action-list/ActionListItem.astro +62 -0
  9. package/dist/astro-action-list/index.ts +1 -0
  10. package/dist/astro-avatar/Avatar.astro +5 -4
  11. package/dist/astro-callout/Callout.astro +4 -1
  12. package/dist/astro-card/Card.astro +3 -2
  13. package/dist/astro-card/CardTitle.astro +6 -4
  14. package/dist/astro-data-table/DataTable.astro +7 -3
  15. package/dist/astro-funnel-chart/FunnelChart.astro +64 -0
  16. package/dist/astro-funnel-chart/index.ts +1 -0
  17. package/dist/astro-line-chart/LineChart.astro +82 -0
  18. package/dist/astro-line-chart/index.ts +1 -0
  19. package/dist/astro-native-select/NativeSelect.astro +26 -0
  20. package/dist/astro-native-select/index.ts +1 -0
  21. package/dist/astro-page-header/PageHeader.astro +46 -0
  22. package/dist/astro-page-header/index.ts +1 -0
  23. package/dist/astro-stat-grid/StatGrid.astro +23 -19
  24. package/dist/astro-table/Table.astro +21 -0
  25. package/dist/astro-table/TableCaption.astro +9 -0
  26. package/dist/astro-table/TableCell.astro +13 -0
  27. package/dist/astro-table/TableHead.astro +13 -0
  28. package/dist/astro-table/TableRow.astro +9 -0
  29. package/dist/astro-table/index.ts +1 -0
  30. package/dist/avatar/avatar.styles.ts +7 -2
  31. package/dist/avatar/avatar.ts +8 -3
  32. package/dist/avatar/index.ts +1 -0
  33. package/dist/badge/badge.styles.ts +6 -1
  34. package/dist/badge/badge.ts +24 -2
  35. package/dist/button/button.styles.ts +10 -4
  36. package/dist/button/button.ts +4 -1
  37. package/dist/callout/callout.ts +12 -3
  38. package/dist/card/card.styles.ts +10 -1
  39. package/dist/card/card.ts +14 -0
  40. package/dist/card/index.ts +3 -0
  41. package/dist/data-table/data-table.styles.ts +18 -0
  42. package/dist/data-table/data-table.ts +49 -7
  43. package/dist/data-table/index.ts +8 -0
  44. package/dist/diff-viewer/diff-viewer.styles.ts +3 -0
  45. package/dist/funnel-chart/funnel-chart.styles.ts +16 -0
  46. package/dist/funnel-chart/funnel-chart.ts +68 -0
  47. package/dist/funnel-chart/index.ts +10 -0
  48. package/dist/index.ts +6 -0
  49. package/dist/input/input.styles.ts +2 -2
  50. package/dist/line-chart/index.ts +20 -0
  51. package/dist/line-chart/line-chart.styles.ts +16 -0
  52. package/dist/line-chart/line-chart.ts +215 -0
  53. package/dist/logger/faro-engine.ts +2 -2
  54. package/dist/native-select/index.ts +6 -0
  55. package/dist/native-select/native-select.styles.ts +30 -0
  56. package/dist/native-select/native-select.ts +5 -0
  57. package/dist/page-header/index.ts +15 -0
  58. package/dist/page-header/page-header.styles.ts +21 -0
  59. package/dist/page-header/page-header.ts +27 -0
  60. package/dist/progress-display/progress-display.styles.ts +1 -1
  61. package/dist/select/index.ts +2 -0
  62. package/dist/select/select.styles.ts +9 -3
  63. package/dist/select/select.ts +11 -3
  64. package/dist/separator/index.ts +2 -0
  65. package/dist/separator/separator.styles.ts +17 -2
  66. package/dist/shared/cn.ts +6 -3
  67. package/dist/slide-viewer/slide-viewer.styles.ts +2 -0
  68. package/dist/stat-grid/index.ts +9 -0
  69. package/dist/stat-grid/stat-grid.styles.ts +73 -6
  70. package/dist/stat-grid/stat-grid.ts +25 -5
  71. package/dist/sticky-note/sticky-note.styles.ts +2 -0
  72. package/dist/table/index.ts +16 -0
  73. package/dist/table/table.styles.ts +72 -0
  74. package/dist/table/table.ts +24 -0
  75. package/dist/tabs/index.ts +1 -0
  76. package/dist/tabs/tabs.ts +29 -0
  77. package/dist/textarea/textarea.styles.ts +2 -2
  78. package/package.json +7 -1
@@ -0,0 +1,33 @@
1
+ import { cva } from '../shared/index.ts'
2
+
3
+ /** The list: rows separated by hairlines, no bullets. */
4
+ export const actionListClass = 'm-0 flex list-none flex-col divide-y divide-border-subtle p-0'
5
+
6
+ /** A row button: full width, whole row is the hit target. */
7
+ export const actionListItemVariants = cva({
8
+ base:
9
+ 'flex w-full items-center gap-4 bg-transparent text-left text-sm text-foreground transition-colors ' +
10
+ 'hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring ' +
11
+ 'disabled:pointer-events-none disabled:opacity-50',
12
+ variants: {
13
+ density: {
14
+ default: 'px-4 py-3',
15
+ compact: 'px-3 py-2',
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ density: 'default',
20
+ },
21
+ })
22
+
23
+ /** Leading text column (title, description, meta). */
24
+ export const actionListItemBodyClass = 'flex min-w-0 flex-1 flex-col gap-0.5'
25
+ /** Primary line. */
26
+ export const actionListItemTitleClass = 'break-words font-semibold text-foreground'
27
+ /** Supporting line under the title. */
28
+ export const actionListItemDescriptionClass = 'break-words text-xs text-muted-foreground'
29
+ /** Tertiary line (owner, tags, ids). */
30
+ export const actionListItemMetaClass = 'break-words text-xs text-muted-foreground'
31
+ /** Trailing column (dates, sizes, counts). */
32
+ export const actionListItemTrailingClass =
33
+ 'flex flex-none items-center gap-4 text-xs tabular-nums text-muted-foreground'
@@ -0,0 +1,31 @@
1
+ /** Row padding. */
2
+ export type ActionListDensity = 'default' | 'compact'
3
+
4
+ export interface ActionListItemProps {
5
+ disabled?: boolean
6
+ density?: ActionListDensity
7
+ }
8
+
9
+ export interface ActionListItemAPI {
10
+ /** ARIA attributes for the row button. */
11
+ ariaProps: Record<string, string | number | boolean>
12
+ /** Data attributes for styling hooks. */
13
+ dataAttributes: Record<string, string>
14
+ }
15
+
16
+ /**
17
+ * Build the framework-agnostic props for one ActionList row. Each row is ONE
18
+ * action (open, select, go to) rendered as a native button, so it is reachable
19
+ * by Tab and activated by Enter / Space — unlike a clickable table row.
20
+ */
21
+ export function createActionListItem(props: ActionListItemProps = {}): ActionListItemAPI {
22
+ const { disabled = false, density = 'default' } = props
23
+ const dataAttributes: Record<string, string> = {
24
+ 'data-slot': 'action-list-item',
25
+ 'data-density': density,
26
+ }
27
+ if (disabled) dataAttributes['data-disabled'] = ''
28
+ const ariaProps: Record<string, string | number | boolean> = {}
29
+ if (disabled) ariaProps['aria-disabled'] = true
30
+ return { ariaProps, dataAttributes }
31
+ }
@@ -0,0 +1,16 @@
1
+ export {
2
+ createActionListItem,
3
+ type ActionListDensity,
4
+ type ActionListItemProps,
5
+ type ActionListItemAPI,
6
+ } from './action-list.js'
7
+
8
+ export {
9
+ actionListClass,
10
+ actionListItemVariants,
11
+ actionListItemBodyClass,
12
+ actionListItemTitleClass,
13
+ actionListItemDescriptionClass,
14
+ actionListItemMetaClass,
15
+ actionListItemTrailingClass,
16
+ } from './action-list.styles.js'
@@ -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
@@ -0,0 +1,12 @@
1
+ ---
2
+ import { actionListClass } from '../action-list/index.ts'
3
+ import { cn } from '../shared/index.ts'
4
+
5
+ interface Props extends astroHTML.JSX.HTMLAttributes {}
6
+
7
+ const { class: className, ...rest } = Astro.props
8
+ ---
9
+
10
+ <ul class={cn(actionListClass, className)} data-slot="action-list" {...rest}>
11
+ <slot />
12
+ </ul>
@@ -0,0 +1,62 @@
1
+ ---
2
+ import {
3
+ createActionListItem,
4
+ actionListItemVariants,
5
+ actionListItemBodyClass,
6
+ actionListItemTitleClass,
7
+ actionListItemDescriptionClass,
8
+ actionListItemMetaClass,
9
+ actionListItemTrailingClass,
10
+ type ActionListDensity,
11
+ } from '../action-list/index.ts'
12
+ import { cn } from '../shared/index.ts'
13
+
14
+ interface Props extends astroHTML.JSX.ButtonHTMLAttributes {
15
+ /** Primary line. */
16
+ title: string
17
+ /** Optional supporting line under the title. */
18
+ description?: string
19
+ /** Optional tertiary line. */
20
+ meta?: string
21
+ /** Optional trailing column (or use the `trailing` slot). */
22
+ trailing?: string
23
+ /** Row padding. */
24
+ density?: ActionListDensity
25
+ disabled?: boolean
26
+ }
27
+
28
+ const {
29
+ title,
30
+ description,
31
+ meta,
32
+ trailing,
33
+ density = 'default',
34
+ disabled = false,
35
+ class: className,
36
+ ...rest
37
+ } = Astro.props
38
+ const api = createActionListItem({ disabled, density })
39
+ const hasTrailing = trailing != null || Astro.slots.has('trailing')
40
+ ---
41
+
42
+ <li>
43
+ <button
44
+ type="button"
45
+ disabled={disabled}
46
+ class={cn(actionListItemVariants({ density }), className)}
47
+ {...api.ariaProps}
48
+ {...api.dataAttributes}
49
+ {...rest}
50
+ >
51
+ <span class={actionListItemBodyClass}>
52
+ <span class={actionListItemTitleClass}>{title}</span>
53
+ {description && <span class={actionListItemDescriptionClass}>{description}</span>}
54
+ {meta && <span class={actionListItemMetaClass}>{meta}</span>}
55
+ </span>
56
+ {hasTrailing && (
57
+ <span class={actionListItemTrailingClass}>
58
+ <slot name="trailing">{trailing}</slot>
59
+ </span>
60
+ )}
61
+ </button>
62
+ </li>
@@ -0,0 +1 @@
1
+ export {}
@@ -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}
@@ -7,7 +7,10 @@ interface Props extends astroHTML.JSX.HTMLAttributes {
7
7
  }
8
8
 
9
9
  const { variant = 'default', class: className, ...props } = Astro.props
10
- const api = createCallout({ role: variant === 'destructive' ? 'alert' : 'region' })
10
+ const api = createCallout({
11
+ destructive: variant === 'destructive',
12
+ labelled: Boolean(props['aria-label'] || props['aria-labelledby']),
13
+ })
11
14
  ---
12
15
 
13
16
  <div
@@ -3,15 +3,16 @@ import { createCard, cardVariants } from '../card/index.ts'
3
3
  import { cn } from '../shared/index.ts'
4
4
 
5
5
  interface Props extends astroHTML.JSX.HTMLAttributes {
6
+ variant?: 'default' | 'subtle' | 'tertiary'
6
7
  padding?: 'none' | 'default' | 'compact'
7
8
  }
8
9
 
9
- const { padding, class: className, ...props } = Astro.props
10
+ const { variant, padding, class: className, ...props } = Astro.props
10
11
  const api = createCard(props)
11
12
  ---
12
13
 
13
14
  <div
14
- class={cn(cardVariants({ padding }), className)}
15
+ class={cn(cardVariants({ variant, padding }), className)}
15
16
  {...api.ariaProps}
16
17
  {...api.dataAttributes}
17
18
  {...props}
@@ -1,15 +1,17 @@
1
1
  ---
2
- import { createCardTitle, cardTitleVariants } from '../card/index.ts'
2
+ import { createCardTitle, cardTitleVariants, type CardTitleLevel } from '../card/index.ts'
3
3
  import { cn } from '../shared/index.ts'
4
4
 
5
5
  interface Props extends astroHTML.JSX.HTMLAttributes {
6
6
  size?: 'sm' | 'default' | 'lg'
7
+ /** Heading element. Defaults to `h3`. */
8
+ as?: CardTitleLevel
7
9
  }
8
10
 
9
- const { size, class: className, ...props } = Astro.props
11
+ const { size, as: Heading = 'h3', class: className, ...props } = Astro.props
10
12
  const api = createCardTitle()
11
13
  ---
12
14
 
13
- <h3 class={cn(cardTitleVariants({ size }), className)} {...api.dataAttributes} {...props}>
15
+ <Heading class={cn(cardTitleVariants({ size }), className)} {...api.dataAttributes} {...props}>
14
16
  <slot />
15
- </h3>
17
+ </Heading>
@@ -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.accessor(row) ?? '')}
104
+ {String(getColumnValue(col, row) ?? '')}
101
105
  </td>
102
106
  ))}
103
107
  </tr>
@@ -0,0 +1,64 @@
1
+ ---
2
+ import {
3
+ computeFunnel,
4
+ funnelChartClasses as c,
5
+ funnelChartDefaults,
6
+ DEFAULT_FUNNEL_MIN_BAR_PERCENT,
7
+ type FunnelChartStep,
8
+ } from '../funnel-chart/index.ts'
9
+ import { cn } from '../shared/index.ts'
10
+
11
+ interface Props extends astroHTML.JSX.HTMLAttributes {
12
+ /** The funnel steps, top first. */
13
+ steps: FunnelChartStep[]
14
+ formatValue?: (value: number) => string
15
+ formatPercent?: (percent: number) => string
16
+ minBarPercent?: number
17
+ shareLabel?: (share: string) => string
18
+ conversionLabel?: (percent: string) => string
19
+ droppedLabel?: (count: string) => string
20
+ }
21
+
22
+ const {
23
+ steps,
24
+ formatValue = funnelChartDefaults.formatValue,
25
+ formatPercent = funnelChartDefaults.formatPercent,
26
+ minBarPercent = DEFAULT_FUNNEL_MIN_BAR_PERCENT,
27
+ shareLabel = funnelChartDefaults.shareLabel,
28
+ conversionLabel = funnelChartDefaults.conversionLabel,
29
+ droppedLabel = funnelChartDefaults.droppedLabel,
30
+ class: className,
31
+ ...rest
32
+ } = Astro.props
33
+ const rows = computeFunnel(steps, minBarPercent)
34
+ ---
35
+
36
+ <ol class={cn(c.root, className)} data-slot="funnel-chart" {...rest}>
37
+ {
38
+ rows.map((row) => (
39
+ <li>
40
+ <div class={c.bar}>
41
+ <div class={c.fill} style={`width:${row.barPercent}%`} aria-hidden="true" />
42
+ <svg class={c.chevron} width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
43
+ <path d="m9 18 6-6-6-6" />
44
+ </svg>
45
+ <div class={c.text}>
46
+ <b class={c.label}>{row.step.label}</b>
47
+ {row.step.description && <span class={c.description}>{row.step.description}</span>}
48
+ </div>
49
+ <div class={c.figure}>
50
+ <b class={c.value}>{formatValue(row.step.value)}</b>
51
+ <span class={c.share}>{shareLabel(formatPercent(row.shareOfTop))}</span>
52
+ </div>
53
+ </div>
54
+ {row.next && (
55
+ <div class={c.transition}>
56
+ <span class={c.conversion}>{conversionLabel(formatPercent(row.next.conversion))}</span>
57
+ <span aria-hidden="true">·</span>
58
+ <span class={c.dropped}>{droppedLabel(formatValue(row.next.dropped))}</span>
59
+ </div>
60
+ )}
61
+ </li>
62
+ ))
63
+ }
64
+ </ol>
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,82 @@
1
+ ---
2
+ import {
3
+ computeLineChart,
4
+ lineChartClasses as c,
5
+ DEFAULT_LINE_CHART_WIDTH,
6
+ DEFAULT_LINE_CHART_HEIGHT,
7
+ type LineChartSeries,
8
+ } from '../line-chart/index.ts'
9
+ import { cn } from '../shared/index.ts'
10
+
11
+ interface Props extends astroHTML.JSX.HTMLAttributes {
12
+ /** The series to plot, all on one shared y-scale. */
13
+ series: LineChartSeries[]
14
+ /** One category label per data point. */
15
+ labels: string[]
16
+ /** Accessible name of the chart image. */
17
+ ariaLabel: string
18
+ height?: number
19
+ width?: number
20
+ /** Formats a y-axis tick. */
21
+ formatTick?: (value: number) => string
22
+ /** Fill the area under each line. Defaults to on for a single series only. */
23
+ area?: boolean
24
+ showLegend?: boolean
25
+ }
26
+
27
+ const {
28
+ series,
29
+ labels,
30
+ ariaLabel,
31
+ height = DEFAULT_LINE_CHART_HEIGHT,
32
+ width = DEFAULT_LINE_CHART_WIDTH,
33
+ formatTick = (value: number) => String(Math.round(value)),
34
+ area,
35
+ showLegend = true,
36
+ class: className,
37
+ ...rest
38
+ } = Astro.props
39
+ const geo = computeLineChart({ series, labels, width, height })
40
+ const fillArea = area ?? series.length === 1
41
+ const gradientBase = `line-chart-${ariaLabel.replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase()}`
42
+ ---
43
+
44
+ <div class={cn(c.root, className)} data-slot="line-chart" {...rest}>
45
+ <svg class={c.svg} viewBox={`0 0 ${width} ${height}`} style={`height:${height}px`} role="img" aria-label={ariaLabel}>
46
+ {geo.ticks.map((t) => (
47
+ <g>
48
+ <line class={c.gridline} x1={geo.padding.left} y1={t.y} x2={width - geo.padding.right} y2={t.y} />
49
+ <text class={c.axis} x={geo.padding.left - 8} y={t.y + 3} text-anchor="end">{formatTick(t.value)}</text>
50
+ </g>
51
+ ))}
52
+ {geo.xLabels.map((l) => (
53
+ <text class={c.axis} x={l.x} y={height - 6} text-anchor="middle">{l.label}</text>
54
+ ))}
55
+ {geo.paths.map((p, si) => (
56
+ <g>
57
+ {fillArea && (
58
+ <Fragment>
59
+ <defs>
60
+ <linearGradient id={`${gradientBase}-${si}`} x1="0" y1="0" x2="0" y2="1">
61
+ <stop offset="0%" stop-color={p.color} stop-opacity="0.22" />
62
+ <stop offset="100%" stop-color={p.color} stop-opacity="0" />
63
+ </linearGradient>
64
+ </defs>
65
+ <path d={p.area} fill={`url(#${gradientBase}-${si})`} />
66
+ </Fragment>
67
+ )}
68
+ <path d={p.line} fill="none" stroke={p.color} stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
69
+ </g>
70
+ ))}
71
+ </svg>
72
+ {showLegend && (
73
+ <div class={c.legend}>
74
+ {series.map((s, si) => (
75
+ <span class={c.legendItem}>
76
+ <i class={c.legendSwatch} style={`background:${geo.paths[si].color}`} aria-hidden="true" />
77
+ {s.name}
78
+ </span>
79
+ ))}
80
+ </div>
81
+ )}
82
+ </div>
@@ -0,0 +1 @@
1
+ export {}
@@ -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 {}
@@ -0,0 +1,46 @@
1
+ ---
2
+ import {
3
+ createPageHeader,
4
+ pageHeaderClass,
5
+ pageHeaderTextClass,
6
+ pageHeaderKickerClass,
7
+ pageHeaderTitleClass,
8
+ pageHeaderDescriptionClass,
9
+ pageHeaderActionsClass,
10
+ type PageHeaderTitleLevel,
11
+ } from '../page-header/index.ts'
12
+ import { cn } from '../shared/index.ts'
13
+
14
+ interface Props extends astroHTML.JSX.HTMLAttributes {
15
+ /** Optional eyebrow rendered above the title. */
16
+ kicker?: string
17
+ /** The page title. */
18
+ title: string
19
+ /** Optional supporting copy under the title (or use the `description` slot). */
20
+ description?: string
21
+ /** Heading element for the title. Defaults to `h1`. */
22
+ as?: PageHeaderTitleLevel
23
+ }
24
+
25
+ const { kicker, title, description, as: Title = 'h1', class: className, ...rest } = Astro.props
26
+ const hasActions = Astro.slots.has('actions')
27
+ const hasDescription = description != null || Astro.slots.has('description')
28
+ const { dataAttributes } = createPageHeader({ hasActions })
29
+ ---
30
+
31
+ <header class={cn(pageHeaderClass, className)} {...dataAttributes} {...rest}>
32
+ <div class={pageHeaderTextClass}>
33
+ {kicker && <p class={pageHeaderKickerClass}>{kicker}</p>}
34
+ <Title class={pageHeaderTitleClass}>{title}</Title>
35
+ {hasDescription && (
36
+ <div class={pageHeaderDescriptionClass}>
37
+ <slot name="description">{description}</slot>
38
+ </div>
39
+ )}
40
+ </div>
41
+ {hasActions && (
42
+ <div class={pageHeaderActionsClass}>
43
+ <slot name="actions" />
44
+ </div>
45
+ )}
46
+ </header>
@@ -0,0 +1 @@
1
+ export {}
@@ -2,44 +2,48 @@
2
2
  import {
3
3
  createStatGrid,
4
4
  statColumns,
5
+ statColumnsKey,
5
6
  statGridVariants,
6
- statItemClass,
7
- statValueClass,
8
- statLabelClass,
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 (value and label as strings). */
19
+ /** The stat items to display in the grid. */
15
20
  items: StatItem[]
16
- /**
17
- * Number of columns. Defaults to statColumns(items.length):
18
- * 1 item → 1, 2 items → 2, 3+ items → 3.
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: colsKey }), className)}
33
- style={gridStyle}
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={statItemClass}>
41
- <span class={statValueClass}>{item.value}</span>
42
- <span class={statLabelClass}>{item.label}</span>
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>