@mono-lit/helper 0.0.4 → 0.0.6
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/{form-control-core-CIdMLXMo.js → form-control-core-rW2WvOcb.js} +1 -1
- package/dist/{hydration-flush-DHrl4_KK.js → hydration-flush-9AJE8FRl.js} +398 -1
- package/dist/index.js +6 -5
- package/dist/{index.node-dvrNviMx.js → index.node-tTkISFn2.js} +2 -2
- package/dist/index.node.js +6 -5
- package/dist/input-format-GrRCJBqV.js +251 -0
- package/dist/input.css_raw-CQu2fAsU.js +5 -0
- package/dist/{light-slots-DdDdZ6zs.js → light-slots-BnBR29E7.js} +1 -1
- package/dist/{light-slots-4ZCjOcBg.js → light-slots-Cb6e-LU3.js} +1 -1
- package/dist/{mono-button-CeDB9khW.js → mono-button-Cy5-Y0At.js} +3 -3
- package/dist/{mono-button.shadow-D9NdMsmH.js → mono-button.shadow-CIBGisyo.js} +1 -2
- package/dist/{mono-data-grid-BHlo1IgR.js → mono-data-grid-KDpu4d4_.js} +5 -4
- package/dist/{mono-element-C7vCschg.js → mono-element-Bc9wElPy.js} +1 -1
- package/dist/{mono-form-controller-IYn0MuUQ.js → mono-form-controller-BkMR8g14.js} +18 -3
- package/dist/mono-ui-KOBJcnXk.js +758 -0
- package/dist/{reactive-DFCdZbH9.js → normalize-error-BmLbgANw.js} +1 -48
- package/dist/nuxt.js +1 -1
- 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/table/column-autosize.d.ts +74 -0
- package/dist/src/components/table/mono-data-grid.d.ts +10 -0
- package/dist/src/components/table/mono-table-th-core.d.ts +3 -0
- package/dist/src/components/table/table-types.d.ts +8 -1
- package/dist/src/components/tag-input/mono-tag-input.d.ts +12 -0
- package/dist/src/components/tag-input/mono-tag-input.shadow.d.ts +9 -1
- package/dist/src/components/tag-input/tag-input-core.d.ts +12 -1
- package/dist/src/components/tag-input/tag-input-types.d.ts +33 -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/mono-size-attrs.d.ts +10 -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 +3 -2
- package/dist/src/entries/index.node.d.ts +3 -2
- package/dist/ssr-lit/runtime/components/LitWrapper.vue +11 -11
- package/dist/ssr-lit/runtime/components/LitWrapperClient.vue +11 -11
- package/dist/ssr-lit/runtime/components/LitWrapperServer.vue +38 -38
- package/dist/ssr-lit/runtime/plugins/antiFouc.server.js +25 -25
- package/dist/ssr-lit/runtime/plugins/hydrateSupport.client.js +4 -4
- package/dist/ssr-lit/runtime/plugins/polyfill.client.js +9 -9
- package/dist/ssr-lit/runtime/utils/customElements.js +11 -11
- package/dist/ssr-lit/runtime/utils/litElementRenderer.js +28 -28
- 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 +19 -6
- package/dist/ui/alert.js +3 -3
- package/dist/ui/breadcrumb.js +3 -3
- package/dist/ui/button-dropdown.js +3 -3
- package/dist/ui/button.js +1 -1
- package/dist/ui/card.js +3 -3
- package/dist/ui/chart.js +3 -4
- package/dist/ui/checkbox.js +3 -3
- package/dist/ui/chip.js +2 -2
- package/dist/ui/date.js +68 -7
- package/dist/ui/drawer.js +14 -5
- package/dist/ui/dropdown-table.js +63 -9
- package/dist/ui/dropdown.js +6 -5
- package/dist/ui/file-upload.js +3 -3
- package/dist/ui/filter.js +2 -2
- package/dist/ui/form.js +1 -1
- package/dist/ui/index.css +4276 -2543
- package/dist/ui/input.js +365 -264
- package/dist/ui/menu.js +3 -3
- package/dist/ui/modal.js +15 -6
- package/dist/ui/nav.js +3 -3
- package/dist/ui/otp.js +834 -0
- package/dist/ui/radio.js +3 -3
- package/dist/ui/rich-text-editor.js +3 -4
- package/dist/ui/select.js +70 -7
- package/dist/ui/shadow/accordion.js +31 -4
- package/dist/ui/shadow/alert.js +2 -2
- package/dist/ui/shadow/breadcrumb.js +1 -1
- package/dist/ui/shadow/button-dropdown.js +3 -3
- package/dist/ui/shadow/button.js +1 -1
- package/dist/ui/shadow/card.js +1 -2
- package/dist/ui/shadow/chart.js +5 -6
- package/dist/ui/shadow/checkbox.js +2 -2
- package/dist/ui/shadow/chip.js +1 -1
- package/dist/ui/shadow/date.js +69 -9
- package/dist/ui/shadow/drawer.js +12 -3
- package/dist/ui/shadow/dropdown-table.js +64 -10
- package/dist/ui/shadow/dropdown.js +4 -3
- package/dist/ui/shadow/file-upload.js +2 -2
- package/dist/ui/shadow/filter.js +1 -2
- package/dist/ui/shadow/input.js +369 -264
- package/dist/ui/shadow/menu.js +1 -1
- package/dist/ui/shadow/modal.js +13 -4
- package/dist/ui/shadow/nav.js +1 -1
- package/dist/ui/shadow/otp.js +841 -0
- package/dist/ui/shadow/radio.js +2 -2
- package/dist/ui/shadow/rich-text-editor.js +2 -3
- package/dist/ui/shadow/select.js +74 -10
- package/dist/ui/shadow/sidebar.js +3 -3
- package/dist/ui/shadow/slider.js +1081 -0
- package/dist/ui/shadow/switch.js +2 -2
- package/dist/ui/shadow/table.js +40 -11
- package/dist/ui/shadow/tabs.js +1 -1
- package/dist/ui/shadow/tag-input.js +160 -15
- package/dist/ui/shadow/textarea.js +65 -5
- package/dist/ui/sidebar.js +5 -5
- package/dist/ui/slider.js +1078 -0
- package/dist/ui/switch.js +3 -3
- package/dist/ui/table.js +36 -7
- package/dist/ui/tabs.js +3 -3
- package/dist/ui/tag-input.js +167 -7
- package/dist/ui/textarea.js +65 -5
- 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 +31 -13
- package/dist/css-size-DWAPgQdT.js +0 -38
- package/dist/input.css_raw-CGESifay.js +0 -5
- package/dist/mono-ui-BwUc0L6B.js +0 -360
package/dist/ui/shadow/input.js
CHANGED
|
@@ -1,264 +1,16 @@
|
|
|
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-
|
|
1
|
+
import { _ as buildSizeStyle, a as __decorate, f as customElement, i as withShadowUtilityStyles, l as defineHybridPropAliases, r as toShadowCss, s as booleanStringConverter, t as flushSsrHydration } from "../../hydration-flush-9AJE8FRl.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 { t as
|
|
5
|
-
import { t as MonoFormControlCore } from "../../form-control-core-
|
|
6
|
-
import { t as
|
|
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";
|
|
5
|
+
import { t as MonoFormControlCore } from "../../form-control-core-rW2WvOcb.js";
|
|
6
|
+
import { n as FieldTooltipController, t as field_tooltip_css_raw_default } from "../../field-tooltip.css_raw-DEctHXKm.js";
|
|
7
|
+
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";
|
|
8
|
+
import { t as input_css_raw_default } from "../../input.css_raw-CQu2fAsU.js";
|
|
7
9
|
import { LitElement, html, isServer, nothing, unsafeCSS } from "lit";
|
|
8
10
|
import { property, query, state } from "lit/decorators.js";
|
|
9
11
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
10
12
|
import { styleMap } from "lit/directives/style-map.js";
|
|
11
13
|
|
|
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
14
|
//#region src/components/input/input-core.ts
|
|
263
15
|
const numberStringConverter = {
|
|
264
16
|
fromAttribute(value) {
|
|
@@ -272,6 +24,18 @@ const numberStringConverter = {
|
|
|
272
24
|
}
|
|
273
25
|
};
|
|
274
26
|
/**
|
|
27
|
+
* `modelValue` reflects to `model-value`, but only while it is TEXT — a `type="file"` value is a
|
|
28
|
+
* `File` / `File[]`, which would otherwise land in the DOM as `"[object File]"`.
|
|
29
|
+
*/
|
|
30
|
+
const modelValueConverter = {
|
|
31
|
+
fromAttribute(value) {
|
|
32
|
+
return value ?? "";
|
|
33
|
+
},
|
|
34
|
+
toAttribute(value) {
|
|
35
|
+
return typeof value === "string" ? value : null;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
275
39
|
* `MonoInputCore` — all render-mode-agnostic logic for `mono-input`: reactive
|
|
276
40
|
* props, hybrid prop aliases (camelCase ↔ kebab ↔ lowercase), attribute
|
|
277
41
|
* observation, value/model two-way sync, events, validation/class computation,
|
|
@@ -294,13 +58,27 @@ const MonoInputCore = (superClass) => {
|
|
|
294
58
|
this.size = "md";
|
|
295
59
|
this.color = "primary";
|
|
296
60
|
this.variant = "outlined";
|
|
61
|
+
this.labelPosition = "top";
|
|
62
|
+
this.validationPosition = "bottom";
|
|
297
63
|
this.modelValue = "";
|
|
298
64
|
this.value = "";
|
|
65
|
+
this.accept = "";
|
|
66
|
+
this.multiple = false;
|
|
67
|
+
this.showValue = true;
|
|
299
68
|
this.formatOn = "input";
|
|
300
69
|
this.name = "";
|
|
301
70
|
this.placeholder = "";
|
|
302
71
|
this.label = "";
|
|
303
72
|
this.helperText = "";
|
|
73
|
+
this.tooltip = null;
|
|
74
|
+
this._tooltip = new FieldTooltipController(this, () => this.tooltip);
|
|
75
|
+
this._validation = new FieldValidationController(this, () => ({
|
|
76
|
+
position: this.validationPosition,
|
|
77
|
+
...this._validationNotice ?? {
|
|
78
|
+
state: void 0,
|
|
79
|
+
text: void 0
|
|
80
|
+
}
|
|
81
|
+
}), () => this.renderRoot?.querySelector?.("[mono-input] > [mono-field]"));
|
|
304
82
|
this.validationState = "default";
|
|
305
83
|
this.validationMessage = "";
|
|
306
84
|
this.error = false;
|
|
@@ -325,6 +103,8 @@ const MonoInputCore = (superClass) => {
|
|
|
325
103
|
this._inputId = `mono-input-${Math.random().toString(36).slice(2)}`;
|
|
326
104
|
this._messageId = `${this._inputId}-message`;
|
|
327
105
|
defineHybridPropAliases(this, [
|
|
106
|
+
"labelPosition",
|
|
107
|
+
"validationPosition",
|
|
328
108
|
"modelValue",
|
|
329
109
|
"formatDisplay",
|
|
330
110
|
"formatValue",
|
|
@@ -338,6 +118,8 @@ const MonoInputCore = (superClass) => {
|
|
|
338
118
|
"ariaLabelText",
|
|
339
119
|
"minLength",
|
|
340
120
|
"maxLength",
|
|
121
|
+
"maxFileSize",
|
|
122
|
+
"showValue",
|
|
341
123
|
"cssClass",
|
|
342
124
|
"minWidth",
|
|
343
125
|
"maxWidth",
|
|
@@ -386,6 +168,8 @@ const MonoInputCore = (superClass) => {
|
|
|
386
168
|
"arialabel",
|
|
387
169
|
"minlength",
|
|
388
170
|
"maxlength",
|
|
171
|
+
"maxfilesize",
|
|
172
|
+
"showvalue",
|
|
389
173
|
"css-class",
|
|
390
174
|
"cssclass",
|
|
391
175
|
"formatdisplay",
|
|
@@ -446,10 +230,53 @@ const MonoInputCore = (superClass) => {
|
|
|
446
230
|
case "maxlength":
|
|
447
231
|
this.maxLength = this._toOptionalNumber(newValue);
|
|
448
232
|
return;
|
|
233
|
+
case "maxfilesize":
|
|
234
|
+
this.maxFileSize = this._toOptionalNumber(newValue);
|
|
235
|
+
return;
|
|
236
|
+
case "showvalue":
|
|
237
|
+
this.showValue = newValue === null ? true : booleanStringConverter.fromAttribute(newValue);
|
|
238
|
+
return;
|
|
449
239
|
}
|
|
450
240
|
}
|
|
241
|
+
/** The validation message and its state — the same precedence the message row uses. */
|
|
242
|
+
get _validationNotice() {
|
|
243
|
+
if (this.validationMessage) return {
|
|
244
|
+
state: this._resolvedValidationState,
|
|
245
|
+
text: this.validationMessage
|
|
246
|
+
};
|
|
247
|
+
if (this.errorMessage) return {
|
|
248
|
+
state: "invalid",
|
|
249
|
+
text: this.errorMessage
|
|
250
|
+
};
|
|
251
|
+
if (this.successMessage) return {
|
|
252
|
+
state: "valid",
|
|
253
|
+
text: this.successMessage
|
|
254
|
+
};
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
/** The message that moved to the icon / float bubble, kept for assistive tech only. */
|
|
258
|
+
_renderMovedMessage() {
|
|
259
|
+
const notice = this._validationNotice;
|
|
260
|
+
return html`<div
|
|
261
|
+
class=${`${this._cls("mono-input-message", "message")} ${notice.state}`}
|
|
262
|
+
mono-message=${notice.state}
|
|
263
|
+
mono-sr-only
|
|
264
|
+
role=${notice.state === "invalid" ? "alert" : nothing}
|
|
265
|
+
>${notice.text}</div>`;
|
|
266
|
+
}
|
|
451
267
|
willUpdate(changed) {
|
|
452
|
-
if (
|
|
268
|
+
if (this._isFile) {
|
|
269
|
+
if (changed.has("modelValue") || changed.has("type")) {
|
|
270
|
+
const files = this._fileList();
|
|
271
|
+
const names = this._fileNames(files);
|
|
272
|
+
this.value = names;
|
|
273
|
+
this._display = names;
|
|
274
|
+
if (!files.length && this._fileEl) this._fileEl.value = "";
|
|
275
|
+
}
|
|
276
|
+
this._internalWrite = false;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (changed.has("modelValue") && this.value !== this.modelValue) this.value = typeof this.modelValue === "string" ? this.modelValue : "";
|
|
453
280
|
if (changed.has("value") && this.modelValue !== this.value) this.modelValue = String(this.value ?? "");
|
|
454
281
|
if (!this._internalWrite && !this._focused && (changed.has("value") || changed.has("modelValue") || changed.has("formatDisplay"))) this._syncDisplayFromValue();
|
|
455
282
|
this._internalWrite = false;
|
|
@@ -496,7 +323,7 @@ const MonoInputCore = (superClass) => {
|
|
|
496
323
|
this.readonly ? "readonly" : "",
|
|
497
324
|
this._resolvedValidationState !== "default" ? `is-${this._resolvedValidationState}` : "",
|
|
498
325
|
this._hasPrefixSlot ? "has-prefix" : "",
|
|
499
|
-
this._hasSuffixSlot || this.clearable ? "has-suffix" : "",
|
|
326
|
+
this._hasSuffixSlot || this.clearable || this._isFile ? "has-suffix" : "",
|
|
500
327
|
this.value ? "has-value" : "",
|
|
501
328
|
this.cssClassName,
|
|
502
329
|
this.cssClass?.root
|
|
@@ -512,12 +339,127 @@ const MonoInputCore = (superClass) => {
|
|
|
512
339
|
this.readonly ? "readonly" : "",
|
|
513
340
|
this._resolvedValidationState !== "default" ? `is-${this._resolvedValidationState}` : "",
|
|
514
341
|
this._hasPrefixSlot ? "has-prefix" : "",
|
|
515
|
-
this._hasSuffixSlot || this.clearable ? "has-suffix" : "",
|
|
342
|
+
this._hasSuffixSlot || this.clearable || this._isFile ? "has-suffix" : "",
|
|
516
343
|
this.value ? "has-value" : ""
|
|
517
344
|
].filter(Boolean).join(" ");
|
|
518
345
|
}
|
|
346
|
+
get _isFile() {
|
|
347
|
+
return this.type === "file";
|
|
348
|
+
}
|
|
349
|
+
get _isRange() {
|
|
350
|
+
return this.type === "range";
|
|
351
|
+
}
|
|
352
|
+
/** `min` / `max` / `step` as the native range resolves them (0 / 100 / 1 when unset). */
|
|
353
|
+
_rangeBounds() {
|
|
354
|
+
const min = this._toOptionalNumber(this.min) ?? 0;
|
|
355
|
+
const rawMax = this._toOptionalNumber(this.max) ?? 100;
|
|
356
|
+
const max = rawMax < min ? min : rawMax;
|
|
357
|
+
const step = this._toOptionalNumber(this.step);
|
|
358
|
+
return {
|
|
359
|
+
min,
|
|
360
|
+
max,
|
|
361
|
+
step: step && step > 0 ? step : 1
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* The value the slider is actually AT, as a string. An empty value is not a position — the
|
|
366
|
+
* native range parks itself at the midpoint — so the readout and the fill follow the native
|
|
367
|
+
* rule rather than reading `''` as 0. The model is not rewritten: nothing was chosen yet.
|
|
368
|
+
*/
|
|
369
|
+
get _rangeValue() {
|
|
370
|
+
const { min, max, step } = this._rangeBounds();
|
|
371
|
+
const target = this._toOptionalNumber(this.value) ?? min + (max - min) / 2;
|
|
372
|
+
const snapped = min + Math.round((target - min) / step) * step;
|
|
373
|
+
return String(Number(Math.min(max, Math.max(min, snapped)).toFixed(10)));
|
|
374
|
+
}
|
|
375
|
+
/** How far along the track the value sits, 0–100 — drives the fill. */
|
|
376
|
+
get _rangeProgress() {
|
|
377
|
+
const { min, max } = this._rangeBounds();
|
|
378
|
+
if (max === min) return 0;
|
|
379
|
+
return (Number(this._rangeValue) - min) / (max - min) * 100;
|
|
380
|
+
}
|
|
381
|
+
/** The readout text: the value drawn through `format-display`. */
|
|
382
|
+
get _rangeText() {
|
|
383
|
+
return this._formatDisplay(this._rangeValue, { pad: true });
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* A native range ignores `readonly`, so a readonly slider would still move. Put the thumb
|
|
387
|
+
* back where the value says and report that the event was handled.
|
|
388
|
+
*/
|
|
389
|
+
_revertReadonlyRange(event) {
|
|
390
|
+
if (!this._isRange || !this.readonly || this.disabled) return false;
|
|
391
|
+
event.currentTarget.value = this._rangeValue;
|
|
392
|
+
event.stopPropagation();
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
/** `modelValue` as an array of files, whatever shape it arrived in (File, File[], null, ''). */
|
|
396
|
+
_fileList() {
|
|
397
|
+
const isFile = (v) => !!v && typeof v === "object" && typeof v.name === "string";
|
|
398
|
+
const v = this.modelValue;
|
|
399
|
+
if (Array.isArray(v)) return v.filter(isFile);
|
|
400
|
+
return isFile(v) ? [v] : [];
|
|
401
|
+
}
|
|
402
|
+
_fileNames(files) {
|
|
403
|
+
return files.map((f) => f.name).join(", ");
|
|
404
|
+
}
|
|
405
|
+
_openFilePicker() {
|
|
406
|
+
if (this.disabled || this.readonly) return;
|
|
407
|
+
this._fileEl?.click();
|
|
408
|
+
}
|
|
409
|
+
_handleFileKeydown(event) {
|
|
410
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
this._openFilePicker();
|
|
413
|
+
}
|
|
414
|
+
/** Files above `maxFileSize` are dropped, each one reported through `error`. */
|
|
415
|
+
_acceptFiles(files, sourceEvent) {
|
|
416
|
+
const limit = this.maxFileSize;
|
|
417
|
+
if (!limit || limit <= 0) return files;
|
|
418
|
+
return files.filter((file) => {
|
|
419
|
+
if (file.size <= limit) return true;
|
|
420
|
+
const detail = {
|
|
421
|
+
message: `File "${file.name}" exceeds the maximum size limit.`,
|
|
422
|
+
reason: "max-file-size",
|
|
423
|
+
file,
|
|
424
|
+
maxFileSize: limit,
|
|
425
|
+
sourceEvent
|
|
426
|
+
};
|
|
427
|
+
dispatchMonoEvent(this, "error", detail);
|
|
428
|
+
return false;
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The picker's native `input` fires BEFORE its `change`, i.e. before the value below is
|
|
433
|
+
* written — left to bubble, a host `@input` (and a bound form) would read the OLD value. Our
|
|
434
|
+
* own `input` is emitted from the change handler instead.
|
|
435
|
+
*/
|
|
436
|
+
_swallowNativeInput(event) {
|
|
437
|
+
event.stopPropagation();
|
|
438
|
+
}
|
|
439
|
+
_handleFileChange(event) {
|
|
440
|
+
if (this.disabled || this.readonly) return;
|
|
441
|
+
const input = event.currentTarget;
|
|
442
|
+
const files = this._acceptFiles(Array.from(input.files ?? []), event);
|
|
443
|
+
if (!files.length) {
|
|
444
|
+
input.value = "";
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this._internalWrite = true;
|
|
448
|
+
const oldValue = this.modelValue;
|
|
449
|
+
const nextValue = this.multiple ? files : files[0];
|
|
450
|
+
this.modelValue = nextValue;
|
|
451
|
+
this.value = this._display = this._fileNames(files);
|
|
452
|
+
const detail = this._createModelDetail({
|
|
453
|
+
modelValue: nextValue,
|
|
454
|
+
oldValue,
|
|
455
|
+
sourceEvent: event
|
|
456
|
+
});
|
|
457
|
+
this._emitInput(detail);
|
|
458
|
+
this._emitChange(detail);
|
|
459
|
+
}
|
|
519
460
|
_createModelDetail(args) {
|
|
520
461
|
return {
|
|
462
|
+
...this._isFile ? { files: this._fileList() } : {},
|
|
521
463
|
modelValue: args.modelValue,
|
|
522
464
|
currentValue: args.modelValue,
|
|
523
465
|
oldValue: args.oldValue,
|
|
@@ -667,11 +609,12 @@ const MonoInputCore = (superClass) => {
|
|
|
667
609
|
} catch {}
|
|
668
610
|
}
|
|
669
611
|
_handleInput(event) {
|
|
612
|
+
if (this._revertReadonlyRange(event)) return;
|
|
670
613
|
if (this.disabled || this.readonly) return;
|
|
671
614
|
this._internalWrite = true;
|
|
672
615
|
const input = event.currentTarget;
|
|
673
616
|
const oldValue = this.modelValue;
|
|
674
|
-
if (!this._hasFormat) {
|
|
617
|
+
if (!this._hasFormat || this._isRange) {
|
|
675
618
|
const nextValue = input.value;
|
|
676
619
|
this.value = nextValue;
|
|
677
620
|
this.modelValue = nextValue;
|
|
@@ -697,11 +640,12 @@ const MonoInputCore = (superClass) => {
|
|
|
697
640
|
}));
|
|
698
641
|
}
|
|
699
642
|
_handleChange(event) {
|
|
643
|
+
if (this._revertReadonlyRange(event)) return;
|
|
700
644
|
if (this.disabled || this.readonly) return;
|
|
701
645
|
this._internalWrite = true;
|
|
702
646
|
const input = event.currentTarget;
|
|
703
647
|
const oldValue = this.modelValue;
|
|
704
|
-
if (!this._hasFormat) {
|
|
648
|
+
if (!this._hasFormat || this._isRange) {
|
|
705
649
|
const nextValue = input.value;
|
|
706
650
|
this.value = nextValue;
|
|
707
651
|
this.modelValue = nextValue;
|
|
@@ -735,6 +679,21 @@ const MonoInputCore = (superClass) => {
|
|
|
735
679
|
if (this.disabled || this.readonly) return;
|
|
736
680
|
this._internalWrite = true;
|
|
737
681
|
const oldValue = this.modelValue;
|
|
682
|
+
if (this._isFile) {
|
|
683
|
+
const empty = this.multiple ? [] : null;
|
|
684
|
+
this.modelValue = empty;
|
|
685
|
+
this.value = this._display = "";
|
|
686
|
+
if (this._fileEl) this._fileEl.value = "";
|
|
687
|
+
this._inputEl?.focus();
|
|
688
|
+
const detail = this._createModelDetail({
|
|
689
|
+
modelValue: empty,
|
|
690
|
+
oldValue
|
|
691
|
+
});
|
|
692
|
+
this._emitInput(detail);
|
|
693
|
+
this._emitChange(detail);
|
|
694
|
+
this._emitClear(detail);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
738
697
|
const nextValue = "";
|
|
739
698
|
this.value = nextValue;
|
|
740
699
|
this.modelValue = nextValue;
|
|
@@ -769,7 +728,10 @@ const MonoInputCore = (superClass) => {
|
|
|
769
728
|
mono-size=${this.size === "md" ? nothing : this.size}
|
|
770
729
|
mono-color=${this.color === "primary" ? nothing : this.color}
|
|
771
730
|
mono-variant=${this.variant === "outlined" ? nothing : this.variant}
|
|
731
|
+
mono-label-position=${this.labelPosition === "top" ? nothing : this.labelPosition}
|
|
732
|
+
mono-validation-position=${this.validationPosition === "bottom" ? nothing : this.validationPosition}
|
|
772
733
|
mono-validation-state=${state === "default" ? nothing : state}
|
|
734
|
+
mono-type=${this._isFile || this._isRange ? this.type : nothing}
|
|
773
735
|
?mono-disabled=${this.disabled}
|
|
774
736
|
?mono-readonly=${this.readonly}
|
|
775
737
|
?mono-required=${this.required}
|
|
@@ -784,8 +746,111 @@ const MonoInputCore = (superClass) => {
|
|
|
784
746
|
get _describedBy() {
|
|
785
747
|
return this.validationMessage || this.errorMessage || this.successMessage || this.helperText || this._hasHelperSlot ? this._messageId : void 0;
|
|
786
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* `type="file"`: a hidden native picker, and in front of it a read-only text input showing
|
|
751
|
+
* the picked name(s). The text input keeps `mono-native` + `.mono-input-native`, so focus,
|
|
752
|
+
* the label's `for`, sizing and the focus ring all behave exactly as on a text field.
|
|
753
|
+
*/
|
|
754
|
+
_renderFileNative() {
|
|
755
|
+
return html`
|
|
756
|
+
<input
|
|
757
|
+
type="file"
|
|
758
|
+
mono-file-input
|
|
759
|
+
hidden
|
|
760
|
+
tabindex="-1"
|
|
761
|
+
accept=${ifDefined(this.accept || void 0)}
|
|
762
|
+
?multiple=${this.multiple}
|
|
763
|
+
name=${ifDefined(this.name || void 0)}
|
|
764
|
+
?disabled=${this.disabled}
|
|
765
|
+
@input=${this._swallowNativeInput}
|
|
766
|
+
@change=${this._handleFileChange}
|
|
767
|
+
/>
|
|
768
|
+
<input
|
|
769
|
+
id=${this._inputId}
|
|
770
|
+
class=${`${this._cls("mono-input-native", "native")} ${this.size}`}
|
|
771
|
+
mono-native
|
|
772
|
+
mono-file
|
|
773
|
+
type="text"
|
|
774
|
+
readonly
|
|
775
|
+
.value=${this._display}
|
|
776
|
+
placeholder=${ifDefined(this.placeholder || void 0)}
|
|
777
|
+
?disabled=${this.disabled}
|
|
778
|
+
?required=${this.required}
|
|
779
|
+
aria-label=${ifDefined(this._ariaLabel)}
|
|
780
|
+
aria-invalid=${this._resolvedValidationState === "invalid" ? "true" : "false"}
|
|
781
|
+
aria-describedby=${ifDefined(this._describedBy)}
|
|
782
|
+
aria-haspopup="dialog"
|
|
783
|
+
@focus=${this._handleFocus}
|
|
784
|
+
@blur=${this._handleBlur}
|
|
785
|
+
@click=${this._openFilePicker}
|
|
786
|
+
@keydown=${this._handleFileKeydown}
|
|
787
|
+
/>
|
|
788
|
+
`;
|
|
789
|
+
}
|
|
790
|
+
/** `type="file"` — the upload glyph at the end of the field; clicking it opens the picker too. */
|
|
791
|
+
_renderFileIcon() {
|
|
792
|
+
if (!this._isFile) return nothing;
|
|
793
|
+
return html`
|
|
794
|
+
<span
|
|
795
|
+
class=${this._cls("mono-input-suffix", "fileIcon")}
|
|
796
|
+
mono-suffix
|
|
797
|
+
mono-file-icon
|
|
798
|
+
aria-hidden="true"
|
|
799
|
+
@click=${this._openFilePicker}
|
|
800
|
+
>${this.renderIcon("upload")}</span>
|
|
801
|
+
`;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* `type="range"`: Basecoat's slider. The fill is a gradient stop on the track, so the element
|
|
805
|
+
* writes the position as `--mono-input-range-value` (Basecoat's `--slider-value`) — hand-written
|
|
806
|
+
* markup sets the same property itself.
|
|
807
|
+
*/
|
|
808
|
+
_renderRangeNative() {
|
|
809
|
+
const { min, max, step } = this._rangeBounds();
|
|
810
|
+
const text = this._rangeText;
|
|
811
|
+
return html`
|
|
812
|
+
<input
|
|
813
|
+
id=${this._inputId}
|
|
814
|
+
class=${`${this._cls("mono-input-native", "native")} ${this.size}`}
|
|
815
|
+
mono-native
|
|
816
|
+
mono-range
|
|
817
|
+
type="range"
|
|
818
|
+
min=${min}
|
|
819
|
+
max=${max}
|
|
820
|
+
step=${ifDefined(this.step == null ? void 0 : step)}
|
|
821
|
+
.value=${this._rangeValue}
|
|
822
|
+
name=${ifDefined(this.name || void 0)}
|
|
823
|
+
style=${`--mono-input-range-value: ${this._rangeProgress}%`}
|
|
824
|
+
?disabled=${this.disabled}
|
|
825
|
+
?required=${this.required}
|
|
826
|
+
aria-label=${ifDefined(this._ariaLabel)}
|
|
827
|
+
aria-valuetext=${ifDefined(text !== this._rangeValue ? text : void 0)}
|
|
828
|
+
aria-readonly=${ifDefined(this.readonly ? "true" : void 0)}
|
|
829
|
+
aria-invalid=${this._resolvedValidationState === "invalid" ? "true" : "false"}
|
|
830
|
+
aria-describedby=${ifDefined(this._describedBy)}
|
|
831
|
+
@focus=${this._handleFocus}
|
|
832
|
+
@blur=${this._handleBlur}
|
|
833
|
+
@input=${this._handleInput}
|
|
834
|
+
@change=${this._handleChange}
|
|
835
|
+
/>
|
|
836
|
+
`;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* `type="range"` — the value readout. It sits at the end of the LABEL row when there is a
|
|
840
|
+
* label (the EJI ONE layout), and after the slider when there is not; each build calls this
|
|
841
|
+
* from both places and exactly one of them renders. `aria-hidden`: the slider already
|
|
842
|
+
* announces its value (`aria-valuetext` carries the formatted text).
|
|
843
|
+
*/
|
|
844
|
+
_renderRangeValue(where) {
|
|
845
|
+
if (!this._isRange || !this.showValue) return nothing;
|
|
846
|
+
const inLabel = !!(this.label || this._hasLabelSlot);
|
|
847
|
+
if (where === "label" !== inLabel) return nothing;
|
|
848
|
+
return html`<span class=${this._cls("mono-input-range-value", "rangeValue")} mono-range-value aria-hidden="true">${this._rangeText}</span>`;
|
|
849
|
+
}
|
|
787
850
|
/** The native <input>, shared by both builds' render(). */
|
|
788
851
|
_renderNative() {
|
|
852
|
+
if (this._isFile) return this._renderFileNative();
|
|
853
|
+
if (this._isRange) return this._renderRangeNative();
|
|
789
854
|
return html`
|
|
790
855
|
<input
|
|
791
856
|
id=${this._inputId}
|
|
@@ -818,7 +883,7 @@ const MonoInputCore = (superClass) => {
|
|
|
818
883
|
}
|
|
819
884
|
/** The clear button (uses the build-specific icon). */
|
|
820
885
|
_renderClear() {
|
|
821
|
-
if (!(this.clearable && this.value && !this.disabled && !this.readonly)) return nothing;
|
|
886
|
+
if (this._isRange || !(this.clearable && this.value && !this.disabled && !this.readonly)) return nothing;
|
|
822
887
|
return html`
|
|
823
888
|
<button
|
|
824
889
|
type="button"
|
|
@@ -859,10 +924,31 @@ const MonoInputCore = (superClass) => {
|
|
|
859
924
|
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "variant", void 0);
|
|
860
925
|
__decorate([property({
|
|
861
926
|
type: String,
|
|
927
|
+
attribute: "label-position"
|
|
928
|
+
})], MonoInputCoreClass.prototype, "labelPosition", void 0);
|
|
929
|
+
__decorate([property({
|
|
930
|
+
type: String,
|
|
931
|
+
attribute: "validation-position"
|
|
932
|
+
})], MonoInputCoreClass.prototype, "validationPosition", void 0);
|
|
933
|
+
__decorate([property({
|
|
862
934
|
attribute: "model-value",
|
|
863
|
-
reflect: true
|
|
935
|
+
reflect: true,
|
|
936
|
+
converter: modelValueConverter
|
|
864
937
|
})], MonoInputCoreClass.prototype, "modelValue", void 0);
|
|
865
938
|
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "value", void 0);
|
|
939
|
+
__decorate([property({ type: String })], MonoInputCoreClass.prototype, "accept", void 0);
|
|
940
|
+
__decorate([property({
|
|
941
|
+
reflect: true,
|
|
942
|
+
converter: booleanStringConverter
|
|
943
|
+
})], MonoInputCoreClass.prototype, "multiple", void 0);
|
|
944
|
+
__decorate([property({
|
|
945
|
+
attribute: "max-file-size",
|
|
946
|
+
converter: numberStringConverter
|
|
947
|
+
})], MonoInputCoreClass.prototype, "maxFileSize", void 0);
|
|
948
|
+
__decorate([property({
|
|
949
|
+
attribute: "show-value",
|
|
950
|
+
converter: { fromAttribute: (v) => v === null ? true : booleanStringConverter.fromAttribute(v) }
|
|
951
|
+
})], MonoInputCoreClass.prototype, "showValue", void 0);
|
|
866
952
|
__decorate([property({ attribute: false })], MonoInputCoreClass.prototype, "formatDisplay", void 0);
|
|
867
953
|
__decorate([property({ attribute: false })], MonoInputCoreClass.prototype, "formatValue", void 0);
|
|
868
954
|
__decorate([property({ attribute: "format-on" })], MonoInputCoreClass.prototype, "formatOn", void 0);
|
|
@@ -874,6 +960,7 @@ const MonoInputCore = (superClass) => {
|
|
|
874
960
|
type: String,
|
|
875
961
|
attribute: "helper-text"
|
|
876
962
|
})], MonoInputCoreClass.prototype, "helperText", void 0);
|
|
963
|
+
__decorate([property()], MonoInputCoreClass.prototype, "tooltip", void 0);
|
|
877
964
|
__decorate([property({
|
|
878
965
|
type: String,
|
|
879
966
|
attribute: "validation-state"
|
|
@@ -955,6 +1042,7 @@ const MonoInputCore = (superClass) => {
|
|
|
955
1042
|
attribute: "max-height"
|
|
956
1043
|
})], MonoInputCoreClass.prototype, "maxHeight", void 0);
|
|
957
1044
|
__decorate([query(".mono-input-native")], MonoInputCoreClass.prototype, "_inputEl", void 0);
|
|
1045
|
+
__decorate([query("[mono-file-input]")], MonoInputCoreClass.prototype, "_fileEl", void 0);
|
|
958
1046
|
__decorate([state()], MonoInputCoreClass.prototype, "_display", void 0);
|
|
959
1047
|
__decorate([state()], MonoInputCoreClass.prototype, "_focused", void 0);
|
|
960
1048
|
__decorate([state()], MonoInputCoreClass.prototype, "_hasPrefixSlotState", void 0);
|
|
@@ -972,7 +1060,7 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
972
1060
|
this.disableWarning?.("change-in-update");
|
|
973
1061
|
}
|
|
974
1062
|
static {
|
|
975
|
-
this.styles = [unsafeCSS(toShadowCss(input_css_raw_default, {
|
|
1063
|
+
this.styles = [unsafeCSS(toShadowCss(input_css_raw_default + label_position_css_raw_default + field_tooltip_css_raw_default + field_validation_css_raw_default, {
|
|
976
1064
|
host: "mono-input",
|
|
977
1065
|
append: SHADOW_EXTRA_CSS
|
|
978
1066
|
}))];
|
|
@@ -1001,7 +1089,12 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1001
1089
|
_onSlotChange(name, event) {
|
|
1002
1090
|
this._setSlotState(name, this._slotHasContent(event.target));
|
|
1003
1091
|
}
|
|
1004
|
-
renderIcon(
|
|
1092
|
+
renderIcon(name) {
|
|
1093
|
+
if (name === "upload") return html`
|
|
1094
|
+
<svg viewBox="0 0 24 24" mono-icon fill="currentColor" aria-hidden="true">
|
|
1095
|
+
<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" />
|
|
1096
|
+
</svg>
|
|
1097
|
+
`;
|
|
1005
1098
|
return html`
|
|
1006
1099
|
<svg viewBox="0 0 24 24" mono-icon fill="currentColor" aria-hidden="true">
|
|
1007
1100
|
<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 +1111,23 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1018
1111
|
>
|
|
1019
1112
|
<slot name="label" @slotchange=${(e) => this._onSlotChange("label", e)}>${this.label}</slot>
|
|
1020
1113
|
${this.required ? html`<span class=${this._cls("mono-input-required", "required")} mono-required-mark>*</span>` : nothing}
|
|
1114
|
+
${this._tooltip.render()}
|
|
1115
|
+
${this._renderRangeValue("label")}
|
|
1021
1116
|
</label>
|
|
1022
1117
|
`;
|
|
1023
1118
|
}
|
|
1024
1119
|
_renderHelper() {
|
|
1025
1120
|
const base = this._cls("mono-input-message", "message");
|
|
1121
|
+
if (this._validation.moved) return html`${this._renderHelperText(base)}${this._renderMovedMessage()}`;
|
|
1026
1122
|
if (this.validationMessage) {
|
|
1027
1123
|
const state = this._resolvedValidationState;
|
|
1028
1124
|
return html`<div class=${`${base} ${state}`} mono-message=${state} role=${state === "invalid" ? "alert" : nothing}>${this.validationMessage}</div>`;
|
|
1029
1125
|
}
|
|
1030
1126
|
if (this.errorMessage) return html`<div class=${`${base} invalid`} mono-message="invalid" role="alert">${this.errorMessage}</div>`;
|
|
1031
1127
|
if (this.successMessage) return html`<div class=${`${base} valid`} mono-message="valid">${this.successMessage}</div>`;
|
|
1128
|
+
return this._renderHelperText(base);
|
|
1129
|
+
}
|
|
1130
|
+
_renderHelperText(base) {
|
|
1032
1131
|
return html`
|
|
1033
1132
|
<div
|
|
1034
1133
|
class=${`${base} helper`}
|
|
@@ -1047,7 +1146,10 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1047
1146
|
<slot name="prefix" @slotchange=${(e) => this._onSlotChange("prefix", e)}></slot>
|
|
1048
1147
|
</span>
|
|
1049
1148
|
${this._renderNative()}
|
|
1149
|
+
${this._renderRangeValue("field")}
|
|
1150
|
+
${this._validation.renderIcon()}
|
|
1050
1151
|
${this._renderClear()}
|
|
1152
|
+
${this._renderFileIcon()}
|
|
1051
1153
|
<span class=${this._cls("mono-input-suffix", "suffix")} mono-suffix ?mono-empty=${!this._hasSuffixSlot}>
|
|
1052
1154
|
<slot name="suffix" @slotchange=${(e) => this._onSlotChange("suffix", e)}></slot>
|
|
1053
1155
|
</span>
|
|
@@ -1055,6 +1157,7 @@ let MonoInputShadow = class MonoInputShadow extends withShadowUtilityStyles(Mono
|
|
|
1055
1157
|
<div id=${this._messageId} class=${this.cssClass?.messageWrap ?? ""} mono-message-wrap>
|
|
1056
1158
|
${this._renderHelper()}
|
|
1057
1159
|
</div>
|
|
1160
|
+
${this._validation.renderFloat()}
|
|
1058
1161
|
`);
|
|
1059
1162
|
}
|
|
1060
1163
|
connectedCallback() {
|
|
@@ -1147,7 +1250,9 @@ function getInputIcon(type) {
|
|
|
1147
1250
|
time: "M12 7v5l3 3M12 22a10 10 0 1 0 0-20a10 10 0 0 0 0 20z",
|
|
1148
1251
|
month: "M7 3v3M17 3v3M5 5h14v14H5zM8 11h8",
|
|
1149
1252
|
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"
|
|
1253
|
+
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
|
+
file: "M12 16V8M8.5 11.5L12 8l3.5 3.5M7 18h10",
|
|
1255
|
+
range: "M4 12h16M9 9a3 3 0 1 0 0 6a3 3 0 0 0 0-6z"
|
|
1151
1256
|
};
|
|
1152
1257
|
return iconMap[type] ?? iconMap.text;
|
|
1153
1258
|
}
|