@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.
Files changed (135) hide show
  1. package/dist/control-tooltip-7vegYmq3.js +678 -0
  2. package/dist/{tooltip-FCMRUSLi.js → control-tooltip-C3rA_nSH.js} +12 -119
  3. package/dist/{data-source-controller-Cf_sMxEh.js → data-source-controller-C6C4ej_M.js} +1 -1
  4. package/dist/{data-source-options-Bk-UoaZV.js → data-source-options-Cz9fVLK8.js} +3 -2
  5. package/dist/{data-source-read-Cuqw3LcP.js → data-source-read-i6CYSUEl.js} +2 -1
  6. package/dist/{excel-DtRpxmxs.js → excel-BPdDt06G.js} +1 -1
  7. package/dist/{export-B91P6J1y.js → export-DgomqI_3.js} +2 -2
  8. package/dist/export.js +2 -2
  9. package/dist/{field-icons-FmyBWa0g.js → field-icons-t8rXRCKs.js} +50 -1
  10. package/dist/field-tooltip-FyMi9vNM.js +266 -0
  11. package/dist/field-tooltip.css_raw-DEctHXKm.js +270 -0
  12. package/dist/field-validation-CHHQi1zl.js +170 -0
  13. package/dist/field-validation.css_raw-B3Iu33rS.js +178 -0
  14. package/dist/index.js +4 -3
  15. package/dist/{index.node-dvrNviMx.js → index.node-DzCuvHLg.js} +1 -1
  16. package/dist/index.node.js +4 -3
  17. package/dist/input-format-GrRCJBqV.js +251 -0
  18. package/dist/input.css_raw-CQu2fAsU.js +5 -0
  19. package/dist/{mono-data-grid-BHlo1IgR.js → mono-data-grid-KDpu4d4_.js} +5 -4
  20. package/dist/{mono-form-controller-IYn0MuUQ.js → mono-form-controller-BkMR8g14.js} +18 -3
  21. package/dist/{reactive-DFCdZbH9.js → normalize-error-BmLbgANw.js} +1 -48
  22. package/dist/nuxt.js +36 -8
  23. package/dist/{popup-portal-HWhkd7oy.js → popup-portal-C8ttHDRX.js} +17 -1
  24. package/dist/{popup-stack-De05Y-mJ.js → popup-stack-C64WCuXq.js} +21 -1
  25. package/dist/src/components/accordion/accordion-core.d.ts +4 -1
  26. package/dist/src/components/accordion/mono-accordion.shadow.d.ts +4 -1
  27. package/dist/src/components/date/date-core.d.ts +3 -0
  28. package/dist/src/components/date/date-types.d.ts +36 -1
  29. package/dist/src/components/drawer/drawer-core.d.ts +1 -0
  30. package/dist/src/components/dropdown-table/dropdown-table-core.d.ts +3 -0
  31. package/dist/src/components/dropdown-table/dropdown-table-types.d.ts +31 -0
  32. package/dist/src/components/form/form-types.d.ts +23 -5
  33. package/dist/src/components/input/index.d.ts +2 -2
  34. package/dist/src/components/input/index.shadow.d.ts +2 -2
  35. package/dist/src/components/input/input-core.d.ts +23 -3
  36. package/dist/src/components/input/input-types.d.ts +84 -9
  37. package/dist/src/components/input/mono-input.d.ts +3 -2
  38. package/dist/src/components/input/mono-input.shadow.d.ts +3 -2
  39. package/dist/src/components/modal/modal-core.d.ts +1 -0
  40. package/dist/src/components/otp/index.d.ts +5 -0
  41. package/dist/src/components/otp/index.shadow.d.ts +6 -0
  42. package/dist/src/components/otp/mono-otp.d.ts +32 -0
  43. package/dist/src/components/otp/mono-otp.shadow.d.ts +30 -0
  44. package/dist/src/components/otp/otp-core.d.ts +91 -0
  45. package/dist/src/components/otp/otp-types.d.ts +161 -0
  46. package/dist/src/components/otp/otp-utils.d.ts +41 -0
  47. package/dist/src/components/select/select-core.d.ts +3 -0
  48. package/dist/src/components/select/select-types.d.ts +31 -0
  49. package/dist/src/components/slider/index.d.ts +5 -0
  50. package/dist/src/components/slider/index.shadow.d.ts +6 -0
  51. package/dist/src/components/slider/mono-slider.d.ts +33 -0
  52. package/dist/src/components/slider/mono-slider.shadow.d.ts +29 -0
  53. package/dist/src/components/slider/slider-core.d.ts +77 -0
  54. package/dist/src/components/slider/slider-math.d.ts +102 -0
  55. package/dist/src/components/slider/slider-types.d.ts +188 -0
  56. package/dist/src/components/tag-input/tag-input-core.d.ts +3 -0
  57. package/dist/src/components/tag-input/tag-input-types.d.ts +31 -0
  58. package/dist/src/components/textarea/textarea-core.d.ts +3 -0
  59. package/dist/src/components/textarea/textarea-types.d.ts +31 -0
  60. package/dist/src/composables/field-bubble.d.ts +34 -0
  61. package/dist/src/composables/field-icons.d.ts +10 -0
  62. package/dist/src/composables/field-tooltip.d.ts +52 -0
  63. package/dist/src/composables/field-validation.d.ts +58 -0
  64. package/dist/src/composables/popup-portal.d.ts +12 -0
  65. package/dist/src/composables/popup-stack.d.ts +21 -0
  66. package/dist/src/entries/index.d.ts +1 -1
  67. package/dist/src/entries/index.node.d.ts +1 -1
  68. package/dist/src/nuxt/index.d.ts +2 -2
  69. package/dist/src/nuxt/ssr-lit/index.d.ts +4 -0
  70. package/dist/ssr-lit/runtime/components/LitWrapper.vue +11 -0
  71. package/dist/ssr-lit/runtime/components/LitWrapperClient.vue +11 -0
  72. package/dist/ssr-lit/runtime/components/LitWrapperServer.vue +38 -0
  73. package/dist/ssr-lit/runtime/plugins/antiFouc.server.js +25 -0
  74. package/dist/ssr-lit/runtime/plugins/hydrateSupport.client.js +4 -0
  75. package/dist/ssr-lit/runtime/plugins/polyfill.client.js +9 -0
  76. package/dist/ssr-lit/runtime/utils/customElements.js +11 -0
  77. package/dist/ssr-lit/runtime/utils/litElementRenderer.js +28 -0
  78. package/dist/{styles-D-ergrfR.js → styles-BODNpZV1.js} +1 -1
  79. package/dist/tooltip-Drekihf5.js +102 -0
  80. package/dist/tooltip-loader-3U9FTOXj.js +20 -0
  81. package/dist/tooltip-loader-Dcp9AUZn.js +23 -0
  82. package/dist/tooltip.js +3 -1
  83. package/dist/ui/accordion.js +16 -3
  84. package/dist/ui/alert.js +2 -2
  85. package/dist/ui/breadcrumb.js +1 -1
  86. package/dist/ui/button-dropdown.js +1 -1
  87. package/dist/ui/chart.js +1 -1
  88. package/dist/ui/date.js +66 -5
  89. package/dist/ui/drawer.js +11 -2
  90. package/dist/ui/dropdown-table.js +60 -5
  91. package/dist/ui/dropdown.js +3 -2
  92. package/dist/ui/file-upload.js +1 -1
  93. package/dist/ui/form.js +1 -1
  94. package/dist/ui/index.css +8857 -3974
  95. package/dist/ui/input.js +362 -260
  96. package/dist/ui/menu.js +1 -1
  97. package/dist/ui/modal.js +11 -2
  98. package/dist/ui/otp.js +835 -0
  99. package/dist/ui/select.js +67 -3
  100. package/dist/ui/shadow/accordion.js +31 -4
  101. package/dist/ui/shadow/alert.js +1 -1
  102. package/dist/ui/shadow/button-dropdown.js +1 -1
  103. package/dist/ui/shadow/chart.js +4 -4
  104. package/dist/ui/shadow/date.js +67 -6
  105. package/dist/ui/shadow/drawer.js +11 -2
  106. package/dist/ui/shadow/dropdown-table.js +62 -7
  107. package/dist/ui/shadow/dropdown.js +3 -2
  108. package/dist/ui/shadow/input.js +367 -261
  109. package/dist/ui/shadow/modal.js +11 -2
  110. package/dist/ui/shadow/otp.js +842 -0
  111. package/dist/ui/shadow/select.js +71 -6
  112. package/dist/ui/shadow/sidebar.js +2 -2
  113. package/dist/ui/shadow/slider.js +1082 -0
  114. package/dist/ui/shadow/table.js +6 -6
  115. package/dist/ui/shadow/tag-input.js +72 -7
  116. package/dist/ui/shadow/textarea.js +63 -2
  117. package/dist/ui/sidebar.js +2 -2
  118. package/dist/ui/slider.js +1079 -0
  119. package/dist/ui/table.js +2 -2
  120. package/dist/ui/tag-input.js +67 -3
  121. package/dist/ui/textarea.js +62 -1
  122. package/dist/ui/theme/luma.css +505 -466
  123. package/dist/ui/theme/lyra.css +505 -466
  124. package/dist/ui/theme/maia.css +501 -466
  125. package/dist/ui/theme/mira.css +505 -466
  126. package/dist/ui/theme/nova.css +503 -466
  127. package/dist/ui/theme/one.css +742 -699
  128. package/dist/ui/theme/rhea.css +506 -466
  129. package/dist/ui/theme/sera.css +507 -466
  130. package/dist/ui/theme/vega.css +495 -466
  131. package/dist/{vite-DcyRlTTf.js → vite-CivRCJaf.js} +2 -0
  132. package/dist/vite.js +1 -1
  133. package/dist/vue.d.ts +10 -0
  134. package/package.json +30 -15
  135. package/dist/input.css_raw-CGESifay.js +0 -5
@@ -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 input_css_raw_default } from "../../input.css_raw-CGESifay.js";
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 (changed.has("modelValue") && this.value !== this.modelValue) this.value = this.modelValue ?? "";
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(_name) {
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
  }