@cfasim-ui/docs 0.6.4 → 0.7.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.
- package/charts/ChoroplethMap/ChoroplethMap.md +105 -29
- package/charts/ChoroplethMap/ChoroplethMap.vue +528 -161
- package/charts/DataTable/DataTable.vue +1 -7
- package/charts/_shared/ChartZoomControls.vue +138 -0
- package/charts/_shared/index.ts +4 -15
- package/charts/_shared/seriesCsv.ts +2 -2
- package/charts/_shared/touch.ts +8 -0
- package/charts/_shared/useChartFoundation.ts +0 -1
- package/components/MultiSelect/MultiSelect.md +1 -1
- package/components/MultiSelect/MultiSelect.vue +24 -83
- package/components/NumberInput/NumberInput.md +5 -5
- package/components/NumberInput/NumberInput.vue +12 -59
- package/components/SelectBox/SelectBox.md +1 -0
- package/components/SelectBox/SelectBox.vue +34 -125
- package/components/TextInput/TextInput.md +2 -1
- package/components/TextInput/TextInput.vue +11 -51
- package/components/ToggleGroup/ToggleGroup.md +2 -2
- package/components/ToggleGroup/ToggleGroup.vue +21 -26
- package/components/_internal/FieldLabel.vue +27 -0
- package/components/_internal/field.ts +27 -0
- package/components/_internal/input.css +54 -0
- package/components/_internal/listbox.css +53 -0
- package/components/env.d.ts +4 -0
- package/index.json +8 -8
- package/package.json +1 -1
- package/pyodide/messages.ts +23 -0
- package/pyodide/pyodide.worker.ts +2 -21
- package/pyodide/pyodideWorkerApi.ts +7 -44
- package/shared/index.ts +1 -0
- package/shared/workerError.ts +17 -0
- package/wasm/messages.ts +6 -0
- package/wasm/wasm.worker.ts +1 -7
- package/wasm/wasmWorkerApi.ts +8 -26
|
@@ -19,8 +19,11 @@ import {
|
|
|
19
19
|
SelectTrigger,
|
|
20
20
|
SelectValue,
|
|
21
21
|
SelectViewport,
|
|
22
|
-
useId,
|
|
23
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";
|
|
24
27
|
|
|
25
28
|
export interface SelectOption {
|
|
26
29
|
value: string;
|
|
@@ -29,17 +32,16 @@ export interface SelectOption {
|
|
|
29
32
|
|
|
30
33
|
const model = defineModel<string>();
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
label?: string;
|
|
34
|
-
hideLabel?: boolean;
|
|
35
|
-
ariaLabel?: string;
|
|
35
|
+
interface Props extends FieldProps {
|
|
36
36
|
options: SelectOption[];
|
|
37
37
|
placeholder?: string;
|
|
38
38
|
/** Turn the field into a filterable single-select autocomplete (combobox). */
|
|
39
39
|
autocomplete?: boolean;
|
|
40
|
-
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const props = defineProps<Props>();
|
|
41
43
|
|
|
42
|
-
const id =
|
|
44
|
+
const { id, labelId, ariaProps } = useField(props);
|
|
43
45
|
|
|
44
46
|
// Shows the selected option's label in the combobox input when not filtering.
|
|
45
47
|
function displayValue(value: string) {
|
|
@@ -49,14 +51,14 @@ function displayValue(value: string) {
|
|
|
49
51
|
|
|
50
52
|
<template>
|
|
51
53
|
<div class="select-box">
|
|
52
|
-
<
|
|
53
|
-
v-if="label"
|
|
54
|
-
:id="`${id}-label`"
|
|
55
|
-
:for="autocomplete ? `${id}-input` : undefined"
|
|
54
|
+
<FieldLabel
|
|
56
55
|
class="select-label"
|
|
57
|
-
:
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
:label="label"
|
|
57
|
+
:label-id="labelId"
|
|
58
|
+
:hide-label="hideLabel"
|
|
59
|
+
:hint="hint"
|
|
60
|
+
:html-for="autocomplete ? `${id}-input` : undefined"
|
|
61
|
+
/>
|
|
60
62
|
|
|
61
63
|
<ComboboxRoot
|
|
62
64
|
v-if="autocomplete"
|
|
@@ -70,55 +72,34 @@ function displayValue(value: string) {
|
|
|
70
72
|
class="select-input"
|
|
71
73
|
:display-value="displayValue"
|
|
72
74
|
:placeholder="placeholder"
|
|
73
|
-
|
|
74
|
-
:aria-label="!props.label ? props.ariaLabel : undefined"
|
|
75
|
+
v-bind="ariaProps"
|
|
75
76
|
/>
|
|
76
77
|
<ComboboxTrigger class="select-icon-button" aria-label="Toggle options">
|
|
77
78
|
<span class="select-icon" aria-hidden="true">
|
|
78
|
-
<
|
|
79
|
-
width="12"
|
|
80
|
-
height="12"
|
|
81
|
-
viewBox="0 0 12 12"
|
|
82
|
-
fill="none"
|
|
83
|
-
stroke="currentColor"
|
|
84
|
-
stroke-width="2"
|
|
85
|
-
stroke-linecap="round"
|
|
86
|
-
stroke-linejoin="round"
|
|
87
|
-
>
|
|
88
|
-
<path d="M3 4.5L6 7.5L9 4.5" />
|
|
89
|
-
</svg>
|
|
79
|
+
<Icon icon="keyboard_arrow_down" :size="16" />
|
|
90
80
|
</span>
|
|
91
81
|
</ComboboxTrigger>
|
|
92
82
|
</ComboboxAnchor>
|
|
93
83
|
<ComboboxPortal>
|
|
94
84
|
<ComboboxContent
|
|
95
|
-
class="
|
|
85
|
+
class="cfasim-listbox-content select-content-autocomplete"
|
|
96
86
|
position="popper"
|
|
97
87
|
:side-offset="4"
|
|
98
88
|
:body-lock="false"
|
|
99
89
|
>
|
|
100
|
-
<ComboboxViewport class="
|
|
101
|
-
<ComboboxEmpty class="
|
|
90
|
+
<ComboboxViewport class="cfasim-listbox-viewport">
|
|
91
|
+
<ComboboxEmpty class="cfasim-listbox-empty">
|
|
92
|
+
No matches
|
|
93
|
+
</ComboboxEmpty>
|
|
102
94
|
<ComboboxItem
|
|
103
95
|
v-for="opt in options"
|
|
104
96
|
:key="opt.value"
|
|
105
97
|
:value="opt.value"
|
|
106
|
-
class="
|
|
98
|
+
class="cfasim-listbox-item"
|
|
107
99
|
>
|
|
108
100
|
<span>{{ opt.label }}</span>
|
|
109
|
-
<ComboboxItemIndicator class="
|
|
110
|
-
<
|
|
111
|
-
width="12"
|
|
112
|
-
height="12"
|
|
113
|
-
viewBox="0 0 12 12"
|
|
114
|
-
fill="none"
|
|
115
|
-
stroke="currentColor"
|
|
116
|
-
stroke-width="2"
|
|
117
|
-
stroke-linecap="round"
|
|
118
|
-
stroke-linejoin="round"
|
|
119
|
-
>
|
|
120
|
-
<path d="M2 6L5 9L10 3" />
|
|
121
|
-
</svg>
|
|
101
|
+
<ComboboxItemIndicator class="cfasim-listbox-indicator">
|
|
102
|
+
<Icon icon="check" :size="14" />
|
|
122
103
|
</ComboboxItemIndicator>
|
|
123
104
|
</ComboboxItem>
|
|
124
105
|
</ComboboxViewport>
|
|
@@ -127,55 +108,29 @@ function displayValue(value: string) {
|
|
|
127
108
|
</ComboboxRoot>
|
|
128
109
|
|
|
129
110
|
<SelectRoot v-else v-model="model">
|
|
130
|
-
<SelectTrigger
|
|
131
|
-
class="select-trigger"
|
|
132
|
-
:aria-labelledby="props.label ? `${id}-label` : undefined"
|
|
133
|
-
:aria-label="!props.label ? props.ariaLabel : undefined"
|
|
134
|
-
>
|
|
111
|
+
<SelectTrigger class="select-trigger" v-bind="ariaProps">
|
|
135
112
|
<SelectValue :placeholder="placeholder" />
|
|
136
113
|
<span class="select-icon" aria-hidden="true">
|
|
137
|
-
<
|
|
138
|
-
width="12"
|
|
139
|
-
height="12"
|
|
140
|
-
viewBox="0 0 12 12"
|
|
141
|
-
fill="none"
|
|
142
|
-
stroke="currentColor"
|
|
143
|
-
stroke-width="2"
|
|
144
|
-
stroke-linecap="round"
|
|
145
|
-
stroke-linejoin="round"
|
|
146
|
-
>
|
|
147
|
-
<path d="M3 4.5L6 7.5L9 4.5" />
|
|
148
|
-
</svg>
|
|
114
|
+
<Icon icon="keyboard_arrow_down" :size="16" />
|
|
149
115
|
</span>
|
|
150
116
|
</SelectTrigger>
|
|
151
117
|
<SelectPortal>
|
|
152
118
|
<SelectContent
|
|
153
|
-
class="select-content"
|
|
119
|
+
class="cfasim-listbox-content select-content"
|
|
154
120
|
position="popper"
|
|
155
121
|
:side-offset="4"
|
|
156
122
|
:body-lock="false"
|
|
157
123
|
>
|
|
158
|
-
<SelectViewport class="
|
|
124
|
+
<SelectViewport class="cfasim-listbox-viewport">
|
|
159
125
|
<SelectItem
|
|
160
126
|
v-for="opt in options"
|
|
161
127
|
:key="opt.value"
|
|
162
128
|
:value="opt.value"
|
|
163
|
-
class="
|
|
129
|
+
class="cfasim-listbox-item"
|
|
164
130
|
>
|
|
165
131
|
<SelectItemText>{{ opt.label }}</SelectItemText>
|
|
166
|
-
<SelectItemIndicator class="
|
|
167
|
-
<
|
|
168
|
-
width="12"
|
|
169
|
-
height="12"
|
|
170
|
-
viewBox="0 0 12 12"
|
|
171
|
-
fill="none"
|
|
172
|
-
stroke="currentColor"
|
|
173
|
-
stroke-width="2"
|
|
174
|
-
stroke-linecap="round"
|
|
175
|
-
stroke-linejoin="round"
|
|
176
|
-
>
|
|
177
|
-
<path d="M2 6L5 9L10 3" />
|
|
178
|
-
</svg>
|
|
132
|
+
<SelectItemIndicator class="cfasim-listbox-indicator">
|
|
133
|
+
<Icon icon="check" :size="14" />
|
|
179
134
|
</SelectItemIndicator>
|
|
180
135
|
</SelectItem>
|
|
181
136
|
</SelectViewport>
|
|
@@ -291,14 +246,8 @@ function displayValue(value: string) {
|
|
|
291
246
|
</style>
|
|
292
247
|
|
|
293
248
|
<style>
|
|
249
|
+
/* Sizing only — the dropdown skin lives in _internal/listbox.css. */
|
|
294
250
|
.select-content {
|
|
295
|
-
z-index: 100;
|
|
296
|
-
background: var(--color-bg-0);
|
|
297
|
-
border: 1px solid var(--color-border);
|
|
298
|
-
border-radius: 0.25em;
|
|
299
|
-
box-shadow:
|
|
300
|
-
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
301
|
-
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
302
251
|
min-width: var(--reka-select-trigger-width);
|
|
303
252
|
max-height: var(--reka-select-content-available-height);
|
|
304
253
|
}
|
|
@@ -308,44 +257,4 @@ function displayValue(value: string) {
|
|
|
308
257
|
width: var(--reka-combobox-trigger-width);
|
|
309
258
|
max-height: var(--reka-combobox-content-available-height);
|
|
310
259
|
}
|
|
311
|
-
|
|
312
|
-
.select-viewport {
|
|
313
|
-
padding: 0.25em;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
.select-empty {
|
|
317
|
-
padding: 0.5em;
|
|
318
|
-
font-size: var(--font-size-sm);
|
|
319
|
-
color: var(--color-text-secondary);
|
|
320
|
-
text-align: center;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.select-item {
|
|
324
|
-
display: flex;
|
|
325
|
-
align-items: center;
|
|
326
|
-
justify-content: space-between;
|
|
327
|
-
gap: 0.5em;
|
|
328
|
-
padding: 0.25em 0.5em;
|
|
329
|
-
border-radius: 0.25em;
|
|
330
|
-
font-size: var(--font-size-sm);
|
|
331
|
-
white-space: nowrap;
|
|
332
|
-
cursor: pointer;
|
|
333
|
-
user-select: none;
|
|
334
|
-
outline: none;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
.select-item[data-highlighted] {
|
|
338
|
-
background: var(--color-primary);
|
|
339
|
-
color: white;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.select-item[data-state="checked"] {
|
|
343
|
-
font-weight: 600;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
.select-indicator {
|
|
347
|
-
display: flex;
|
|
348
|
-
align-items: center;
|
|
349
|
-
flex-shrink: 0;
|
|
350
|
-
}
|
|
351
260
|
</style>
|
|
@@ -77,6 +77,7 @@ it.
|
|
|
77
77
|
|------|------|----------|---------|
|
|
78
78
|
| `label` | `string` | No | — |
|
|
79
79
|
| `hideLabel` | `boolean` | No | — |
|
|
80
|
-
| `
|
|
80
|
+
| `ariaLabel` | `string` | No | — |
|
|
81
81
|
| `hint` | `string` | No | — |
|
|
82
|
+
| `placeholder` | `string` | No | — |
|
|
82
83
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, watch } from "vue";
|
|
3
3
|
import Hint from "../Hint/Hint.vue";
|
|
4
|
+
import type { FieldProps } from "../_internal/field";
|
|
5
|
+
import "../_internal/input.css";
|
|
4
6
|
|
|
5
7
|
const model = defineModel<string>();
|
|
6
8
|
const local = ref(model.value);
|
|
@@ -13,18 +15,17 @@ function commit() {
|
|
|
13
15
|
model.value = local.value;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
label?: string;
|
|
18
|
-
hideLabel?: boolean;
|
|
18
|
+
interface Props extends FieldProps {
|
|
19
19
|
placeholder?: string;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = defineProps<Props>();
|
|
22
23
|
</script>
|
|
23
24
|
|
|
24
25
|
<template>
|
|
25
|
-
<label v-if="props.label" class="input-label">
|
|
26
|
+
<label v-if="props.label" class="cfasim-input-label">
|
|
26
27
|
<span
|
|
27
|
-
class="input-label-row"
|
|
28
|
+
class="cfasim-input-label-row"
|
|
28
29
|
:class="{ 'visually-hidden': props.hideLabel }"
|
|
29
30
|
>
|
|
30
31
|
{{ props.label }}
|
|
@@ -32,6 +33,7 @@ const props = defineProps<{
|
|
|
32
33
|
</span>
|
|
33
34
|
<input
|
|
34
35
|
type="text"
|
|
36
|
+
class="cfasim-input"
|
|
35
37
|
v-model="local"
|
|
36
38
|
:placeholder="props.placeholder"
|
|
37
39
|
@blur="commit"
|
|
@@ -41,54 +43,12 @@ const props = defineProps<{
|
|
|
41
43
|
<div v-else>
|
|
42
44
|
<input
|
|
43
45
|
type="text"
|
|
46
|
+
class="cfasim-input"
|
|
44
47
|
v-model="local"
|
|
45
48
|
:placeholder="props.placeholder"
|
|
49
|
+
:aria-label="props.ariaLabel"
|
|
46
50
|
@blur="commit"
|
|
47
51
|
@keydown.enter="commit"
|
|
48
52
|
/>
|
|
49
53
|
</div>
|
|
50
54
|
</template>
|
|
51
|
-
|
|
52
|
-
<style scoped>
|
|
53
|
-
.input-label {
|
|
54
|
-
display: flex;
|
|
55
|
-
flex-direction: column;
|
|
56
|
-
gap: 0.25em;
|
|
57
|
-
font-size: var(--font-size-sm);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.input-label-row {
|
|
61
|
-
display: flex;
|
|
62
|
-
align-items: center;
|
|
63
|
-
justify-content: space-between;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
input {
|
|
67
|
-
display: block;
|
|
68
|
-
width: 100%;
|
|
69
|
-
height: 2.5em;
|
|
70
|
-
padding: 0 0.75em;
|
|
71
|
-
font-size: inherit;
|
|
72
|
-
background-color: var(--color-bg-0);
|
|
73
|
-
color: var(--color-text);
|
|
74
|
-
border: 1px solid var(--color-border);
|
|
75
|
-
border-radius: 0.375em;
|
|
76
|
-
transition:
|
|
77
|
-
border-color var(--transition-fast),
|
|
78
|
-
box-shadow var(--transition-fast);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
input:hover {
|
|
82
|
-
border-color: var(--color-border-hover);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
input:focus {
|
|
86
|
-
outline: none;
|
|
87
|
-
border-color: var(--color-border-focus);
|
|
88
|
-
box-shadow: var(--shadow-focus);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
input::placeholder {
|
|
92
|
-
color: var(--color-text-tertiary);
|
|
93
|
-
}
|
|
94
|
-
</style>
|
|
@@ -142,12 +142,12 @@ Set `orientation="vertical"` to stack the items, and mark individual options
|
|
|
142
142
|
|
|
143
143
|
| Prop | Type | Required | Default |
|
|
144
144
|
|------|------|----------|---------|
|
|
145
|
-
| `options` | `ToggleGroupOption[]` | Yes | — |
|
|
146
|
-
| `multiple` | `boolean` | No | — |
|
|
147
145
|
| `label` | `string` | No | — |
|
|
148
146
|
| `hideLabel` | `boolean` | No | — |
|
|
149
147
|
| `ariaLabel` | `string` | No | — |
|
|
150
148
|
| `hint` | `string` | No | — |
|
|
149
|
+
| `options` | `ToggleGroupOption[]` | Yes | — |
|
|
150
|
+
| `multiple` | `boolean` | No | — |
|
|
151
151
|
| `disabled` | `boolean` | No | — |
|
|
152
152
|
| `orientation` | `"horizontal" \| "vertical"` | No | `"horizontal"` |
|
|
153
153
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ToggleGroupItem, ToggleGroupRoot
|
|
2
|
+
import { ToggleGroupItem, ToggleGroupRoot } from "reka-ui";
|
|
3
3
|
import { computed } from "vue";
|
|
4
|
-
import
|
|
4
|
+
import FieldLabel from "../_internal/FieldLabel.vue";
|
|
5
|
+
import { useField, type FieldProps } from "../_internal/field";
|
|
5
6
|
|
|
6
7
|
export interface ToggleGroupOption {
|
|
7
8
|
value: string;
|
|
@@ -12,21 +13,18 @@ export interface ToggleGroupOption {
|
|
|
12
13
|
// `string` in single mode, `string[]` in multiple mode.
|
|
13
14
|
const model = defineModel<string | string[]>();
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ariaLabel?: string;
|
|
22
|
-
hint?: string;
|
|
23
|
-
disabled?: boolean;
|
|
24
|
-
orientation?: "horizontal" | "vertical";
|
|
25
|
-
}>(),
|
|
26
|
-
{ orientation: "horizontal" },
|
|
27
|
-
);
|
|
16
|
+
interface Props extends FieldProps {
|
|
17
|
+
options: ToggleGroupOption[];
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
orientation?: "horizontal" | "vertical";
|
|
21
|
+
}
|
|
28
22
|
|
|
29
|
-
const
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
orientation: "horizontal",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const { labelId, ariaProps } = useField(props);
|
|
30
28
|
const type = computed<"single" | "multiple">(() =>
|
|
31
29
|
props.multiple ? "multiple" : "single",
|
|
32
30
|
);
|
|
@@ -34,23 +32,20 @@ const type = computed<"single" | "multiple">(() =>
|
|
|
34
32
|
|
|
35
33
|
<template>
|
|
36
34
|
<div class="toggle-group-field">
|
|
37
|
-
<
|
|
38
|
-
v-if="label"
|
|
39
|
-
:id="`${id}-label`"
|
|
35
|
+
<FieldLabel
|
|
40
36
|
class="toggle-group-label"
|
|
41
|
-
:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
:label="label"
|
|
38
|
+
:label-id="labelId"
|
|
39
|
+
:hide-label="hideLabel"
|
|
40
|
+
:hint="hint"
|
|
41
|
+
/>
|
|
46
42
|
<ToggleGroupRoot
|
|
47
43
|
v-model="model"
|
|
48
44
|
:type="type"
|
|
49
45
|
:disabled="disabled"
|
|
50
46
|
:orientation="orientation"
|
|
51
47
|
:class="['toggle-group', `toggle-group--${orientation}`]"
|
|
52
|
-
|
|
53
|
-
:aria-label="!label ? ariaLabel : undefined"
|
|
48
|
+
v-bind="ariaProps"
|
|
54
49
|
>
|
|
55
50
|
<ToggleGroupItem
|
|
56
51
|
v-for="opt in options"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Hint from "../Hint/Hint.vue";
|
|
3
|
+
|
|
4
|
+
// The visible label for a form field, with the shared hideLabel/hint
|
|
5
|
+
// behavior. Renders nothing without a `label`; the parent supplies its
|
|
6
|
+
// own class via fallthrough attrs so existing scoped styles keep working.
|
|
7
|
+
defineProps<{
|
|
8
|
+
label?: string;
|
|
9
|
+
labelId?: string;
|
|
10
|
+
hideLabel?: boolean;
|
|
11
|
+
hint?: string;
|
|
12
|
+
/** `for` attribute, for fields whose control is a real labelable element. */
|
|
13
|
+
htmlFor?: string;
|
|
14
|
+
}>();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<label
|
|
19
|
+
v-if="label"
|
|
20
|
+
:id="labelId"
|
|
21
|
+
:for="htmlFor"
|
|
22
|
+
:class="{ 'visually-hidden': hideLabel }"
|
|
23
|
+
>
|
|
24
|
+
{{ label }}
|
|
25
|
+
<Hint v-if="hint && !hideLabel" :text="hint" />
|
|
26
|
+
</label>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useId } from "reka-ui";
|
|
3
|
+
|
|
4
|
+
/** Props shared by every labeled form field. */
|
|
5
|
+
export interface FieldProps {
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Visually hide the label while keeping it for assistive tech. */
|
|
8
|
+
hideLabel?: boolean;
|
|
9
|
+
/** Accessible name used when no visible `label` is provided. */
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
hint?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generated field/label ids plus the aria attributes that point a control
|
|
16
|
+
* at its label: `aria-labelledby` when a visible `label` exists, else
|
|
17
|
+
* `aria-label`. Spread onto the control with `v-bind="ariaProps.value"`.
|
|
18
|
+
*/
|
|
19
|
+
export function useField(props: FieldProps) {
|
|
20
|
+
const id = useId();
|
|
21
|
+
const labelId = `${id}-label`;
|
|
22
|
+
const ariaProps = computed(() => ({
|
|
23
|
+
"aria-labelledby": props.label ? labelId : undefined,
|
|
24
|
+
"aria-label": !props.label ? props.ariaLabel : undefined,
|
|
25
|
+
}));
|
|
26
|
+
return { id, labelId, ariaProps };
|
|
27
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Shared text-field skin for TextInput/NumberInput. Global (not scoped)
|
|
2
|
+
* so both components can use one copy; prefixed to avoid consumer
|
|
3
|
+
* collisions. */
|
|
4
|
+
|
|
5
|
+
.cfasim-input-label {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 0.25em;
|
|
9
|
+
font-size: var(--font-size-sm);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.cfasim-input-label-row {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.cfasim-input {
|
|
19
|
+
display: block;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 2.5em;
|
|
22
|
+
padding: 0 0.75em;
|
|
23
|
+
font-size: inherit;
|
|
24
|
+
background-color: var(--color-bg-0);
|
|
25
|
+
color: var(--color-text);
|
|
26
|
+
border: 1px solid var(--color-border);
|
|
27
|
+
border-radius: 0.375em;
|
|
28
|
+
transition:
|
|
29
|
+
border-color var(--transition-fast),
|
|
30
|
+
box-shadow var(--transition-fast);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.cfasim-input:hover {
|
|
34
|
+
border-color: var(--color-border-hover);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.cfasim-input:focus {
|
|
38
|
+
outline: none;
|
|
39
|
+
border-color: var(--color-border-focus);
|
|
40
|
+
box-shadow: var(--shadow-focus);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.cfasim-input[aria-invalid="true"] {
|
|
44
|
+
border-color: var(--color-error);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.cfasim-input[aria-invalid="true"]:focus {
|
|
48
|
+
border-color: var(--color-error);
|
|
49
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-error) 25%, transparent);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cfasim-input::placeholder {
|
|
53
|
+
color: var(--color-text-tertiary);
|
|
54
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* Shared skin for the SelectBox/MultiSelect dropdown (reka-ui Select and
|
|
2
|
+
* Combobox portals render outside the component, so these are global).
|
|
3
|
+
* Sizing (width/max-height from the reka trigger vars) stays per-component. */
|
|
4
|
+
|
|
5
|
+
.cfasim-listbox-content {
|
|
6
|
+
z-index: 100;
|
|
7
|
+
background: var(--color-bg-0);
|
|
8
|
+
border: 1px solid var(--color-border);
|
|
9
|
+
border-radius: 0.25em;
|
|
10
|
+
box-shadow:
|
|
11
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
12
|
+
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.cfasim-listbox-viewport {
|
|
16
|
+
padding: 0.25em;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.cfasim-listbox-empty {
|
|
20
|
+
padding: 0.5em;
|
|
21
|
+
font-size: var(--font-size-sm);
|
|
22
|
+
color: var(--color-text-secondary);
|
|
23
|
+
text-align: center;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.cfasim-listbox-item {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: space-between;
|
|
30
|
+
gap: 0.5em;
|
|
31
|
+
padding: 0.25em 0.5em;
|
|
32
|
+
border-radius: 0.25em;
|
|
33
|
+
font-size: var(--font-size-sm);
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
user-select: none;
|
|
37
|
+
outline: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.cfasim-listbox-item[data-highlighted] {
|
|
41
|
+
background: var(--color-primary);
|
|
42
|
+
color: white;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.cfasim-listbox-item[data-state="checked"] {
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.cfasim-listbox-indicator {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
}
|
package/index.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.7.1",
|
|
3
3
|
"package": "@cfasim-ui/docs",
|
|
4
4
|
"content": {
|
|
5
5
|
"components": [
|
|
@@ -216,6 +216,13 @@
|
|
|
216
216
|
"source": "charts/ChoroplethMap/ChoroplethMap.vue",
|
|
217
217
|
"keywords": []
|
|
218
218
|
},
|
|
219
|
+
{
|
|
220
|
+
"name": "DataTable",
|
|
221
|
+
"slug": "data-table",
|
|
222
|
+
"docs": "charts/DataTable/DataTable.md",
|
|
223
|
+
"source": "charts/DataTable/DataTable.vue",
|
|
224
|
+
"keywords": []
|
|
225
|
+
},
|
|
219
226
|
{
|
|
220
227
|
"name": "LineChart",
|
|
221
228
|
"slug": "line-chart",
|
|
@@ -231,13 +238,6 @@
|
|
|
231
238
|
"confidence band",
|
|
232
239
|
"svg"
|
|
233
240
|
]
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
"name": "DataTable",
|
|
237
|
-
"slug": "data-table",
|
|
238
|
-
"docs": "charts/DataTable/DataTable.md",
|
|
239
|
-
"source": "charts/DataTable/DataTable.vue",
|
|
240
|
-
"keywords": []
|
|
241
241
|
}
|
|
242
242
|
]
|
|
243
243
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RunMessage {
|
|
2
|
+
id: number;
|
|
3
|
+
type?: "run";
|
|
4
|
+
python: string;
|
|
5
|
+
context?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface CallMessage {
|
|
8
|
+
id: number;
|
|
9
|
+
type: "call";
|
|
10
|
+
module: string;
|
|
11
|
+
fn: string;
|
|
12
|
+
kwargs?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface LoadModuleMessage {
|
|
15
|
+
id: number;
|
|
16
|
+
type: "loadModule";
|
|
17
|
+
module: string;
|
|
18
|
+
}
|
|
19
|
+
export type WorkerMessage = RunMessage | CallMessage | LoadModuleMessage;
|
|
20
|
+
export type OutgoingMessage =
|
|
21
|
+
| Omit<RunMessage, "id">
|
|
22
|
+
| Omit<CallMessage, "id">
|
|
23
|
+
| Omit<LoadModuleMessage, "id">;
|