@cfasim-ui/components 0.7.7 → 0.8.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 (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,279 @@
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
+ } from "reka-ui";
14
+ import { computed, nextTick, ref, watch } from "vue";
15
+ import Icon from "../Icon/Icon.vue";
16
+ import FieldLabel from "../_internal/FieldLabel.vue";
17
+ import { useField, type FieldProps } from "../_internal/field";
18
+ import "../_internal/listbox.css";
19
+
20
+ export interface MultiSelectOption {
21
+ value: string;
22
+ label: string;
23
+ }
24
+
25
+ const model = defineModel<string[]>({ default: () => [] });
26
+
27
+ interface Props extends FieldProps {
28
+ options: MultiSelectOption[];
29
+ placeholder?: string;
30
+ }
31
+
32
+ const props = defineProps<Props>();
33
+
34
+ const { labelId, ariaProps } = useField(props);
35
+ const fieldRef = ref<HTMLElement | null>(null);
36
+
37
+ // In multiple mode the list stays open after a selection, so reka keeps
38
+ // `isUserInputted` true and suppresses its own `resetSearchTermOnSelect`. Clear
39
+ // the typed text (and the underlying filter) ourselves on every selection
40
+ // change by dispatching a native input event the combobox listens for.
41
+ watch(model, () => {
42
+ nextTick(() => {
43
+ const input = fieldRef.value?.querySelector("input");
44
+ if (input?.value) {
45
+ input.value = "";
46
+ input.dispatchEvent(new Event("input", { bubbles: true }));
47
+ }
48
+ });
49
+ });
50
+
51
+ const selectedOptions = computed(() =>
52
+ model.value
53
+ .map((v) => props.options.find((o) => o.value === v))
54
+ .filter((o): o is MultiSelectOption => Boolean(o)),
55
+ );
56
+
57
+ function remove(value: string) {
58
+ model.value = model.value.filter((v) => v !== value);
59
+ }
60
+
61
+ // Backspace in an empty input removes the last selected chip.
62
+ function onInputKeydown(event: KeyboardEvent) {
63
+ const target = event.target as HTMLInputElement;
64
+ if (event.key === "Backspace" && !target.value && model.value.length) {
65
+ remove(model.value[model.value.length - 1]);
66
+ }
67
+ }
68
+ </script>
69
+
70
+ <template>
71
+ <div class="multi-select">
72
+ <FieldLabel
73
+ class="multi-select-label"
74
+ :label="label"
75
+ :label-id="labelId"
76
+ :hide-label="hideLabel"
77
+ :hint="hint"
78
+ />
79
+ <ComboboxRoot
80
+ v-model="model"
81
+ multiple
82
+ open-on-click
83
+ class="multi-select-root"
84
+ >
85
+ <ComboboxAnchor class="multi-select-anchor">
86
+ <div ref="fieldRef" class="multi-select-field">
87
+ <ul v-if="selectedOptions.length" class="multi-select-chips">
88
+ <li
89
+ v-for="opt in selectedOptions"
90
+ :key="opt.value"
91
+ class="multi-select-chip"
92
+ >
93
+ <span class="multi-select-chip-label">{{ opt.label }}</span>
94
+ <button
95
+ type="button"
96
+ class="multi-select-chip-remove"
97
+ :aria-label="`Remove ${opt.label}`"
98
+ @click="remove(opt.value)"
99
+ >
100
+ <Icon icon="close" :size="14" />
101
+ </button>
102
+ </li>
103
+ </ul>
104
+ <ComboboxInput
105
+ class="multi-select-input"
106
+ :placeholder="selectedOptions.length ? undefined : placeholder"
107
+ v-bind="ariaProps"
108
+ @keydown="onInputKeydown"
109
+ />
110
+ </div>
111
+ <ComboboxTrigger
112
+ class="multi-select-trigger"
113
+ aria-label="Toggle options"
114
+ >
115
+ <span class="multi-select-icon" aria-hidden="true">
116
+ <Icon icon="keyboard_arrow_down" :size="16" />
117
+ </span>
118
+ </ComboboxTrigger>
119
+ </ComboboxAnchor>
120
+ <ComboboxPortal>
121
+ <ComboboxContent
122
+ class="cfasim-listbox-content multi-select-content"
123
+ position="popper"
124
+ :side-offset="4"
125
+ :body-lock="false"
126
+ >
127
+ <ComboboxViewport class="cfasim-listbox-viewport">
128
+ <ComboboxEmpty class="cfasim-listbox-empty">
129
+ No matches
130
+ </ComboboxEmpty>
131
+ <ComboboxItem
132
+ v-for="opt in options"
133
+ :key="opt.value"
134
+ :value="opt.value"
135
+ class="cfasim-listbox-item"
136
+ >
137
+ <span>{{ opt.label }}</span>
138
+ <ComboboxItemIndicator class="cfasim-listbox-indicator">
139
+ <Icon icon="check" :size="14" />
140
+ </ComboboxItemIndicator>
141
+ </ComboboxItem>
142
+ </ComboboxViewport>
143
+ </ComboboxContent>
144
+ </ComboboxPortal>
145
+ </ComboboxRoot>
146
+ </div>
147
+ </template>
148
+
149
+ <style scoped>
150
+ .multi-select {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 0.25em;
154
+ }
155
+
156
+ .multi-select-label {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ font-size: var(--font-size-sm);
161
+ }
162
+
163
+ .multi-select-anchor {
164
+ display: flex;
165
+ align-items: center;
166
+ gap: 0.25em;
167
+ min-height: 2.5em;
168
+ padding: 0.25em 0.5em;
169
+ font-size: var(--font-size-sm);
170
+ background: var(--color-bg-0);
171
+ border: 1px solid var(--color-border);
172
+ border-radius: 0.375em;
173
+ transition:
174
+ border-color var(--transition-fast),
175
+ box-shadow var(--transition-fast);
176
+ }
177
+
178
+ .multi-select-anchor:hover {
179
+ border-color: var(--color-border-hover);
180
+ }
181
+
182
+ .multi-select-anchor:focus-within {
183
+ outline: none;
184
+ border-color: var(--color-border-focus);
185
+ box-shadow: var(--shadow-focus);
186
+ }
187
+
188
+ .multi-select-field {
189
+ display: flex;
190
+ flex-wrap: wrap;
191
+ align-items: center;
192
+ gap: 0.25em;
193
+ flex: 1;
194
+ min-width: 0;
195
+ }
196
+
197
+ .multi-select-chips {
198
+ display: contents;
199
+ list-style: none;
200
+ margin: 0;
201
+ padding: 0;
202
+ }
203
+
204
+ .multi-select-chip {
205
+ display: inline-flex;
206
+ align-items: center;
207
+ gap: 0.25em;
208
+ /* Reset list margins (e.g. a consumer's `li + li` spacing) so chips line up. */
209
+ margin: 0;
210
+ padding: 0.125em 0.25em 0.125em 0.5em;
211
+ background: var(--color-bg-2);
212
+ border: 1px solid var(--color-border);
213
+ border-radius: 0.25em;
214
+ line-height: 1.4;
215
+ }
216
+
217
+ .multi-select-chip-remove {
218
+ display: inline-flex;
219
+ align-items: center;
220
+ padding: 0;
221
+ border: none;
222
+ background: none;
223
+ color: var(--color-text-secondary);
224
+ cursor: pointer;
225
+ border-radius: 0.25em;
226
+ }
227
+
228
+ .multi-select-chip-remove:hover {
229
+ color: var(--color-text);
230
+ }
231
+
232
+ .multi-select-chip-remove:focus-visible {
233
+ outline: 2px solid var(--color-primary);
234
+ outline-offset: 1px;
235
+ }
236
+
237
+ .multi-select-input {
238
+ flex: 1;
239
+ min-width: 3em;
240
+ height: 1.75em;
241
+ padding: 0;
242
+ border: none;
243
+ background: none;
244
+ color: var(--color-text);
245
+ font: inherit;
246
+ }
247
+
248
+ .multi-select-input:focus {
249
+ outline: none;
250
+ }
251
+
252
+ .multi-select-input::placeholder {
253
+ color: var(--color-text-tertiary);
254
+ }
255
+
256
+ .multi-select-trigger {
257
+ display: inline-flex;
258
+ align-items: center;
259
+ flex-shrink: 0;
260
+ padding: 0;
261
+ border: none;
262
+ background: none;
263
+ color: inherit;
264
+ cursor: pointer;
265
+ }
266
+
267
+ .multi-select-icon {
268
+ display: flex;
269
+ align-items: center;
270
+ }
271
+ </style>
272
+
273
+ <style>
274
+ /* Sizing only — the dropdown skin lives in _internal/listbox.css. */
275
+ .multi-select-content {
276
+ width: var(--reka-combobox-trigger-width);
277
+ max-height: var(--reka-combobox-content-available-height);
278
+ }
279
+ </style>