@mono-lit/helper 0.0.3 → 0.0.5
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/control-tooltip-7vegYmq3.js +678 -0
- package/dist/{tooltip-FCMRUSLi.js → control-tooltip-C3rA_nSH.js} +12 -119
- package/dist/{data-source-controller-Cf_sMxEh.js → data-source-controller-C6C4ej_M.js} +1 -1
- package/dist/{data-source-options-Bk-UoaZV.js → data-source-options-Cz9fVLK8.js} +3 -2
- package/dist/{data-source-read-Cuqw3LcP.js → data-source-read-i6CYSUEl.js} +2 -1
- package/dist/{excel-DtRpxmxs.js → excel-BPdDt06G.js} +1 -1
- package/dist/{export-B91P6J1y.js → export-DgomqI_3.js} +2 -2
- package/dist/export.js +2 -2
- package/dist/{field-icons-FmyBWa0g.js → field-icons-t8rXRCKs.js} +50 -1
- package/dist/field-tooltip-FyMi9vNM.js +266 -0
- package/dist/field-tooltip.css_raw-DEctHXKm.js +270 -0
- package/dist/field-validation-CHHQi1zl.js +170 -0
- package/dist/field-validation.css_raw-B3Iu33rS.js +178 -0
- package/dist/index.js +4 -3
- package/dist/{index.node-dvrNviMx.js → index.node-DzCuvHLg.js} +1 -1
- package/dist/index.node.js +4 -3
- package/dist/input-format-GrRCJBqV.js +251 -0
- package/dist/input.css_raw-CQu2fAsU.js +5 -0
- package/dist/{mono-data-grid-BHlo1IgR.js → mono-data-grid-KDpu4d4_.js} +5 -4
- package/dist/{mono-form-controller-IYn0MuUQ.js → mono-form-controller-BkMR8g14.js} +18 -3
- package/dist/{reactive-DFCdZbH9.js → normalize-error-BmLbgANw.js} +1 -48
- package/dist/nuxt.js +36 -8
- package/dist/{popup-portal-HWhkd7oy.js → popup-portal-C8ttHDRX.js} +17 -1
- package/dist/{popup-stack-De05Y-mJ.js → popup-stack-C64WCuXq.js} +21 -1
- package/dist/src/components/accordion/accordion-core.d.ts +4 -1
- package/dist/src/components/accordion/mono-accordion.shadow.d.ts +4 -1
- package/dist/src/components/date/date-core.d.ts +3 -0
- package/dist/src/components/date/date-types.d.ts +36 -1
- package/dist/src/components/drawer/drawer-core.d.ts +1 -0
- package/dist/src/components/dropdown-table/dropdown-table-core.d.ts +3 -0
- package/dist/src/components/dropdown-table/dropdown-table-types.d.ts +31 -0
- package/dist/src/components/form/form-types.d.ts +23 -5
- package/dist/src/components/input/index.d.ts +2 -2
- package/dist/src/components/input/index.shadow.d.ts +2 -2
- package/dist/src/components/input/input-core.d.ts +23 -3
- package/dist/src/components/input/input-types.d.ts +84 -9
- package/dist/src/components/input/mono-input.d.ts +3 -2
- package/dist/src/components/input/mono-input.shadow.d.ts +3 -2
- package/dist/src/components/modal/modal-core.d.ts +1 -0
- package/dist/src/components/otp/index.d.ts +5 -0
- package/dist/src/components/otp/index.shadow.d.ts +6 -0
- package/dist/src/components/otp/mono-otp.d.ts +32 -0
- package/dist/src/components/otp/mono-otp.shadow.d.ts +30 -0
- package/dist/src/components/otp/otp-core.d.ts +91 -0
- package/dist/src/components/otp/otp-types.d.ts +161 -0
- package/dist/src/components/otp/otp-utils.d.ts +41 -0
- package/dist/src/components/select/select-core.d.ts +3 -0
- package/dist/src/components/select/select-types.d.ts +31 -0
- package/dist/src/components/slider/index.d.ts +5 -0
- package/dist/src/components/slider/index.shadow.d.ts +6 -0
- package/dist/src/components/slider/mono-slider.d.ts +33 -0
- package/dist/src/components/slider/mono-slider.shadow.d.ts +29 -0
- package/dist/src/components/slider/slider-core.d.ts +77 -0
- package/dist/src/components/slider/slider-math.d.ts +102 -0
- package/dist/src/components/slider/slider-types.d.ts +188 -0
- package/dist/src/components/tag-input/tag-input-core.d.ts +3 -0
- package/dist/src/components/tag-input/tag-input-types.d.ts +31 -0
- package/dist/src/components/textarea/textarea-core.d.ts +3 -0
- package/dist/src/components/textarea/textarea-types.d.ts +31 -0
- package/dist/src/composables/field-bubble.d.ts +34 -0
- package/dist/src/composables/field-icons.d.ts +10 -0
- package/dist/src/composables/field-tooltip.d.ts +52 -0
- package/dist/src/composables/field-validation.d.ts +58 -0
- package/dist/src/composables/popup-portal.d.ts +12 -0
- package/dist/src/composables/popup-stack.d.ts +21 -0
- package/dist/src/entries/index.d.ts +1 -1
- package/dist/src/entries/index.node.d.ts +1 -1
- package/dist/src/nuxt/index.d.ts +2 -2
- package/dist/src/nuxt/ssr-lit/index.d.ts +4 -0
- package/dist/ssr-lit/runtime/components/LitWrapper.vue +11 -0
- package/dist/ssr-lit/runtime/components/LitWrapperClient.vue +11 -0
- package/dist/ssr-lit/runtime/components/LitWrapperServer.vue +38 -0
- package/dist/ssr-lit/runtime/plugins/antiFouc.server.js +25 -0
- package/dist/ssr-lit/runtime/plugins/hydrateSupport.client.js +4 -0
- package/dist/ssr-lit/runtime/plugins/polyfill.client.js +9 -0
- package/dist/ssr-lit/runtime/utils/customElements.js +11 -0
- package/dist/ssr-lit/runtime/utils/litElementRenderer.js +28 -0
- package/dist/{styles-D-ergrfR.js → styles-BODNpZV1.js} +1 -1
- package/dist/tooltip-Drekihf5.js +102 -0
- package/dist/tooltip-loader-3U9FTOXj.js +20 -0
- package/dist/tooltip-loader-Dcp9AUZn.js +23 -0
- package/dist/tooltip.js +3 -1
- package/dist/ui/accordion.js +16 -3
- package/dist/ui/alert.js +2 -2
- package/dist/ui/breadcrumb.js +1 -1
- package/dist/ui/button-dropdown.js +1 -1
- package/dist/ui/chart.js +1 -1
- package/dist/ui/date.js +66 -5
- package/dist/ui/drawer.js +11 -2
- package/dist/ui/dropdown-table.js +60 -5
- package/dist/ui/dropdown.js +3 -2
- package/dist/ui/file-upload.js +1 -1
- package/dist/ui/form.js +1 -1
- package/dist/ui/index.css +8857 -3974
- package/dist/ui/input.js +362 -260
- package/dist/ui/menu.js +1 -1
- package/dist/ui/modal.js +11 -2
- package/dist/ui/otp.js +835 -0
- package/dist/ui/select.js +67 -3
- package/dist/ui/shadow/accordion.js +31 -4
- package/dist/ui/shadow/alert.js +1 -1
- package/dist/ui/shadow/button-dropdown.js +1 -1
- package/dist/ui/shadow/chart.js +4 -4
- package/dist/ui/shadow/date.js +67 -6
- package/dist/ui/shadow/drawer.js +11 -2
- package/dist/ui/shadow/dropdown-table.js +62 -7
- package/dist/ui/shadow/dropdown.js +3 -2
- package/dist/ui/shadow/input.js +367 -261
- package/dist/ui/shadow/modal.js +11 -2
- package/dist/ui/shadow/otp.js +842 -0
- package/dist/ui/shadow/select.js +71 -6
- package/dist/ui/shadow/sidebar.js +2 -2
- package/dist/ui/shadow/slider.js +1082 -0
- package/dist/ui/shadow/table.js +6 -6
- package/dist/ui/shadow/tag-input.js +72 -7
- package/dist/ui/shadow/textarea.js +63 -2
- package/dist/ui/sidebar.js +2 -2
- package/dist/ui/slider.js +1079 -0
- package/dist/ui/table.js +2 -2
- package/dist/ui/tag-input.js +67 -3
- package/dist/ui/textarea.js +62 -1
- package/dist/ui/theme/luma.css +505 -466
- package/dist/ui/theme/lyra.css +505 -466
- package/dist/ui/theme/maia.css +501 -466
- package/dist/ui/theme/mira.css +505 -466
- package/dist/ui/theme/nova.css +503 -466
- package/dist/ui/theme/one.css +742 -699
- package/dist/ui/theme/rhea.css +506 -466
- package/dist/ui/theme/sera.css +507 -466
- package/dist/ui/theme/vega.css +495 -466
- package/dist/{vite-DcyRlTTf.js → vite-CivRCJaf.js} +2 -0
- package/dist/vite.js +1 -1
- package/dist/vue.d.ts +10 -0
- package/package.json +30 -15
- package/dist/input.css_raw-CGESifay.js +0 -5
package/dist/ui/shadow/input.js
CHANGED
|
@@ -1,264 +1,17 @@
|
|
|
1
1
|
import { a as __decorate, f as customElement, i as withShadowUtilityStyles, l as defineHybridPropAliases, r as toShadowCss, s as booleanStringConverter, t as flushSsrHydration } from "../../hydration-flush-DHrl4_KK.js";
|
|
2
2
|
import { t as dispatchMonoEvent } from "../../mono-event-BPku9HpS.js";
|
|
3
3
|
import { n as cssPart } from "../../css-class-B6Tc_MAx.js";
|
|
4
|
+
import { a as parseFormat, c as stripFormat, i as normaliseValue, n as caretAfterFormat, o as resolveFormat, r as localeSeparators, s as significantFor, t as applyFormat } from "../../input-format-GrRCJBqV.js";
|
|
4
5
|
import { t as buildSizeStyle } from "../../css-size-DWAPgQdT.js";
|
|
5
6
|
import { t as MonoFormControlCore } from "../../form-control-core-CIdMLXMo.js";
|
|
6
|
-
import { t as
|
|
7
|
+
import { n as FieldTooltipController, t as field_tooltip_css_raw_default } from "../../field-tooltip.css_raw-DEctHXKm.js";
|
|
8
|
+
import { n as label_position_css_raw_default, r as FieldValidationController, t as field_validation_css_raw_default } from "../../field-validation.css_raw-B3Iu33rS.js";
|
|
9
|
+
import { t as input_css_raw_default } from "../../input.css_raw-CQu2fAsU.js";
|
|
7
10
|
import { LitElement, html, isServer, nothing, unsafeCSS } from "lit";
|
|
8
11
|
import { property, query, state } from "lit/decorators.js";
|
|
9
12
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
10
13
|
import { styleMap } from "lit/directives/style-map.js";
|
|
11
14
|
|
|
12
|
-
//#region src/components/input/input-format.ts
|
|
13
|
-
const SEPARATOR_CACHE = /* @__PURE__ */ new Map();
|
|
14
|
-
/**
|
|
15
|
-
* Ask `Intl` which characters this locale uses, rather than assuming `,` and `.`.
|
|
16
|
-
*
|
|
17
|
-
* Several locales use neither — `fr-FR` groups with a narrow no-break space, `de-CH` with an
|
|
18
|
-
* apostrophe — and a hardcoded pair would silently mangle them.
|
|
19
|
-
*/
|
|
20
|
-
function localeSeparators(locale) {
|
|
21
|
-
const key = locale ?? "";
|
|
22
|
-
const cached = SEPARATOR_CACHE.get(key);
|
|
23
|
-
if (cached) return cached;
|
|
24
|
-
let separators = {
|
|
25
|
-
group: ",",
|
|
26
|
-
decimal: "."
|
|
27
|
-
};
|
|
28
|
-
try {
|
|
29
|
-
const parts = new Intl.NumberFormat(locale || void 0, {
|
|
30
|
-
useGrouping: true,
|
|
31
|
-
minimumFractionDigits: 1
|
|
32
|
-
}).formatToParts(12345.6);
|
|
33
|
-
separators = {
|
|
34
|
-
group: parts.find((p) => p.type === "group")?.value ?? ",",
|
|
35
|
-
decimal: parts.find((p) => p.type === "decimal")?.value ?? "."
|
|
36
|
-
};
|
|
37
|
-
} catch {}
|
|
38
|
-
SEPARATOR_CACHE.set(key, separators);
|
|
39
|
-
return separators;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Parse a pattern string into a descriptor, or `null` when it is not a pattern we understand.
|
|
43
|
-
*
|
|
44
|
-
* `null` is deliberately not an error: an unparseable pattern leaves the field an ordinary
|
|
45
|
-
* unformatted input rather than blanking it or throwing mid-keystroke.
|
|
46
|
-
*/
|
|
47
|
-
function parseFormat(pattern) {
|
|
48
|
-
const raw = String(pattern ?? "");
|
|
49
|
-
if (!raw) return null;
|
|
50
|
-
const hole = raw.indexOf("{}");
|
|
51
|
-
if (hole !== -1) return {
|
|
52
|
-
kind: "template",
|
|
53
|
-
before: raw.slice(0, hole),
|
|
54
|
-
after: raw.slice(hole + 2)
|
|
55
|
-
};
|
|
56
|
-
let body = raw;
|
|
57
|
-
let prefix = "";
|
|
58
|
-
let suffix = "";
|
|
59
|
-
const leading = body.match(/^"([^"]*)"/);
|
|
60
|
-
if (leading) {
|
|
61
|
-
prefix = leading[1] ?? "";
|
|
62
|
-
body = body.slice(leading[0].length);
|
|
63
|
-
}
|
|
64
|
-
const trailing = body.match(/"([^"]*)"$/);
|
|
65
|
-
if (trailing) {
|
|
66
|
-
suffix = trailing[1] ?? "";
|
|
67
|
-
body = body.slice(0, body.length - trailing[0].length);
|
|
68
|
-
}
|
|
69
|
-
if (!body || !/^[#0,]*(?:\.[#0]*)?$/.test(body)) return null;
|
|
70
|
-
if (!/[#0]/.test(body)) return null;
|
|
71
|
-
const [intPart = "", fracPart] = body.split(".");
|
|
72
|
-
return {
|
|
73
|
-
kind: "number",
|
|
74
|
-
grouping: intPart.includes(","),
|
|
75
|
-
minFraction: fracPart ? (fracPart.match(/0/g) ?? []).length : 0,
|
|
76
|
-
maxFraction: fracPart ? fracPart.length : 0,
|
|
77
|
-
prefix,
|
|
78
|
-
suffix
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Strip a formatted string back to its raw value.
|
|
83
|
-
*
|
|
84
|
-
* For numbers the result is always canonical — digits, an optional leading `-`, and `.` as the
|
|
85
|
-
* decimal point — whatever the locale draws with, so it is safe to hand to `Number()`.
|
|
86
|
-
*/
|
|
87
|
-
/**
|
|
88
|
-
* Decide whether a GROUP character in the text is really the decimal point the user typed.
|
|
89
|
-
*
|
|
90
|
-
* It exists because the two roles collide: in `id-ID` the grouping character is `.`, so typing
|
|
91
|
-
* `0.9` produced `09` — the strip deleted the point before anything could read it as one. Yet
|
|
92
|
-
* `1.000` typed in the same field must keep meaning a thousand, and that is not a matter of
|
|
93
|
-
* guessing intent: the field re-strips its OWN formatted text on every keystroke, and
|
|
94
|
-
* `addGrouping` only ever emits runs of exactly three digits.
|
|
95
|
-
*
|
|
96
|
-
* So a group character is the decimal point when
|
|
97
|
-
* · the pattern has a fraction at all,
|
|
98
|
-
* · the locale's real decimal character is nowhere in the text (an explicit `1.000,5` wins), and
|
|
99
|
-
* · the digits after the LAST group character number at most `maxFraction` and are not exactly 3.
|
|
100
|
-
*
|
|
101
|
-
* A run of length ZERO qualifies deliberately: a trailing `0.` must survive, or the point is
|
|
102
|
-
* deleted the instant it is typed and `0.9` can never be reached left to right — `applyFormat`
|
|
103
|
-
* carries the matching rule that keeps a lone trailing point on screen.
|
|
104
|
-
*
|
|
105
|
-
* The consequence, and it is documented rather than worked around: under a dot-grouping locale a
|
|
106
|
-
* THREE-digit fraction (`0.123`) is unreachable by dot and has to be typed `0,123`.
|
|
107
|
-
*
|
|
108
|
-
* Returns the split, or `null` when every group character is grouping. Works for either locale
|
|
109
|
-
* shape — under `en-US` the roles are simply swapped and a typed `,` is promoted the same way.
|
|
110
|
-
*/
|
|
111
|
-
function decimalGroupSplit(text, descriptor, group, decimal) {
|
|
112
|
-
if (descriptor.maxFraction <= 0) return null;
|
|
113
|
-
if (!group || group === decimal) return null;
|
|
114
|
-
if (text.includes(decimal)) return null;
|
|
115
|
-
const at = text.lastIndexOf(group);
|
|
116
|
-
if (at === -1) return null;
|
|
117
|
-
const tail = text.slice(at + group.length);
|
|
118
|
-
if (tail.length > descriptor.maxFraction || tail.length === 3) return null;
|
|
119
|
-
if (tail && !/^\d+$/.test(tail)) return null;
|
|
120
|
-
return {
|
|
121
|
-
head: text.slice(0, at),
|
|
122
|
-
tail
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
function stripFormat(text, descriptor, locale) {
|
|
126
|
-
const value = String(text ?? "");
|
|
127
|
-
if (!descriptor) return value;
|
|
128
|
-
if (descriptor.kind === "template") {
|
|
129
|
-
let out = value;
|
|
130
|
-
if (descriptor.before && out.startsWith(descriptor.before)) out = out.slice(descriptor.before.length);
|
|
131
|
-
if (descriptor.after && out.endsWith(descriptor.after)) out = out.slice(0, out.length - descriptor.after.length);
|
|
132
|
-
return out;
|
|
133
|
-
}
|
|
134
|
-
const { group, decimal } = localeSeparators(locale);
|
|
135
|
-
let out = value;
|
|
136
|
-
if (descriptor.prefix) out = out.split(descriptor.prefix).join("");
|
|
137
|
-
if (descriptor.suffix) out = out.split(descriptor.suffix).join("");
|
|
138
|
-
const promoted = decimalGroupSplit(out, descriptor, group, decimal);
|
|
139
|
-
if (promoted) out = `${promoted.head.split(group).join("")}.${promoted.tail}`;
|
|
140
|
-
else {
|
|
141
|
-
out = out.split(group).join("");
|
|
142
|
-
if (decimal !== ".") out = out.split(decimal).join(".");
|
|
143
|
-
}
|
|
144
|
-
const negative = out.trimStart().startsWith("-");
|
|
145
|
-
out = out.replace(/[^\d.]/g, "");
|
|
146
|
-
const first = out.indexOf(".");
|
|
147
|
-
if (first !== -1) out = out.slice(0, first + 1) + out.slice(first + 1).replace(/\./g, "");
|
|
148
|
-
if (descriptor.maxFraction === 0) out = out.split(".")[0] ?? "";
|
|
149
|
-
return negative && out ? `-${out}` : out;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Render a raw value for DISPLAY.
|
|
153
|
-
*
|
|
154
|
-
* The number branch deliberately does NOT hand the whole string to `Intl`: a value being typed is
|
|
155
|
-
* usually not yet a finished number, and `Intl` would tidy away exactly the characters the user is
|
|
156
|
-
* in the middle of writing — a trailing decimal point (`10000.`) and trailing fraction zeros
|
|
157
|
-
* (`10.50`) both vanish, which makes the field fight the person using it. Only the INTEGER part is
|
|
158
|
-
* grouped; the fraction is carried through verbatim, clamped to the pattern's ceiling.
|
|
159
|
-
*/
|
|
160
|
-
function applyFormat(raw, descriptor, locale, opts = {}) {
|
|
161
|
-
const value = String(raw ?? "");
|
|
162
|
-
if (!descriptor) return value;
|
|
163
|
-
if (descriptor.kind === "template") {
|
|
164
|
-
if (!value) return "";
|
|
165
|
-
return `${descriptor.before}${value}${descriptor.after}`;
|
|
166
|
-
}
|
|
167
|
-
if (!value || value === "-") return value;
|
|
168
|
-
const negative = value.startsWith("-");
|
|
169
|
-
const unsigned = negative ? value.slice(1) : value;
|
|
170
|
-
const [intRaw = "", fracRaw] = unsigned.split(".");
|
|
171
|
-
const hasPoint = unsigned.includes(".");
|
|
172
|
-
const { group, decimal } = localeSeparators(locale);
|
|
173
|
-
const digits = intRaw.replace(/\D/g, "").replace(/^0+(?=\d)/, "");
|
|
174
|
-
const grouped = descriptor.grouping ? addGrouping(digits, group) : digits;
|
|
175
|
-
let fraction = (fracRaw ?? "").replace(/\D/g, "").slice(0, descriptor.maxFraction);
|
|
176
|
-
if (opts.pad && descriptor.minFraction > 0) fraction = fraction.padEnd(descriptor.minFraction, "0");
|
|
177
|
-
let out = grouped || (hasPoint ? "0" : "");
|
|
178
|
-
if (fraction) out += decimal + fraction;
|
|
179
|
-
else if (hasPoint && descriptor.maxFraction > 0 && !opts.pad) out += decimal;
|
|
180
|
-
if (!out) return "";
|
|
181
|
-
return `${descriptor.prefix}${negative ? "-" : ""}${out}${descriptor.suffix}`;
|
|
182
|
-
}
|
|
183
|
-
/** Group a digit string from the right. */
|
|
184
|
-
function addGrouping(digits, separator) {
|
|
185
|
-
if (digits.length < 4) return digits;
|
|
186
|
-
let out = "";
|
|
187
|
-
for (let i = 0; i < digits.length; i++) {
|
|
188
|
-
if (i > 0 && (digits.length - i) % 3 === 0) out += separator;
|
|
189
|
-
out += digits[i];
|
|
190
|
-
}
|
|
191
|
-
return out;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Normalise a raw value for the VALUE side.
|
|
195
|
-
*
|
|
196
|
-
* A number pattern yields a canonical numeric string (`"10000000.25"`) rather than the formatted
|
|
197
|
-
* text, so `Number(v)` works and a store that does arithmetic on it does not have to know a format
|
|
198
|
-
* was ever involved. A template yields the assembled string, because there the decoration IS the
|
|
199
|
-
* value — an email is not an email without its domain.
|
|
200
|
-
*/
|
|
201
|
-
function normaliseValue(raw, descriptor) {
|
|
202
|
-
const value = String(raw ?? "");
|
|
203
|
-
if (!descriptor) return value;
|
|
204
|
-
if (descriptor.kind === "template") return value ? `${descriptor.before}${value}${descriptor.after}` : "";
|
|
205
|
-
if (!value || value === "-") return "";
|
|
206
|
-
const negative = value.startsWith("-");
|
|
207
|
-
const [intRaw = "", fracRaw] = (negative ? value.slice(1) : value).split(".");
|
|
208
|
-
const digits = intRaw.replace(/\D/g, "");
|
|
209
|
-
const fraction = (fracRaw ?? "").replace(/\D/g, "").slice(0, descriptor.maxFraction);
|
|
210
|
-
let out = digits.replace(/^0+(?=\d)/, "");
|
|
211
|
-
if (!out) out = digits ? "0" : "";
|
|
212
|
-
if (!out && !fraction) return "";
|
|
213
|
-
if (fraction) out = `${out || "0"}.${fraction}`;
|
|
214
|
-
return negative && out ? `-${out}` : out;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Where the caret belongs after the text was reformatted.
|
|
218
|
-
*
|
|
219
|
-
* Reformatting rewrites the whole field, and the browser then parks the caret at the end — so
|
|
220
|
-
* inserting a digit in the middle of a number would throw you to the end on every keystroke. The
|
|
221
|
-
* fix is to count in SIGNIFICANT characters (the ones the user actually typed) rather than in
|
|
222
|
-
* string offsets: count how many sit left of the caret before, then walk the new text until the
|
|
223
|
-
* same number have gone by.
|
|
224
|
-
*/
|
|
225
|
-
function caretAfterFormat(next, caretInPrev, prev, isSignificant) {
|
|
226
|
-
let typed = 0;
|
|
227
|
-
for (let i = 0; i < Math.min(caretInPrev, prev.length); i++) if (isSignificant(prev[i])) typed++;
|
|
228
|
-
if (typed === 0) {
|
|
229
|
-
let i = 0;
|
|
230
|
-
while (i < next.length && !isSignificant(next[i])) i++;
|
|
231
|
-
return i;
|
|
232
|
-
}
|
|
233
|
-
let seen = 0;
|
|
234
|
-
for (let i = 0; i < next.length; i++) if (isSignificant(next[i])) {
|
|
235
|
-
seen++;
|
|
236
|
-
if (seen === typed) return i + 1;
|
|
237
|
-
}
|
|
238
|
-
return next.length;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Which characters count as "typed" for caret purposes — everything the format did NOT insert.
|
|
242
|
-
*/
|
|
243
|
-
function significantFor(descriptor, locale) {
|
|
244
|
-
if (!descriptor || descriptor.kind === "template") return () => true;
|
|
245
|
-
const { decimal } = localeSeparators(locale);
|
|
246
|
-
return (ch) => /\d/.test(ch) || ch === decimal || ch === "-";
|
|
247
|
-
}
|
|
248
|
-
/** Resolve either form of a format prop to its finished text. */
|
|
249
|
-
function resolveFormat(format, raw, ctx, mode) {
|
|
250
|
-
if (format == null || format === "") return null;
|
|
251
|
-
if (typeof format === "function") return String(format({
|
|
252
|
-
value: raw,
|
|
253
|
-
type: ctx.type,
|
|
254
|
-
locale: ctx.locale
|
|
255
|
-
}) ?? "");
|
|
256
|
-
const descriptor = parseFormat(format);
|
|
257
|
-
if (!descriptor) return null;
|
|
258
|
-
return mode === "display" ? applyFormat(raw, descriptor, ctx.locale) : normaliseValue(raw, descriptor);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
//#endregion
|
|
262
15
|
//#region src/components/input/input-core.ts
|
|
263
16
|
const numberStringConverter = {
|
|
264
17
|
fromAttribute(value) {
|
|
@@ -272,6 +25,18 @@ const numberStringConverter = {
|
|
|
272
25
|
}
|
|
273
26
|
};
|
|
274
27
|
/**
|
|
28
|
+
* `modelValue` reflects to `model-value`, but only while it is TEXT — a `type="file"` value is a
|
|
29
|
+
* `File` / `File[]`, which would otherwise land in the DOM as `"[object File]"`.
|
|
30
|
+
*/
|
|
31
|
+
const modelValueConverter = {
|
|
32
|
+
fromAttribute(value) {
|
|
33
|
+
return value ?? "";
|
|
34
|
+
},
|
|
35
|
+
toAttribute(value) {
|
|
36
|
+
return typeof value === "string" ? value : null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
275
40
|
* `MonoInputCore` — all render-mode-agnostic logic for `mono-input`: reactive
|
|
276
41
|
* props, hybrid prop aliases (camelCase ↔ kebab ↔ lowercase), attribute
|
|
277
42
|
* observation, value/model two-way sync, events, validation/class computation,
|
|
@@ -294,13 +59,27 @@ const MonoInputCore = (superClass) => {
|
|
|
294
59
|
this.size = "md";
|
|
295
60
|
this.color = "primary";
|
|
296
61
|
this.variant = "outlined";
|
|
62
|
+
this.labelPosition = "top";
|
|
63
|
+
this.validationPosition = "bottom";
|
|
297
64
|
this.modelValue = "";
|
|
298
65
|
this.value = "";
|
|
66
|
+
this.accept = "";
|
|
67
|
+
this.multiple = false;
|
|
68
|
+
this.showValue = true;
|
|
299
69
|
this.formatOn = "input";
|
|
300
70
|
this.name = "";
|
|
301
71
|
this.placeholder = "";
|
|
302
72
|
this.label = "";
|
|
303
73
|
this.helperText = "";
|
|
74
|
+
this.tooltip = null;
|
|
75
|
+
this._tooltip = new FieldTooltipController(this, () => this.tooltip);
|
|
76
|
+
this._validation = new FieldValidationController(this, () => ({
|
|
77
|
+
position: this.validationPosition,
|
|
78
|
+
...this._validationNotice ?? {
|
|
79
|
+
state: void 0,
|
|
80
|
+
text: void 0
|
|
81
|
+
}
|
|
82
|
+
}), () => this.renderRoot?.querySelector?.("[mono-input] > [mono-field]"));
|
|
304
83
|
this.validationState = "default";
|
|
305
84
|
this.validationMessage = "";
|
|
306
85
|
this.error = false;
|
|
@@ -325,6 +104,8 @@ const MonoInputCore = (superClass) => {
|
|
|
325
104
|
this._inputId = `mono-input-${Math.random().toString(36).slice(2)}`;
|
|
326
105
|
this._messageId = `${this._inputId}-message`;
|
|
327
106
|
defineHybridPropAliases(this, [
|
|
107
|
+
"labelPosition",
|
|
108
|
+
"validationPosition",
|
|
328
109
|
"modelValue",
|
|
329
110
|
"formatDisplay",
|
|
330
111
|
"formatValue",
|
|
@@ -338,6 +119,8 @@ const MonoInputCore = (superClass) => {
|
|
|
338
119
|
"ariaLabelText",
|
|
339
120
|
"minLength",
|
|
340
121
|
"maxLength",
|
|
122
|
+
"maxFileSize",
|
|
123
|
+
"showValue",
|
|
341
124
|
"cssClass",
|
|
342
125
|
"minWidth",
|
|
343
126
|
"maxWidth",
|
|
@@ -386,6 +169,8 @@ const MonoInputCore = (superClass) => {
|
|
|
386
169
|
"arialabel",
|
|
387
170
|
"minlength",
|
|
388
171
|
"maxlength",
|
|
172
|
+
"maxfilesize",
|
|
173
|
+
"showvalue",
|
|
389
174
|
"css-class",
|
|
390
175
|
"cssclass",
|
|
391
176
|
"formatdisplay",
|
|
@@ -446,10 +231,53 @@ const MonoInputCore = (superClass) => {
|
|
|
446
231
|
case "maxlength":
|
|
447
232
|
this.maxLength = this._toOptionalNumber(newValue);
|
|
448
233
|
return;
|
|
234
|
+
case "maxfilesize":
|
|
235
|
+
this.maxFileSize = this._toOptionalNumber(newValue);
|
|
236
|
+
return;
|
|
237
|
+
case "showvalue":
|
|
238
|
+
this.showValue = newValue === null ? true : booleanStringConverter.fromAttribute(newValue);
|
|
239
|
+
return;
|
|
449
240
|
}
|
|
450
241
|
}
|
|
242
|
+
/** The validation message and its state — the same precedence the message row uses. */
|
|
243
|
+
get _validationNotice() {
|
|
244
|
+
if (this.validationMessage) return {
|
|
245
|
+
state: this._resolvedValidationState,
|
|
246
|
+
text: this.validationMessage
|
|
247
|
+
};
|
|
248
|
+
if (this.errorMessage) return {
|
|
249
|
+
state: "invalid",
|
|
250
|
+
text: this.errorMessage
|
|
251
|
+
};
|
|
252
|
+
if (this.successMessage) return {
|
|
253
|
+
state: "valid",
|
|
254
|
+
text: this.successMessage
|
|
255
|
+
};
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
/** The message that moved to the icon / float bubble, kept for assistive tech only. */
|
|
259
|
+
_renderMovedMessage() {
|
|
260
|
+
const notice = this._validationNotice;
|
|
261
|
+
return html`<div
|
|
262
|
+
class=${`${this._cls("mono-input-message", "message")} ${notice.state}`}
|
|
263
|
+
mono-message=${notice.state}
|
|
264
|
+
mono-sr-only
|
|
265
|
+
role=${notice.state === "invalid" ? "alert" : nothing}
|
|
266
|
+
>${notice.text}</div>`;
|
|
267
|
+
}
|
|
451
268
|
willUpdate(changed) {
|
|
452
|
-
if (
|
|
269
|
+
if (this._isFile) {
|
|
270
|
+
if (changed.has("modelValue") || changed.has("type")) {
|
|
271
|
+
const files = this._fileList();
|
|
272
|
+
const names = this._fileNames(files);
|
|
273
|
+
this.value = names;
|
|
274
|
+
this._display = names;
|
|
275
|
+
if (!files.length && this._fileEl) this._fileEl.value = "";
|
|
276
|
+
}
|
|
277
|
+
this._internalWrite = false;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (changed.has("modelValue") && this.value !== this.modelValue) this.value = typeof this.modelValue === "string" ? this.modelValue : "";
|
|
453
281
|
if (changed.has("value") && this.modelValue !== this.value) this.modelValue = String(this.value ?? "");
|
|
454
282
|
if (!this._internalWrite && !this._focused && (changed.has("value") || changed.has("modelValue") || changed.has("formatDisplay"))) this._syncDisplayFromValue();
|
|
455
283
|
this._internalWrite = false;
|
|
@@ -496,7 +324,7 @@ const MonoInputCore = (superClass) => {
|
|
|
496
324
|
this.readonly ? "readonly" : "",
|
|
497
325
|
this._resolvedValidationState !== "default" ? `is-${this._resolvedValidationState}` : "",
|
|
498
326
|
this._hasPrefixSlot ? "has-prefix" : "",
|
|
499
|
-
this._hasSuffixSlot || this.clearable ? "has-suffix" : "",
|
|
327
|
+
this._hasSuffixSlot || this.clearable || this._isFile ? "has-suffix" : "",
|
|
500
328
|
this.value ? "has-value" : "",
|
|
501
329
|
this.cssClassName,
|
|
502
330
|
this.cssClass?.root
|
|
@@ -512,12 +340,127 @@ const MonoInputCore = (superClass) => {
|
|
|
512
340
|
this.readonly ? "readonly" : "",
|
|
513
341
|
this._resolvedValidationState !== "default" ? `is-${this._resolvedValidationState}` : "",
|
|
514
342
|
this._hasPrefixSlot ? "has-prefix" : "",
|
|
515
|
-
this._hasSuffixSlot || this.clearable ? "has-suffix" : "",
|
|
343
|
+
this._hasSuffixSlot || this.clearable || this._isFile ? "has-suffix" : "",
|
|
516
344
|
this.value ? "has-value" : ""
|
|
517
345
|
].filter(Boolean).join(" ");
|
|
518
346
|
}
|
|
347
|
+
get _isFile() {
|
|
348
|
+
return this.type === "file";
|
|
349
|
+
}
|
|
350
|
+
get _isRange() {
|
|
351
|
+
return this.type === "range";
|
|
352
|
+
}
|
|
353
|
+
/** `min` / `max` / `step` as the native range resolves them (0 / 100 / 1 when unset). */
|
|
354
|
+
_rangeBounds() {
|
|
355
|
+
const min = this._toOptionalNumber(this.min) ?? 0;
|
|
356
|
+
const rawMax = this._toOptionalNumber(this.max) ?? 100;
|
|
357
|
+
const max = rawMax < min ? min : rawMax;
|
|
358
|
+
const step = this._toOptionalNumber(this.step);
|
|
359
|
+
return {
|
|
360
|
+
min,
|
|
361
|
+
max,
|
|
362
|
+
step: step && step > 0 ? step : 1
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* The value the slider is actually AT, as a string. An empty value is not a position — the
|
|
367
|
+
* native range parks itself at the midpoint — so the readout and the fill follow the native
|
|
368
|
+
* rule rather than reading `''` as 0. The model is not rewritten: nothing was chosen yet.
|
|
369
|
+
*/
|
|
370
|
+
get _rangeValue() {
|
|
371
|
+
const { min, max, step } = this._rangeBounds();
|
|
372
|
+
const target = this._toOptionalNumber(this.value) ?? min + (max - min) / 2;
|
|
373
|
+
const snapped = min + Math.round((target - min) / step) * step;
|
|
374
|
+
return String(Number(Math.min(max, Math.max(min, snapped)).toFixed(10)));
|
|
375
|
+
}
|
|
376
|
+
/** How far along the track the value sits, 0–100 — drives the fill. */
|
|
377
|
+
get _rangeProgress() {
|
|
378
|
+
const { min, max } = this._rangeBounds();
|
|
379
|
+
if (max === min) return 0;
|
|
380
|
+
return (Number(this._rangeValue) - min) / (max - min) * 100;
|
|
381
|
+
}
|
|
382
|
+
/** The readout text: the value drawn through `format-display`. */
|
|
383
|
+
get _rangeText() {
|
|
384
|
+
return this._formatDisplay(this._rangeValue, { pad: true });
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* A native range ignores `readonly`, so a readonly slider would still move. Put the thumb
|
|
388
|
+
* back where the value says and report that the event was handled.
|
|
389
|
+
*/
|
|
390
|
+
_revertReadonlyRange(event) {
|
|
391
|
+
if (!this._isRange || !this.readonly || this.disabled) return false;
|
|
392
|
+
event.currentTarget.value = this._rangeValue;
|
|
393
|
+
event.stopPropagation();
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
/** `modelValue` as an array of files, whatever shape it arrived in (File, File[], null, ''). */
|
|
397
|
+
_fileList() {
|
|
398
|
+
const isFile = (v) => !!v && typeof v === "object" && typeof v.name === "string";
|
|
399
|
+
const v = this.modelValue;
|
|
400
|
+
if (Array.isArray(v)) return v.filter(isFile);
|
|
401
|
+
return isFile(v) ? [v] : [];
|
|
402
|
+
}
|
|
403
|
+
_fileNames(files) {
|
|
404
|
+
return files.map((f) => f.name).join(", ");
|
|
405
|
+
}
|
|
406
|
+
_openFilePicker() {
|
|
407
|
+
if (this.disabled || this.readonly) return;
|
|
408
|
+
this._fileEl?.click();
|
|
409
|
+
}
|
|
410
|
+
_handleFileKeydown(event) {
|
|
411
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
412
|
+
event.preventDefault();
|
|
413
|
+
this._openFilePicker();
|
|
414
|
+
}
|
|
415
|
+
/** Files above `maxFileSize` are dropped, each one reported through `error`. */
|
|
416
|
+
_acceptFiles(files, sourceEvent) {
|
|
417
|
+
const limit = this.maxFileSize;
|
|
418
|
+
if (!limit || limit <= 0) return files;
|
|
419
|
+
return files.filter((file) => {
|
|
420
|
+
if (file.size <= limit) return true;
|
|
421
|
+
const detail = {
|
|
422
|
+
message: `File "${file.name}" exceeds the maximum size limit.`,
|
|
423
|
+
reason: "max-file-size",
|
|
424
|
+
file,
|
|
425
|
+
maxFileSize: limit,
|
|
426
|
+
sourceEvent
|
|
427
|
+
};
|
|
428
|
+
dispatchMonoEvent(this, "error", detail);
|
|
429
|
+
return false;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* The picker's native `input` fires BEFORE its `change`, i.e. before the value below is
|
|
434
|
+
* written — left to bubble, a host `@input` (and a bound form) would read the OLD value. Our
|
|
435
|
+
* own `input` is emitted from the change handler instead.
|
|
436
|
+
*/
|
|
437
|
+
_swallowNativeInput(event) {
|
|
438
|
+
event.stopPropagation();
|
|
439
|
+
}
|
|
440
|
+
_handleFileChange(event) {
|
|
441
|
+
if (this.disabled || this.readonly) return;
|
|
442
|
+
const input = event.currentTarget;
|
|
443
|
+
const files = this._acceptFiles(Array.from(input.files ?? []), event);
|
|
444
|
+
if (!files.length) {
|
|
445
|
+
input.value = "";
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
this._internalWrite = true;
|
|
449
|
+
const oldValue = this.modelValue;
|
|
450
|
+
const nextValue = this.multiple ? files : files[0];
|
|
451
|
+
this.modelValue = nextValue;
|
|
452
|
+
this.value = this._display = this._fileNames(files);
|
|
453
|
+
const detail = this._createModelDetail({
|
|
454
|
+
modelValue: nextValue,
|
|
455
|
+
oldValue,
|
|
456
|
+
sourceEvent: event
|
|
457
|
+
});
|
|
458
|
+
this._emitInput(detail);
|
|
459
|
+
this._emitChange(detail);
|
|
460
|
+
}
|
|
519
461
|
_createModelDetail(args) {
|
|
520
462
|
return {
|
|
463
|
+
...this._isFile ? { files: this._fileList() } : {},
|
|
521
464
|
modelValue: args.modelValue,
|
|
522
465
|
currentValue: args.modelValue,
|
|
523
466
|
oldValue: args.oldValue,
|
|
@@ -667,11 +610,12 @@ const MonoInputCore = (superClass) => {
|
|
|
667
610
|
} catch {}
|
|
668
611
|
}
|
|
669
612
|
_handleInput(event) {
|
|
613
|
+
if (this._revertReadonlyRange(event)) return;
|
|
670
614
|
if (this.disabled || this.readonly) return;
|
|
671
615
|
this._internalWrite = true;
|
|
672
616
|
const input = event.currentTarget;
|
|
673
617
|
const oldValue = this.modelValue;
|
|
674
|
-
if (!this._hasFormat) {
|
|
618
|
+
if (!this._hasFormat || this._isRange) {
|
|
675
619
|
const nextValue = input.value;
|
|
676
620
|
this.value = nextValue;
|
|
677
621
|
this.modelValue = nextValue;
|
|
@@ -697,11 +641,12 @@ const MonoInputCore = (superClass) => {
|
|
|
697
641
|
}));
|
|
698
642
|
}
|
|
699
643
|
_handleChange(event) {
|
|
644
|
+
if (this._revertReadonlyRange(event)) return;
|
|
700
645
|
if (this.disabled || this.readonly) return;
|
|
701
646
|
this._internalWrite = true;
|
|
702
647
|
const input = event.currentTarget;
|
|
703
648
|
const oldValue = this.modelValue;
|
|
704
|
-
if (!this._hasFormat) {
|
|
649
|
+
if (!this._hasFormat || this._isRange) {
|
|
705
650
|
const nextValue = input.value;
|
|
706
651
|
this.value = nextValue;
|
|
707
652
|
this.modelValue = nextValue;
|
|
@@ -735,6 +680,21 @@ const MonoInputCore = (superClass) => {
|
|
|
735
680
|
if (this.disabled || this.readonly) return;
|
|
736
681
|
this._internalWrite = true;
|
|
737
682
|
const oldValue = this.modelValue;
|
|
683
|
+
if (this._isFile) {
|
|
684
|
+
const empty = this.multiple ? [] : null;
|
|
685
|
+
this.modelValue = empty;
|
|
686
|
+
this.value = this._display = "";
|
|
687
|
+
if (this._fileEl) this._fileEl.value = "";
|
|
688
|
+
this._inputEl?.focus();
|
|
689
|
+
const detail = this._createModelDetail({
|
|
690
|
+
modelValue: empty,
|
|
691
|
+
oldValue
|
|
692
|
+
});
|
|
693
|
+
this._emitInput(detail);
|
|
694
|
+
this._emitChange(detail);
|
|
695
|
+
this._emitClear(detail);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
738
698
|
const nextValue = "";
|
|
739
699
|
this.value = nextValue;
|
|
740
700
|
this.modelValue = nextValue;
|
|
@@ -769,7 +729,10 @@ const MonoInputCore = (superClass) => {
|
|
|
769
729
|
mono-size=${this.size === "md" ? nothing : this.size}
|
|
770
730
|
mono-color=${this.color === "primary" ? nothing : this.color}
|
|
771
731
|
mono-variant=${this.variant === "outlined" ? nothing : this.variant}
|
|
732
|
+
mono-label-position=${this.labelPosition === "top" ? nothing : this.labelPosition}
|
|
733
|
+
mono-validation-position=${this.validationPosition === "bottom" ? nothing : this.validationPosition}
|
|
772
734
|
mono-validation-state=${state === "default" ? nothing : state}
|
|
735
|
+
mono-type=${this._isFile || this._isRange ? this.type : nothing}
|
|
773
736
|
?mono-disabled=${this.disabled}
|
|
774
737
|
?mono-readonly=${this.readonly}
|
|
775
738
|
?mono-required=${this.required}
|
|
@@ -784,8 +747,111 @@ const MonoInputCore = (superClass) => {
|
|
|
784
747
|
get _describedBy() {
|
|
785
748
|
return this.validationMessage || this.errorMessage || this.successMessage || this.helperText || this._hasHelperSlot ? this._messageId : void 0;
|
|
786
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* `type="file"`: a hidden native picker, and in front of it a read-only text input showing
|
|
752
|
+
* the picked name(s). The text input keeps `mono-native` + `.mono-input-native`, so focus,
|
|
753
|
+
* the label's `for`, sizing and the focus ring all behave exactly as on a text field.
|
|
754
|
+
*/
|
|
755
|
+
_renderFileNative() {
|
|
756
|
+
return html`
|
|
757
|
+
<input
|
|
758
|
+
type="file"
|
|
759
|
+
mono-file-input
|
|
760
|
+
hidden
|
|
761
|
+
tabindex="-1"
|
|
762
|
+
accept=${ifDefined(this.accept || void 0)}
|
|
763
|
+
?multiple=${this.multiple}
|
|
764
|
+
name=${ifDefined(this.name || void 0)}
|
|
765
|
+
?disabled=${this.disabled}
|
|
766
|
+
@input=${this._swallowNativeInput}
|
|
767
|
+
@change=${this._handleFileChange}
|
|
768
|
+
/>
|
|
769
|
+
<input
|
|
770
|
+
id=${this._inputId}
|
|
771
|
+
class=${`${this._cls("mono-input-native", "native")} ${this.size}`}
|
|
772
|
+
mono-native
|
|
773
|
+
mono-file
|
|
774
|
+
type="text"
|
|
775
|
+
readonly
|
|
776
|
+
.value=${this._display}
|
|
777
|
+
placeholder=${ifDefined(this.placeholder || void 0)}
|
|
778
|
+
?disabled=${this.disabled}
|
|
779
|
+
?required=${this.required}
|
|
780
|
+
aria-label=${ifDefined(this._ariaLabel)}
|
|
781
|
+
aria-invalid=${this._resolvedValidationState === "invalid" ? "true" : "false"}
|
|
782
|
+
aria-describedby=${ifDefined(this._describedBy)}
|
|
783
|
+
aria-haspopup="dialog"
|
|
784
|
+
@focus=${this._handleFocus}
|
|
785
|
+
@blur=${this._handleBlur}
|
|
786
|
+
@click=${this._openFilePicker}
|
|
787
|
+
@keydown=${this._handleFileKeydown}
|
|
788
|
+
/>
|
|
789
|
+
`;
|
|
790
|
+
}
|
|
791
|
+
/** `type="file"` — the upload glyph at the end of the field; clicking it opens the picker too. */
|
|
792
|
+
_renderFileIcon() {
|
|
793
|
+
if (!this._isFile) return nothing;
|
|
794
|
+
return html`
|
|
795
|
+
<span
|
|
796
|
+
class=${this._cls("mono-input-suffix", "fileIcon")}
|
|
797
|
+
mono-suffix
|
|
798
|
+
mono-file-icon
|
|
799
|
+
aria-hidden="true"
|
|
800
|
+
@click=${this._openFilePicker}
|
|
801
|
+
>${this.renderIcon("upload")}</span>
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* `type="range"`: Basecoat's slider. The fill is a gradient stop on the track, so the element
|
|
806
|
+
* writes the position as `--mono-input-range-value` (Basecoat's `--slider-value`) — hand-written
|
|
807
|
+
* markup sets the same property itself.
|
|
808
|
+
*/
|
|
809
|
+
_renderRangeNative() {
|
|
810
|
+
const { min, max, step } = this._rangeBounds();
|
|
811
|
+
const text = this._rangeText;
|
|
812
|
+
return html`
|
|
813
|
+
<input
|
|
814
|
+
id=${this._inputId}
|
|
815
|
+
class=${`${this._cls("mono-input-native", "native")} ${this.size}`}
|
|
816
|
+
mono-native
|
|
817
|
+
mono-range
|
|
818
|
+
type="range"
|
|
819
|
+
min=${min}
|
|
820
|
+
max=${max}
|
|
821
|
+
step=${ifDefined(this.step == null ? void 0 : step)}
|
|
822
|
+
.value=${this._rangeValue}
|
|
823
|
+
name=${ifDefined(this.name || void 0)}
|
|
824
|
+
style=${`--mono-input-range-value: ${this._rangeProgress}%`}
|
|
825
|
+
?disabled=${this.disabled}
|
|
826
|
+
?required=${this.required}
|
|
827
|
+
aria-label=${ifDefined(this._ariaLabel)}
|
|
828
|
+
aria-valuetext=${ifDefined(text !== this._rangeValue ? text : void 0)}
|
|
829
|
+
aria-readonly=${ifDefined(this.readonly ? "true" : void 0)}
|
|
830
|
+
aria-invalid=${this._resolvedValidationState === "invalid" ? "true" : "false"}
|
|
831
|
+
aria-describedby=${ifDefined(this._describedBy)}
|
|
832
|
+
@focus=${this._handleFocus}
|
|
833
|
+
@blur=${this._handleBlur}
|
|
834
|
+
@input=${this._handleInput}
|
|
835
|
+
@change=${this._handleChange}
|
|
836
|
+
/>
|
|
837
|
+
`;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* `type="range"` — the value readout. It sits at the end of the LABEL row when there is a
|
|
841
|
+
* label (the EJI ONE layout), and after the slider when there is not; each build calls this
|
|
842
|
+
* from both places and exactly one of them renders. `aria-hidden`: the slider already
|
|
843
|
+
* announces its value (`aria-valuetext` carries the formatted text).
|
|
844
|
+
*/
|
|
845
|
+
_renderRangeValue(where) {
|
|
846
|
+
if (!this._isRange || !this.showValue) return nothing;
|
|
847
|
+
const inLabel = !!(this.label || this._hasLabelSlot);
|
|
848
|
+
if (where === "label" !== inLabel) return nothing;
|
|
849
|
+
return html`<span class=${this._cls("mono-input-range-value", "rangeValue")} mono-range-value aria-hidden="true">${this._rangeText}</span>`;
|
|
850
|
+
}
|
|
787
851
|
/** The native <input>, shared by both builds' render(). */
|
|
788
852
|
_renderNative() {
|
|
853
|
+
if (this._isFile) return this._renderFileNative();
|
|
854
|
+
if (this._isRange) return this._renderRangeNative();
|
|
789
855
|
return html`
|
|
790
856
|
<input
|
|
791
857
|
id=${this._inputId}
|
|
@@ -818,7 +884,7 @@ const MonoInputCore = (superClass) => {
|
|
|
818
884
|
}
|
|
819
885
|
/** The clear button (uses the build-specific icon). */
|
|
820
886
|
_renderClear() {
|
|
821
|
-
if (!(this.clearable && this.value && !this.disabled && !this.readonly)) return nothing;
|
|
887
|
+
if (this._isRange || !(this.clearable && this.value && !this.disabled && !this.readonly)) return nothing;
|
|
822
888
|
return html`
|
|
823
889
|
<button
|
|
824
890
|
type="button"
|
|
@@ -859,10 +925,31 @@ const MonoInputCore = (superClass) => {
|
|
|
859
925
|
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "variant", void 0);
|
|
860
926
|
__decorate([property({
|
|
861
927
|
type: String,
|
|
928
|
+
attribute: "label-position"
|
|
929
|
+
})], MonoInputCoreClass.prototype, "labelPosition", void 0);
|
|
930
|
+
__decorate([property({
|
|
931
|
+
type: String,
|
|
932
|
+
attribute: "validation-position"
|
|
933
|
+
})], MonoInputCoreClass.prototype, "validationPosition", void 0);
|
|
934
|
+
__decorate([property({
|
|
862
935
|
attribute: "model-value",
|
|
863
|
-
reflect: true
|
|
936
|
+
reflect: true,
|
|
937
|
+
converter: modelValueConverter
|
|
864
938
|
})], MonoInputCoreClass.prototype, "modelValue", void 0);
|
|
865
939
|
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "value", void 0);
|
|
940
|
+
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "accept", void 0);
|
|
941
|
+
__decorate([property({
|
|
942
|
+
reflect: true,
|
|
943
|
+
converter: booleanStringConverter
|
|
944
|
+
})], MonoInputCoreClass.prototype, "multiple", void 0);
|
|
945
|
+
__decorate([property({
|
|
946
|
+
attribute: "max-file-size",
|
|
947
|
+
converter: numberStringConverter
|
|
948
|
+
})], MonoInputCoreClass.prototype, "maxFileSize", void 0);
|
|
949
|
+
__decorate([property({
|
|
950
|
+
attribute: "show-value",
|
|
951
|
+
converter: { fromAttribute: (v) => v === null ? true : booleanStringConverter.fromAttribute(v) }
|
|
952
|
+
})], MonoInputCoreClass.prototype, "showValue", void 0);
|
|
866
953
|
__decorate([property({ attribute: false })], MonoInputCoreClass.prototype, "formatDisplay", void 0);
|
|
867
954
|
__decorate([property({ attribute: false })], MonoInputCoreClass.prototype, "formatValue", void 0);
|
|
868
955
|
__decorate([property({ attribute: "format-on" })], MonoInputCoreClass.prototype, "formatOn", void 0);
|
|
@@ -874,6 +961,7 @@ const MonoInputCore = (superClass) => {
|
|
|
874
961
|
type: String,
|
|
875
962
|
attribute: "helper-text"
|
|
876
963
|
})], MonoInputCoreClass.prototype, "helperText", void 0);
|
|
964
|
+
__decorate([property()], MonoInputCoreClass.prototype, "tooltip", void 0);
|
|
877
965
|
__decorate([property({
|
|
878
966
|
type: String,
|
|
879
967
|
attribute: "validation-state"
|
|
@@ -955,6 +1043,7 @@ const MonoInputCore = (superClass) => {
|
|
|
955
1043
|
attribute: "max-height"
|
|
956
1044
|
})], MonoInputCoreClass.prototype, "maxHeight", void 0);
|
|
957
1045
|
__decorate([query(".mono-input-native")], MonoInputCoreClass.prototype, "_inputEl", void 0);
|
|
1046
|
+
__decorate([query("[mono-file-input]")], MonoInputCoreClass.prototype, "_fileEl", void 0);
|
|
958
1047
|
__decorate([state()], MonoInputCoreClass.prototype, "_display", void 0);
|
|
959
1048
|
__decorate([state()], MonoInputCoreClass.prototype, "_focused", void 0);
|
|
960
1049
|
__decorate([state()], MonoInputCoreClass.prototype, "_hasPrefixSlotState", void 0);
|
|
@@ -972,7 +1061,7 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
972
1061
|
this.disableWarning?.("change-in-update");
|
|
973
1062
|
}
|
|
974
1063
|
static {
|
|
975
|
-
this.styles = [unsafeCSS(toShadowCss(input_css_raw_default, {
|
|
1064
|
+
this.styles = [unsafeCSS(toShadowCss(input_css_raw_default + label_position_css_raw_default + field_tooltip_css_raw_default + field_validation_css_raw_default, {
|
|
976
1065
|
host: "mono-input",
|
|
977
1066
|
append: SHADOW_EXTRA_CSS
|
|
978
1067
|
}))];
|
|
@@ -1001,7 +1090,12 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1001
1090
|
_onSlotChange(name, event) {
|
|
1002
1091
|
this._setSlotState(name, this._slotHasContent(event.target));
|
|
1003
1092
|
}
|
|
1004
|
-
renderIcon(
|
|
1093
|
+
renderIcon(name) {
|
|
1094
|
+
if (name === "upload") return html`
|
|
1095
|
+
<svg viewBox="0 0 24 24" mono-icon fill="currentColor" aria-hidden="true">
|
|
1096
|
+
<path d="M6.5 20q-2.28 0-3.89-1.57Q1 16.85 1 14.58q0-1.95 1.17-3.48q1.18-1.53 3.08-1.95q.63-2.3 2.5-3.72Q9.63 4 12 4q2.93 0 4.96 2.04Q19 8.07 19 11q1.73.2 2.86 1.5q1.14 1.28 1.14 3q0 1.88-1.31 3.19T18.5 20H13q-.82 0-1.41-.59Q11 18.83 11 18v-5.15L9.4 14.4L8 13l4-4l4 4l-1.4 1.4l-1.6-1.55V18h5.5q1.05 0 1.77-.73q.73-.72.73-1.77t-.73-1.77Q19.55 13 18.5 13H17v-2q0-2.07-1.46-3.54Q14.08 6 12 6Q9.93 6 8.46 7.46Q7 8.93 7 11h-.5q-1.45 0-2.47 1.03Q3 13.05 3 14.5T4.03 17q1.02 1 2.47 1H9v2m3-7" />
|
|
1097
|
+
</svg>
|
|
1098
|
+
`;
|
|
1005
1099
|
return html`
|
|
1006
1100
|
<svg viewBox="0 0 24 24" mono-icon fill="currentColor" aria-hidden="true">
|
|
1007
1101
|
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
|
@@ -1018,17 +1112,23 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1018
1112
|
>
|
|
1019
1113
|
<slot name="label" @slotchange=${(e) => this._onSlotChange("label", e)}>${this.label}</slot>
|
|
1020
1114
|
${this.required ? html`<span class=${this._cls("mono-input-required", "required")} mono-required-mark>*</span>` : nothing}
|
|
1115
|
+
${this._tooltip.render()}
|
|
1116
|
+
${this._renderRangeValue("label")}
|
|
1021
1117
|
</label>
|
|
1022
1118
|
`;
|
|
1023
1119
|
}
|
|
1024
1120
|
_renderHelper() {
|
|
1025
1121
|
const base = this._cls("mono-input-message", "message");
|
|
1122
|
+
if (this._validation.moved) return html`${this._renderHelperText(base)}${this._renderMovedMessage()}`;
|
|
1026
1123
|
if (this.validationMessage) {
|
|
1027
1124
|
const state = this._resolvedValidationState;
|
|
1028
1125
|
return html`<div class=${`${base} ${state}`} mono-message=${state} role=${state === "invalid" ? "alert" : nothing}>${this.validationMessage}</div>`;
|
|
1029
1126
|
}
|
|
1030
1127
|
if (this.errorMessage) return html`<div class=${`${base} invalid`} mono-message="invalid" role="alert">${this.errorMessage}</div>`;
|
|
1031
1128
|
if (this.successMessage) return html`<div class=${`${base} valid`} mono-message="valid">${this.successMessage}</div>`;
|
|
1129
|
+
return this._renderHelperText(base);
|
|
1130
|
+
}
|
|
1131
|
+
_renderHelperText(base) {
|
|
1032
1132
|
return html`
|
|
1033
1133
|
<div
|
|
1034
1134
|
class=${`${base} helper`}
|
|
@@ -1047,7 +1147,10 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1047
1147
|
<slot name="prefix" @slotchange=${(e) => this._onSlotChange("prefix", e)}></slot>
|
|
1048
1148
|
</span>
|
|
1049
1149
|
${this._renderNative()}
|
|
1150
|
+
${this._renderRangeValue("field")}
|
|
1151
|
+
${this._validation.renderIcon()}
|
|
1050
1152
|
${this._renderClear()}
|
|
1153
|
+
${this._renderFileIcon()}
|
|
1051
1154
|
<span class=${this._cls("mono-input-suffix", "suffix")} mono-suffix ?mono-empty=${!this._hasSuffixSlot}>
|
|
1052
1155
|
<slot name="suffix" @slotchange=${(e) => this._onSlotChange("suffix", e)}></slot>
|
|
1053
1156
|
</span>
|
|
@@ -1055,6 +1158,7 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1055
1158
|
<div id=${this._messageId} class=${this.cssClass?.messageWrap ?? ""} mono-message-wrap>
|
|
1056
1159
|
${this._renderHelper()}
|
|
1057
1160
|
</div>
|
|
1161
|
+
${this._validation.renderFloat()}
|
|
1058
1162
|
`);
|
|
1059
1163
|
}
|
|
1060
1164
|
connectedCallback() {
|
|
@@ -1147,7 +1251,9 @@ function getInputIcon(type) {
|
|
|
1147
1251
|
time: "M12 7v5l3 3M12 22a10 10 0 1 0 0-20a10 10 0 0 0 0 20z",
|
|
1148
1252
|
month: "M7 3v3M17 3v3M5 5h14v14H5zM8 11h8",
|
|
1149
1253
|
week: "M7 3v3M17 3v3M5 5h14v16H5zM9 11h6M9 15h4",
|
|
1150
|
-
search: "M21 21l-4.35-4.35M10.5 18a7.5 7.5 0 1 1 0-15a7.5 7.5 0 0 1 0 15z"
|
|
1254
|
+
search: "M21 21l-4.35-4.35M10.5 18a7.5 7.5 0 1 1 0-15a7.5 7.5 0 0 1 0 15z",
|
|
1255
|
+
file: "M12 16V8M8.5 11.5L12 8l3.5 3.5M7 18h10",
|
|
1256
|
+
range: "M4 12h16M9 9a3 3 0 1 0 0 6a3 3 0 0 0 0-6z"
|
|
1151
1257
|
};
|
|
1152
1258
|
return iconMap[type] ?? iconMap.text;
|
|
1153
1259
|
}
|