@happyvertical/smrt-ui 0.55.6 → 0.55.8
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/README.md +30 -0
- package/dist/components/forms/CodeSelect.svelte +12 -2
- package/dist/components/forms/CodeSelect.svelte.d.ts.map +1 -1
- package/dist/components/forms/Combobox.svelte +4 -3
- package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
- package/dist/components/forms/DictationButton.svelte +110 -3
- package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -1
- package/dist/components/forms/DictationStatus.svelte +33 -5
- package/dist/components/forms/DictationStatus.svelte.d.ts +2 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -1
- package/dist/components/forms/FieldLabel.svelte +44 -0
- package/dist/components/forms/FieldLabel.svelte.d.ts +20 -0
- package/dist/components/forms/FieldLabel.svelte.d.ts.map +1 -0
- package/dist/components/forms/FormGroup.svelte +2 -17
- package/dist/components/forms/FormGroup.svelte.d.ts.map +1 -1
- package/dist/components/forms/code-select-options.d.ts +8 -1
- package/dist/components/forms/code-select-options.d.ts.map +1 -1
- package/dist/components/forms/code-select-options.js +46 -5
- package/dist/components/forms/code-select-types.d.ts +4 -0
- package/dist/components/forms/code-select-types.d.ts.map +1 -1
- package/dist/components/forms/dictation.svelte.d.ts +84 -2
- package/dist/components/forms/dictation.svelte.d.ts.map +1 -1
- package/dist/components/forms/dictation.svelte.js +236 -2
- package/dist/components/forms/hands-free-capture.d.ts +70 -0
- package/dist/components/forms/hands-free-capture.d.ts.map +1 -0
- package/dist/components/forms/hands-free-capture.js +19 -0
- package/dist/components/forms/hands-free-vad.d.ts +5 -0
- package/dist/components/forms/hands-free-vad.d.ts.map +1 -0
- package/dist/components/forms/hands-free-vad.js +80 -0
- package/dist/components/forms/index.d.ts +2 -0
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +2 -0
- package/dist/i18n/strings.ui.d.ts +7 -0
- package/dist/i18n/strings.ui.d.ts.map +1 -1
- package/dist/i18n/strings.ui.js +7 -0
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -81,6 +81,36 @@ pnpm add @happyvertical/smrt-ui
|
|
|
81
81
|
body (`Content-Type` is the recording's type, `?language=&durationMs=`)
|
|
82
82
|
and answers `{ text }`; 413, 503 and 401/403 become "too long", "not set
|
|
83
83
|
up" and "not allowed" messages.
|
|
84
|
+
- Hands-free dictation: `new Dictation({ mode: 'hands-free', handsFreeCapture:
|
|
85
|
+
createHandsFreeCapture, … })` keeps the microphone on and writes each
|
|
86
|
+
sentence down when the speaker pauses; one tap on the microphone ends it.
|
|
87
|
+
`createHandsFreeCapture` comes from `@happyvertical/smrt-ui/forms/hands-free`,
|
|
88
|
+
the one entry that imports the optional peer `@happyvertical/speech`
|
|
89
|
+
(>= 0.102.5: on-device voice activity detection with an adaptive noise
|
|
90
|
+
floor, pre-roll and a 30 s split; no audio leaves the page). It needs a
|
|
91
|
+
speech source with `transcribePcm` (smrt-svelte's on-device `whisper-local`
|
|
92
|
+
and `moonshine`); with any of the three missing it is plain press-to-talk.
|
|
93
|
+
Utterances are written down one at a time in the order spoken
|
|
94
|
+
(`dictation.queued` counts those waiting); `speaking` and `level` (0 to 1)
|
|
95
|
+
drive the button: a steady ring while listening, a halo that swells with the
|
|
96
|
+
voice while speaking, a spinner while writing down, all still under
|
|
97
|
+
`prefers-reduced-motion`. `vad` tunes `silenceMs` (default 800),
|
|
98
|
+
`minSpeechMs`, `preRollMs`, `maxUtteranceMs` and `sensitivity`.
|
|
99
|
+
The help text ("Just talk; I write it down when you pause...") is the
|
|
100
|
+
microphone's tooltip and `aria-describedby`, not a line in the form;
|
|
101
|
+
`DictationStatus` keeps a visually hidden polite live region ("Listening",
|
|
102
|
+
"Hearing you...", "Writing it down...") and shows errors. Its `sending` prop
|
|
103
|
+
adds the one short visible hint, "Sending...". The chat composer's
|
|
104
|
+
`sendOnPause` / `sendOnPauseMs` (default 1200) builds send-on-pause on it.
|
|
105
|
+
Half-duplex: browsers do not reliably cancel `speechSynthesis` from the
|
|
106
|
+
microphone, so call `dictation.suspend()` while the page reads a reply aloud
|
|
107
|
+
and `dictation.resume()` after (the chat composer/dock do this from their
|
|
108
|
+
`speaking` prop). The microphone stays open but nothing is heard (an
|
|
109
|
+
utterance in progress is discarded, a ~350 ms guard follows `resume()`);
|
|
110
|
+
`dictation.suspended` drives the dimmed, dashed-outline button and the
|
|
111
|
+
"Paused while the assistant speaks" tooltip/live region. A custom
|
|
112
|
+
`HandsFreeCapture` may implement optional `suspend()` / `resume()`;
|
|
113
|
+
`Dictation` ignores utterances that arrive while suspended either way.
|
|
84
114
|
|
|
85
115
|
### Link tabs
|
|
86
116
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ComponentProps } from 'svelte';
|
|
3
3
|
import type { CodeSelectProps } from './code-select-types.js';
|
|
4
|
+
import FieldLabel from './FieldLabel.svelte';
|
|
4
5
|
import Input from './Input.svelte';
|
|
5
6
|
import Select from './Select.svelte';
|
|
6
7
|
|
|
@@ -18,9 +19,17 @@ let {
|
|
|
18
19
|
locale: _locale,
|
|
19
20
|
name,
|
|
20
21
|
form,
|
|
22
|
+
label,
|
|
23
|
+
id: idProp,
|
|
24
|
+
required,
|
|
21
25
|
...rest
|
|
22
26
|
}: Props = $props();
|
|
23
27
|
const hiddenId = $props.id();
|
|
28
|
+
const labelId = `${hiddenId}-label`;
|
|
29
|
+
const controlId = $derived(idProp ?? `${hiddenId}-control`);
|
|
30
|
+
const labelProps = $derived(
|
|
31
|
+
label ? { id: controlId, 'aria-labelledby': labelId } : { id: idProp },
|
|
32
|
+
);
|
|
24
33
|
let select: Select | undefined = $state();
|
|
25
34
|
let input: Input | undefined = $state();
|
|
26
35
|
const choices = $derived(options.filter((option) => option.value !== ''));
|
|
@@ -38,10 +47,11 @@ export function getElement(): HTMLInputElement | HTMLSelectElement | null {
|
|
|
38
47
|
return (freeText ? input : select)?.getElement() ?? null;
|
|
39
48
|
}
|
|
40
49
|
</script>
|
|
50
|
+
{#if label}<FieldLabel id={labelId} for={controlId} {label} {required} />{/if}
|
|
41
51
|
{#if freeText}
|
|
42
|
-
<Input {...inputRest} bind:this={input} bind:value type="text" {name} {form} {placeholder} disabled={disabled || readOnly} />
|
|
52
|
+
<Input {...inputRest} {...labelProps} {required} bind:this={input} bind:value type="text" {name} {form} {placeholder} disabled={disabled || readOnly} />
|
|
43
53
|
{:else}
|
|
44
|
-
<Select {...rest} bind:this={select} bind:value {name} {form} disabled={disabled || readOnly}>
|
|
54
|
+
<Select {...rest} {...labelProps} {required} bind:this={select} bind:value {name} {form} disabled={disabled || readOnly}>
|
|
45
55
|
<option value="">{placeholder}</option>
|
|
46
56
|
{#if retained}<option value={value}>{value}</option>{/if}
|
|
47
57
|
{#each choices as option}<option value={option.value} disabled={option.disabled}>{option.label ?? option.value}</option>{/each}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeSelect.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/CodeSelect.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeSelect.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/CodeSelect.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAM9D,UAAU,KAAM,SAAQ,eAAe;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA4DD,QAAA,MAAM,UAAU;iBAtBG,IAAI;sBAIC,gBAAgB,GAAG,iBAAiB,GAAG,IAAI;WAkBX,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
matchingOption,
|
|
18
18
|
prepareTextControlValue,
|
|
19
19
|
} from './control-value-validation.js';
|
|
20
|
+
import FieldLabel from './FieldLabel.svelte';
|
|
20
21
|
import { useControlRegistration } from './use-control-registration.svelte.js';
|
|
21
22
|
export interface Props {
|
|
22
23
|
/** Array of options to select from. */
|
|
@@ -334,7 +335,7 @@ useControlRegistration(() => {
|
|
|
334
335
|
<div bind:this={rootEl} class="combobox {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
|
|
335
336
|
data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
|
|
336
337
|
data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}>
|
|
337
|
-
<
|
|
338
|
+
<FieldLabel for={inputId} {label} {required} />
|
|
338
339
|
<!-- The form posts the committed value (an option id), never the label the person sees. -->
|
|
339
340
|
{#if name}<input type="hidden" {name} {value} {disabled} />{/if}
|
|
340
341
|
<input bind:this={inputEl} id={inputId} role="combobox" autocomplete="off" {placeholder} {disabled} required={required && !value} aria-required={required ? 'true' : undefined} value={query}
|
|
@@ -346,8 +347,8 @@ useControlRegistration(() => {
|
|
|
346
347
|
{#if status !== undefined}<div class="sr-status" role="status" aria-live="polite">{open ? status : ''}</div>{/if}
|
|
347
348
|
</div>
|
|
348
349
|
<style>
|
|
349
|
-
.combobox { position: relative; display: grid; gap:
|
|
350
|
-
|
|
350
|
+
.combobox { position: relative; display: grid; gap: 0; color: var(--smrt-color-on-surface); }
|
|
351
|
+
input { box-sizing: border-box; width: 100%; padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: inherit; }
|
|
351
352
|
input:focus { outline: 2px solid var(--smrt-color-primary); outline-offset: 1px; } .options { position: absolute; z-index: var(--smrt-z-index-dropdown); top: 100%; left: 0; right: 0; display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface-container); box-shadow: var(--smrt-elevation-2); }
|
|
352
353
|
.options button { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 0; border-radius: var(--smrt-radius-extra-small); background: transparent; color: var(--smrt-color-on-surface); text-align: left; } .options button.active { background: var(--smrt-color-secondary-container); }
|
|
353
354
|
.options.empty { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); color: var(--smrt-color-on-surface-variant); }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Combobox.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,QAAQ,CAAC;AAM/C,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Combobox.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,QAAQ,CAAC;AAM/C,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAYlC,MAAM,WAAW,KAAK;IACpB,uCAAuC;IACvC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;IACzC,gDAAgD;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyRD,QAAA,MAAM,QAAQ,gDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
* 44px target, named by `aria-label`, `aria-pressed` while listening, and a
|
|
8
8
|
* listening dot that does not rely on color alone (the name changes too).
|
|
9
9
|
* Pair it with `DictationStatus` for the words ("Listening…", errors).
|
|
10
|
+
*
|
|
11
|
+
* Hands-free (`mode: 'hands-free'`): a steady ring means it is listening,
|
|
12
|
+
* a halo that swells with the voice means someone is speaking, and a small
|
|
13
|
+
* spinner means sentences are being written down. The ring is the
|
|
14
|
+
* non-motion cue; the halo and spinner stop moving for people who ask for
|
|
15
|
+
* reduced motion. While the page speaks a reply (`Dictation.suspend()`) the
|
|
16
|
+
* button dims with a dashed outline and its tooltip reads "Paused while the
|
|
17
|
+
* assistant speaks". Tapping ends it either way. The help text ("Just talk;
|
|
18
|
+
* I write it down when you pause...") is the tooltip and the button's
|
|
19
|
+
* `aria-describedby`, never a line of text in the form.
|
|
10
20
|
*/
|
|
11
21
|
import { M } from '../../i18n/strings.ui.js';
|
|
12
22
|
import { useI18n } from '../../i18n/use-i18n.js';
|
|
@@ -40,6 +50,11 @@ let {
|
|
|
40
50
|
|
|
41
51
|
const { t } = useI18n();
|
|
42
52
|
const active = $derived(dictation.active);
|
|
53
|
+
const handsFree = $derived(dictation.handsFree);
|
|
54
|
+
// Paused while the assistant speaks (half-duplex): not hearing, still open.
|
|
55
|
+
const paused = $derived(active && handsFree && dictation.suspended);
|
|
56
|
+
const speaking = $derived(active && handsFree && dictation.speaking && !paused);
|
|
57
|
+
const writing = $derived(handsFree && dictation.queued > 0);
|
|
43
58
|
// While a recording is being written down, a tap would do nothing.
|
|
44
59
|
const busy = $derived(dictation.state === 'transcribing');
|
|
45
60
|
const name = $derived(
|
|
@@ -47,6 +62,22 @@ const name = $derived(
|
|
|
47
62
|
? (stopLabel ?? t(M['ui.dictation.stop']))
|
|
48
63
|
: (label ?? t(M['ui.dictation.start'])),
|
|
49
64
|
);
|
|
65
|
+
const uid = $props.id();
|
|
66
|
+
const helpId = `smrt-dictation-help-${uid}`;
|
|
67
|
+
const hint = $derived(
|
|
68
|
+
active
|
|
69
|
+
? handsFree
|
|
70
|
+
? paused
|
|
71
|
+
? t(M['ui.dictation.paused_speaking'])
|
|
72
|
+
: t(M['ui.dictation.listening_hands_free'])
|
|
73
|
+
: name
|
|
74
|
+
: (title ??
|
|
75
|
+
t(
|
|
76
|
+
dictation.wantsHandsFree
|
|
77
|
+
? M['ui.dictation.start_hands_free']
|
|
78
|
+
: M['ui.dictation.start_hint'],
|
|
79
|
+
)),
|
|
80
|
+
);
|
|
50
81
|
|
|
51
82
|
function handleClick() {
|
|
52
83
|
// A tap is a user gesture: wake the audio so the ready beep can play.
|
|
@@ -60,14 +91,19 @@ function handleClick() {
|
|
|
60
91
|
type="button"
|
|
61
92
|
variant="ghost"
|
|
62
93
|
size="sm"
|
|
63
|
-
class={`smrt-dictation-button${active ? ' smrt-dictation-button--listening' : ''} ${className}`.trim()}
|
|
94
|
+
class={`smrt-dictation-button${active ? ' smrt-dictation-button--listening' : ''}${active && handsFree ? ' smrt-dictation-button--hands-free' : ''}${speaking ? ' smrt-dictation-button--speaking' : ''}${paused ? ' smrt-dictation-button--paused' : ''} ${className}`.trim()}
|
|
64
95
|
aria-label={name}
|
|
65
96
|
aria-pressed={active}
|
|
66
97
|
aria-busy={busy || undefined}
|
|
67
|
-
title={
|
|
98
|
+
title={hint}
|
|
99
|
+
aria-describedby={hint !== name ? helpId : undefined}
|
|
68
100
|
disabled={disabled || busy}
|
|
69
101
|
onclick={handleClick}
|
|
70
102
|
data-dictation-state={dictation.state}
|
|
103
|
+
data-dictation-mode={handsFree ? 'hands-free' : undefined}
|
|
104
|
+
data-dictation-speaking={speaking || undefined}
|
|
105
|
+
data-dictation-paused={paused || undefined}
|
|
106
|
+
style={speaking ? `--smrt-dictation-level: ${dictation.level}` : undefined}
|
|
71
107
|
>
|
|
72
108
|
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
73
109
|
<rect x="9" y="2" width="6" height="12" rx="3"></rect>
|
|
@@ -77,7 +113,13 @@ function handleClick() {
|
|
|
77
113
|
{#if active}
|
|
78
114
|
<span class="smrt-dictation-dot" aria-hidden="true"></span>
|
|
79
115
|
{/if}
|
|
116
|
+
{#if writing || (handsFree && dictation.state === 'transcribing')}
|
|
117
|
+
<span class="smrt-dictation-writing" aria-hidden="true"></span>
|
|
118
|
+
{/if}
|
|
80
119
|
</Button>
|
|
120
|
+
{#if hint !== name}
|
|
121
|
+
<span id={helpId} class="smrt-dictation-help">{hint}</span>
|
|
122
|
+
{/if}
|
|
81
123
|
|
|
82
124
|
<style>
|
|
83
125
|
:global(.smrt-dictation-button) {
|
|
@@ -99,6 +141,16 @@ function handleClick() {
|
|
|
99
141
|
color: var(--smrt-color-on-error-container, #410002);
|
|
100
142
|
}
|
|
101
143
|
|
|
144
|
+
/* Read out with the button's name; never shown. */
|
|
145
|
+
.smrt-dictation-help {
|
|
146
|
+
position: absolute;
|
|
147
|
+
inline-size: 1px;
|
|
148
|
+
block-size: 1px;
|
|
149
|
+
overflow: hidden;
|
|
150
|
+
clip-path: inset(50%);
|
|
151
|
+
white-space: nowrap;
|
|
152
|
+
}
|
|
153
|
+
|
|
102
154
|
.smrt-dictation-dot {
|
|
103
155
|
position: absolute;
|
|
104
156
|
inset-block-start: 6px;
|
|
@@ -110,6 +162,54 @@ function handleClick() {
|
|
|
110
162
|
animation: smrt-dictation-pulse 1.2s ease-in-out infinite;
|
|
111
163
|
}
|
|
112
164
|
|
|
165
|
+
/* Hands-free: a steady ring while listening ... */
|
|
166
|
+
:global(.smrt-dictation-button--hands-free) {
|
|
167
|
+
box-shadow: 0 0 0 2px var(--smrt-color-error, #ba1a1a);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ... and a halo that swells with the voice while someone is speaking. */
|
|
171
|
+
:global(.smrt-dictation-button--speaking) {
|
|
172
|
+
box-shadow:
|
|
173
|
+
0 0 0 3px var(--smrt-color-error, #ba1a1a),
|
|
174
|
+
0 0 0
|
|
175
|
+
calc(4px + 10px * var(--smrt-dictation-level, 0))
|
|
176
|
+
color-mix(in srgb, var(--smrt-color-error, #ba1a1a) 28%, transparent);
|
|
177
|
+
transition: box-shadow 80ms linear;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Paused while the assistant speaks: a dashed, dimmed ring (not color alone). */
|
|
181
|
+
:global(.smrt-dictation-button--paused) {
|
|
182
|
+
opacity: 0.6;
|
|
183
|
+
box-shadow: none;
|
|
184
|
+
outline: 2px dashed var(--smrt-color-outline, #73777f);
|
|
185
|
+
outline-offset: -2px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
:global(.smrt-dictation-button--paused) .smrt-dictation-dot {
|
|
189
|
+
animation: none;
|
|
190
|
+
opacity: 0.4;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Sentences are being written down. */
|
|
194
|
+
.smrt-dictation-writing {
|
|
195
|
+
position: absolute;
|
|
196
|
+
inset-block-end: 6px;
|
|
197
|
+
inset-inline-end: 6px;
|
|
198
|
+
inline-size: 10px;
|
|
199
|
+
block-size: 10px;
|
|
200
|
+
border-radius: 50%;
|
|
201
|
+
border: 2px solid var(--smrt-color-outline-variant, #c4c6cf);
|
|
202
|
+
border-block-start-color: var(--smrt-color-primary, #2f5ea8);
|
|
203
|
+
background: var(--smrt-color-surface, #fff);
|
|
204
|
+
animation: smrt-dictation-spin 0.9s linear infinite;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@keyframes smrt-dictation-spin {
|
|
208
|
+
to {
|
|
209
|
+
transform: rotate(360deg);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
113
213
|
@keyframes smrt-dictation-pulse {
|
|
114
214
|
0%,
|
|
115
215
|
100% {
|
|
@@ -121,8 +221,15 @@ function handleClick() {
|
|
|
121
221
|
}
|
|
122
222
|
|
|
123
223
|
@media (prefers-reduced-motion: reduce) {
|
|
124
|
-
.smrt-dictation-dot
|
|
224
|
+
.smrt-dictation-dot,
|
|
225
|
+
.smrt-dictation-writing {
|
|
125
226
|
animation: none;
|
|
126
227
|
}
|
|
228
|
+
|
|
229
|
+
/* Still ringed, but the halo no longer animates with the voice. */
|
|
230
|
+
:global(.smrt-dictation-button--speaking) {
|
|
231
|
+
transition: none;
|
|
232
|
+
box-shadow: 0 0 0 4px var(--smrt-color-error, #ba1a1a);
|
|
233
|
+
}
|
|
127
234
|
}
|
|
128
235
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DictationButton.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DictationButton.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DictationButton.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DictationButton.svelte.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD,MAAM,WAAW,KAAK;IACpB,kDAAkD;IAClD,SAAS,EAAE,SAAS,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+ED,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* DictationStatus — what dictation is doing, in words (see `Dictation`).
|
|
4
4
|
*
|
|
5
|
-
* "Listening…" (with the words heard so far) while the microphone is on
|
|
5
|
+
* "Listening…" (with the words heard so far) while the microphone is on.
|
|
6
|
+
* Hands-free is announced, not shown: the line is visually hidden and says
|
|
7
|
+
* "Listening", "Hearing you…" while someone is speaking and "Writing it down…"
|
|
8
|
+
* while sentences are being written down (the long help text is the
|
|
9
|
+
* microphone's tooltip) and "Paused while the assistant speaks" while
|
|
10
|
+
* listening is suspended. "Sending…" (`sending`) is the one short hands-free
|
|
11
|
+
* hint that stays visible, since a message is about to leave.
|
|
6
12
|
* "Writing it down…" while a recording is turned into text, and
|
|
7
13
|
* a plain explanation when it cannot listen: the browser cannot turn speech
|
|
8
14
|
* into text, the microphone is blocked, or nothing was heard. Nothing while
|
|
@@ -15,20 +21,28 @@ import type { Dictation, DictationErrorKind } from './dictation.svelte.js';
|
|
|
15
21
|
export interface Props {
|
|
16
22
|
/** The dictation session whose listening state and messages this shows. */
|
|
17
23
|
dictation: Dictation;
|
|
24
|
+
/** A message is about to be sent (hands-free send on pause): shows "Sending…". */
|
|
25
|
+
sending?: boolean;
|
|
18
26
|
/** Extra class names. */
|
|
19
27
|
class?: string;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
let { dictation, class: className = '' }: Props = $props();
|
|
30
|
+
let { dictation, sending = false, class: className = '' }: Props = $props();
|
|
23
31
|
|
|
24
32
|
const { t } = useI18n();
|
|
25
33
|
const showLine = $derived(
|
|
26
|
-
|
|
34
|
+
sending ||
|
|
35
|
+
dictation.state === 'starting' ||
|
|
27
36
|
dictation.state === 'listening' ||
|
|
28
37
|
dictation.state === 'stopping' ||
|
|
29
38
|
dictation.state === 'transcribing',
|
|
30
39
|
);
|
|
31
40
|
|
|
41
|
+
// Hands-free states are announced to screen readers only.
|
|
42
|
+
const announceOnly = $derived(
|
|
43
|
+
!sending && dictation.handsFree && dictation.state === 'listening',
|
|
44
|
+
);
|
|
45
|
+
|
|
32
46
|
function errorText(kind: DictationErrorKind | null): string {
|
|
33
47
|
switch (kind) {
|
|
34
48
|
case 'unsupported':
|
|
@@ -47,6 +61,8 @@ function errorText(kind: DictationErrorKind | null): string {
|
|
|
47
61
|
return t(M['ui.dictation.not_transcribed']);
|
|
48
62
|
case 'unavailable':
|
|
49
63
|
return t(M['ui.dictation.unavailable']);
|
|
64
|
+
case 'model-missing':
|
|
65
|
+
return t(M['ui.dictation.model_missing']);
|
|
50
66
|
case 'forbidden':
|
|
51
67
|
return t(M['ui.dictation.forbidden']);
|
|
52
68
|
default:
|
|
@@ -58,12 +74,24 @@ function errorText(kind: DictationErrorKind | null): string {
|
|
|
58
74
|
<div class={`smrt-dictation-status ${className}`.trim()}>
|
|
59
75
|
<p
|
|
60
76
|
class="smrt-dictation-status-line"
|
|
61
|
-
class:smrt-dictation-status--empty={!showLine}
|
|
77
|
+
class:smrt-dictation-status--empty={!showLine || announceOnly}
|
|
62
78
|
role="status"
|
|
63
79
|
aria-live="polite"
|
|
64
80
|
>
|
|
65
|
-
{#if
|
|
81
|
+
{#if sending}
|
|
82
|
+
{t(M['ui.dictation.sending'])}
|
|
83
|
+
{:else if dictation.state === 'starting'}
|
|
66
84
|
{t(M['ui.dictation.starting'])}
|
|
85
|
+
{:else if dictation.handsFree && dictation.state === 'listening'}
|
|
86
|
+
{#if dictation.suspended}
|
|
87
|
+
{t(M['ui.dictation.paused_speaking'])}
|
|
88
|
+
{:else if dictation.queued > 0}
|
|
89
|
+
{t(M['ui.dictation.transcribing'])}
|
|
90
|
+
{:else if dictation.speaking}
|
|
91
|
+
{t(M['ui.dictation.hearing'])}
|
|
92
|
+
{:else}
|
|
93
|
+
{t(M['ui.dictation.listening_short'])}
|
|
94
|
+
{/if}
|
|
67
95
|
{:else if dictation.state === 'listening' || dictation.state === 'stopping'}
|
|
68
96
|
<span class="smrt-dictation-status-dot" aria-hidden="true"></span>
|
|
69
97
|
{t(M['ui.dictation.listening'])}
|
|
@@ -2,6 +2,8 @@ import type { Dictation } from './dictation.svelte.js';
|
|
|
2
2
|
export interface Props {
|
|
3
3
|
/** The dictation session whose listening state and messages this shows. */
|
|
4
4
|
dictation: Dictation;
|
|
5
|
+
/** A message is about to be sent (hands-free send on pause): shows "Sending…". */
|
|
6
|
+
sending?: boolean;
|
|
5
7
|
/** Extra class names. */
|
|
6
8
|
class?: string;
|
|
7
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DictationStatus.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DictationStatus.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DictationStatus.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DictationStatus.svelte.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAsB,MAAM,uBAAuB,CAAC;AAG3E,MAAM,WAAW,KAAK;IACpB,2EAA2E;IAC3E,SAAS,EAAE,SAAS,CAAC;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4FD,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The one label treatment for generated form fields: a plain label above its
|
|
4
|
+
* control. Every field component (text, money, date, select, relation, …) and
|
|
5
|
+
* every read-only field display renders this, so size, weight, colour and
|
|
6
|
+
* spacing cannot drift between field types.
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Label text. */
|
|
10
|
+
label: string;
|
|
11
|
+
/** Id of the labelled control (`for`). Omit for read-only displays. */
|
|
12
|
+
for?: string;
|
|
13
|
+
/** Id of the label element, for `aria-labelledby` on the control. */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Shows the required marker. */
|
|
16
|
+
required?: boolean;
|
|
17
|
+
}
|
|
18
|
+
let { label, for: forId, id, required = false }: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{#if forId}
|
|
22
|
+
<label {id} for={forId} class="smrt-field-label">{label}{#if required}<span class="required" aria-hidden="true">*</span>{/if}</label>
|
|
23
|
+
{:else}
|
|
24
|
+
<span {id} class="smrt-field-label">{label}{#if required}<span class="required" aria-hidden="true">*</span>{/if}</span>
|
|
25
|
+
{/if}
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.smrt-field-label {
|
|
29
|
+
display: block;
|
|
30
|
+
margin-bottom: var(--smrt-spacing-1, 4px);
|
|
31
|
+
font-size: var(--smrt-typography-label-large-size, 0.875rem);
|
|
32
|
+
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
33
|
+
line-height: var(--smrt-typography-label-large-line-height, 1.4);
|
|
34
|
+
letter-spacing: normal;
|
|
35
|
+
text-transform: none;
|
|
36
|
+
font-family: inherit;
|
|
37
|
+
color: var(--smrt-color-on-surface, #374151);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.required {
|
|
41
|
+
color: var(--smrt-color-error, #ba1a1a);
|
|
42
|
+
margin-left: var(--smrt-spacing-1, 4px);
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one label treatment for generated form fields: a plain label above its
|
|
3
|
+
* control. Every field component (text, money, date, select, relation, …) and
|
|
4
|
+
* every read-only field display renders this, so size, weight, colour and
|
|
5
|
+
* spacing cannot drift between field types.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Label text. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Id of the labelled control (`for`). Omit for read-only displays. */
|
|
11
|
+
for?: string;
|
|
12
|
+
/** Id of the label element, for `aria-labelledby` on the control. */
|
|
13
|
+
id?: string;
|
|
14
|
+
/** Shows the required marker. */
|
|
15
|
+
required?: boolean;
|
|
16
|
+
}
|
|
17
|
+
declare const FieldLabel: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
type FieldLabel = ReturnType<typeof FieldLabel>;
|
|
19
|
+
export default FieldLabel;
|
|
20
|
+
//# sourceMappingURL=FieldLabel.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldLabel.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FieldLabel.svelte.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,UAAU,KAAK;IACb,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAgBD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { ControlInteractionOptions } from './control-interaction.js';
|
|
4
|
+
import FieldLabel from './FieldLabel.svelte';
|
|
4
5
|
import {
|
|
5
6
|
type FormGroupContextValue,
|
|
6
7
|
nextFieldId,
|
|
@@ -62,12 +63,7 @@ setFormGroupContext(
|
|
|
62
63
|
</script>
|
|
63
64
|
|
|
64
65
|
<div class="form-group">
|
|
65
|
-
<
|
|
66
|
-
{label}
|
|
67
|
-
{#if required}
|
|
68
|
-
<span class="required" aria-hidden="true">*</span>
|
|
69
|
-
{/if}
|
|
70
|
-
</label>
|
|
66
|
+
<FieldLabel for={fieldId} {label} {required} />
|
|
71
67
|
{@render children()}
|
|
72
68
|
{#if hintId}
|
|
73
69
|
<p id={hintId} class="form-hint">{hint}</p>
|
|
@@ -82,18 +78,7 @@ setFormGroupContext(
|
|
|
82
78
|
margin-bottom: 1rem;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
|
-
.form-label {
|
|
86
|
-
display: block;
|
|
87
|
-
font-size: var(--smrt-typography-label-large-size, 0.875rem);
|
|
88
|
-
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
89
|
-
color: var(--smrt-color-on-surface, #374151);
|
|
90
|
-
margin-bottom: 0.375rem;
|
|
91
|
-
}
|
|
92
81
|
|
|
93
|
-
.required {
|
|
94
|
-
color: var(--smrt-color-error, #ba1a1a);
|
|
95
|
-
margin-left: 0.125rem;
|
|
96
|
-
}
|
|
97
82
|
|
|
98
83
|
.form-hint {
|
|
99
84
|
margin: 0.25rem 0 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormGroup.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FormGroup.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"FormGroup.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FormGroup.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAS1E,MAAM,WAAW,KAAK;IACpB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,CAAC;CACnB;AA4DD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { CodeSelectOption } from './code-select-types.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* ISO 4217 codes that are not circulating currencies: funds codes, precious
|
|
4
|
+
* metals, the IMF unit and bond-market units, and the testing / "no currency"
|
|
5
|
+
* codes. They stay valid stored values (the select retains an unknown value)
|
|
6
|
+
* but are not offered as choices.
|
|
7
|
+
*/
|
|
8
|
+
export declare const NON_CIRCULATING_CURRENCY_CODES: ReadonlySet<string>;
|
|
9
|
+
/** Build localized choices for circulating currencies from the shared ISO metadata. */
|
|
3
10
|
export declare function currencyOptions(locale: string): CodeSelectOption[];
|
|
4
11
|
/** Build localized ISO alpha-2 country choices. */
|
|
5
12
|
export declare function countryOptions(locale: string): CodeSelectOption[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-select-options.d.ts","sourceRoot":"","sources":["../../../src/components/forms/code-select-options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAiT/D,
|
|
1
|
+
{"version":3,"file":"code-select-options.d.ts","sourceRoot":"","sources":["../../../src/components/forms/code-select-options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAiT/D;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,CA4B7D,CAAC;AACH,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAWlE;AACD,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAGjE;AACD,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GACd,SAAS,gBAAgB,EAAE,GAAG,SAAS,CAEzC"}
|
|
@@ -296,13 +296,54 @@ function displayNames(locale, type) {
|
|
|
296
296
|
return new Intl.DisplayNames('en', { type });
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
-
/**
|
|
299
|
+
/**
|
|
300
|
+
* ISO 4217 codes that are not circulating currencies: funds codes, precious
|
|
301
|
+
* metals, the IMF unit and bond-market units, and the testing / "no currency"
|
|
302
|
+
* codes. They stay valid stored values (the select retains an unknown value)
|
|
303
|
+
* but are not offered as choices.
|
|
304
|
+
*/
|
|
305
|
+
export const NON_CIRCULATING_CURRENCY_CODES = new Set([
|
|
306
|
+
// Funds codes
|
|
307
|
+
'BOV',
|
|
308
|
+
'CHE',
|
|
309
|
+
'CHW',
|
|
310
|
+
'CLF',
|
|
311
|
+
'COU',
|
|
312
|
+
'MXV',
|
|
313
|
+
'USN',
|
|
314
|
+
'USS',
|
|
315
|
+
'UYI',
|
|
316
|
+
'UYW',
|
|
317
|
+
// Precious metals
|
|
318
|
+
'XAG',
|
|
319
|
+
'XAU',
|
|
320
|
+
'XPD',
|
|
321
|
+
'XPT',
|
|
322
|
+
// Bond-market units, IMF special drawing rights, other supranational units
|
|
323
|
+
'XBA',
|
|
324
|
+
'XBB',
|
|
325
|
+
'XBC',
|
|
326
|
+
'XBD',
|
|
327
|
+
'XDR',
|
|
328
|
+
'XSU',
|
|
329
|
+
'XUA',
|
|
330
|
+
// Testing and "no currency"
|
|
331
|
+
'XTS',
|
|
332
|
+
'XXX',
|
|
333
|
+
]);
|
|
334
|
+
/** Build localized choices for circulating currencies from the shared ISO metadata. */
|
|
300
335
|
export function currencyOptions(locale) {
|
|
301
336
|
const names = displayNames(locale, 'currency');
|
|
302
|
-
return [...ISO_4217_MINOR_UNITS.keys()]
|
|
303
|
-
value
|
|
304
|
-
|
|
305
|
-
|
|
337
|
+
return [...ISO_4217_MINOR_UNITS.keys()]
|
|
338
|
+
.filter((value) => !NON_CIRCULATING_CURRENCY_CODES.has(value))
|
|
339
|
+
.sort()
|
|
340
|
+
.flatMap((value) => {
|
|
341
|
+
const name = names.of(value);
|
|
342
|
+
// A code the runtime cannot name (shown as "XYZ — XYZ") is not a currency to offer.
|
|
343
|
+
if (!name || name === value)
|
|
344
|
+
return [];
|
|
345
|
+
return [{ value, label: `${value} — ${name}` }];
|
|
346
|
+
});
|
|
306
347
|
}
|
|
307
348
|
/** Build localized ISO alpha-2 country choices. */
|
|
308
349
|
export function countryOptions(locale) {
|
|
@@ -21,6 +21,10 @@ export interface CodeSelectProps extends Omit<HTMLSelectAttributes, 'children' |
|
|
|
21
21
|
interaction?: ControlInteractionOptions | false;
|
|
22
22
|
/** Custom choices replace defaults; an empty list is an intentional override. */
|
|
23
23
|
options?: readonly CodeSelectOption[];
|
|
24
|
+
/** Visible label rendered above the control with the shared field label; becomes its accessible name. */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** Shows the required marker on `label`. */
|
|
27
|
+
required?: boolean;
|
|
24
28
|
/** Locale used for built-in currency/country display names; invalid locales use English. */
|
|
25
29
|
locale?: string;
|
|
26
30
|
/** Empty choice label. Defaults to an em dash and never auto-selects a code. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-select-types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/code-select-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1E,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AACD,uFAAuF;AACvF,MAAM,WAAW,eACf,SAAQ,IAAI,CACV,oBAAoB,EACpB,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAC5C;IACD,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,sDAAsD;IACtD,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,iFAAiF;IACjF,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AACD,wDAAwD;AACxD,MAAM,WAAW,mBAAoB,SAAQ,eAAe;CAAG;AAC/D,wDAAwD;AACxD,MAAM,WAAW,kBAAmB,SAAQ,eAAe;CAAG;AAC9D,6FAA6F;AAC7F,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,aAAa,CAAC,iBAAiB,CAAC,CAAC,EACnE,IAAI,CAAC,aAAa,CAAC,gBAAgB,GAAG,iBAAiB,CAAC,EAAE,UAAU,CAAC;IACvE,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"code-select-types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/code-select-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1E,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AACD,uFAAuF;AACvF,MAAM,WAAW,eACf,SAAQ,IAAI,CACV,oBAAoB,EACpB,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAC5C;IACD,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAClC,sDAAsD;IACtD,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,iFAAiF;IACjF,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,yGAAyG;IACzG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AACD,wDAAwD;AACxD,MAAM,WAAW,mBAAoB,SAAQ,eAAe;CAAG;AAC/D,wDAAwD;AACxD,MAAM,WAAW,kBAAmB,SAAQ,eAAe;CAAG;AAC9D,6FAA6F;AAC7F,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,aAAa,CAAC,iBAAiB,CAAC,CAAC,EACnE,IAAI,CAAC,aAAa,CAAC,gBAAgB,GAAG,iBAAiB,CAAC,EAAE,UAAU,CAAC;IACvE,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|