@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,260 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ ComboboxAnchor,
4
+ ComboboxContent,
5
+ ComboboxEmpty,
6
+ ComboboxInput,
7
+ ComboboxItem,
8
+ ComboboxItemIndicator,
9
+ ComboboxPortal,
10
+ ComboboxRoot,
11
+ ComboboxTrigger,
12
+ ComboboxViewport,
13
+ SelectContent,
14
+ SelectItem,
15
+ SelectItemIndicator,
16
+ SelectItemText,
17
+ SelectPortal,
18
+ SelectRoot,
19
+ SelectTrigger,
20
+ SelectValue,
21
+ SelectViewport,
22
+ } from "reka-ui";
23
+ import Icon from "../Icon/Icon.vue";
24
+ import FieldLabel from "../_internal/FieldLabel.vue";
25
+ import { useField, type FieldProps } from "../_internal/field";
26
+ import "../_internal/listbox.css";
27
+
28
+ export interface SelectOption {
29
+ value: string;
30
+ label: string;
31
+ }
32
+
33
+ const model = defineModel<string>();
34
+
35
+ interface Props extends FieldProps {
36
+ options: SelectOption[];
37
+ placeholder?: string;
38
+ /** Turn the field into a filterable single-select autocomplete (combobox). */
39
+ autocomplete?: boolean;
40
+ }
41
+
42
+ const props = defineProps<Props>();
43
+
44
+ const { id, labelId, ariaProps } = useField(props);
45
+
46
+ // Shows the selected option's label in the combobox input when not filtering.
47
+ function displayValue(value: string) {
48
+ return props.options.find((o) => o.value === value)?.label ?? "";
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <div class="select-box">
54
+ <FieldLabel
55
+ class="select-label"
56
+ :label="label"
57
+ :label-id="labelId"
58
+ :hide-label="hideLabel"
59
+ :hint="hint"
60
+ :html-for="autocomplete ? `${id}-input` : undefined"
61
+ />
62
+
63
+ <ComboboxRoot
64
+ v-if="autocomplete"
65
+ v-model="model"
66
+ open-on-click
67
+ class="select-combobox-root"
68
+ >
69
+ <ComboboxAnchor class="select-anchor">
70
+ <ComboboxInput
71
+ :id="`${id}-input`"
72
+ class="select-input"
73
+ :display-value="displayValue"
74
+ :placeholder="placeholder"
75
+ v-bind="ariaProps"
76
+ />
77
+ <ComboboxTrigger class="select-icon-button" aria-label="Toggle options">
78
+ <span class="select-icon" aria-hidden="true">
79
+ <Icon icon="keyboard_arrow_down" :size="16" />
80
+ </span>
81
+ </ComboboxTrigger>
82
+ </ComboboxAnchor>
83
+ <ComboboxPortal>
84
+ <ComboboxContent
85
+ class="cfasim-listbox-content select-content-autocomplete"
86
+ position="popper"
87
+ :side-offset="4"
88
+ :body-lock="false"
89
+ >
90
+ <ComboboxViewport class="cfasim-listbox-viewport">
91
+ <ComboboxEmpty class="cfasim-listbox-empty">
92
+ No matches
93
+ </ComboboxEmpty>
94
+ <ComboboxItem
95
+ v-for="opt in options"
96
+ :key="opt.value"
97
+ :value="opt.value"
98
+ class="cfasim-listbox-item"
99
+ >
100
+ <span>{{ opt.label }}</span>
101
+ <ComboboxItemIndicator class="cfasim-listbox-indicator">
102
+ <Icon icon="check" :size="14" />
103
+ </ComboboxItemIndicator>
104
+ </ComboboxItem>
105
+ </ComboboxViewport>
106
+ </ComboboxContent>
107
+ </ComboboxPortal>
108
+ </ComboboxRoot>
109
+
110
+ <SelectRoot v-else v-model="model">
111
+ <SelectTrigger class="select-trigger" v-bind="ariaProps">
112
+ <SelectValue :placeholder="placeholder" />
113
+ <span class="select-icon" aria-hidden="true">
114
+ <Icon icon="keyboard_arrow_down" :size="16" />
115
+ </span>
116
+ </SelectTrigger>
117
+ <SelectPortal>
118
+ <SelectContent
119
+ class="cfasim-listbox-content select-content"
120
+ position="popper"
121
+ :side-offset="4"
122
+ :body-lock="false"
123
+ >
124
+ <SelectViewport class="cfasim-listbox-viewport">
125
+ <SelectItem
126
+ v-for="opt in options"
127
+ :key="opt.value"
128
+ :value="opt.value"
129
+ class="cfasim-listbox-item"
130
+ >
131
+ <SelectItemText>{{ opt.label }}</SelectItemText>
132
+ <SelectItemIndicator class="cfasim-listbox-indicator">
133
+ <Icon icon="check" :size="14" />
134
+ </SelectItemIndicator>
135
+ </SelectItem>
136
+ </SelectViewport>
137
+ </SelectContent>
138
+ </SelectPortal>
139
+ </SelectRoot>
140
+ </div>
141
+ </template>
142
+
143
+ <style scoped>
144
+ .select-box {
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 0.25em;
148
+ }
149
+
150
+ .select-label {
151
+ font-size: var(--font-size-sm);
152
+ }
153
+
154
+ .select-trigger {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ justify-content: space-between;
158
+ gap: 0.5em;
159
+ font-size: var(--font-size-sm);
160
+ height: 2.5em;
161
+ padding: 0 0.75em;
162
+ border: 1px solid var(--color-border);
163
+ border-radius: 0.375em;
164
+ background: var(--color-bg-0);
165
+ cursor: pointer;
166
+ width: auto;
167
+ font-family: inherit;
168
+ color: inherit;
169
+ line-height: 1.4;
170
+ }
171
+
172
+ .select-trigger:hover {
173
+ border-color: var(--color-border-hover);
174
+ }
175
+
176
+ .select-trigger:focus-visible {
177
+ outline: 2px solid var(--color-primary);
178
+ outline-offset: -1px;
179
+ }
180
+
181
+ .select-trigger[data-placeholder] {
182
+ color: var(--color-text-secondary);
183
+ }
184
+
185
+ /* Autocomplete (combobox) field */
186
+ .select-anchor {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 0.5em;
190
+ height: 2.5em;
191
+ padding: 0 0.5em 0 0.75em;
192
+ font-size: var(--font-size-sm);
193
+ background: var(--color-bg-0);
194
+ border: 1px solid var(--color-border);
195
+ border-radius: 0.375em;
196
+ transition:
197
+ border-color var(--transition-fast),
198
+ box-shadow var(--transition-fast);
199
+ }
200
+
201
+ .select-anchor:hover {
202
+ border-color: var(--color-border-hover);
203
+ }
204
+
205
+ .select-anchor:focus-within {
206
+ outline: none;
207
+ border-color: var(--color-border-focus);
208
+ box-shadow: var(--shadow-focus);
209
+ }
210
+
211
+ .select-input {
212
+ flex: 1;
213
+ min-width: 0;
214
+ height: 100%;
215
+ padding: 0;
216
+ border: none;
217
+ background: none;
218
+ color: inherit;
219
+ font: inherit;
220
+ }
221
+
222
+ .select-input:focus {
223
+ outline: none;
224
+ }
225
+
226
+ .select-input::placeholder {
227
+ color: var(--color-text-secondary);
228
+ }
229
+
230
+ .select-icon-button {
231
+ display: inline-flex;
232
+ align-items: center;
233
+ flex-shrink: 0;
234
+ padding: 0;
235
+ border: none;
236
+ background: none;
237
+ color: inherit;
238
+ cursor: pointer;
239
+ }
240
+
241
+ .select-icon {
242
+ display: flex;
243
+ align-items: center;
244
+ flex-shrink: 0;
245
+ }
246
+ </style>
247
+
248
+ <style>
249
+ /* Sizing only — the dropdown skin lives in _internal/listbox.css. */
250
+ .select-content {
251
+ min-width: var(--reka-select-trigger-width);
252
+ max-height: var(--reka-select-content-available-height);
253
+ }
254
+
255
+ /* Combobox exposes the anchor width under its own custom property. */
256
+ .select-content-autocomplete {
257
+ width: var(--reka-combobox-trigger-width);
258
+ max-height: var(--reka-combobox-content-available-height);
259
+ }
260
+ </style>
@@ -0,0 +1,106 @@
1
+ # SidebarLayout
2
+
3
+ A responsive two-panel layout with a collapsible sidebar and main content area. On mobile, the sidebar becomes an overlay.
4
+
5
+ When the sidebar is collapsed it is marked [`inert`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert), so its contents (including the collapse button) are removed from the tab order and the accessibility tree while visually hidden. The expand button is likewise `inert` while the sidebar is open. This keeps keyboard and screen-reader focus on whichever control is actually visible.
6
+
7
+ ## Demo
8
+
9
+ <a href="/cfa-simulator/docs/demos/sidebar-layout/index.html" target="_blank">Open in full window ↗</a>
10
+
11
+ <div style="border: 1px solid var(--vp-c-border); border-radius: 8px; overflow: hidden; height: 500px;">
12
+ <iframe src="/cfa-simulator/docs/demos/sidebar-layout/index.html" style="width: 100%; height: 100%; border: none;" />
13
+ </div>
14
+
15
+ ## Tabs Demo (Router Mode)
16
+
17
+ <a href="/cfa-simulator/docs/demos/sidebar-tabs/index.html" target="_blank">Open in full window ↗</a>
18
+
19
+ <div style="border: 1px solid var(--vp-c-border); border-radius: 8px; overflow: hidden; height: 500px;">
20
+ <iframe src="/cfa-simulator/docs/demos/sidebar-tabs/index.html" style="width: 100%; height: 100%; border: none;" />
21
+ </div>
22
+
23
+ ## Usage
24
+
25
+ ```vue
26
+ <SidebarLayout>
27
+ <template #sidebar>
28
+ <h2>Controls</h2>
29
+ <NumberInput v-model="value" label="Parameter" slider live />
30
+ </template>
31
+ <h1>Main Content</h1>
32
+ <p>Your charts and data go here.</p>
33
+ </SidebarLayout>
34
+ ```
35
+
36
+ ## Slots
37
+
38
+ | Slot | Description |
39
+ | --------- | ------------------------------------------ |
40
+ | `sidebar` | Content rendered in the left sidebar panel |
41
+ | `default` | Main content area |
42
+
43
+ ## Props
44
+
45
+ | Prop | Type | Default | Description |
46
+ | ------------- | --------- | ----------- | ---------------------------------------------------- |
47
+ | `hideTopbar` | `boolean` | `false` | Hides the topbar that contains the light/dark toggle |
48
+ | `tabs` | `Tab[]` | `undefined` | Array of tab definitions to render in the main area |
49
+ | `v-model:tab` | `string` | `undefined` | The active tab value (two-way binding) |
50
+
51
+ ### Tab type
52
+
53
+ ```ts
54
+ interface Tab {
55
+ value: string; // unique identifier
56
+ label: string; // display text
57
+ to?: string; // optional route path for vue-router integration
58
+ }
59
+ ```
60
+
61
+ ## Tabs
62
+
63
+ When the `tabs` prop is provided, a tab bar renders at the top of the main content area. Tabs support two modes:
64
+
65
+ ### Local mode
66
+
67
+ Use `v-model:tab` to control which tab is active. Render content conditionally in the default slot.
68
+
69
+ ```vue
70
+ <script setup>
71
+ import { ref } from "vue";
72
+ const activeTab = ref("chart");
73
+ </script>
74
+
75
+ <SidebarLayout
76
+ v-model:tab="activeTab"
77
+ :tabs="[
78
+ { value: 'chart', label: 'Chart' },
79
+ { value: 'data', label: 'Data' },
80
+ ]"
81
+ >
82
+ <template #sidebar>
83
+ <h2>Controls</h2>
84
+ </template>
85
+ <div v-if="activeTab === 'chart'">Chart content</div>
86
+ <div v-if="activeTab === 'data'">Data table</div>
87
+ </SidebarLayout>
88
+ ```
89
+
90
+ ### Router mode
91
+
92
+ When tabs include a `to` property and vue-router is installed, clicking a tab navigates to that route. The active tab is automatically determined from the current route.
93
+
94
+ ```vue
95
+ <SidebarLayout
96
+ :tabs="[
97
+ { value: 'chart', label: 'Chart', to: '/model/chart' },
98
+ { value: 'data', label: 'Data', to: '/model/data' },
99
+ ]"
100
+ >
101
+ <template #sidebar>
102
+ <h2>Controls</h2>
103
+ </template>
104
+ <RouterView />
105
+ </SidebarLayout>
106
+ ```