@mono-lit/helper 0.0.9 → 0.1.0

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 (164) hide show
  1. package/dist/accordion-core-DaNyPWRr.js +277 -0
  2. package/dist/alert-core-DWtHbX_-.js +134 -0
  3. package/dist/breadcrumb-list-core-Dh68T8Cf.js +747 -0
  4. package/dist/{mono-button-CLkHg3WW.js → button-core-DerXg84Q.js} +3 -276
  5. package/dist/button-dropdown-core-DKYEOyjy.js +654 -0
  6. package/dist/button-utils-D5-66M02.js +63 -0
  7. package/dist/card-utils-l80qAmrN.js +312 -0
  8. package/dist/chart-core-DV7hD3ZY.js +801 -0
  9. package/dist/checkbox-utils-DKdrhAzF.js +337 -0
  10. package/dist/chip-utils-DpRrd7Zj.js +403 -0
  11. package/dist/{data-source-controller-C6C4ej_M.js → data-source-controller-DS_JLrmS.js} +1 -1
  12. package/dist/{data-source-options-fxT5QIX4.js → data-source-options-DJUNU26C.js} +6 -6
  13. package/dist/{data-source-read-BL266IW0.js → data-source-read-uKJpRAzx.js} +2 -2
  14. package/dist/date-core-CyuSyvvG.js +734 -0
  15. package/dist/drawer-core-DbFWvNln.js +759 -0
  16. package/dist/dropdown-core-BCH1pK14.js +511 -0
  17. package/dist/dropdown-table-core-SfHUJVFq.js +1342 -0
  18. package/dist/export.js +1 -1
  19. package/dist/{field-path-BabiMrrA.js → field-path-DK2VBWHl.js} +1 -1
  20. package/dist/field-tooltip.css_raw-DVblekCl.js +5 -0
  21. package/dist/{field-validation.css_raw-Cx6SGrfP.js → field-validation.css_raw-BU7C6173.js} +1 -170
  22. package/dist/file-upload-core-Cz_dBQMx.js +624 -0
  23. package/dist/filter-builder-core-DyWjbBP7.js +381 -0
  24. package/dist/{filter-builder-B6Y2mmbM.js → filter-builder-vbqIuPhX.js} +1 -1
  25. package/dist/{filter-eval-ChDSaAUH.js → filter-eval-CqeBnMYh.js} +1 -1
  26. package/dist/{filter-odata-Bi27XUpQ.js → filter-odata-GsczYKNC.js} +1 -1
  27. package/dist/{hydration-flush-BaE9gN3C.js → hydration-flush-Volt9X7Q.js} +2 -1071
  28. package/dist/index.js +11 -11
  29. package/dist/{index.node-DTqDBca7.js → index.node-Csy9zrr1.js} +4 -4
  30. package/dist/index.node.js +11 -11
  31. package/dist/input-utils-BglY-k0H.js +1145 -0
  32. package/dist/menu-core-DkAYuCZ2.js +831 -0
  33. package/dist/modal-core-CSBSPN_m.js +844 -0
  34. package/dist/mono-button-DVvZ03P-.js +280 -0
  35. package/dist/{mono-button.shadow-DqZMpcqY.js → mono-button.shadow-BANxMGln.js} +6 -594
  36. package/dist/{mono-data-chart-NKPdAgVq.js → mono-data-chart-ChF4J9RT.js} +6 -6
  37. package/dist/{mono-data-dropdown-B2iRpJuI.js → mono-data-dropdown-Bgj2PDfm.js} +1 -1
  38. package/dist/{mono-data-grid-llJCX9MC.js → mono-data-grid-DJkp96N-.js} +144 -46
  39. package/dist/{mono-event-BPku9HpS.js → mono-event-CAZf0F_k.js} +14 -9
  40. package/dist/{mono-form-controller-BrA10erm.js → mono-form-controller-DxZLvyg6.js} +2 -1
  41. package/dist/mono-table-checkbox-core-BKOMx8R5.js +5288 -0
  42. package/dist/nav-core-B5qU2dzn.js +225 -0
  43. package/dist/{normalize-error-D93O4q8g.js → normalize-error-BEcd1tmS.js} +2 -2
  44. package/dist/nuxt.js +1 -1
  45. package/dist/otp-core-DXg3vFLh.js +758 -0
  46. package/dist/radio-utils-Dk9Tl1jz.js +319 -0
  47. package/dist/rich-text-editor-core-Ddd_QaLn.js +1280 -0
  48. package/dist/select-utils-BgArYHk8.js +2140 -0
  49. package/dist/sidebar-core-CD6txDo3.js +583 -0
  50. package/dist/slider-core-vcWr321-.js +1014 -0
  51. package/dist/src/components/dropdown-table/dropdown-table-core.d.ts +2 -2
  52. package/dist/src/components/dropdown-table/dropdown-table-types.d.ts +1 -1
  53. package/dist/src/components/dropdown-table/index.d.ts +1 -1
  54. package/dist/src/components/dropdown-table/index.shadow.d.ts +1 -1
  55. package/dist/src/components/dropdown-table/mono-data-dropdown.d.ts +1 -1
  56. package/dist/src/components/form/form-types.d.ts +9 -3
  57. package/dist/src/components/input/input-core.d.ts +1 -1
  58. package/dist/src/components/input/input-types.d.ts +1 -1
  59. package/dist/src/components/otp/otp-core.d.ts +1 -1
  60. package/dist/src/components/select/select-core.d.ts +1 -1
  61. package/dist/src/components/select/select-types.d.ts +1 -1
  62. package/dist/src/components/slider/slider-core.d.ts +1 -1
  63. package/dist/src/components/slider/slider-types.d.ts +1 -1
  64. package/dist/src/components/table/filter-suggestion.d.ts +171 -0
  65. package/dist/src/components/table/index.d.ts +5 -4
  66. package/dist/src/components/table/index.shadow.d.ts +5 -4
  67. package/dist/src/components/table/mono-data-grid.d.ts +22 -3
  68. package/dist/src/components/table/mono-table-empty-core.d.ts +37 -0
  69. package/dist/src/components/table/mono-table-search-core.d.ts +22 -3
  70. package/dist/src/components/table/table-types.d.ts +74 -4
  71. package/dist/src/components/tag-input/tag-input-core.d.ts +1 -1
  72. package/dist/src/components/tag-input/tag-input-types.d.ts +1 -1
  73. package/dist/src/components/textarea/textarea-core.d.ts +1 -1
  74. package/dist/src/composables/mono-event.d.ts +7 -1
  75. package/dist/src/entries/index.d.ts +8 -6
  76. package/dist/src/entries/index.node.d.ts +8 -6
  77. package/dist/src/search/index.d.ts +1 -1
  78. package/dist/src/utils/group-count.d.ts +36 -0
  79. package/dist/switch-utils-CTZ4id2V.js +338 -0
  80. package/dist/tabs-core-r5-B8k_N.js +317 -0
  81. package/dist/tag-input-utils-D2JA90DQ.js +3060 -0
  82. package/dist/textarea-utils-VU1KeXm8.js +821 -0
  83. package/dist/ui/accordion.js +2 -274
  84. package/dist/ui/alert.js +2 -131
  85. package/dist/ui/breadcrumb.js +4 -745
  86. package/dist/ui/button-dropdown.js +4 -652
  87. package/dist/ui/button.js +2 -63
  88. package/dist/ui/card.js +2 -310
  89. package/dist/ui/chart.js +4 -800
  90. package/dist/ui/checkbox.js +4 -335
  91. package/dist/ui/chip.js +4 -402
  92. package/dist/ui/date.js +3 -732
  93. package/dist/ui/drawer.js +3 -757
  94. package/dist/ui/dropdown-table.js +5 -1341
  95. package/dist/ui/dropdown.js +4 -509
  96. package/dist/ui/file-upload.js +3 -622
  97. package/dist/ui/filter.js +5 -380
  98. package/dist/ui/form.js +1 -1
  99. package/dist/ui/index.css +118 -1
  100. package/dist/ui/input.js +3 -1141
  101. package/dist/ui/menu.js +4 -828
  102. package/dist/ui/modal.js +4 -843
  103. package/dist/ui/nav.js +4 -223
  104. package/dist/ui/otp.js +4 -756
  105. package/dist/ui/radio.js +4 -317
  106. package/dist/ui/rich-text-editor.js +4 -1278
  107. package/dist/ui/select.js +5 -2138
  108. package/dist/ui/shadow/accordion.js +4 -273
  109. package/dist/ui/shadow/alert.js +4 -130
  110. package/dist/ui/shadow/breadcrumb.js +5 -744
  111. package/dist/ui/shadow/button-dropdown.js +6 -652
  112. package/dist/ui/shadow/button.js +3 -63
  113. package/dist/ui/shadow/card.js +4 -309
  114. package/dist/ui/shadow/chart.js +6 -1576
  115. package/dist/ui/shadow/checkbox.js +4 -334
  116. package/dist/ui/shadow/chip.js +4 -396
  117. package/dist/ui/shadow/date.js +7 -731
  118. package/dist/ui/shadow/drawer.js +4 -756
  119. package/dist/ui/shadow/dropdown-table.js +8 -1762
  120. package/dist/ui/shadow/dropdown.js +4 -508
  121. package/dist/ui/shadow/file-upload.js +4 -620
  122. package/dist/ui/shadow/filter.js +7 -379
  123. package/dist/ui/shadow/input.js +6 -1140
  124. package/dist/ui/shadow/menu.js +5 -828
  125. package/dist/ui/shadow/modal.js +6 -1063
  126. package/dist/ui/shadow/nav.js +5 -223
  127. package/dist/ui/shadow/otp.js +6 -754
  128. package/dist/ui/shadow/radio.js +4 -316
  129. package/dist/ui/shadow/rich-text-editor.js +4 -1276
  130. package/dist/ui/shadow/select.js +6 -2136
  131. package/dist/ui/shadow/sidebar.js +5 -581
  132. package/dist/ui/shadow/slider.js +5 -1010
  133. package/dist/ui/shadow/switch.js +4 -335
  134. package/dist/ui/shadow/table.js +10 -4501
  135. package/dist/ui/shadow/tabs.js +6 -315
  136. package/dist/ui/shadow/tag-input.js +7 -3057
  137. package/dist/ui/shadow/textarea.js +6 -817
  138. package/dist/ui/sidebar.js +3 -581
  139. package/dist/ui/slider.js +4 -1012
  140. package/dist/ui/switch.js +4 -336
  141. package/dist/ui/table.js +9 -4502
  142. package/dist/ui/tabs.js +4 -315
  143. package/dist/ui/tag-input.js +5 -3058
  144. package/dist/ui/textarea.js +4 -819
  145. package/dist/{vite-CivRCJaf.js → vite-CfW7z2wn.js} +21 -14
  146. package/dist/vite.js +1 -1
  147. package/dist/vue.d.ts +3 -2
  148. package/package.json +10 -11
  149. package/dist/chip-strip-bNfLn52Z.js +0 -215
  150. package/dist/data-source-controller-Dx6IQ2CN.js +0 -243
  151. package/dist/data-source-options-BM4r4PRw.js +0 -558
  152. package/dist/data-source-read-HyBCRg1t.js +0 -242
  153. package/dist/excel-BXzr3uI1.js +0 -784
  154. package/dist/export-ixRdCS2y.js +0 -2388
  155. package/dist/field-tooltip-addon-CWCp19mJ.js +0 -702
  156. package/dist/field-tooltip.css_raw-U5UeTa2d.js +0 -269
  157. package/dist/filter-builder-C0T3pMx2.js +0 -208
  158. package/dist/form-control-core-BbiACMtL.js +0 -226
  159. package/dist/icon-ClxnYhBG.js +0 -19
  160. package/dist/import-Dc4eXC02.js +0 -612
  161. package/dist/light-slots-_no0r2Z-.js +0 -246
  162. package/dist/markdown-uGlE_OfJ.js +0 -20191
  163. package/dist/mono-data-grid-e9ZVBlNN.js +0 -4240
  164. package/dist/normalize-error-BmLbgANw.js +0 -1048
