@cfasim-ui/components 0.7.8 → 0.8.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.
Files changed (89) hide show
  1. package/dist/Box/Box.d.ts +7 -11
  2. package/dist/Button/Button.d.ts +7 -11
  3. package/dist/ButtonGroup/ButtonGroup.d.ts +8 -12
  4. package/dist/Container/Container.d.ts +7 -11
  5. package/dist/Expander/Expander.d.ts +12 -14
  6. package/dist/Grid/Grid.d.ts +6 -5
  7. package/dist/Hint/Hint.d.ts +2 -1
  8. package/dist/Icon/Icon.d.ts +3 -2
  9. package/dist/LightDarkToggle/LightDarkToggle.d.ts +2 -1
  10. package/dist/MultiSelect/MultiSelect.d.ts +6 -6
  11. package/dist/NumberInput/NumberInput.d.ts +13 -11
  12. package/dist/ParamEditor/ParamEditor.d.ts +3 -2
  13. package/dist/ParamEditor/ParamEditorImpl.d.ts +3 -5
  14. package/dist/{ParamEditorImpl-D0xZTyAN.js → ParamEditorImpl-CkElC6PI.js} +3075 -3041
  15. package/dist/SelectBox/SelectBox.d.ts +8 -6
  16. package/dist/{SelectBox-CByXZfaC.js → SelectBox-Df8dE2r-.js} +6 -6
  17. package/dist/SidebarLayout/SidebarLayout.d.ts +21 -20
  18. package/dist/Spinner/Spinner.d.ts +3 -2
  19. package/dist/TextInput/TextInput.d.ts +7 -5
  20. package/dist/Toggle/Toggle.d.ts +8 -6
  21. package/dist/ToggleGroup/ToggleGroup.d.ts +8 -6
  22. package/dist/_internal/FieldLabel.d.ts +2 -1
  23. package/dist/index.js +20 -20
  24. package/docs/Box.md +49 -0
  25. package/docs/Button.md +67 -0
  26. package/docs/ButtonGroup.md +64 -0
  27. package/docs/Container.md +103 -0
  28. package/docs/Expander.md +34 -0
  29. package/docs/Grid.md +170 -0
  30. package/docs/Hint.md +29 -0
  31. package/docs/Icon.md +188 -0
  32. package/docs/MultiSelect.md +143 -0
  33. package/docs/NumberInput.md +485 -0
  34. package/docs/ParamEditor.md +97 -0
  35. package/docs/SelectBox.md +182 -0
  36. package/docs/SidebarLayout.md +106 -0
  37. package/docs/Spinner.md +51 -0
  38. package/docs/TextInput.md +83 -0
  39. package/docs/Toggle.md +81 -0
  40. package/docs/ToggleGroup.md +163 -0
  41. package/docs/index.json +194 -0
  42. package/package.json +24 -20
  43. package/src/Box/Box.md +41 -0
  44. package/src/Box/Box.vue +52 -0
  45. package/src/Button/Button.md +59 -0
  46. package/src/Button/Button.vue +81 -0
  47. package/src/ButtonGroup/ButtonGroup.md +62 -0
  48. package/src/ButtonGroup/ButtonGroup.vue +91 -0
  49. package/src/Container/Container.md +99 -0
  50. package/src/Container/Container.vue +62 -0
  51. package/src/Expander/Expander.md +23 -0
  52. package/src/Expander/Expander.vue +95 -0
  53. package/src/Grid/Grid.md +175 -0
  54. package/src/Grid/Grid.vue +145 -0
  55. package/src/Hint/Hint.md +24 -0
  56. package/src/Hint/Hint.vue +83 -0
  57. package/src/Icon/Icon.md +176 -0
  58. package/src/Icon/Icon.vue +104 -0
  59. package/src/Icon/defaultIcons.ts +92 -0
  60. package/src/Icon/github.svg +1 -0
  61. package/src/Icon/registry.ts +68 -0
  62. package/src/LightDarkToggle/LightDarkToggle.vue +49 -0
  63. package/src/MultiSelect/MultiSelect.md +142 -0
  64. package/src/MultiSelect/MultiSelect.vue +279 -0
  65. package/src/NumberInput/NumberInput.md +455 -0
  66. package/src/NumberInput/NumberInput.vue +575 -0
  67. package/src/ParamEditor/ParamEditor.md +87 -0
  68. package/src/ParamEditor/ParamEditor.vue +43 -0
  69. package/src/ParamEditor/ParamEditorImpl.vue +358 -0
  70. package/src/SelectBox/SelectBox.md +169 -0
  71. package/src/SelectBox/SelectBox.vue +260 -0
  72. package/src/SidebarLayout/SidebarLayout.md +106 -0
  73. package/src/SidebarLayout/SidebarLayout.vue +468 -0
  74. package/src/Spinner/Spinner.md +45 -0
  75. package/src/Spinner/Spinner.vue +55 -0
  76. package/src/TextInput/TextInput.md +68 -0
  77. package/src/TextInput/TextInput.vue +54 -0
  78. package/src/Toggle/Toggle.md +68 -0
  79. package/src/Toggle/Toggle.vue +81 -0
  80. package/src/ToggleGroup/ToggleGroup.md +158 -0
  81. package/src/ToggleGroup/ToggleGroup.vue +138 -0
  82. package/src/_internal/FieldLabel.vue +27 -0
  83. package/src/_internal/field.ts +27 -0
  84. package/src/_internal/gap.ts +17 -0
  85. package/src/_internal/input.css +54 -0
  86. package/src/_internal/listbox.css +53 -0
  87. package/src/env.d.ts +4 -0
  88. package/src/index.ts +33 -0
  89. package/src/svg.d.ts +5 -0
