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