@vielzeug/refine 1.5.1 → 1.5.2
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/dist/async.cjs +9 -9
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +30 -31
- package/dist/async.js.map +1 -1
- package/dist/button.cjs +6 -6
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +29 -30
- package/dist/button.js.map +1 -1
- package/dist/calendar.cjs +44 -44
- package/dist/calendar.cjs.map +1 -1
- package/dist/calendar.js +115 -117
- package/dist/calendar.js.map +1 -1
- package/dist/checkbox-group.cjs +7 -7
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +71 -73
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +3 -3
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +28 -29
- package/dist/checkbox.js.map +1 -1
- package/dist/code-window.cjs +7 -7
- package/dist/code-window.cjs.map +1 -1
- package/dist/code-window.js +17 -18
- package/dist/code-window.js.map +1 -1
- package/dist/combobox.cjs +1 -1
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +11 -12
- package/dist/combobox.js.map +1 -1
- package/dist/custom-elements.json +5267 -5267
- package/dist/datagrid.cjs +66 -66
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +267 -268
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +11 -11
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +8 -9
- package/dist/date-picker.js.map +1 -1
- package/dist/file-input.cjs +6 -6
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +50 -51
- package/dist/file-input.js.map +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +47 -48
- package/dist/grid.js.map +1 -1
- package/dist/icon.cjs +1 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +26 -27
- package/dist/icon.js.map +1 -1
- package/dist/input.cjs +20 -20
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +98 -100
- package/dist/input.js.map +1 -1
- package/dist/message-composer.cjs +18 -18
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js +113 -115
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +11 -11
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +78 -79
- package/dist/navbar.js.map +1 -1
- package/dist/otp-input.cjs +4 -4
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +31 -32
- package/dist/otp-input.js.map +1 -1
- package/dist/progress.cjs +13 -13
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js +45 -46
- package/dist/progress.js.map +1 -1
- package/dist/radio-group.cjs +5 -5
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +83 -85
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +3 -3
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +27 -28
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +4 -4
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +30 -31
- package/dist/rating.js.map +1 -1
- package/dist/select.cjs +4 -4
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +88 -89
- package/dist/select.js.map +1 -1
- package/dist/sidebar.cjs +11 -11
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +97 -98
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +3 -3
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +23 -24
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +3 -3
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +65 -66
- package/dist/slider.js.map +1 -1
- package/dist/switch.cjs +3 -3
- package/dist/switch.cjs.map +1 -1
- package/dist/switch.js +23 -24
- package/dist/switch.js.map +1 -1
- package/dist/tab-panel.cjs +7 -7
- package/dist/tab-panel.cjs.map +1 -1
- package/dist/tab-panel.js +21 -23
- package/dist/tab-panel.js.map +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +4 -5
- package/dist/testing.js.map +1 -1
- package/dist/textarea.cjs +13 -13
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +92 -94
- package/dist/textarea.js.map +1 -1
- package/dist/time-picker.cjs +9 -9
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +47 -48
- package/dist/time-picker.js.map +1 -1
- package/package.json +1 -1
- package/dist/ore/dist/_dev.cjs +0 -2
- package/dist/ore/dist/_dev.cjs.map +0 -1
- package/dist/ore/dist/_dev.js +0 -12
- package/dist/ore/dist/_dev.js.map +0 -1
- package/dist/ore/dist/base-element.cjs +0 -2
- package/dist/ore/dist/base-element.cjs.map +0 -1
- package/dist/ore/dist/base-element.js +0 -158
- package/dist/ore/dist/base-element.js.map +0 -1
- package/dist/ore/dist/context.cjs +0 -2
- package/dist/ore/dist/context.cjs.map +0 -1
- package/dist/ore/dist/context.js +0 -11
- package/dist/ore/dist/context.js.map +0 -1
- package/dist/ore/dist/define.cjs +0 -2
- package/dist/ore/dist/define.cjs.map +0 -1
- package/dist/ore/dist/define.js +0 -27
- package/dist/ore/dist/define.js.map +0 -1
- package/dist/ore/dist/directives/classMap.cjs +0 -1
- package/dist/ore/dist/directives/classMap.js +0 -1
- package/dist/ore/dist/directives/each.cjs +0 -1
- package/dist/ore/dist/directives/each.js +0 -5
- package/dist/ore/dist/directives/live.cjs +0 -2
- package/dist/ore/dist/directives/live.cjs.map +0 -1
- package/dist/ore/dist/directives/live.js +0 -6
- package/dist/ore/dist/directives/live.js.map +0 -1
- package/dist/ore/dist/directives/model.cjs +0 -1
- package/dist/ore/dist/directives/model.js +0 -3
- package/dist/ore/dist/directives/raw.cjs +0 -2
- package/dist/ore/dist/directives/raw.cjs.map +0 -1
- package/dist/ore/dist/directives/raw.js +0 -38
- package/dist/ore/dist/directives/raw.js.map +0 -1
- package/dist/ore/dist/directives/styleMap.cjs +0 -2
- package/dist/ore/dist/directives/styleMap.cjs.map +0 -1
- package/dist/ore/dist/directives/styleMap.js +0 -20
- package/dist/ore/dist/directives/styleMap.js.map +0 -1
- package/dist/ore/dist/directives/when.cjs +0 -2
- package/dist/ore/dist/directives/when.cjs.map +0 -1
- package/dist/ore/dist/directives/when.js +0 -38
- package/dist/ore/dist/directives/when.js.map +0 -1
- package/dist/ore/dist/directives.cjs +0 -1
- package/dist/ore/dist/directives.js +0 -8
- package/dist/ore/dist/errors.cjs +0 -3
- package/dist/ore/dist/errors.cjs.map +0 -1
- package/dist/ore/dist/errors.js +0 -50
- package/dist/ore/dist/errors.js.map +0 -1
- package/dist/ore/dist/forms/context.cjs +0 -2
- package/dist/ore/dist/forms/context.cjs.map +0 -1
- package/dist/ore/dist/forms/context.js +0 -6
- package/dist/ore/dist/forms/context.js.map +0 -1
- package/dist/ore/dist/forms/field.cjs +0 -2
- package/dist/ore/dist/forms/field.cjs.map +0 -1
- package/dist/ore/dist/forms/field.js +0 -42
- package/dist/ore/dist/forms/field.js.map +0 -1
- package/dist/ore/dist/forms.cjs +0 -1
- package/dist/ore/dist/forms.js +0 -3
- package/dist/ore/dist/observers/intersection-observe.cjs +0 -2
- package/dist/ore/dist/observers/intersection-observe.cjs.map +0 -1
- package/dist/ore/dist/observers/intersection-observe.js +0 -12
- package/dist/ore/dist/observers/intersection-observe.js.map +0 -1
- package/dist/ore/dist/observers/media-observe.cjs +0 -1
- package/dist/ore/dist/observers/media-observe.js +0 -1
- package/dist/ore/dist/observers/mutation-observe.cjs +0 -1
- package/dist/ore/dist/observers/mutation-observe.js +0 -1
- package/dist/ore/dist/observers/resize-observe.cjs +0 -2
- package/dist/ore/dist/observers/resize-observe.cjs.map +0 -1
- package/dist/ore/dist/observers/resize-observe.js +0 -20
- package/dist/ore/dist/observers/resize-observe.js.map +0 -1
- package/dist/ore/dist/observers.cjs +0 -1
- package/dist/ore/dist/observers.js +0 -5
- package/dist/ore/dist/props.cjs +0 -2
- package/dist/ore/dist/props.cjs.map +0 -1
- package/dist/ore/dist/props.js +0 -56
- package/dist/ore/dist/props.js.map +0 -1
- package/dist/ore/dist/runtime.cjs +0 -2
- package/dist/ore/dist/runtime.cjs.map +0 -1
- package/dist/ore/dist/runtime.js +0 -25
- package/dist/ore/dist/runtime.js.map +0 -1
- package/dist/ore/dist/testing/flush.cjs +0 -2
- package/dist/ore/dist/testing/flush.cjs.map +0 -1
- package/dist/ore/dist/testing/flush.js +0 -11
- package/dist/ore/dist/testing/flush.js.map +0 -1
- package/dist/ore/dist/testing/mount.cjs +0 -2
- package/dist/ore/dist/testing/mount.cjs.map +0 -1
- package/dist/ore/dist/testing/mount.js +0 -94
- package/dist/ore/dist/testing/mount.js.map +0 -1
- package/dist/ore/dist/testing/query.cjs +0 -2
- package/dist/ore/dist/testing/query.cjs.map +0 -1
- package/dist/ore/dist/testing/query.js +0 -12
- package/dist/ore/dist/testing/query.js.map +0 -1
- package/dist/ore/dist/testing/render-hook.cjs +0 -1
- package/dist/ore/dist/testing/render-hook.js +0 -3
- package/dist/ore/dist/testing/testing.cjs +0 -1
- package/dist/ore/dist/testing/testing.js +0 -4
- package/dist/ore/dist/testing/wait.cjs +0 -1
- package/dist/ore/dist/testing/wait.js +0 -1
- package/dist/ore/dist/testing.cjs +0 -1
- package/dist/ore/dist/testing.js +0 -6
- package/dist/ore/dist/types/bindings.cjs +0 -2
- package/dist/ore/dist/types/bindings.cjs.map +0 -1
- package/dist/ore/dist/types/bindings.js +0 -15
- package/dist/ore/dist/types/bindings.js.map +0 -1
- package/dist/ore/dist/types.cjs +0 -2
- package/dist/ore/dist/types.cjs.map +0 -1
- package/dist/ore/dist/types.js +0 -15
- package/dist/ore/dist/types.js.map +0 -1
- package/dist/ore/dist/utils/css.cjs +0 -2
- package/dist/ore/dist/utils/css.cjs.map +0 -1
- package/dist/ore/dist/utils/css.js +0 -18
- package/dist/ore/dist/utils/css.js.map +0 -1
- package/dist/ore/dist/utils/dom.cjs +0 -2
- package/dist/ore/dist/utils/dom.cjs.map +0 -1
- package/dist/ore/dist/utils/dom.js +0 -43
- package/dist/ore/dist/utils/dom.js.map +0 -1
- package/dist/ore/dist/utils/id.cjs +0 -2
- package/dist/ore/dist/utils/id.cjs.map +0 -1
- package/dist/ore/dist/utils/id.js +0 -4
- package/dist/ore/dist/utils/id.js.map +0 -1
package/dist/textarea.js
CHANGED
|
@@ -1,125 +1,123 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "./
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import "
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import { bind as v, define as y, html as b, inject as x, onCleanup as S, onElement as C, prop as w, ref as T, useEmit as E, watchEffect as D } from "@vielzeug/ore";
|
|
18
|
-
import { watch as O } from "@vielzeug/ripple";
|
|
1
|
+
import { sizeVariantMixin as e } from "./styles/mixins/shape.css.js";
|
|
2
|
+
import { forcedColorsFocusMixin as t } from "./styles/mixins/theme.css.js";
|
|
3
|
+
import { fieldMixins as n } from "./styles/index.js";
|
|
4
|
+
import { disablableBundle as r, roundableBundle as i, sizableBundle as a, themableBundle as o } from "./shared/prop-bundles.js";
|
|
5
|
+
import { TEXTAREA_SIZE_PRESET as s } from "./shared/size-presets.js";
|
|
6
|
+
import { lifecycleSignal as c } from "./headless/ore.js";
|
|
7
|
+
import { createAutoResize as l } from "./headless/auto-resize.js";
|
|
8
|
+
import { counterClassName as u } from "./headless/field-base.js";
|
|
9
|
+
import { createTextField as d } from "./headless/text-field.js";
|
|
10
|
+
import { errorAttr as f } from "./inputs/shared/field-binding.js";
|
|
11
|
+
import { FORM_CTX as p, useFormContext as m } from "./inputs/shared/form-context.js";
|
|
12
|
+
import h from "./inputs/textarea/textarea.css.js";
|
|
13
|
+
import { bind as g, define as _, html as v, inject as y, onCleanup as b, onElement as x, prop as S, ref as C, useEmit as w, watchEffect as T } from "@vielzeug/ore";
|
|
14
|
+
import { live as E } from "@vielzeug/ore/directives";
|
|
15
|
+
import { watch as D } from "@vielzeug/ripple";
|
|
16
|
+
import { useField as O } from "@vielzeug/ore/forms";
|
|
19
17
|
//#region src/inputs/textarea/textarea.ts
|
|
20
18
|
var k = "ore-textarea";
|
|
21
|
-
|
|
19
|
+
_(k, {
|
|
22
20
|
formAssociated: !0,
|
|
23
21
|
props: {
|
|
24
|
-
...s,
|
|
25
22
|
...o,
|
|
26
|
-
...i,
|
|
27
23
|
...a,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
24
|
+
...r,
|
|
25
|
+
...i,
|
|
26
|
+
"auto-resize": S.bool(!1),
|
|
27
|
+
error: S.string(),
|
|
28
|
+
fullwidth: S.bool(!1),
|
|
29
|
+
helper: S.string(),
|
|
30
|
+
label: S.string(),
|
|
31
|
+
"label-placement": S.oneOf(["inset", "outside"], "inset"),
|
|
32
|
+
maxlength: S.json(void 0),
|
|
33
|
+
name: S.string(),
|
|
34
|
+
"no-resize": S.bool(!1),
|
|
35
|
+
placeholder: S.string(),
|
|
36
|
+
readonly: S.bool(!1),
|
|
37
|
+
ref: S.json(void 0),
|
|
38
|
+
required: S.bool(!1),
|
|
39
|
+
resize: S.string(),
|
|
40
|
+
rows: S.json(void 0),
|
|
41
|
+
value: S.string(),
|
|
42
|
+
variant: S.string()
|
|
45
43
|
},
|
|
46
|
-
setup(
|
|
47
|
-
let
|
|
48
|
-
disabled:
|
|
49
|
-
error:
|
|
50
|
-
helper:
|
|
51
|
-
label:
|
|
52
|
-
labelPlacement:
|
|
53
|
-
maxLength:
|
|
54
|
-
onChange: (e,
|
|
55
|
-
|
|
44
|
+
setup(e) {
|
|
45
|
+
let t = w(), n = T, r = y(p), i = m(e, r), a = C(), o = l({ enabled: e["auto-resize"] }), s = c(b), h = d({
|
|
46
|
+
disabled: i.disabled,
|
|
47
|
+
error: e.error,
|
|
48
|
+
helper: e.helper,
|
|
49
|
+
label: e.label,
|
|
50
|
+
labelPlacement: e["label-placement"],
|
|
51
|
+
maxLength: e.maxlength,
|
|
52
|
+
onChange: (e, n) => {
|
|
53
|
+
t("change", {
|
|
56
54
|
originalEvent: e,
|
|
57
|
-
value:
|
|
55
|
+
value: n
|
|
58
56
|
});
|
|
59
57
|
},
|
|
60
|
-
onInput: (e,
|
|
61
|
-
|
|
58
|
+
onInput: (e, n) => {
|
|
59
|
+
t("input", {
|
|
62
60
|
originalEvent: e,
|
|
63
|
-
value:
|
|
61
|
+
value: n
|
|
64
62
|
});
|
|
65
63
|
},
|
|
66
64
|
prefix: "textarea",
|
|
67
|
-
readonly:
|
|
68
|
-
required:
|
|
69
|
-
signal:
|
|
70
|
-
validateOn:
|
|
71
|
-
value:
|
|
65
|
+
readonly: e.readonly,
|
|
66
|
+
required: e.required,
|
|
67
|
+
signal: s,
|
|
68
|
+
validateOn: r?.validateOn,
|
|
69
|
+
value: e.value
|
|
72
70
|
});
|
|
73
|
-
|
|
74
|
-
disabled:
|
|
71
|
+
h.attachFormField(O({
|
|
72
|
+
disabled: h.disabled,
|
|
75
73
|
onReset: () => {
|
|
76
|
-
|
|
74
|
+
h.reset(), requestAnimationFrame(() => o.recompute());
|
|
77
75
|
},
|
|
78
76
|
toFormValue: (e) => e,
|
|
79
|
-
validationMessage:
|
|
80
|
-
validity:
|
|
81
|
-
value:
|
|
77
|
+
validationMessage: h.validationMessage,
|
|
78
|
+
validity: h.validity,
|
|
79
|
+
value: h.value
|
|
82
80
|
}));
|
|
83
|
-
let { ariaDescribedBy:
|
|
84
|
-
return
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
let a =
|
|
88
|
-
|
|
89
|
-
}),
|
|
90
|
-
|
|
81
|
+
let { ariaDescribedBy: _, ariaErrorMessage: S, ariaInvalid: k, ariaLabelledBy: A, assistiveId: j, counter: M, errorId: N, errorText: P, fieldId: F, helperText: I, labelId: L, labelVisible: R } = h;
|
|
82
|
+
return x(a, (t) => {
|
|
83
|
+
let r = h.wire(t), i = o.wire(t);
|
|
84
|
+
e.ref.value?.(t);
|
|
85
|
+
let a = D(e.ref, (e) => {
|
|
86
|
+
e?.(t);
|
|
87
|
+
}), s = n(() => {
|
|
88
|
+
t.style.resize = e["auto-resize"].value || e["no-resize"].value ? "none" : e.resize.value || "vertical", requestAnimationFrame(() => o.recompute());
|
|
91
89
|
});
|
|
92
90
|
return () => {
|
|
93
|
-
a.dispose(),
|
|
91
|
+
a.dispose(), e.ref.value?.(null), r(), i(), s();
|
|
94
92
|
};
|
|
95
|
-
}),
|
|
96
|
-
error:
|
|
97
|
-
size:
|
|
98
|
-
variant:
|
|
99
|
-
} }),
|
|
93
|
+
}), g({ attr: {
|
|
94
|
+
error: f(P),
|
|
95
|
+
size: i.size,
|
|
96
|
+
variant: i.variant
|
|
97
|
+
} }), v`
|
|
100
98
|
<div class="textarea-wrapper" part="wrapper">
|
|
101
99
|
<label class="label" part="label" for="${F}" id="${L}" ?hidden="${() => !R.value}"
|
|
102
|
-
>${
|
|
100
|
+
>${e.label}</label
|
|
103
101
|
>
|
|
104
102
|
<div class="field" part="field">
|
|
105
103
|
<textarea
|
|
106
104
|
part="textarea"
|
|
107
|
-
ref="${
|
|
105
|
+
ref="${a}"
|
|
108
106
|
id="${F}"
|
|
109
|
-
:name="${
|
|
110
|
-
:placeholder="${
|
|
111
|
-
:rows="${
|
|
112
|
-
:maxlength="${
|
|
113
|
-
?disabled="${
|
|
114
|
-
?readonly="${
|
|
115
|
-
?required="${
|
|
116
|
-
:value="${
|
|
117
|
-
:aria-describedby="${
|
|
118
|
-
:aria-errormessage="${
|
|
107
|
+
:name="${e.name}"
|
|
108
|
+
:placeholder="${e.placeholder}"
|
|
109
|
+
:rows="${e.rows}"
|
|
110
|
+
:maxlength="${e.maxlength}"
|
|
111
|
+
?disabled="${e.disabled}"
|
|
112
|
+
?readonly="${e.readonly}"
|
|
113
|
+
?required="${e.required}"
|
|
114
|
+
:value="${E(h.value)}"
|
|
115
|
+
:aria-describedby="${_}"
|
|
116
|
+
:aria-errormessage="${S}"
|
|
119
117
|
:aria-invalid="${k}"
|
|
120
118
|
:aria-labelledby="${A}"></textarea>
|
|
121
119
|
</div>
|
|
122
|
-
<span class="${() =>
|
|
120
|
+
<span class="${() => u(M?.value)}" aria-live="polite" ?hidden="${() => !M}">${() => M?.value.counterText.replace(" / ", "/") ?? ""}</span>
|
|
123
121
|
<div id="${j}" class="helper-text" aria-live="polite" part="helper" ?hidden="${() => !!P.value || !I.value}">
|
|
124
122
|
${() => I.value}
|
|
125
123
|
</div>
|
|
@@ -131,10 +129,10 @@ y(k, {
|
|
|
131
129
|
},
|
|
132
130
|
shadow: { delegatesFocus: !0 },
|
|
133
131
|
styles: [
|
|
134
|
-
...
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
...n,
|
|
133
|
+
e(s),
|
|
134
|
+
t("textarea"),
|
|
135
|
+
h
|
|
138
136
|
]
|
|
139
137
|
});
|
|
140
138
|
//#endregion
|
package/dist/textarea.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.js","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"textarea.js","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/time-picker.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/theme.css.cjs"),n=require("./shared/prop-bundles.cjs"),r=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/theme.css.cjs"),n=require("./shared/prop-bundles.cjs"),r=require("./inputs/shared/form-context.cjs"),i=require("./inputs/time-picker/time-picker.css.cjs");let a=require("@vielzeug/ore"),o=require("@vielzeug/ripple"),s=require("@vielzeug/ore/forms");function c(e){if(!e)return null;let t=/^(\d{1,2}):(\d{2})$/.exec(e.trim());if(!t)return null;let n=Number(t[1]),r=Number(t[2]);return n<0||n>23||r<0||r>59?null:{hours:n,minutes:r}}function l(e,t){return`${String(e).padStart(2,`0`)}:${String(t).padStart(2,`0`)}`}function u(e,t,n){if(n===`12`){let n=e>=12?`PM`:`AM`,r=e%12==0?12:e%12;return`${String(r).padStart(2,`0`)}:${String(t).padStart(2,`0`)} ${n}`}return l(e,t)}function d(e,t,n,r){let i=e*60+t,a=c(n),o=c(r),s=a?a.hours*60+a.minutes:0,l=o?o.hours*60+o.minutes:1439,u=Math.min(Math.max(i,s),l);return{hours:Math.floor(u/60),minutes:u%60}}var f=`ore-time-picker`;(0,a.define)(f,{formAssociated:!0,props:{...n.themableBundle,...n.sizableBundle,...n.disablableBundle,...n.roundableBundle,error:a.prop.string(),fullwidth:a.prop.bool(!1),helper:a.prop.string(),label:a.prop.string(),"label-placement":a.prop.oneOf([`inset`,`outside`],`inset`),max:a.prop.string(),min:a.prop.string(),"minute-step":a.prop.number(5),name:a.prop.string(),placeholder:a.prop.string(),required:a.prop.bool(!1),"time-format":a.prop.oneOf([`12`,`24`],`24`),value:a.prop.string(),variant:a.prop.string()},setup(e){let t=(0,a.getHost)(),n=(0,a.useEmit)(),i=(0,o.signal)(!1),f=(0,o.signal)(c(e.value.value)),p=r.useFormContext(e,(0,a.inject)(r.FORM_CTX)),m=p.disabled;(0,s.useField)({disabled:m,toFormValue:e=>e||null,value:(0,o.computed)(()=>{let e=f.value;return e?l(e.hours,e.minutes):``})});let h=(0,o.computed)(()=>e[`time-format`].value??`24`),g=(0,o.computed)(()=>Math.max(1,Math.min(59,e[`minute-step`].value??5))),_=(0,o.computed)(()=>f.value?u(f.value.hours,f.value.minutes,h.value):e.placeholder.value||``),v=(0,o.computed)(()=>h.value===`12`?Array.from({length:12},(e,t)=>t+1):Array.from({length:24},(e,t)=>t)),y=(0,o.computed)(()=>{let e=g.value,t=[];for(let n=0;n<60;n+=e)t.push(n);return t}),b=`time-picker-${Math.random().toString(36).slice(2,9)}-dropdown`;function x(e){if(h.value===`24`)return e;let t=e%12;return t===0?12:t}function S(e,t){return h.value===`24`?e:t===`AM`?e===12?0:e:e===12?12:e+12}function C(t,n){let r=t*60+n,i=c(e.min.value),a=c(e.max.value);return!!(i&&r<i.hours*60+i.minutes||a&&r>a.hours*60+a.minutes)}let w=(0,o.signal)(f.value?.hours??0),T=(0,o.signal)(f.value?.minutes??0),E=(0,o.signal)((f.value?.hours??0)>=12?`PM`:`AM`),D=e.value.value;(0,a.onMounted)(()=>{let t=setInterval(()=>{let t=e.value.value;if(t!==D){D=t;let e=c(t);f.value=e,e&&(w.value=e.hours,T.value=e.minutes,E.value=e.hours>=12?`PM`:`AM`)}},50);return()=>clearInterval(t)});function O(t,r){let i=d(t,r,e.min.value,e.max.value),a=Math.round(i.minutes/g.value)*g.value,o=Math.min(a,59);f.value={hours:i.hours,minutes:o},n(`change`,{value:l(i.hours,o)})}function k(e){let t=h.value===`12`?S(w.value,E.value):w.value;T.value=e,O(t,e),j()}function A(){if(m.value)return;let e=f.value;e?(w.value=h.value===`12`?x(e.hours):e.hours,T.value=e.minutes,E.value=e.hours>=12?`PM`:`AM`):(w.value=h.value===`12`?12:0,T.value=0,E.value=`AM`),i.value=!0}function j(){i.value=!1}function M(){i.value?j():A()}function N(e){i.value&&(e.composedPath().includes(t)||j())}(0,a.onMounted)(()=>(document.addEventListener(`pointerdown`,N,{capture:!0}),()=>document.removeEventListener(`pointerdown`,N,{capture:!0})));function P(e){m.value||(e.key===`Enter`||e.key===` `?(e.preventDefault(),M()):e.key===`Escape`&&i.value?(e.preventDefault(),j()):(e.key===`ArrowDown`||e.key===`ArrowUp`)&&!i.value&&(e.preventDefault(),A()))}function F(e){e.key===`Escape`&&(e.preventDefault(),j())}function I(e){let t=v.value,n=t.indexOf(w.value);e.key===`ArrowUp`?(e.preventDefault(),w.value=t[(n-1+t.length)%t.length]):e.key===`ArrowDown`&&(e.preventDefault(),w.value=t[(n+1)%t.length])}function L(e){let t=y.value,n=t.indexOf(T.value);e.key===`ArrowUp`?(e.preventDefault(),T.value=t[(n-1+t.length)%t.length]):e.key===`ArrowDown`&&(e.preventDefault(),T.value=t[(n+1)%t.length])}function R(e){(e.key===`ArrowUp`||e.key===`ArrowDown`)&&(e.preventDefault(),E.value=E.value===`AM`?`PM`:`AM`)}return(0,a.bind)({attr:{open:()=>i.value?!0:void 0}}),a.html`
|
|
2
2
|
<!-- Trigger -->
|
|
3
3
|
<ore-input
|
|
4
4
|
class="trigger"
|
|
@@ -7,7 +7,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
7
7
|
role="combobox"
|
|
8
8
|
aria-haspopup="listbox"
|
|
9
9
|
aria-controls="${b}"
|
|
10
|
-
:aria-expanded="${()=>String(
|
|
10
|
+
:aria-expanded="${()=>String(i.value)}"
|
|
11
11
|
:aria-disabled="${()=>m.value?`true`:null}"
|
|
12
12
|
:value="${()=>_.value}"
|
|
13
13
|
:label="${()=>e.label.value??``}"
|
|
@@ -33,14 +33,14 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
33
33
|
id="${b}"
|
|
34
34
|
role="listbox"
|
|
35
35
|
aria-label="${()=>e.label.value?`${e.label.value} — select time`:`Select time`}"
|
|
36
|
-
?data-open="${
|
|
36
|
+
?data-open="${i}"
|
|
37
37
|
@keydown="${F}">
|
|
38
38
|
<div class="cols-row">
|
|
39
39
|
<!-- Hours column -->
|
|
40
40
|
<div class="col" part="column" role="group" aria-label="Hours">
|
|
41
41
|
<div class="col-label" aria-hidden="true">HH</div>
|
|
42
42
|
<div class="col-scroll">
|
|
43
|
-
${()=>v.value.map(e=>{let t=C(h.value===`12`?S(e,E.value):e,T.value);return
|
|
43
|
+
${()=>v.value.map(e=>{let t=C(h.value===`12`?S(e,E.value):e,T.value);return a.html`<div
|
|
44
44
|
class="option"
|
|
45
45
|
part="option"
|
|
46
46
|
role="option"
|
|
@@ -61,7 +61,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
61
61
|
<div class="col" part="column" role="group" aria-label="Minutes">
|
|
62
62
|
<div class="col-label" aria-hidden="true">MM</div>
|
|
63
63
|
<div class="col-scroll">
|
|
64
|
-
${()=>y.value.map(e=>{let t=C(h.value===`12`?S(w.value,E.value):w.value,e);return
|
|
64
|
+
${()=>y.value.map(e=>{let t=C(h.value===`12`?S(w.value,E.value):w.value,e);return a.html`<div
|
|
65
65
|
class="option"
|
|
66
66
|
part="option"
|
|
67
67
|
role="option"
|
|
@@ -77,10 +77,10 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
79
|
<!-- AM/PM column (12h mode only) -->
|
|
80
|
-
${()=>h.value===`12`?
|
|
80
|
+
${()=>h.value===`12`?a.html` <div class="col col-period" part="column" role="group" aria-label="Period">
|
|
81
81
|
<div class="col-label" aria-hidden="true">AM/PM</div>
|
|
82
82
|
<div class="col-scroll">
|
|
83
|
-
${[`AM`,`PM`].map(e=>
|
|
83
|
+
${[`AM`,`PM`].map(e=>a.html`<div
|
|
84
84
|
class="option"
|
|
85
85
|
part="option"
|
|
86
86
|
role="option"
|
|
@@ -91,8 +91,8 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
91
91
|
${e}
|
|
92
92
|
</div>`)}
|
|
93
93
|
</div>
|
|
94
|
-
</div>`:
|
|
94
|
+
</div>`:a.html``}
|
|
95
95
|
</div>
|
|
96
96
|
</div>
|
|
97
|
-
`},shadow:{delegatesFocus:!0},styles:[t.colorThemeMixin,e.reducedMotionMixin,
|
|
97
|
+
`},shadow:{delegatesFocus:!0},styles:[t.colorThemeMixin,e.reducedMotionMixin,i.default]}),exports.TIME_PICKER_TAG=f;
|
|
98
98
|
//# sourceMappingURL=time-picker.cjs.map
|
package/dist/time-picker.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-picker.cjs","names":[],"sources":["../src/inputs/time-picker/time-picker.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onMounted, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed, signal } from '@vielzeug/ripple';\n\nimport type { VisualVariant } from '../../shared';\n\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './time-picker.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type OreTimePickerEvents = {\n change: { value: string | null };\n};\n\nexport type OreTimePickerProps = {\n /** Theme color */\n color?: string;\n /** Disable the picker */\n disabled?: boolean;\n /** Validation error message */\n error?: string;\n /** Expand to container width */\n fullwidth?: boolean;\n /** Helper text below the field */\n helper?: string;\n /** Visible label */\n label?: string;\n /** Label placement */\n 'label-placement'?: 'inset' | 'outside';\n /** Maximum selectable time in HH:MM format (24-hour) */\n max?: string;\n /** Minimum selectable time in HH:MM format (24-hour) */\n min?: string;\n /** Minute step interval (1–59) */\n 'minute-step'?: number;\n /** Form field name */\n name?: string;\n /** Placeholder shown in the trigger */\n placeholder?: string;\n /** Mark field as required */\n required?: boolean;\n /** Border radius */\n rounded?: string;\n /** Component size */\n size?: string;\n /** Display format: '12' for AM/PM, '24' for 24-hour (default: '24') */\n 'time-format'?: '12' | '24';\n /** Selected time in HH:MM format (24-hour) */\n value?: string;\n /** Visual variant */\n variant?: Exclude<VisualVariant, 'text' | 'frost'>;\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Parse \"HH:MM\" → { hours: 0-23, minutes: 0-59 } | null */\nfunction parseTime(val: string | undefined | null): { hours: number; minutes: number } | null {\n if (!val) return null;\n\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(val.trim());\n\n if (!match) return null;\n\n const h = Number(match[1]);\n const m = Number(match[2]);\n\n if (h < 0 || h > 23 || m < 0 || m > 59) return null;\n\n return { hours: h, minutes: m };\n}\n\n/** Format { hours, minutes } → \"HH:MM\" (24-hour, zero-padded) */\nfunction formatTime(h: number, m: number): string {\n return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n}\n\n/** Format for display label (respects 12/24 mode) */\nfunction formatDisplay(h: number, m: number, format: '12' | '24'): string {\n if (format === '12') {\n const period = h >= 12 ? 'PM' : 'AM';\n const displayH = h % 12 === 0 ? 12 : h % 12;\n\n return `${String(displayH).padStart(2, '0')}:${String(m).padStart(2, '0')} ${period}`;\n }\n\n return formatTime(h, m);\n}\n\n/** Clamp value to [min, max] in total minutes */\nfunction clampTime(\n h: number,\n m: number,\n minVal: string | undefined | null,\n maxVal: string | undefined | null,\n): { hours: number; minutes: number } {\n const total = h * 60 + m;\n const minParsed = parseTime(minVal);\n const maxParsed = parseTime(maxVal);\n const minMins = minParsed ? minParsed.hours * 60 + minParsed.minutes : 0;\n const maxMins = maxParsed ? maxParsed.hours * 60 + maxParsed.minutes : 23 * 60 + 59;\n const clamped = Math.min(Math.max(total, minMins), maxMins);\n\n return { hours: Math.floor(clamped / 60), minutes: clamped % 60 };\n}\n\n/**\n * An accessible time picker with a scrollable clock dropdown.\n * Supports 12/24-hour display formats, min/max bounds, minute steps, and form association.\n *\n * @element ore-time-picker\n *\n * @attr {string} value - Selected time in HH:MM (24-hour) format\n * @attr {string} min - Minimum selectable time (HH:MM)\n * @attr {string} max - Maximum selectable time (HH:MM)\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} placeholder - Trigger placeholder\n * @attr {boolean} disabled - Disable the picker\n * @attr {boolean} required - Required field\n * @attr {string} name - Form field name\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} time-format - '12' or '24' (default: '24')\n * @attr {number} minute-step - Minute increment (default: 5)\n *\n * @fires change - Fired when a time is selected. detail: { value: string | null }\n *\n * @slot label - Custom label for the trigger field\n * @slot prefix - Content before the trigger text (e.g. icon)\n * @slot helper - Custom helper text\n * @slot error - Custom error content\n *\n * @cssprop --time-picker-bg - Dropdown background\n * @cssprop --time-picker-border-color - Dropdown border color\n * @cssprop --time-picker-radius - Dropdown border radius\n * @cssprop --time-picker-shadow - Dropdown shadow\n * @cssprop --time-picker-selected-bg - Selected option background\n * @cssprop --time-picker-option-hover-bg - Option hover background\n *\n * @part field - The trigger field\n * @part dropdown - The floating time dropdown\n * @part column - A scrollable column (hours / minutes / period)\n * @part option - An individual time option cell\n *\n * @example\n * ```html\n * <!-- 24-hour clock, 15-minute steps -->\n * <ore-time-picker label=\"Meeting time\" value=\"09:30\" minute-step=\"15\" color=\"primary\"></ore-time-picker>\n *\n * <!-- 12-hour AM/PM format -->\n * <ore-time-picker label=\"Appointment\" time-format=\"12\" minute-step=\"30\"></ore-time-picker>\n *\n * <!-- Outside label, bordered variant -->\n * <ore-time-picker label=\"Start time\" label-placement=\"outside\" variant=\"bordered\"></ore-time-picker>\n * ```\n */\nexport const TIME_PICKER_TAG = 'ore-time-picker' as const;\n\ndefine<OreTimePickerProps>(TIME_PICKER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n max: prop.string(),\n min: prop.string(),\n 'minute-step': prop.number(5),\n name: prop.string(),\n placeholder: prop.string(),\n required: prop.bool(false),\n 'time-format': prop.oneOf(['12', '24'] as const, '24'),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTimePickerEvents>();\n\n // ── Signals ──────────────────────────────────────────────────────────────\n\n const isOpen = signal(false);\n const selectedTime = signal<{ hours: number; minutes: number } | null>(parseTime(props.value.value));\n\n // ── Form context ─────────────────────────────────────────────────────────\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n const isDisabled = fCtxProps.disabled;\n\n // ── Form value ────────────────────────────────────────────────────────────\n\n useField<string>({\n disabled: isDisabled,\n toFormValue: (v) => v || null,\n value: computed(() => {\n const t = selectedTime.value;\n\n return t ? formatTime(t.hours, t.minutes) : '';\n }),\n });\n\n // ── Computed helpers ──────────────────────────────────────────────────────\n\n const fmt = computed(() => props['time-format'].value ?? '24');\n const minuteStep = computed(() => Math.max(1, Math.min(59, props['minute-step'].value ?? 5)));\n\n const triggerText = computed(() => {\n if (!selectedTime.value) return props.placeholder.value || '';\n\n return formatDisplay(selectedTime.value.hours, selectedTime.value.minutes, fmt.value as '12' | '24');\n });\n\n /** All hour options for the column (0-23 in 24h; 1-12 in 12h) */\n const hourOptions = computed<number[]>(() => {\n if (fmt.value === '12') {\n return Array.from({ length: 12 }, (_, i) => i + 1);\n }\n\n return Array.from({ length: 24 }, (_, i) => i);\n });\n\n /** All minute options for the column, stepped */\n const minuteOptions = computed<number[]>(() => {\n const step = minuteStep.value;\n const opts: number[] = [];\n\n for (let m = 0; m < 60; m += step) opts.push(m);\n\n return opts;\n });\n\n const dialogId = `time-picker-${Math.random().toString(36).slice(2, 9)}-dropdown`;\n\n /** Convert 24h hour to display hour in current format */\n function toDisplayHour(h24: number): number {\n if (fmt.value === '24') return h24;\n\n const h12 = h24 % 12;\n\n return h12 === 0 ? 12 : h12;\n }\n\n /** Convert display hour + period → 24h hour */\n function to24Hour(displayHour: number, period: 'AM' | 'PM'): number {\n if (fmt.value === '24') return displayHour;\n\n if (period === 'AM') return displayHour === 12 ? 0 : displayHour;\n\n return displayHour === 12 ? 12 : displayHour + 12;\n }\n\n /** Whether a given HH:MM is out of [min, max] */\n function isTimeDisabled(h: number, m: number): boolean {\n const total = h * 60 + m;\n const minParsed = parseTime(props.min.value);\n const maxParsed = parseTime(props.max.value);\n\n if (minParsed && total < minParsed.hours * 60 + minParsed.minutes) return true;\n\n if (maxParsed && total > maxParsed.hours * 60 + maxParsed.minutes) return true;\n\n return false;\n }\n\n // ── State for focused/pending selection ───────────────────────────────────\n\n /** Pending hour (24h) while user is navigating columns */\n const pendingHour = signal<number>(selectedTime.value?.hours ?? 0);\n /** Pending minute */\n const pendingMinute = signal<number>(selectedTime.value?.minutes ?? 0);\n /** Pending period (12h mode) */\n const pendingPeriod = signal<'AM' | 'PM'>((selectedTime.value?.hours ?? 0) >= 12 ? 'PM' : 'AM');\n\n // ── Sync value prop externally ────────────────────────────────────────────\n\n let lastValueProp = props.value.value;\n\n onMounted(() => {\n const interval = setInterval(() => {\n const current = props.value.value;\n\n if (current !== lastValueProp) {\n lastValueProp = current;\n\n const parsed = parseTime(current);\n\n selectedTime.value = parsed;\n\n if (parsed) {\n pendingHour.value = parsed.hours;\n pendingMinute.value = parsed.minutes;\n pendingPeriod.value = parsed.hours >= 12 ? 'PM' : 'AM';\n }\n }\n }, 50);\n\n return () => clearInterval(interval);\n });\n\n // ── Commit ────────────────────────────────────────────────────────────────\n\n function commit(h24: number, m: number): void {\n const clamped = clampTime(h24, m, props.min.value, props.max.value);\n const snapped = Math.round(clamped.minutes / minuteStep.value) * minuteStep.value;\n const snappedMin = Math.min(snapped, 59);\n\n selectedTime.value = { hours: clamped.hours, minutes: snappedMin };\n emit('change', { value: formatTime(clamped.hours, snappedMin) });\n }\n\n function commitFromMinute(m: number): void {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n\n pendingMinute.value = m;\n commit(h24, m);\n closePicker();\n }\n\n // ── Open / close ──────────────────────────────────────────────────────────\n\n function openPicker(): void {\n if (isDisabled.value) return;\n\n const current = selectedTime.value;\n\n if (current) {\n pendingHour.value = fmt.value === '12' ? toDisplayHour(current.hours) : current.hours;\n pendingMinute.value = current.minutes;\n pendingPeriod.value = current.hours >= 12 ? 'PM' : 'AM';\n } else {\n pendingHour.value = fmt.value === '12' ? 12 : 0;\n pendingMinute.value = 0;\n pendingPeriod.value = 'AM';\n }\n\n isOpen.value = true;\n }\n\n function closePicker(): void {\n isOpen.value = false;\n }\n\n function handleTriggerClick(): void {\n if (isOpen.value) closePicker();\n else openPicker();\n }\n\n // ── Outside click ─────────────────────────────────────────────────────────\n\n function handleOutsideClick(e: PointerEvent | MouseEvent): void {\n if (!isOpen.value) return;\n\n if (!e.composedPath().includes(el)) closePicker();\n }\n\n onMounted(() => {\n document.addEventListener('pointerdown', handleOutsideClick, { capture: true });\n\n return () => document.removeEventListener('pointerdown', handleOutsideClick, { capture: true });\n });\n\n // ── Keyboard ──────────────────────────────────────────────────────────────\n\n function handleTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return;\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleTriggerClick();\n } else if (e.key === 'Escape' && isOpen.value) {\n e.preventDefault();\n closePicker();\n } else if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !isOpen.value) {\n e.preventDefault();\n openPicker();\n }\n }\n\n function handleDropdownKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n closePicker();\n }\n }\n\n function handleHourKeydown(e: KeyboardEvent): void {\n const opts = hourOptions.value;\n const idx = opts.indexOf(pendingHour.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingHour.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingHour.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handleMinuteKeydown(e: KeyboardEvent): void {\n const opts = minuteOptions.value;\n const idx = opts.indexOf(pendingMinute.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingMinute.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingMinute.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handlePeriodKeydown(e: KeyboardEvent): void {\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n pendingPeriod.value = pendingPeriod.value === 'AM' ? 'PM' : 'AM';\n }\n }\n\n // ── Host bindings ─────────────────────────────────────────────────────────\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n // ── ore-input prop helpers ────────────────────────────────────────────────\n\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => fCtxProps.size?.value ?? undefined;\n const inputVariant = () => fCtxProps.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n\n return html`\n <!-- Trigger -->\n <ore-input\n class=\"trigger\"\n readonly\n tabindex=\"0\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${dialogId}\"\n :aria-expanded=\"${() => String(isOpen.value)}\"\n :aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n :value=\"${inputValue}\"\n :label=\"${inputLabel}\"\n :placeholder=\"${inputPlaceholder}\"\n :label-placement=\"${inputLabelPlacement}\"\n :color=\"${inputColor}\"\n :size=\"${inputSize}\"\n :variant=\"${inputVariant}\"\n :rounded=\"${inputRounded}\"\n :helper=\"${inputHelper}\"\n :error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n @click=\"${handleTriggerClick}\"\n @keydown=\"${handleTriggerKeydown}\">\n <ore-icon slot=\"suffix\" name=\"clock\" size=\"16\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </ore-input>\n\n <!-- Dropdown -->\n <div\n class=\"dropdown\"\n id=\"${dialogId}\"\n role=\"listbox\"\n aria-label=\"${() => (props.label.value ? `${props.label.value} — select time` : 'Select time')}\"\n ?data-open=\"${isOpen}\"\n @keydown=\"${handleDropdownKeydown}\">\n <div class=\"cols-row\">\n <!-- Hours column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Hours\">\n <div class=\"col-label\" aria-hidden=\"true\">HH</div>\n <div class=\"col-scroll\">\n ${() =>\n hourOptions.value.map((h) => {\n const h24 = fmt.value === '12' ? to24Hour(h, pendingPeriod.value) : h;\n const disabled = isTimeDisabled(h24, pendingMinute.value);\n\n return html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingHour.value === h && !disabled ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingHour.value === h)}\"\n :aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) pendingHour.value = h;\n }}\"\n @keydown=\"${handleHourKeydown}\">\n ${String(h).padStart(2, '0')}\n </div>`;\n })}\n </div>\n </div>\n\n <div class=\"col-sep\" aria-hidden=\"true\">:</div>\n\n <!-- Minutes column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Minutes\">\n <div class=\"col-label\" aria-hidden=\"true\">MM</div>\n <div class=\"col-scroll\">\n ${() =>\n minuteOptions.value.map((m) => {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n const disabled = isTimeDisabled(h24, m);\n\n return html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingMinute.value === m && !disabled ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingMinute.value === m)}\"\n :aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) commitFromMinute(m);\n }}\"\n @keydown=\"${handleMinuteKeydown}\">\n ${String(m).padStart(2, '0')}\n </div>`;\n })}\n </div>\n </div>\n\n <!-- AM/PM column (12h mode only) -->\n ${() =>\n fmt.value === '12'\n ? html` <div class=\"col col-period\" part=\"column\" role=\"group\" aria-label=\"Period\">\n <div class=\"col-label\" aria-hidden=\"true\">AM/PM</div>\n <div class=\"col-scroll\">\n ${(['AM', 'PM'] as const).map(\n (p) =>\n html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingPeriod.value === p ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingPeriod.value === p)}\"\n @click=\"${() => {\n pendingPeriod.value = p;\n }}\"\n @keydown=\"${handlePeriodKeydown}\">\n ${p}\n </div>`,\n )}\n </div>\n </div>`\n : html``}\n </div>\n </div>\n `;\n },\n\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"time-picker.cjs","names":[],"sources":["../src/inputs/time-picker/time-picker.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onMounted, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed, signal } from '@vielzeug/ripple';\n\nimport type { VisualVariant } from '../../shared';\n\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './time-picker.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type OreTimePickerEvents = {\n change: { value: string | null };\n};\n\nexport type OreTimePickerProps = {\n /** Theme color */\n color?: string;\n /** Disable the picker */\n disabled?: boolean;\n /** Validation error message */\n error?: string;\n /** Expand to container width */\n fullwidth?: boolean;\n /** Helper text below the field */\n helper?: string;\n /** Visible label */\n label?: string;\n /** Label placement */\n 'label-placement'?: 'inset' | 'outside';\n /** Maximum selectable time in HH:MM format (24-hour) */\n max?: string;\n /** Minimum selectable time in HH:MM format (24-hour) */\n min?: string;\n /** Minute step interval (1–59) */\n 'minute-step'?: number;\n /** Form field name */\n name?: string;\n /** Placeholder shown in the trigger */\n placeholder?: string;\n /** Mark field as required */\n required?: boolean;\n /** Border radius */\n rounded?: string;\n /** Component size */\n size?: string;\n /** Display format: '12' for AM/PM, '24' for 24-hour (default: '24') */\n 'time-format'?: '12' | '24';\n /** Selected time in HH:MM format (24-hour) */\n value?: string;\n /** Visual variant */\n variant?: Exclude<VisualVariant, 'text' | 'frost'>;\n};\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Parse \"HH:MM\" → { hours: 0-23, minutes: 0-59 } | null */\nfunction parseTime(val: string | undefined | null): { hours: number; minutes: number } | null {\n if (!val) return null;\n\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(val.trim());\n\n if (!match) return null;\n\n const h = Number(match[1]);\n const m = Number(match[2]);\n\n if (h < 0 || h > 23 || m < 0 || m > 59) return null;\n\n return { hours: h, minutes: m };\n}\n\n/** Format { hours, minutes } → \"HH:MM\" (24-hour, zero-padded) */\nfunction formatTime(h: number, m: number): string {\n return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n}\n\n/** Format for display label (respects 12/24 mode) */\nfunction formatDisplay(h: number, m: number, format: '12' | '24'): string {\n if (format === '12') {\n const period = h >= 12 ? 'PM' : 'AM';\n const displayH = h % 12 === 0 ? 12 : h % 12;\n\n return `${String(displayH).padStart(2, '0')}:${String(m).padStart(2, '0')} ${period}`;\n }\n\n return formatTime(h, m);\n}\n\n/** Clamp value to [min, max] in total minutes */\nfunction clampTime(\n h: number,\n m: number,\n minVal: string | undefined | null,\n maxVal: string | undefined | null,\n): { hours: number; minutes: number } {\n const total = h * 60 + m;\n const minParsed = parseTime(minVal);\n const maxParsed = parseTime(maxVal);\n const minMins = minParsed ? minParsed.hours * 60 + minParsed.minutes : 0;\n const maxMins = maxParsed ? maxParsed.hours * 60 + maxParsed.minutes : 23 * 60 + 59;\n const clamped = Math.min(Math.max(total, minMins), maxMins);\n\n return { hours: Math.floor(clamped / 60), minutes: clamped % 60 };\n}\n\n/**\n * An accessible time picker with a scrollable clock dropdown.\n * Supports 12/24-hour display formats, min/max bounds, minute steps, and form association.\n *\n * @element ore-time-picker\n *\n * @attr {string} value - Selected time in HH:MM (24-hour) format\n * @attr {string} min - Minimum selectable time (HH:MM)\n * @attr {string} max - Maximum selectable time (HH:MM)\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} placeholder - Trigger placeholder\n * @attr {boolean} disabled - Disable the picker\n * @attr {boolean} required - Required field\n * @attr {string} name - Form field name\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} time-format - '12' or '24' (default: '24')\n * @attr {number} minute-step - Minute increment (default: 5)\n *\n * @fires change - Fired when a time is selected. detail: { value: string | null }\n *\n * @slot label - Custom label for the trigger field\n * @slot prefix - Content before the trigger text (e.g. icon)\n * @slot helper - Custom helper text\n * @slot error - Custom error content\n *\n * @cssprop --time-picker-bg - Dropdown background\n * @cssprop --time-picker-border-color - Dropdown border color\n * @cssprop --time-picker-radius - Dropdown border radius\n * @cssprop --time-picker-shadow - Dropdown shadow\n * @cssprop --time-picker-selected-bg - Selected option background\n * @cssprop --time-picker-option-hover-bg - Option hover background\n *\n * @part field - The trigger field\n * @part dropdown - The floating time dropdown\n * @part column - A scrollable column (hours / minutes / period)\n * @part option - An individual time option cell\n *\n * @example\n * ```html\n * <!-- 24-hour clock, 15-minute steps -->\n * <ore-time-picker label=\"Meeting time\" value=\"09:30\" minute-step=\"15\" color=\"primary\"></ore-time-picker>\n *\n * <!-- 12-hour AM/PM format -->\n * <ore-time-picker label=\"Appointment\" time-format=\"12\" minute-step=\"30\"></ore-time-picker>\n *\n * <!-- Outside label, bordered variant -->\n * <ore-time-picker label=\"Start time\" label-placement=\"outside\" variant=\"bordered\"></ore-time-picker>\n * ```\n */\nexport const TIME_PICKER_TAG = 'ore-time-picker' as const;\n\ndefine<OreTimePickerProps>(TIME_PICKER_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n max: prop.string(),\n min: prop.string(),\n 'minute-step': prop.number(5),\n name: prop.string(),\n placeholder: prop.string(),\n required: prop.bool(false),\n 'time-format': prop.oneOf(['12', '24'] as const, '24'),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreTimePickerEvents>();\n\n // ── Signals ──────────────────────────────────────────────────────────────\n\n const isOpen = signal(false);\n const selectedTime = signal<{ hours: number; minutes: number } | null>(parseTime(props.value.value));\n\n // ── Form context ─────────────────────────────────────────────────────────\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n const isDisabled = fCtxProps.disabled;\n\n // ── Form value ────────────────────────────────────────────────────────────\n\n useField<string>({\n disabled: isDisabled,\n toFormValue: (v) => v || null,\n value: computed(() => {\n const t = selectedTime.value;\n\n return t ? formatTime(t.hours, t.minutes) : '';\n }),\n });\n\n // ── Computed helpers ──────────────────────────────────────────────────────\n\n const fmt = computed(() => props['time-format'].value ?? '24');\n const minuteStep = computed(() => Math.max(1, Math.min(59, props['minute-step'].value ?? 5)));\n\n const triggerText = computed(() => {\n if (!selectedTime.value) return props.placeholder.value || '';\n\n return formatDisplay(selectedTime.value.hours, selectedTime.value.minutes, fmt.value as '12' | '24');\n });\n\n /** All hour options for the column (0-23 in 24h; 1-12 in 12h) */\n const hourOptions = computed<number[]>(() => {\n if (fmt.value === '12') {\n return Array.from({ length: 12 }, (_, i) => i + 1);\n }\n\n return Array.from({ length: 24 }, (_, i) => i);\n });\n\n /** All minute options for the column, stepped */\n const minuteOptions = computed<number[]>(() => {\n const step = minuteStep.value;\n const opts: number[] = [];\n\n for (let m = 0; m < 60; m += step) opts.push(m);\n\n return opts;\n });\n\n const dialogId = `time-picker-${Math.random().toString(36).slice(2, 9)}-dropdown`;\n\n /** Convert 24h hour to display hour in current format */\n function toDisplayHour(h24: number): number {\n if (fmt.value === '24') return h24;\n\n const h12 = h24 % 12;\n\n return h12 === 0 ? 12 : h12;\n }\n\n /** Convert display hour + period → 24h hour */\n function to24Hour(displayHour: number, period: 'AM' | 'PM'): number {\n if (fmt.value === '24') return displayHour;\n\n if (period === 'AM') return displayHour === 12 ? 0 : displayHour;\n\n return displayHour === 12 ? 12 : displayHour + 12;\n }\n\n /** Whether a given HH:MM is out of [min, max] */\n function isTimeDisabled(h: number, m: number): boolean {\n const total = h * 60 + m;\n const minParsed = parseTime(props.min.value);\n const maxParsed = parseTime(props.max.value);\n\n if (minParsed && total < minParsed.hours * 60 + minParsed.minutes) return true;\n\n if (maxParsed && total > maxParsed.hours * 60 + maxParsed.minutes) return true;\n\n return false;\n }\n\n // ── State for focused/pending selection ───────────────────────────────────\n\n /** Pending hour (24h) while user is navigating columns */\n const pendingHour = signal<number>(selectedTime.value?.hours ?? 0);\n /** Pending minute */\n const pendingMinute = signal<number>(selectedTime.value?.minutes ?? 0);\n /** Pending period (12h mode) */\n const pendingPeriod = signal<'AM' | 'PM'>((selectedTime.value?.hours ?? 0) >= 12 ? 'PM' : 'AM');\n\n // ── Sync value prop externally ────────────────────────────────────────────\n\n let lastValueProp = props.value.value;\n\n onMounted(() => {\n const interval = setInterval(() => {\n const current = props.value.value;\n\n if (current !== lastValueProp) {\n lastValueProp = current;\n\n const parsed = parseTime(current);\n\n selectedTime.value = parsed;\n\n if (parsed) {\n pendingHour.value = parsed.hours;\n pendingMinute.value = parsed.minutes;\n pendingPeriod.value = parsed.hours >= 12 ? 'PM' : 'AM';\n }\n }\n }, 50);\n\n return () => clearInterval(interval);\n });\n\n // ── Commit ────────────────────────────────────────────────────────────────\n\n function commit(h24: number, m: number): void {\n const clamped = clampTime(h24, m, props.min.value, props.max.value);\n const snapped = Math.round(clamped.minutes / minuteStep.value) * minuteStep.value;\n const snappedMin = Math.min(snapped, 59);\n\n selectedTime.value = { hours: clamped.hours, minutes: snappedMin };\n emit('change', { value: formatTime(clamped.hours, snappedMin) });\n }\n\n function commitFromMinute(m: number): void {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n\n pendingMinute.value = m;\n commit(h24, m);\n closePicker();\n }\n\n // ── Open / close ──────────────────────────────────────────────────────────\n\n function openPicker(): void {\n if (isDisabled.value) return;\n\n const current = selectedTime.value;\n\n if (current) {\n pendingHour.value = fmt.value === '12' ? toDisplayHour(current.hours) : current.hours;\n pendingMinute.value = current.minutes;\n pendingPeriod.value = current.hours >= 12 ? 'PM' : 'AM';\n } else {\n pendingHour.value = fmt.value === '12' ? 12 : 0;\n pendingMinute.value = 0;\n pendingPeriod.value = 'AM';\n }\n\n isOpen.value = true;\n }\n\n function closePicker(): void {\n isOpen.value = false;\n }\n\n function handleTriggerClick(): void {\n if (isOpen.value) closePicker();\n else openPicker();\n }\n\n // ── Outside click ─────────────────────────────────────────────────────────\n\n function handleOutsideClick(e: PointerEvent | MouseEvent): void {\n if (!isOpen.value) return;\n\n if (!e.composedPath().includes(el)) closePicker();\n }\n\n onMounted(() => {\n document.addEventListener('pointerdown', handleOutsideClick, { capture: true });\n\n return () => document.removeEventListener('pointerdown', handleOutsideClick, { capture: true });\n });\n\n // ── Keyboard ──────────────────────────────────────────────────────────────\n\n function handleTriggerKeydown(e: KeyboardEvent): void {\n if (isDisabled.value) return;\n\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleTriggerClick();\n } else if (e.key === 'Escape' && isOpen.value) {\n e.preventDefault();\n closePicker();\n } else if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !isOpen.value) {\n e.preventDefault();\n openPicker();\n }\n }\n\n function handleDropdownKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n closePicker();\n }\n }\n\n function handleHourKeydown(e: KeyboardEvent): void {\n const opts = hourOptions.value;\n const idx = opts.indexOf(pendingHour.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingHour.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingHour.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handleMinuteKeydown(e: KeyboardEvent): void {\n const opts = minuteOptions.value;\n const idx = opts.indexOf(pendingMinute.value);\n\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n pendingMinute.value = opts[(idx - 1 + opts.length) % opts.length];\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n pendingMinute.value = opts[(idx + 1) % opts.length];\n }\n }\n\n function handlePeriodKeydown(e: KeyboardEvent): void {\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n pendingPeriod.value = pendingPeriod.value === 'AM' ? 'PM' : 'AM';\n }\n }\n\n // ── Host bindings ─────────────────────────────────────────────────────────\n\n bind({\n attr: {\n open: () => (isOpen.value ? true : undefined),\n },\n });\n\n // ── ore-input prop helpers ────────────────────────────────────────────────\n\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => fCtxProps.size?.value ?? undefined;\n const inputVariant = () => fCtxProps.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n\n return html`\n <!-- Trigger -->\n <ore-input\n class=\"trigger\"\n readonly\n tabindex=\"0\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${dialogId}\"\n :aria-expanded=\"${() => String(isOpen.value)}\"\n :aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n :value=\"${inputValue}\"\n :label=\"${inputLabel}\"\n :placeholder=\"${inputPlaceholder}\"\n :label-placement=\"${inputLabelPlacement}\"\n :color=\"${inputColor}\"\n :size=\"${inputSize}\"\n :variant=\"${inputVariant}\"\n :rounded=\"${inputRounded}\"\n :helper=\"${inputHelper}\"\n :error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n @click=\"${handleTriggerClick}\"\n @keydown=\"${handleTriggerKeydown}\">\n <ore-icon slot=\"suffix\" name=\"clock\" size=\"16\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </ore-input>\n\n <!-- Dropdown -->\n <div\n class=\"dropdown\"\n id=\"${dialogId}\"\n role=\"listbox\"\n aria-label=\"${() => (props.label.value ? `${props.label.value} — select time` : 'Select time')}\"\n ?data-open=\"${isOpen}\"\n @keydown=\"${handleDropdownKeydown}\">\n <div class=\"cols-row\">\n <!-- Hours column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Hours\">\n <div class=\"col-label\" aria-hidden=\"true\">HH</div>\n <div class=\"col-scroll\">\n ${() =>\n hourOptions.value.map((h) => {\n const h24 = fmt.value === '12' ? to24Hour(h, pendingPeriod.value) : h;\n const disabled = isTimeDisabled(h24, pendingMinute.value);\n\n return html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingHour.value === h && !disabled ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingHour.value === h)}\"\n :aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) pendingHour.value = h;\n }}\"\n @keydown=\"${handleHourKeydown}\">\n ${String(h).padStart(2, '0')}\n </div>`;\n })}\n </div>\n </div>\n\n <div class=\"col-sep\" aria-hidden=\"true\">:</div>\n\n <!-- Minutes column -->\n <div class=\"col\" part=\"column\" role=\"group\" aria-label=\"Minutes\">\n <div class=\"col-label\" aria-hidden=\"true\">MM</div>\n <div class=\"col-scroll\">\n ${() =>\n minuteOptions.value.map((m) => {\n const h24 = fmt.value === '12' ? to24Hour(pendingHour.value, pendingPeriod.value) : pendingHour.value;\n const disabled = isTimeDisabled(h24, m);\n\n return html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingMinute.value === m && !disabled ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingMinute.value === m)}\"\n :aria-disabled=\"${() => String(disabled)}\"\n ?data-disabled=\"${() => disabled}\"\n @click=\"${() => {\n if (!disabled) commitFromMinute(m);\n }}\"\n @keydown=\"${handleMinuteKeydown}\">\n ${String(m).padStart(2, '0')}\n </div>`;\n })}\n </div>\n </div>\n\n <!-- AM/PM column (12h mode only) -->\n ${() =>\n fmt.value === '12'\n ? html` <div class=\"col col-period\" part=\"column\" role=\"group\" aria-label=\"Period\">\n <div class=\"col-label\" aria-hidden=\"true\">AM/PM</div>\n <div class=\"col-scroll\">\n ${(['AM', 'PM'] as const).map(\n (p) =>\n html`<div\n class=\"option\"\n part=\"option\"\n role=\"option\"\n tabindex=\"${() => (pendingPeriod.value === p ? '0' : '-1')}\"\n :aria-selected=\"${() => String(pendingPeriod.value === p)}\"\n @click=\"${() => {\n pendingPeriod.value = p;\n }}\"\n @keydown=\"${handlePeriodKeydown}\">\n ${p}\n </div>`,\n )}\n </div>\n </div>`\n : html``}\n </div>\n </div>\n `;\n },\n\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|