@@ -0,0 +1,624 @@
1
+ import { a as defineHybridPropAliases, i as defineHybridPropAlias, n as arrayHasChanged, r as booleanStringConverter, t as __decorate } from "./decorate-DbYFpi8R.js";
2
+ import { t as dispatchMonoEvent } from "./mono-event-CAZf0F_k.js";
3
+ import { t as isIconifyClass } from "./icon-BM9N-jOt.js";
4
+ import { t as MonoFormControlCore } from "./form-control-core-CRZPZ1ru.js";
5
+ import { html, isServer, nothing } from "lit";
6
+ import { property, query, state } from "lit/decorators.js";
7
+ import { ifDefined } from "lit/directives/if-defined.js";
8
+
9
+ //#region src/components/file-upload/file-upload-utils.ts
10
+ function validateFileUploadProps(props) {
11
+ const validVariants = ["default", "compact"];
12
+ const validStates = [
13
+ "default",
14
+ "valid",
15
+ "invalid",
16
+ "warning"
17
+ ];
18
+ if (props.variant && !validVariants.includes(props.variant)) return false;
19
+ if (props.validationState && !validStates.includes(props.validationState)) return false;
20
+ return true;
21
+ }
22
+ function formatFileSize(size) {
23
+ if (size < 1024) return `${size} B`;
24
+ if (size < 1048576) return `${Math.round(size / 1024)} KB`;
25
+ return `${(size / 1048576).toFixed(1)} MB`;
26
+ }
27
+ /**
28
+ * Pick a default file-type icon. Returns an MDI iconify utility class
29
+ * (`i-mdi-*`, resolved by UnoCSS preset-icons) — see
30
+ * https://icon-sets.iconify.design/mdi/ . The render layer paints these as a
31
+ * masked SVG via {@link isIconifyClass}. The full set is safelisted in
32
+ * `uno.config.ts` so the CSS ships in `dist/ui/index.css`.
33
+ */
34
+ function getFileIcon(type, name) {
35
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
36
+ if (type.startsWith("image/")) return "i-mdi-file-image";
37
+ if (type.includes("pdf") || ext === "pdf") return "i-mdi-file-pdf-box";
38
+ if (type.includes("sheet") || [
39
+ "xls",
40
+ "xlsx",
41
+ "csv"
42
+ ].includes(ext)) return "i-mdi-file-excel";
43
+ if (type.includes("word") || ["doc", "docx"].includes(ext)) return "i-mdi-file-word";
44
+ if (type.includes("zip") || [
45
+ "zip",
46
+ "rar",
47
+ "7z"
48
+ ].includes(ext)) return "i-mdi-zip-box";
49
+ if (type.includes("video/")) return "i-mdi-file-video";
50
+ if (type.includes("audio/")) return "i-mdi-file-music";
51
+ return "i-mdi-file";
52
+ }
53
+ function createUploadItem(file) {
54
+ const isImage = file.type.startsWith("image/");
55
+ return {
56
+ id: `${file.name}-${file.size}-${file.lastModified}`,
57
+ file,
58
+ name: file.name,
59
+ size: file.size,
60
+ type: file.type,
61
+ previewUrl: isImage ? URL.createObjectURL(file) : void 0
62
+ };
63
+ }
64
+
65
+ //#endregion
66
+ //#region src/components/file-upload/file-upload-core.ts
67
+ const numberStringConverter = {
68
+ fromAttribute(value) {
69
+ if (value === null || value === "") return 0;
70
+ const parsed = Number(value);
71
+ return Number.isFinite(parsed) ? parsed : 0;
72
+ },
73
+ toAttribute(value) {
74
+ if (value === void 0 || value === null) return null;
75
+ return String(value);
76
+ }
77
+ };
78
+ /**
79
+ * `MonoFileUploadCore` — all render-mode-agnostic logic for `mono-file-upload`:
80
+ * reactive props (incl. SSR boolean + modelValue-array coercion), hybrid aliases,
81
+ * camelCase attribute fallbacks, the file pipeline (validate / add / drag-drop /
82
+ * remove / clear), the `mno-change`/`mno-remove`/`mno-error` events, and the
83
+ * dropzone/list/message `render()`. Icon rendering goes through overridable hooks
84
+ * (`_renderDropzoneIcon`/`_renderDefaultGlyph`/`_renderRemoveIcon`/
85
+ * `_renderThumbGlyph`) — the light build keeps `data-mono-slot` + `i-mdi-*` iconify
86
+ * classes; the shadow build uses native `<slot>` + inline SVG (iconify can't paint
87
+ * inside a shadow root).
88
+ *
89
+ * SSR-safe: no `document`/`window` access at module/ctor scope; the file pipeline
90
+ * and `_inputEl` (`@query`) only run client-side from user interaction.
91
+ */
92
+ const MonoFileUploadCore = (superClass) => {
93
+ class MonoFileUploadCoreClass extends MonoFormControlCore(superClass) {
94
+ constructor(...args) {
95
+ super(...args);
96
+ this.label = "";
97
+ this.helperText = "";
98
+ this.icon = "";
99
+ this.removeIcon = "";
100
+ this.accept = "";
101
+ this.multiple = false;
102
+ this.disabled = false;
103
+ this.required = false;
104
+ this.dragdrop = true;
105
+ this.maxFileSize = 0;
106
+ this.maxFiles = 0;
107
+ this.modelValue = [];
108
+ this.cssClass = {};
109
+ this.variant = "default";
110
+ this.validationState = "default";
111
+ this.validationMessage = "";
112
+ this._dragOver = false;
113
+ this._hasIconSlot = false;
114
+ this._hasTitleSlot = false;
115
+ this._hasSubtitleSlot = false;
116
+ this._previewsRevoked = false;
117
+ defineHybridPropAliases(this, [
118
+ "helperText",
119
+ "maxFileSize",
120
+ "maxFiles",
121
+ "modelValue",
122
+ "validationState",
123
+ "validationMessage",
124
+ "cssClass",
125
+ "removeIcon"
126
+ ]);
127
+ defineHybridPropAlias(this, "placeholder", "title");
128
+ defineHybridPropAlias(this, "subtext", "subtitle");
129
+ this.title = "Klik atau seret file ke sini";
130
+ this.subtitle = "Pilih file untuk diunggah";
131
+ }
132
+ static get observedAttributes() {
133
+ return [
134
+ ...super.observedAttributes ?? [],
135
+ "helpertext",
136
+ "maxfilesize",
137
+ "maxfiles",
138
+ "modelvalue",
139
+ "validationstate",
140
+ "validationmessage",
141
+ "removeicon",
142
+ "remove-icon",
143
+ "placeholder",
144
+ "subtext"
145
+ ];
146
+ }
147
+ attributeChangedCallback(name, oldValue, newValue) {
148
+ super.attributeChangedCallback(name, oldValue, newValue);
149
+ if (oldValue === newValue) return;
150
+ if (name === "helpertext") {
151
+ this.helperText = newValue ?? "";
152
+ return;
153
+ }
154
+ if (name === "maxfilesize") {
155
+ this.maxFileSize = this._toNumber(newValue);
156
+ return;
157
+ }
158
+ if (name === "maxfiles") {
159
+ this.maxFiles = this._toNumber(newValue);
160
+ return;
161
+ }
162
+ if (name === "modelvalue") {
163
+ this.modelValue = this._toFileUploadItems(newValue);
164
+ return;
165
+ }
166
+ if (name === "validationstate") {
167
+ this.validationState = newValue ?? "default";
168
+ return;
169
+ }
170
+ if (name === "validationmessage") {
171
+ this.validationMessage = newValue ?? "";
172
+ return;
173
+ }
174
+ if (name === "removeicon" || name === "remove-icon") {
175
+ this.removeIcon = newValue ?? "";
176
+ return;
177
+ }
178
+ if (name === "placeholder") {
179
+ this.title = newValue ?? "";
180
+ return;
181
+ }
182
+ if (name === "subtext") this.subtitle = newValue ?? "";
183
+ }
184
+ willUpdate(changed) {
185
+ for (const key of [
186
+ "multiple",
187
+ "disabled",
188
+ "required",
189
+ "dragdrop"
190
+ ]) if (typeof this[key] === "string") {
191
+ const normalized = this[key].toLowerCase().trim();
192
+ this[key] = normalized === "" || normalized === "true";
193
+ }
194
+ if (typeof this.modelValue === "string") this.modelValue = this._toFileUploadItems(this.modelValue);
195
+ super.willUpdate?.(changed);
196
+ }
197
+ connectedCallback() {
198
+ super.connectedCallback?.();
199
+ this._restorePreviews();
200
+ }
201
+ disconnectedCallback() {
202
+ this._revokePreviews();
203
+ this._previewsRevoked = true;
204
+ super.disconnectedCallback();
205
+ }
206
+ /**
207
+ * Re-create the object URLs that `disconnectedCallback` revoked.
208
+ *
209
+ * Revoking on unmount is right — a blob URL pins the whole file in memory — but
210
+ * the files SURVIVE a reconnect (a `v-if` toggle, `<KeepAlive>`, Vue moving the
211
+ * node), and nothing re-created their previews. `modelValue` kept the same
212
+ * `previewUrl` STRING pointing at a dead URL, so every thumbnail was broken from
213
+ * then on. It hid well: an `<img>` already decoded before the unmount keeps
214
+ * painting, so the damage only surfaced once a later re-render built a fresh
215
+ * `<img>` from the same src.
216
+ *
217
+ * Only the disconnect path restores. `clear()` and the single-select replace also
218
+ * revoke, but those files are gone for good and must NOT come back.
219
+ *
220
+ * Mutates the items in place rather than rebuilding `modelValue`: the consumer
221
+ * holds these same object references, so an in-place write keeps both sides on
222
+ * one live URL and avoids emitting a change event for what was never a user edit.
223
+ */
224
+ _restorePreviews() {
225
+ if (isServer || !this._previewsRevoked) return;
226
+ this._previewsRevoked = false;
227
+ let restored = false;
228
+ for (const item of this.modelValue) {
229
+ if (!item.file || !item.previewUrl) continue;
230
+ item.previewUrl = URL.createObjectURL(item.file);
231
+ restored = true;
232
+ }
233
+ if (restored) this.requestUpdate();
234
+ }
235
+ _toNumber(value) {
236
+ if (typeof value === "number") return Number.isFinite(value) ? value : 0;
237
+ if (typeof value === "string") {
238
+ const parsed = Number(value);
239
+ return Number.isFinite(parsed) ? parsed : 0;
240
+ }
241
+ return 0;
242
+ }
243
+ _toFileUploadItems(value) {
244
+ if (Array.isArray(value)) return value;
245
+ if (typeof value === "string") {
246
+ if (!value.trim()) return [];
247
+ try {
248
+ const parsed = JSON.parse(value);
249
+ return Array.isArray(parsed) ? parsed : [];
250
+ } catch {
251
+ return [];
252
+ }
253
+ }
254
+ return [];
255
+ }
256
+ get _wrapperClasses() {
257
+ return [
258
+ "mono-file-upload",
259
+ this.variant,
260
+ this.disabled ? "disabled" : "",
261
+ this._dragOver ? "dragover" : "",
262
+ this.validationState !== "default" ? this.validationState : "",
263
+ this.cssClass?.root ?? ""
264
+ ].filter(Boolean).join(" ");
265
+ }
266
+ _cls(base, key) {
267
+ const extra = this.cssClass?.[key];
268
+ return extra ? `${base} ${extra}` : base;
269
+ }
270
+ _cloneValue(value) {
271
+ return [...value];
272
+ }
273
+ _createModelDetail(args) {
274
+ const currentValue = this._cloneValue(args.modelValue);
275
+ return {
276
+ modelValue: currentValue,
277
+ currentValue,
278
+ oldValue: this._cloneValue(args.oldValue),
279
+ files: currentValue,
280
+ addedFiles: args.addedFiles ? this._cloneValue(args.addedFiles) : [],
281
+ removedFile: args.removedFile,
282
+ action: args.action,
283
+ sourceEvent: args.sourceEvent
284
+ };
285
+ }
286
+ _emitChange(detail) {
287
+ dispatchMonoEvent(this, "change", detail);
288
+ }
289
+ _emitRemove(detail) {
290
+ dispatchMonoEvent(this, "remove", detail);
291
+ }
292
+ _emitError(detail) {
293
+ dispatchMonoEvent(this, "error", detail);
294
+ }
295
+ _emitValueEvents(detail) {
296
+ this._emitChange(detail);
297
+ }
298
+ _revokePreviews(files = this.modelValue) {
299
+ files.forEach((item) => {
300
+ if (item.previewUrl) URL.revokeObjectURL(item.previewUrl);
301
+ });
302
+ }
303
+ _openPicker() {
304
+ if (this.disabled) return;
305
+ this._inputEl?.click();
306
+ }
307
+ _validateFiles(files, sourceEvent) {
308
+ let nextFiles = files;
309
+ if (this.maxFileSize > 0) {
310
+ const oversized = nextFiles.find((file) => file.size > this.maxFileSize);
311
+ if (oversized) {
312
+ this._emitError({
313
+ message: `File "${oversized.name}" exceeds the maximum size limit.`,
314
+ reason: "max-file-size",
315
+ file: oversized,
316
+ maxFileSize: this.maxFileSize,
317
+ sourceEvent
318
+ });
319
+ nextFiles = nextFiles.filter((file) => file.size <= this.maxFileSize);
320
+ }
321
+ }
322
+ if (this.maxFiles > 0) {
323
+ const existingCount = this.multiple ? this.modelValue.length : 0;
324
+ const allowed = Math.max(this.maxFiles - existingCount, 0);
325
+ if (nextFiles.length > allowed) {
326
+ this._emitError({
327
+ message: `Only ${this.maxFiles} file(s) are allowed.`,
328
+ reason: "max-files",
329
+ maxFiles: this.maxFiles,
330
+ sourceEvent
331
+ });
332
+ nextFiles = nextFiles.slice(0, allowed);
333
+ }
334
+ }
335
+ return nextFiles;
336
+ }
337
+ _addFiles(files, sourceEvent) {
338
+ if (!files.length) return;
339
+ const validFiles = this._validateFiles(files, sourceEvent);
340
+ if (!validFiles.length) return;
341
+ const oldValue = this._cloneValue(this.modelValue);
342
+ const addedFiles = validFiles.map((file) => createUploadItem(file));
343
+ let action = "add";
344
+ if (this.multiple) this.modelValue = [...this.modelValue, ...addedFiles];
345
+ else {
346
+ const replacedFiles = this._cloneValue(this.modelValue);
347
+ this._revokePreviews(replacedFiles);
348
+ this.modelValue = addedFiles.slice(0, 1);
349
+ action = oldValue.length ? "replace" : "add";
350
+ }
351
+ const detail = this._createModelDetail({
352
+ modelValue: this.modelValue,
353
+ oldValue,
354
+ addedFiles,
355
+ action,
356
+ sourceEvent
357
+ });
358
+ this._emitValueEvents(detail);
359
+ }
360
+ _handleInputChange(event) {
361
+ const input = event.currentTarget;
362
+ const files = Array.from(input.files ?? []);
363
+ this._addFiles(files, event);
364
+ input.value = "";
365
+ }
366
+ _handleDragOver(event) {
367
+ if (!this.dragdrop || this.disabled) return;
368
+ event.preventDefault();
369
+ this._dragOver = true;
370
+ }
371
+ _handleDragLeave() {
372
+ this._dragOver = false;
373
+ }
374
+ _handleDrop(event) {
375
+ if (!this.dragdrop || this.disabled) return;
376
+ event.preventDefault();
377
+ this._dragOver = false;
378
+ const files = Array.from(event.dataTransfer?.files ?? []);
379
+ this._addFiles(files, event);
380
+ }
381
+ _removeFile(id, sourceEvent) {
382
+ const removedFile = this.modelValue.find((file) => file.id === id);
383
+ if (!removedFile) return;
384
+ const oldValue = this._cloneValue(this.modelValue);
385
+ if (removedFile.previewUrl) URL.revokeObjectURL(removedFile.previewUrl);
386
+ this.modelValue = this.modelValue.filter((file) => file.id !== id);
387
+ const detail = this._createModelDetail({
388
+ modelValue: this.modelValue,
389
+ oldValue,
390
+ removedFile,
391
+ action: "remove",
392
+ sourceEvent
393
+ });
394
+ this._emitRemove({
395
+ ...detail,
396
+ removedFile,
397
+ action: "remove"
398
+ });
399
+ this._emitValueEvents(detail);
400
+ }
401
+ clear() {
402
+ if (!this.modelValue.length) return;
403
+ const oldValue = this._cloneValue(this.modelValue);
404
+ this._revokePreviews();
405
+ this.modelValue = [];
406
+ const detail = this._createModelDetail({
407
+ modelValue: this.modelValue,
408
+ oldValue,
409
+ action: "clear"
410
+ });
411
+ this._emitValueEvents(detail);
412
+ }
413
+ /** Shadow build returns true to render the icon via a native `<slot>`. */
414
+ _useIconSlots() {
415
+ return false;
416
+ }
417
+ /** Default dropzone glyph. Shadow build overrides with an inline SVG. */
418
+ _renderDefaultGlyph() {
419
+ return html`<span
420
+ class="mono-file-upload-default-icon i-mdi-cloud-upload-outline"
421
+ mono-glyph
422
+ aria-hidden="true"
423
+ ></span>`;
424
+ }
425
+ _renderDropzoneIcon() {
426
+ const cls = this._cls("mono-file-upload-icon", "icon");
427
+ if (this._hasIconSlot) return html`<div class=${cls} mono-icon aria-hidden="true"><span data-mono-slot="icon"></span></div>`;
428
+ if (this.icon && isIconifyClass(this.icon)) return html`<div class=${cls} mono-icon aria-hidden="true"><span class=${`mono-file-upload-iconify ${this.icon}`} mono-glyph></span></div>`;
429
+ if (this.icon) return html`<div class=${cls} mono-icon aria-hidden="true">${this.icon}</div>`;
430
+ return html`<div class=${cls} mono-icon aria-hidden="true">${this._renderDefaultGlyph()}</div>`;
431
+ }
432
+ /** Classes of the secondary line — the new `subtitle` key and the old `subtext` one. */
433
+ get _subtitleClasses() {
434
+ return [
435
+ "mono-file-upload-subtitle",
436
+ "mono-file-upload-subtext",
437
+ this.cssClass?.subtitle ?? "",
438
+ this.cssClass?.subtext ?? ""
439
+ ].filter(Boolean).join(" ");
440
+ }
441
+ /**
442
+ * Dropzone headline. A `slot="title"` child beats the prop: the light build
443
+ * renders an empty `[data-mono-slot]` region it re-parents the captured node
444
+ * into; the shadow build overrides this with a native `<slot>` whose fallback
445
+ * is the prop text. (A separate template per branch — Lit's trailing child
446
+ * part would otherwise clear nodes appended after it.)
447
+ */
448
+ _renderTitle() {
449
+ const cls = this._cls("mono-file-upload-title", "title");
450
+ if (this._hasTitleSlot) return html`<div class=${cls} mono-title data-mono-slot="title"></div>`;
451
+ return html`<div class=${cls} mono-title ?mono-empty=${!this.title}>${this.title}</div>`;
452
+ }
453
+ /** Dropzone secondary line — `mono-subtitle`, plus the old `mono-subtext` hook. */
454
+ _renderSubtitle() {
455
+ const cls = this._subtitleClasses;
456
+ if (this._hasSubtitleSlot) return html`<div class=${cls} mono-subtitle mono-subtext data-mono-slot="subtitle"></div>`;
457
+ return html`<div class=${cls} mono-subtitle mono-subtext ?mono-empty=${!this.subtitle}>${this.subtitle}</div>`;
458
+ }
459
+ _renderRemoveIcon() {
460
+ if (this.removeIcon && isIconifyClass(this.removeIcon)) return html`<span class=${`mono-file-upload-iconify ${this.removeIcon}`} mono-glyph></span>`;
461
+ if (this.removeIcon) return html`${this.removeIcon}`;
462
+ return html`<span class="mono-file-upload-iconify mono-icon i-mdi-close" mono-glyph aria-hidden="true"></span>`;
463
+ }
464
+ /** File-type thumb glyph (non-image). Shadow build overrides with an inline SVG. */
465
+ _renderThumbGlyph(item) {
466
+ const icon = getFileIcon(item.type, item.name);
467
+ return isIconifyClass(icon) ? html`<span class=${`mono-file-upload-iconify ${icon}`} mono-glyph aria-hidden="true"></span>` : html`${icon}`;
468
+ }
469
+ _renderMessage() {
470
+ const extra = this.cssClass?.message ?? "";
471
+ if (this.validationMessage) return html`
472
+ <div
473
+ class="mono-file-upload-message ${this.validationState} ${extra}"
474
+ mono-message=${this.validationState}
475
+ role=${this.validationState === "invalid" ? "alert" : nothing}
476
+ >
477
+ ${this.validationMessage}
478
+ </div>
479
+ `;
480
+ if (this.helperText) return html`
481
+ <div class="mono-file-upload-message helper ${extra}" mono-message="helper">${this.helperText}</div>
482
+ `;
483
+ return nothing;
484
+ }
485
+ _renderList() {
486
+ if (!this.modelValue.length) return nothing;
487
+ return html`
488
+ <div class=${this._cls("mono-file-upload-list", "list")} mono-list>
489
+ ${this.modelValue.map((item) => html`
490
+ <div class=${this._cls("mono-file-upload-item", "item")} mono-item>
491
+ <div class=${this._cls("mono-file-upload-thumb", "thumb")} mono-thumb>
492
+ ${item.previewUrl ? html`<img src=${item.previewUrl} alt=${item.name} />` : this._renderThumbGlyph(item)}
493
+ </div>
494
+
495
+ <div class=${this._cls("mono-file-upload-file", "file")} mono-file>
496
+ <div class=${this._cls("mono-file-upload-name", "name")} mono-name>${item.name}</div>
497
+ <div class=${this._cls("mono-file-upload-meta", "meta")} mono-meta>${formatFileSize(item.size)}</div>
498
+ </div>
499
+
500
+ <button
501
+ type="button"
502
+ class=${this._cls("mono-file-upload-remove", "remove")}
503
+ mono-remove
504
+ aria-label=${ifDefined(`Remove ${item.name}`)}
505
+ @click=${(event) => this._removeFile(item.id, event)}
506
+ >
507
+ ${this._renderRemoveIcon()}
508
+ </button>
509
+ </div>
510
+ `)}
511
+ </div>
512
+ `;
513
+ }
514
+ render() {
515
+ return html`
516
+ <div
517
+ class=${this._wrapperClasses}
518
+ mono-file-upload
519
+ mono-variant=${this.variant === "default" ? nothing : this.variant}
520
+ mono-validation-state=${this.validationState === "default" ? nothing : this.validationState}
521
+ ?mono-disabled=${this.disabled}
522
+ ?mono-required=${this.required}
523
+ ?mono-multiple=${this.multiple}
524
+ ?mono-no-dragdrop=${!this.dragdrop}
525
+ ?mono-dragover=${this._dragOver}
526
+ title=""
527
+ >
528
+ ${this.label ? html`
529
+ <label class=${this._cls("mono-file-upload-label", "label")} mono-label>
530
+ ${this.label}
531
+ ${this.required ? html`<span class=${this._cls("mono-file-upload-required", "required")} mono-required-mark>*</span>` : nothing}
532
+ </label>
533
+ ` : nothing}
534
+
535
+ <div
536
+ class=${this._cls("mono-file-upload-dropzone", "dropzone")}
537
+ mono-dropzone
538
+ @click=${this._openPicker}
539
+ @dragover=${this._handleDragOver}
540
+ @dragleave=${this._handleDragLeave}
541
+ @drop=${this._handleDrop}
542
+ >
543
+ ${this._renderDropzoneIcon()}
544
+ ${this._renderTitle()}
545
+ ${this._renderSubtitle()}
546
+
547
+ <input
548
+ class=${this._cls("mono-file-upload-input", "input")}
549
+ mono-native
550
+ type="file"
551
+ accept=${ifDefined(this.accept || void 0)}
552
+ ?multiple=${this.multiple}
553
+ ?disabled=${this.disabled}
554
+ @change=${this._handleInputChange}
555
+ />
556
+ </div>
557
+
558
+ ${this._renderList()}
559
+ ${this._renderMessage()}
560
+ </div>
561
+ `;
562
+ }
563
+ }
564
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "label", void 0);
565
+ __decorate([property({
566
+ type: String,
567
+ attribute: "helper-text"
568
+ })], MonoFileUploadCoreClass.prototype, "helperText", void 0);
569
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "title", void 0);
570
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "subtitle", void 0);
571
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "icon", void 0);
572
+ __decorate([property({
573
+ type: String,
574
+ attribute: "remove-icon"
575
+ })], MonoFileUploadCoreClass.prototype, "removeIcon", void 0);
576
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "accept", void 0);
577
+ __decorate([property({
578
+ reflect: true,
579
+ converter: booleanStringConverter
580
+ })], MonoFileUploadCoreClass.prototype, "multiple", void 0);
581
+ __decorate([property({
582
+ reflect: true,
583
+ converter: booleanStringConverter
584
+ })], MonoFileUploadCoreClass.prototype, "disabled", void 0);
585
+ __decorate([property({
586
+ reflect: true,
587
+ converter: booleanStringConverter
588
+ })], MonoFileUploadCoreClass.prototype, "required", void 0);
589
+ __decorate([property({
590
+ reflect: true,
591
+ converter: booleanStringConverter
592
+ })], MonoFileUploadCoreClass.prototype, "dragdrop", void 0);
593
+ __decorate([property({
594
+ attribute: "max-file-size",
595
+ converter: numberStringConverter
596
+ })], MonoFileUploadCoreClass.prototype, "maxFileSize", void 0);
597
+ __decorate([property({
598
+ attribute: "max-files",
599
+ converter: numberStringConverter
600
+ })], MonoFileUploadCoreClass.prototype, "maxFiles", void 0);
601
+ __decorate([property({
602
+ attribute: false,
603
+ hasChanged: arrayHasChanged
604
+ })], MonoFileUploadCoreClass.prototype, "modelValue", void 0);
605
+ __decorate([property({ attribute: false })], MonoFileUploadCoreClass.prototype, "cssClass", void 0);
606
+ __decorate([property({ type: String })], MonoFileUploadCoreClass.prototype, "variant", void 0);
607
+ __decorate([property({
608
+ type: String,
609
+ attribute: "validation-state"
610
+ })], MonoFileUploadCoreClass.prototype, "validationState", void 0);
611
+ __decorate([property({
612
+ type: String,
613
+ attribute: "validation-message"
614
+ })], MonoFileUploadCoreClass.prototype, "validationMessage", void 0);
615
+ __decorate([state()], MonoFileUploadCoreClass.prototype, "_dragOver", void 0);
616
+ __decorate([state()], MonoFileUploadCoreClass.prototype, "_hasIconSlot", void 0);
617
+ __decorate([state()], MonoFileUploadCoreClass.prototype, "_hasTitleSlot", void 0);
618
+ __decorate([state()], MonoFileUploadCoreClass.prototype, "_hasSubtitleSlot", void 0);
619
+ __decorate([query(".mono-file-upload-input")], MonoFileUploadCoreClass.prototype, "_inputEl", void 0);
620
+ return MonoFileUploadCoreClass;
621
+ };
622
+
623
+ //#endregion
624
+ export { validateFileUploadProps as a, getFileIcon as i, createUploadItem as n, formatFileSize as r, MonoFileUploadCore as t };