@@ -0,0 +1,99 @@
1
+ ---
2
+ keywords: [container, layout, stack, row, flex, scrollable, border, card]
3
+ ---
4
+
5
+ # Container
6
+
7
+ A flexible wrapper for grouping elements vertically (default) or horizontally. Optionally adds a border, fixed height with scrolling, and configurable gap between children.
8
+
9
+ For multi-column layouts use [Grid](./grid).
10
+
11
+ ## Examples
12
+
13
+ ### Vertical stack (default)
14
+
15
+ <ComponentDemo>
16
+ <Container gap="small">
17
+ <Box variant="info">First</Box>
18
+ <Box variant="info">Second</Box>
19
+ <Box variant="info">Third</Box>
20
+ </Container>
21
+
22
+ <template #code>
23
+
24
+ ```vue
25
+ <Container gap="small">
26
+ <Box variant="info">First</Box>
27
+ <Box variant="info">Second</Box>
28
+ <Box variant="info">Third</Box>
29
+ </Container>
30
+ ```
31
+
32
+ </template>
33
+ </ComponentDemo>
34
+
35
+ ### Horizontal row
36
+
37
+ A row of buttons or chips. Wraps onto multiple lines when space runs out.
38
+
39
+ <ComponentDemo>
40
+ <Container horizontal gap="small">
41
+ <Button>Save</Button>
42
+ <Button variant="secondary">Cancel</Button>
43
+ <Button variant="secondary">Reset</Button>
44
+ </Container>
45
+
46
+ <template #code>
47
+
48
+ ```vue
49
+ <Container horizontal gap="small">
50
+ <Button>Save</Button>
51
+ <Button variant="secondary">Cancel</Button>
52
+ <Button variant="secondary">Reset</Button>
53
+ </Container>
54
+ ```
55
+
56
+ </template>
57
+ </ComponentDemo>
58
+
59
+ ### Card with border
60
+
61
+ <ComponentDemo>
62
+ <Container border gap="small">
63
+ <strong>Run summary</strong>
64
+ <span>Generated 1,000 samples in 2.4s</span>
65
+ </Container>
66
+
67
+ <template #code>
68
+
69
+ ```vue
70
+ <Container border gap="small">
71
+ <strong>Run summary</strong>
72
+ <span>Generated 1,000 samples in 2.4s</span>
73
+ </Container>
74
+ ```
75
+
76
+ </template>
77
+ </ComponentDemo>
78
+
79
+ ### Scrollable region
80
+
81
+ Setting `height` automatically enables scrolling when content overflows.
82
+
83
+ <ComponentDemo>
84
+ <Container border :height="180" gap="small">
85
+ <div v-for="i in 30" :key="i">Line {{ i }}</div>
86
+ </Container>
87
+
88
+ <template #code>
89
+
90
+ ```vue
91
+ <Container border :height="180" gap="small">
92
+ <div v-for="i in 30" :key="i">Line {{ i }}</div>
93
+ </Container>
94
+ ```
95
+
96
+ </template>
97
+ </ComponentDemo>
98
+
99
+ <!--@include: ./_api/container.md-->
@@ -0,0 +1,62 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { type GapToken, resolveGap } from "../_internal/gap";
4
+
5
+ export type ContainerGap = GapToken;
6
+
7
+ const props = defineProps<{
8
+ border?: boolean;
9
+ height?: number | string;
10
+ horizontal?: boolean;
11
+ gap?: ContainerGap | string;
12
+ }>();
13
+
14
+ const resolvedGap = computed(() => resolveGap(props.gap));
15
+
16
+ const resolvedHeight = computed(() => {
17
+ const h = props.height;
18
+ if (h == null) return undefined;
19
+ return typeof h === "number" ? `${h}px` : h;
20
+ });
21
+ </script>
22
+
23
+ <template>
24
+ <div
25
+ class="container"
26
+ :class="{
27
+ 'container-border': border,
28
+ 'container-horizontal': horizontal,
29
+ 'container-scrollable': height != null,
30
+ }"
31
+ :style="{
32
+ gap: resolvedGap,
33
+ height: resolvedHeight,
34
+ }"
35
+ >
36
+ <slot />
37
+ </div>
38
+ </template>
39
+
40
+ <style scoped>
41
+ .container {
42
+ display: flex;
43
+ flex-direction: column;
44
+ min-width: 0;
45
+ }
46
+
47
+ .container-horizontal {
48
+ flex-direction: row;
49
+ flex-wrap: wrap;
50
+ align-items: center;
51
+ }
52
+
53
+ .container-border {
54
+ padding: var(--space-4);
55
+ border: 1px solid var(--color-border);
56
+ border-radius: var(--radius-md);
57
+ }
58
+
59
+ .container-scrollable {
60
+ overflow: auto;
61
+ }
62
+ </style>
@@ -0,0 +1,23 @@
1
+ # Expander
2
+
3
+ A collapsible section that toggles content visibility.
4
+
5
+ ## Examples
6
+
7
+ <ComponentDemo>
8
+ <Expander label="Show advanced options">
9
+ <p style="margin: 0">Here is the hidden content.</p>
10
+ </Expander>
11
+
12
+ <template #code>
13
+
14
+ ```vue
15
+ <Expander label="Show advanced options">
16
+ <p>Here is the hidden content.</p>
17
+ </Expander>
18
+ ```
19
+
20
+ </template>
21
+ </ComponentDemo>
22
+
23
+ <!--@include: ./_api/expander.md-->
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ CollapsibleRoot,
4
+ CollapsibleTrigger,
5
+ CollapsibleContent,
6
+ } from "reka-ui";
7
+
8
+ const open = defineModel<boolean>("open", { default: false });
9
+
10
+ defineProps<{
11
+ label?: string;
12
+ }>();
13
+ </script>
14
+
15
+ <template>
16
+ <CollapsibleRoot v-model:open="open" class="expander">
17
+ <CollapsibleTrigger class="expander-trigger">
18
+ <span class="expander-caret" :class="{ open }" />
19
+ <slot name="label">{{ label }}</slot>
20
+ </CollapsibleTrigger>
21
+ <CollapsibleContent class="expander-content">
22
+ <slot />
23
+ </CollapsibleContent>
24
+ </CollapsibleRoot>
25
+ </template>
26
+
27
+ <style scoped>
28
+ .expander-trigger {
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 0.5em;
32
+ width: 100%;
33
+ padding: 0.5em 0;
34
+ background: none;
35
+ border: none;
36
+ cursor: pointer;
37
+ font: inherit;
38
+ font-size: var(--font-size-sm);
39
+ font-weight: 600;
40
+ text-transform: uppercase;
41
+ letter-spacing: 0.05em;
42
+ color: var(--color-text-secondary);
43
+ }
44
+
45
+ .expander-trigger:hover {
46
+ color: var(--color-text);
47
+ }
48
+
49
+ .expander-caret {
50
+ display: inline-block;
51
+ width: 0;
52
+ height: 0;
53
+ border-left: 0.35em solid currentColor;
54
+ border-top: 0.3em solid transparent;
55
+ border-bottom: 0.3em solid transparent;
56
+ transition: transform 0.15s;
57
+ }
58
+
59
+ .expander-caret.open {
60
+ transform: rotate(90deg);
61
+ }
62
+
63
+ .expander-content {
64
+ overflow: hidden;
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: 0.75em;
68
+ }
69
+
70
+ .expander-content[data-state="open"] {
71
+ animation: slideDown 200ms ease-out;
72
+ }
73
+
74
+ .expander-content[data-state="closed"] {
75
+ animation: slideUp 200ms ease-out;
76
+ }
77
+
78
+ @keyframes slideDown {
79
+ from {
80
+ height: 0;
81
+ }
82
+ to {
83
+ height: var(--reka-collapsible-content-height);
84
+ }
85
+ }
86
+
87
+ @keyframes slideUp {
88
+ from {
89
+ height: var(--reka-collapsible-content-height);
90
+ }
91
+ to {
92
+ height: 0;
93
+ }
94
+ }
95
+ </style>
@@ -0,0 +1,175 @@
1
+ ---
2
+ keywords:
3
+ [
4
+ grid,
5
+ columns,
6
+ layout,
7
+ responsive,
8
+ auto-fit,
9
+ cards,
10
+ side-by-side,
11
+ proportional,
12
+ ]
13
+ ---
14
+
15
+ # Grid
16
+
17
+ A CSS-grid wrapper for arranging elements in equal, proportional, or auto-fitting columns.
18
+
19
+ For single-direction stacks (vertical or a flex row that wraps), use [Container](./container) instead.
20
+
21
+ ## Examples
22
+
23
+ ### Equal columns
24
+
25
+ Pass a number for N equal-width columns.
26
+
27
+ <ComponentDemo>
28
+ <Grid :cols="3" gap="medium">
29
+ <Box variant="info">Column 1</Box>
30
+ <Box variant="info">Column 2</Box>
31
+ <Box variant="info">Column 3</Box>
32
+ </Grid>
33
+
34
+ <template #code>
35
+
36
+ ```vue
37
+ <Grid :cols="3" gap="medium">
38
+ <Box variant="info">Column 1</Box>
39
+ <Box variant="info">Column 2</Box>
40
+ <Box variant="info">Column 3</Box>
41
+ </Grid>
42
+ ```
43
+
44
+ </template>
45
+ </ComponentDemo>
46
+
47
+ ### Proportional widths
48
+
49
+ Pass an array of `fr` weights for asymmetric layouts.
50
+
51
+ <ComponentDemo>
52
+ <Grid :cols="[2, 3, 1]" gap="medium">
53
+ <Box variant="info">2fr</Box>
54
+ <Box variant="success">3fr</Box>
55
+ <Box variant="warning">1fr</Box>
56
+ </Grid>
57
+
58
+ <template #code>
59
+
60
+ ```vue
61
+ <Grid :cols="[2, 3, 1]" gap="medium">
62
+ <Box variant="info">2fr</Box>
63
+ <Box variant="success">3fr</Box>
64
+ <Box variant="warning">1fr</Box>
65
+ </Grid>
66
+ ```
67
+
68
+ </template>
69
+ </ComponentDemo>
70
+
71
+ ### Mixed track sizes
72
+
73
+ Strings pass through unchanged, so you can combine fixed and flexible tracks.
74
+
75
+ <ComponentDemo>
76
+ <Grid :cols="['200px', '1fr']" gap="medium">
77
+ <Box variant="info">Fixed 200px</Box>
78
+ <Box variant="info">Fills remaining space</Box>
79
+ </Grid>
80
+
81
+ <template #code>
82
+
83
+ ```vue
84
+ <Grid :cols="['200px', '1fr']" gap="medium">
85
+ <Box variant="info">Fixed 200px</Box>
86
+ <Box variant="info">Fills remaining space</Box>
87
+ </Grid>
88
+ ```
89
+
90
+ </template>
91
+ </ComponentDemo>
92
+
93
+ ### Small-width breakpoint
94
+
95
+ `colsSmall` overrides `cols` when the grid's own width is at or below `breakpoint`. The default breakpoint is `640px` unless you specify it. The check is a CSS container query against the grid itself, so it triggers based on the grid's available width (e.g. when nested inside a sidebar), not the viewport.
96
+
97
+ <ComponentDemo>
98
+ <Grid :cols="3" :cols-small="1" breakpoint="480px" gap="small">
99
+ <Box variant="info">Card 1</Box>
100
+ <Box variant="info">Card 2</Box>
101
+ <Box variant="info">Card 3</Box>
102
+ </Grid>
103
+
104
+ <template #code>
105
+
106
+ ```vue
107
+ <Grid :cols="3" :cols-small="1" breakpoint="480px" gap="small">
108
+ <Box variant="info">Card 1</Box>
109
+ <Box variant="info">Card 2</Box>
110
+ <Box variant="info">Card 3</Box>
111
+ </Grid>
112
+ ```
113
+
114
+ </template>
115
+ </ComponentDemo>
116
+
117
+ ### Responsive auto-fit
118
+
119
+ `minColWidth` switches to `repeat(auto-fit, minmax(...))` so items reflow to fit the viewport without media queries. Great for metric tiles and card grids.
120
+
121
+ <ComponentDemo>
122
+ <Grid min-col-width="180px" gap="medium">
123
+ <Box variant="info">Card 1</Box>
124
+ <Box variant="info">Card 2</Box>
125
+ <Box variant="info">Card 3</Box>
126
+ <Box variant="info">Card 4</Box>
127
+ <Box variant="info">Card 5</Box>
128
+ </Grid>
129
+
130
+ <template #code>
131
+
132
+ ```vue
133
+ <Grid min-col-width="180px" gap="medium">
134
+ <Box variant="info">Card 1</Box>
135
+ <Box variant="info">Card 2</Box>
136
+ <Box variant="info">Card 3</Box>
137
+ <Box variant="info">Card 4</Box>
138
+ <Box variant="info">Card 5</Box>
139
+ </Grid>
140
+ ```
141
+
142
+ </template>
143
+ </ComponentDemo>
144
+
145
+ ### Nested grids
146
+
147
+ <ComponentDemo>
148
+ <Grid :cols="2" gap="medium">
149
+ <Box variant="info">Left</Box>
150
+ <Grid :cols="2" gap="small">
151
+ <Box variant="success">a</Box>
152
+ <Box variant="success">b</Box>
153
+ <Box variant="success">c</Box>
154
+ <Box variant="success">d</Box>
155
+ </Grid>
156
+ </Grid>
157
+
158
+ <template #code>
159
+
160
+ ```vue
161
+ <Grid :cols="2" gap="medium">
162
+ <Box variant="info">Left</Box>
163
+ <Grid :cols="2" gap="small">
164
+ <Box variant="success">a</Box>
165
+ <Box variant="success">b</Box>
166
+ <Box variant="success">c</Box>
167
+ <Box variant="success">d</Box>
168
+ </Grid>
169
+ </Grid>
170
+ ```
171
+
172
+ </template>
173
+ </ComponentDemo>
174
+
175
+ <!--@include: ./_api/grid.md-->
@@ -0,0 +1,145 @@
1
+ <script setup lang="ts">
2
+ import { computed, onUnmounted, watch } from "vue";
3
+ import { type GapToken, resolveGap } from "../_internal/gap";
4
+
5
+ export type GridGap = GapToken;
6
+ export type GridCols = number | (number | string)[];
7
+
8
+ const props = defineProps<{
9
+ cols?: GridCols;
10
+ colsSmall?: GridCols;
11
+ breakpoint?: string;
12
+ gap?: GridGap | string;
13
+ minColWidth?: string;
14
+ }>();
15
+
16
+ const resolvedGap = computed(() => resolveGap(props.gap));
17
+
18
+ const resolvedColumns = computed(() => {
19
+ if (props.minColWidth) {
20
+ return `repeat(auto-fit, minmax(${props.minColWidth}, 1fr))`;
21
+ }
22
+ return colsToTemplate(props.cols ?? 2);
23
+ });
24
+
25
+ const resolvedSmallColumns = computed(() => {
26
+ if (props.minColWidth || props.colsSmall == null) {
27
+ return resolvedColumns.value;
28
+ }
29
+ return colsToTemplate(props.colsSmall);
30
+ });
31
+
32
+ const safeBreakpoint = computed(() =>
33
+ sanitizeBreakpoint(props.breakpoint ?? DEFAULT_BREAKPOINT),
34
+ );
35
+
36
+ let acquired: string | null = null;
37
+ const stopWatch = watch(
38
+ safeBreakpoint,
39
+ (next) => {
40
+ if (acquired === next) return;
41
+ acquireBreakpoint(next);
42
+ if (acquired) releaseBreakpoint(acquired);
43
+ acquired = next;
44
+ },
45
+ { immediate: true },
46
+ );
47
+
48
+ onUnmounted(() => {
49
+ stopWatch();
50
+ if (acquired) {
51
+ releaseBreakpoint(acquired);
52
+ acquired = null;
53
+ }
54
+ });
55
+ </script>
56
+
57
+ <script lang="ts">
58
+ const DEFAULT_BREAKPOINT = "640px";
59
+
60
+ function colsToTemplate(cols: GridCols): string {
61
+ if (typeof cols === "number") return `repeat(${cols}, 1fr)`;
62
+ return cols.map((c) => (typeof c === "number" ? `${c}fr` : c)).join(" ");
63
+ }
64
+
65
+ // Allow only digits, dot, and unit letters/percent. Falls back to the default
66
+ // when input doesn't look like a CSS length, preventing rule-escape via the
67
+ // breakpoint prop.
68
+ function sanitizeBreakpoint(value: string): string {
69
+ return /^\d+(\.\d+)?[a-zA-Z%]+$/.test(value) ? value : DEFAULT_BREAKPOINT;
70
+ }
71
+
72
+ // Module-scope cache: one <style> per unique breakpoint, ref-counted across
73
+ // all Grid instances. The grid's inline `--grid-cols-small` is consumed by
74
+ // the rule in the cached <style>.
75
+ type Entry = { count: number; el: HTMLStyleElement };
76
+ const breakpointSheets = new Map<string, Entry>();
77
+
78
+ function acquireBreakpoint(breakpoint: string) {
79
+ if (typeof document === "undefined") return;
80
+ const existing = breakpointSheets.get(breakpoint);
81
+ if (existing) {
82
+ existing.count++;
83
+ return;
84
+ }
85
+ const el = document.createElement("style");
86
+ el.setAttribute("data-cfasim-grid-bp", breakpoint);
87
+ el.textContent =
88
+ `@container (max-width: ${breakpoint}) {` +
89
+ `[data-cfasim-grid-bp="${breakpoint}"] > .grid {` +
90
+ `grid-template-columns: var(--grid-cols-small) !important;` +
91
+ `}}`;
92
+ document.head.appendChild(el);
93
+ breakpointSheets.set(breakpoint, { count: 1, el });
94
+ }
95
+
96
+ function releaseBreakpoint(breakpoint: string) {
97
+ if (typeof document === "undefined") return;
98
+ const entry = breakpointSheets.get(breakpoint);
99
+ if (!entry) return;
100
+ entry.count--;
101
+ if (entry.count === 0) {
102
+ entry.el.remove();
103
+ breakpointSheets.delete(breakpoint);
104
+ }
105
+ }
106
+
107
+ const hmr = (import.meta as { hot?: { dispose: (cb: () => void) => void } })
108
+ .hot;
109
+ hmr?.dispose(() => {
110
+ breakpointSheets.forEach(({ el }) => el.remove());
111
+ breakpointSheets.clear();
112
+ });
113
+ </script>
114
+
115
+ <template>
116
+ <div :data-cfasim-grid-bp="safeBreakpoint" class="grid-wrapper">
117
+ <div
118
+ class="grid"
119
+ :style="{
120
+ gap: resolvedGap,
121
+ gridTemplateColumns: resolvedColumns,
122
+ '--grid-cols-small': resolvedSmallColumns,
123
+ }"
124
+ >
125
+ <slot />
126
+ </div>
127
+ </div>
128
+ </template>
129
+
130
+ <style scoped>
131
+ .grid-wrapper {
132
+ container-type: inline-size;
133
+ width: 100%;
134
+ min-width: 0;
135
+ }
136
+
137
+ .grid {
138
+ display: grid;
139
+ min-width: 0;
140
+ }
141
+
142
+ .grid > :deep(*) {
143
+ min-width: 0;
144
+ }
145
+ </style>
@@ -0,0 +1,24 @@
1
+ # Hint
2
+
3
+ An info icon that shows a tooltip on hover. Used alongside form labels to provide additional context.
4
+
5
+ ## Examples
6
+
7
+ <ComponentDemo>
8
+ <span style="display: flex; align-items: center; gap: 8px;">
9
+ Population size <Hint text="The total number of individuals in the simulation." />
10
+ </span>
11
+
12
+ <template #code>
13
+
14
+ ```vue
15
+ <span style="display: flex; align-items: center; gap: 8px;">
16
+ Population size
17
+ <Hint text="The total number of individuals in the simulation." />
18
+ </span>
19
+ ```
20
+
21
+ </template>
22
+ </ComponentDemo>
23
+
24
+ <!--@include: ./_api/hint.md-->
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ TooltipArrow,
4
+ TooltipContent,
5
+ TooltipPortal,
6
+ TooltipProvider,
7
+ TooltipRoot,
8
+ TooltipTrigger,
9
+ } from "reka-ui";
10
+ import Icon from "../Icon/Icon.vue";
11
+
12
+ defineProps<{
13
+ text: string;
14
+ }>();
15
+ </script>
16
+
17
+ <template>
18
+ <TooltipProvider>
19
+ <TooltipRoot :delay-duration="0" disable-closing-trigger>
20
+ <TooltipTrigger as-child>
21
+ <button
22
+ type="button"
23
+ class="HintTrigger"
24
+ aria-label="More info"
25
+ @pointerdown.prevent
26
+ >
27
+ <Icon icon="help" :size="16" />
28
+ </button>
29
+ </TooltipTrigger>
30
+ <TooltipPortal>
31
+ <TooltipContent class="HintContent" side="top" :side-offset="4">
32
+ {{ text }}
33
+ <TooltipArrow class="HintArrow" :width="10" :height="5" />
34
+ </TooltipContent>
35
+ </TooltipPortal>
36
+ </TooltipRoot>
37
+ </TooltipProvider>
38
+ </template>
39
+
40
+ <style scoped>
41
+ .HintTrigger {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ width: 1.25em;
46
+ height: 1.25em;
47
+ padding: 0;
48
+ margin: 0;
49
+ border: none;
50
+ border-radius: 50%;
51
+ background: transparent;
52
+ color: var(--color-text-secondary);
53
+ cursor: pointer;
54
+ flex-shrink: 0;
55
+ }
56
+
57
+ .HintTrigger:hover {
58
+ color: var(--color-text);
59
+ }
60
+
61
+ .HintTrigger:focus-visible {
62
+ outline: none;
63
+ box-shadow: var(--shadow-focus);
64
+ }
65
+ </style>
66
+
67
+ <style>
68
+ .HintContent {
69
+ max-width: 15rem;
70
+ padding: 0.5em 0.75em;
71
+ font-size: var(--font-size-xs);
72
+ line-height: 1.4;
73
+ color: var(--color-bg-0);
74
+ background-color: var(--color-text);
75
+ border-radius: 0.25em;
76
+ box-shadow: var(--shadow-md);
77
+ z-index: 100;
78
+ }
79
+
80
+ .HintArrow {
81
+ fill: var(--color-text);
82
+ }
83
+ </style>