kanto-ds 1.2.0 → 1.4.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.
- package/README.md +24 -6
- package/dist/chunks/{defaults-DKzFsNbv.js → defaults-BwgjMcww.js} +27 -26
- package/dist/chunks/{defaults-DKzFsNbv.js.map → defaults-BwgjMcww.js.map} +1 -1
- package/dist/chunks/index-DSSLJirw.js +79 -0
- package/dist/chunks/index-DSSLJirw.js.map +1 -0
- package/dist/chunks/kt-element-E-tfJpGQ.js +52 -0
- package/dist/chunks/kt-element-E-tfJpGQ.js.map +1 -0
- package/dist/components/core/kt-avatar/kt-avatar.js +8 -7
- package/dist/components/core/kt-avatar/kt-avatar.js.map +1 -1
- package/dist/components/core/kt-badge/kt-badge.js +7 -6
- package/dist/components/core/kt-badge/kt-badge.js.map +1 -1
- package/dist/components/core/kt-button/kt-button.js +1 -1
- package/dist/components/core/kt-card/kt-card.js +1 -1
- package/dist/components/core/kt-code/kt-code.js +31 -30
- package/dist/components/core/kt-code/kt-code.js.map +1 -1
- package/dist/components/core/kt-icon/kt-icon.js +2 -2
- package/dist/components/core/kt-kbd/kt-kbd.js +1 -1
- package/dist/components/core/kt-split-button/kt-split-button.d.ts +2 -2
- package/dist/components/core/kt-split-button/kt-split-button.js +27 -26
- package/dist/components/core/kt-split-button/kt-split-button.js.map +1 -1
- package/dist/components/data/kt-chart/kt-chart.d.ts +2 -2
- package/dist/components/data/kt-chart/kt-chart.js +222 -221
- package/dist/components/data/kt-chart/kt-chart.js.map +1 -1
- package/dist/components/data/kt-meter/kt-meter.js +1 -1
- package/dist/components/data/kt-pagination/kt-pagination.d.ts +1 -1
- package/dist/components/data/kt-pagination/kt-pagination.js +29 -28
- package/dist/components/data/kt-pagination/kt-pagination.js.map +1 -1
- package/dist/components/data/kt-stat/kt-stat.js +1 -1
- package/dist/components/data/kt-table/kt-table.d.ts +2 -2
- package/dist/components/data/kt-table/kt-table.js +30 -29
- package/dist/components/data/kt-table/kt-table.js.map +1 -1
- package/dist/components/data/kt-timeline/kt-timeline.js +1 -1
- package/dist/components/feedback/kt-alert/kt-alert.js +17 -16
- package/dist/components/feedback/kt-alert/kt-alert.js.map +1 -1
- package/dist/components/feedback/kt-empty-state/kt-empty-state.js +1 -1
- package/dist/components/feedback/kt-progress-bar/kt-progress-bar.js +1 -1
- package/dist/components/feedback/kt-skeleton/kt-skeleton.js +1 -1
- package/dist/components/feedback/kt-toast/kt-toast.js +25 -19
- package/dist/components/feedback/kt-toast/kt-toast.js.map +1 -1
- package/dist/components/feedback/kt-toast-container/kt-toast-container.js +1 -1
- package/dist/components/feedback/kt-tooltip/kt-tooltip.js +1 -1
- package/dist/components/forms/kt-checkbox/kt-checkbox.d.ts +66 -0
- package/dist/components/forms/kt-checkbox/kt-checkbox.js +198 -0
- package/dist/components/forms/kt-checkbox/kt-checkbox.js.map +1 -0
- package/dist/components/forms/kt-drag-drop/kt-drag-drop.d.ts +2 -2
- package/dist/components/forms/kt-drag-drop/kt-drag-drop.js +60 -57
- package/dist/components/forms/kt-drag-drop/kt-drag-drop.js.map +1 -1
- package/dist/components/forms/kt-form/kt-form.js +11 -10
- package/dist/components/forms/kt-form/kt-form.js.map +1 -1
- package/dist/components/forms/kt-input/kt-input.js +56 -55
- package/dist/components/forms/kt-input/kt-input.js.map +1 -1
- package/dist/components/forms/kt-input-menu/kt-input-menu.d.ts +1 -1
- package/dist/components/forms/kt-input-menu/kt-input-menu.js +57 -56
- package/dist/components/forms/kt-input-menu/kt-input-menu.js.map +1 -1
- package/dist/components/forms/kt-label-input/kt-label-input.js +1 -1
- package/dist/components/forms/kt-radio-group/kt-radio-group.d.ts +103 -0
- package/dist/components/forms/kt-radio-group/kt-radio-group.js +265 -0
- package/dist/components/forms/kt-radio-group/kt-radio-group.js.map +1 -0
- package/dist/components/forms/kt-select/kt-select.d.ts +2 -2
- package/dist/components/forms/kt-select/kt-select.js +62 -61
- package/dist/components/forms/kt-select/kt-select.js.map +1 -1
- package/dist/components/forms/kt-textarea/kt-textarea.js +9 -8
- package/dist/components/forms/kt-textarea/kt-textarea.js.map +1 -1
- package/dist/components/forms/kt-toggle/kt-toggle.js +1 -1
- package/dist/components/navigation/kt-breadcrumb/kt-breadcrumb.d.ts +1 -1
- package/dist/components/navigation/kt-breadcrumb/kt-breadcrumb.js +6 -5
- package/dist/components/navigation/kt-breadcrumb/kt-breadcrumb.js.map +1 -1
- package/dist/components/navigation/kt-header/kt-header.js +11 -10
- package/dist/components/navigation/kt-header/kt-header.js.map +1 -1
- package/dist/components/navigation/kt-page-header/kt-page-header.js +1 -1
- package/dist/components/navigation/kt-segmented-control/kt-segmented-control.js +44 -43
- package/dist/components/navigation/kt-segmented-control/kt-segmented-control.js.map +1 -1
- package/dist/components/navigation/kt-sub-menu-navigation/kt-sub-menu-navigation.d.ts +1 -1
- package/dist/components/navigation/kt-sub-menu-navigation/kt-sub-menu-navigation.js +6 -5
- package/dist/components/navigation/kt-sub-menu-navigation/kt-sub-menu-navigation.js.map +1 -1
- package/dist/components/navigation/kt-tabs/kt-tabs.js +1 -1
- package/dist/components/navigation/kt-toggle-button/kt-toggle-button.js +1 -1
- package/dist/components/navigation/kt-toggle-button-group/kt-toggle-button-group.js +12 -6
- package/dist/components/navigation/kt-toggle-button-group/kt-toggle-button-group.js.map +1 -1
- package/dist/components/overlays/kt-collapsible/kt-collapsible.js +1 -1
- package/dist/components/overlays/kt-confirm-dialog/kt-confirm-dialog.d.ts +3 -3
- package/dist/components/overlays/kt-confirm-dialog/kt-confirm-dialog.js +30 -27
- package/dist/components/overlays/kt-confirm-dialog/kt-confirm-dialog.js.map +1 -1
- package/dist/components/overlays/kt-dropdown/kt-dropdown.d.ts +1 -1
- package/dist/components/overlays/kt-dropdown/kt-dropdown.js +8 -7
- package/dist/components/overlays/kt-dropdown/kt-dropdown.js.map +1 -1
- package/dist/components/overlays/kt-modal/kt-modal.js +5 -4
- package/dist/components/overlays/kt-modal/kt-modal.js.map +1 -1
- package/dist/components/overlays/kt-side-panel/kt-side-panel.d.ts +1 -1
- package/dist/components/overlays/kt-side-panel/kt-side-panel.js +24 -23
- package/dist/components/overlays/kt-side-panel/kt-side-panel.js.map +1 -1
- package/dist/custom-elements.json +1870 -1071
- package/dist/icons/defaults.d.ts +1 -0
- package/dist/icons/index.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +93 -83
- package/dist/index.js.map +1 -1
- package/dist/internal/kt-element.d.ts +6 -1
- package/dist/internal/strings.d.ts +103 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/index.js +176 -157
- package/dist/react/index.js.map +1 -1
- package/dist/react/jsx.d.ts +12 -1
- package/dist/strings/index.d.ts +11 -0
- package/dist/strings/index.js +8 -0
- package/dist/strings/index.js.map +1 -0
- package/dist/tokens/base.css +56 -0
- package/dist/vscode.css-custom-data.json +12 -0
- package/dist/vscode.html-custom-data.json +85 -0
- package/dist/vue/index.d.ts +6 -0
- package/dist/vue/index.js +9 -7
- package/dist/vue/index.js.map +1 -1
- package/dist/web-types.json +263 -39
- package/package.json +19 -3
- package/dist/chunks/kt-element-B_jjQDhg.js +0 -38
- package/dist/chunks/kt-element-B_jjQDhg.js.map +0 -1
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { query as
|
|
3
|
-
import { classMap as
|
|
4
|
-
import { K as
|
|
5
|
-
import { e as
|
|
6
|
-
import { a as
|
|
1
|
+
import { css as v, nothing as p, html as c } from "lit";
|
|
2
|
+
import { query as y, state as g, property as n } from "lit/decorators.js";
|
|
3
|
+
import { classMap as b } from "lit/directives/class-map.js";
|
|
4
|
+
import { K as m, d as x } from "../../../chunks/kt-element-E-tfJpGQ.js";
|
|
5
|
+
import { e as h } from "../../../chunks/events-Bjl5Zmmq.js";
|
|
6
|
+
import { a as k, s as $, b as w } from "../../../chunks/form-control-CxjBTt1c.js";
|
|
7
|
+
import { s as l } from "../../../chunks/index-DSSLJirw.js";
|
|
7
8
|
import "../../core/kt-icon/kt-icon.js";
|
|
8
9
|
import "../../core/kt-button/kt-button.js";
|
|
9
|
-
const
|
|
10
|
-
function
|
|
11
|
-
if (!Number.isFinite(
|
|
12
|
-
let e =
|
|
13
|
-
for (; e >= 1024 && t <
|
|
10
|
+
const u = ["B", "KB", "MB", "GB", "TB"];
|
|
11
|
+
function z(d) {
|
|
12
|
+
if (!Number.isFinite(d) || d < 0) return "";
|
|
13
|
+
let e = d, t = 0;
|
|
14
|
+
for (; e >= 1024 && t < u.length - 1; )
|
|
14
15
|
e /= 1024, t += 1;
|
|
15
|
-
return `${t === 0 ? String(Math.round(e)) : e.toFixed(1)} ${
|
|
16
|
+
return `${t === 0 ? String(Math.round(e)) : e.toFixed(1)} ${u[t]}`;
|
|
16
17
|
}
|
|
17
|
-
var
|
|
18
|
-
for (var s = void 0,
|
|
19
|
-
(
|
|
20
|
-
return s &&
|
|
18
|
+
var S = Object.defineProperty, r = (d, e, t, a) => {
|
|
19
|
+
for (var s = void 0, o = d.length - 1, f; o >= 0; o--)
|
|
20
|
+
(f = d[o]) && (s = f(e, t, s) || s);
|
|
21
|
+
return s && S(e, t, s), s;
|
|
21
22
|
};
|
|
22
|
-
class i extends
|
|
23
|
+
class i extends m {
|
|
23
24
|
constructor() {
|
|
24
|
-
super(...arguments), this.internals = null, this.dragging = !1, this.files = [], this.heading =
|
|
25
|
+
super(...arguments), this.internals = null, this.dragging = !1, this.files = [], this.heading = void 0, this.linkText = void 0, this.recommendedSize = "", this.multiple = !0, this.accept = "", this.maxSize = 0, this.disabled = !1, this.name = "", this.required = !1;
|
|
25
26
|
}
|
|
26
27
|
static {
|
|
27
28
|
this.formAssociated = !0;
|
|
28
29
|
}
|
|
29
30
|
static {
|
|
30
31
|
this.styles = [
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
m.styles,
|
|
33
|
+
v`
|
|
33
34
|
:host {
|
|
34
35
|
display: block;
|
|
35
36
|
user-select: none;
|
|
@@ -156,11 +157,11 @@ class i extends f {
|
|
|
156
157
|
];
|
|
157
158
|
}
|
|
158
159
|
connectedCallback() {
|
|
159
|
-
super.connectedCallback(), this.internals ??=
|
|
160
|
+
super.connectedCallback(), this.internals ??= k(this);
|
|
160
161
|
}
|
|
161
162
|
// Untyped: `files` is private state, which PropertyValues<this> cannot name.
|
|
162
163
|
willUpdate(e) {
|
|
163
|
-
(e.has("files") || e.has("name") || e.has("required")) && this.syncFormValue();
|
|
164
|
+
(e.has("files") || e.has("name") || e.has("required") || this.stringsChanged(e)) && this.syncFormValue();
|
|
164
165
|
}
|
|
165
166
|
syncFormValue() {
|
|
166
167
|
let e = null;
|
|
@@ -168,9 +169,9 @@ class i extends f {
|
|
|
168
169
|
e = new FormData();
|
|
169
170
|
for (const a of this.files) e.append(this.name, a);
|
|
170
171
|
}
|
|
171
|
-
|
|
172
|
+
$(this.internals, e);
|
|
172
173
|
const t = this.required && this.files.length === 0;
|
|
173
|
-
|
|
174
|
+
w(this.internals, { valueMissing: t }, t ? l().selectFile : "");
|
|
174
175
|
}
|
|
175
176
|
formResetCallback() {
|
|
176
177
|
this.files = [], this.dragging = !1;
|
|
@@ -184,7 +185,7 @@ class i extends f {
|
|
|
184
185
|
}
|
|
185
186
|
/** Empties the selection without firing a rejection. */
|
|
186
187
|
clear() {
|
|
187
|
-
this.files = [],
|
|
188
|
+
this.files = [], h(this, "kt-files-change", { files: this.files });
|
|
188
189
|
}
|
|
189
190
|
/**
|
|
190
191
|
* `accept` is advisory on a native input — the OS dialog filters, but a
|
|
@@ -195,14 +196,14 @@ class i extends f {
|
|
|
195
196
|
}
|
|
196
197
|
addFiles(e) {
|
|
197
198
|
if (!e || e.length === 0) return;
|
|
198
|
-
const t = this.multiple ? [...e] : [...e].slice(0, 1), a = t.filter((
|
|
199
|
-
s.length > 0 &&
|
|
199
|
+
const t = this.multiple ? [...e] : [...e].slice(0, 1), a = t.filter((o) => this.accepts(o)), s = t.filter((o) => !this.accepts(o));
|
|
200
|
+
s.length > 0 && h(this, "kt-files-rejected", {
|
|
200
201
|
files: s,
|
|
201
|
-
reason: this.maxSize > 0 && s.some((
|
|
202
|
-
}), a.length !== 0 && (this.files = this.multiple ? [...this.files, ...a] : a,
|
|
202
|
+
reason: this.maxSize > 0 && s.some((o) => o.size > this.maxSize) ? "size" : "type"
|
|
203
|
+
}), a.length !== 0 && (this.files = this.multiple ? [...this.files, ...a] : a, h(this, "kt-files-change", { files: this.files }));
|
|
203
204
|
}
|
|
204
205
|
removeAt(e) {
|
|
205
|
-
this.files = this.files.filter((t, a) => a !== e),
|
|
206
|
+
this.files = this.files.filter((t, a) => a !== e), h(this, "kt-files-change", { files: this.files });
|
|
206
207
|
}
|
|
207
208
|
browse() {
|
|
208
209
|
this.disabled || this.fileInput?.click();
|
|
@@ -217,13 +218,13 @@ class i extends f {
|
|
|
217
218
|
this.disabled || (e.preventDefault(), this.dragging = !1, this.addFiles(e.dataTransfer?.files ?? null));
|
|
218
219
|
}
|
|
219
220
|
render() {
|
|
220
|
-
return
|
|
221
|
+
return c`<div
|
|
221
222
|
part="base"
|
|
222
|
-
class=${
|
|
223
|
+
class=${b({ zone: !0, dragover: this.dragging })}
|
|
223
224
|
role="button"
|
|
224
225
|
tabindex=${this.disabled ? -1 : 0}
|
|
225
|
-
aria-disabled=${this.disabled ? "true" :
|
|
226
|
-
aria-label=${`${this.heading} ${this.linkText}`}
|
|
226
|
+
aria-disabled=${this.disabled ? "true" : p}
|
|
227
|
+
aria-label=${`${this.heading ?? l().dragAndDrop} ${this.linkText ?? l().browseFiles}`}
|
|
227
228
|
@click=${this.browse}
|
|
228
229
|
@keydown=${this.onZoneKeyDown}
|
|
229
230
|
@dragover=${this.onDragOver}
|
|
@@ -235,7 +236,7 @@ class i extends f {
|
|
|
235
236
|
<input
|
|
236
237
|
type="file"
|
|
237
238
|
?multiple=${this.multiple}
|
|
238
|
-
accept=${this.accept ||
|
|
239
|
+
accept=${this.accept || p}
|
|
239
240
|
@click=${(e) => e.stopPropagation()}
|
|
240
241
|
@change=${(e) => this.addFiles(e.target.files)}
|
|
241
242
|
/>
|
|
@@ -243,77 +244,79 @@ class i extends f {
|
|
|
243
244
|
<div class="prompt">
|
|
244
245
|
<span class="prompt-icon"><kt-icon name="file-up" size="24"></kt-icon></span>
|
|
245
246
|
<div>
|
|
246
|
-
<p class="title"
|
|
247
|
-
|
|
247
|
+
<p class="title">
|
|
248
|
+
${this.heading ?? l().dragAndDrop} ${this.linkText ?? l().browseFiles}
|
|
249
|
+
</p>
|
|
250
|
+
${this.recommendedSize ? c`<div class="hint">${l().recommendedSize(this.recommendedSize)}</div>` : p}
|
|
248
251
|
</div>
|
|
249
252
|
</div>
|
|
250
253
|
|
|
251
|
-
${this.files.length > 0 ?
|
|
254
|
+
${this.files.length > 0 ? c`<ul
|
|
252
255
|
part="list"
|
|
253
256
|
class="list"
|
|
254
|
-
aria-label
|
|
257
|
+
aria-label=${l().selectedFiles}
|
|
255
258
|
@click=${(e) => e.stopPropagation()}
|
|
256
259
|
>
|
|
257
260
|
${this.files.map(
|
|
258
|
-
(e, t) =>
|
|
261
|
+
(e, t) => c`<li class="item">
|
|
259
262
|
<span class="preview"><kt-icon name="file" size="24"></kt-icon></span>
|
|
260
263
|
<span class="info">
|
|
261
264
|
<span class="name" title=${e.name}>${e.name}</span>
|
|
262
265
|
<span class="details">
|
|
263
|
-
<span>${
|
|
264
|
-
${e.type ?
|
|
266
|
+
<span>${z(e.size)}</span>
|
|
267
|
+
${e.type ? c`<span>•</span><span>${e.type}</span>` : p}
|
|
265
268
|
</span>
|
|
266
269
|
</span>
|
|
267
270
|
<kt-button
|
|
268
271
|
variant="danger"
|
|
269
272
|
size="small"
|
|
270
273
|
icon="trash-2"
|
|
271
|
-
label=${
|
|
274
|
+
label=${l().remove(e.name)}
|
|
272
275
|
@click=${() => this.removeAt(t)}
|
|
273
276
|
></kt-button>
|
|
274
277
|
</li>`
|
|
275
278
|
)}
|
|
276
|
-
</ul>` :
|
|
279
|
+
</ul>` : p}
|
|
277
280
|
</div>`;
|
|
278
281
|
}
|
|
279
282
|
}
|
|
280
283
|
r([
|
|
281
|
-
|
|
284
|
+
y('input[type="file"]')
|
|
282
285
|
], i.prototype, "fileInput");
|
|
283
286
|
r([
|
|
284
|
-
|
|
287
|
+
g()
|
|
285
288
|
], i.prototype, "dragging");
|
|
286
289
|
r([
|
|
287
|
-
|
|
290
|
+
g()
|
|
288
291
|
], i.prototype, "files");
|
|
289
292
|
r([
|
|
290
|
-
|
|
293
|
+
n({ type: String })
|
|
291
294
|
], i.prototype, "heading");
|
|
292
295
|
r([
|
|
293
|
-
|
|
296
|
+
n({ type: String, attribute: "link-text" })
|
|
294
297
|
], i.prototype, "linkText");
|
|
295
298
|
r([
|
|
296
|
-
|
|
299
|
+
n({ type: String, attribute: "recommended-size" })
|
|
297
300
|
], i.prototype, "recommendedSize");
|
|
298
301
|
r([
|
|
299
|
-
|
|
302
|
+
n({ type: Boolean })
|
|
300
303
|
], i.prototype, "multiple");
|
|
301
304
|
r([
|
|
302
|
-
|
|
305
|
+
n({ type: String })
|
|
303
306
|
], i.prototype, "accept");
|
|
304
307
|
r([
|
|
305
|
-
|
|
308
|
+
n({ type: Number, attribute: "max-size" })
|
|
306
309
|
], i.prototype, "maxSize");
|
|
307
310
|
r([
|
|
308
|
-
|
|
311
|
+
n({ type: Boolean, reflect: !0 })
|
|
309
312
|
], i.prototype, "disabled");
|
|
310
313
|
r([
|
|
311
|
-
|
|
314
|
+
n({ type: String })
|
|
312
315
|
], i.prototype, "name");
|
|
313
316
|
r([
|
|
314
|
-
|
|
317
|
+
n({ type: Boolean, reflect: !0 })
|
|
315
318
|
], i.prototype, "required");
|
|
316
|
-
|
|
319
|
+
x("kt-drag-drop", i);
|
|
317
320
|
export {
|
|
318
321
|
i as KtDragDrop
|
|
319
322
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kt-drag-drop.js","sources":["../../../../src/internal/format.ts","../../../../src/components/forms/kt-drag-drop/kt-drag-drop.ts"],"sourcesContent":["/** Formatting helpers shared by elements that display data. */\n\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * A human-readable file size.\n *\n * Uses 1024 steps, matching what every desktop file manager shows, and one\n * decimal above a kilobyte — \"1.4 MB\" reads; \"1468006 B\" does not.\n */\nexport function formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '';\n\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n\n const rounded = unit === 0 ? String(Math.round(value)) : value.toFixed(1);\n return `${rounded} ${UNITS[unit]}`;\n}\n","import { css, html, nothing, type PropertyValues, type TemplateResult } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { KtElement, defineElement } from '#internal/kt-element';\nimport { emit } from '#internal/events';\nimport {\n attachFormInternals,\n setFormValue,\n setValidity,\n type UsableInternals,\n} from '#internal/form-control';\nimport { formatFileSize } from '#internal/format';\nimport '../../core/kt-icon/kt-icon.js';\nimport '../../core/kt-button/kt-button.js';\n\n/**\n * A file drop zone.\n *\n * Accepts a drop or a click, lists what was picked with sizes and types, and\n * lets each entry be removed. It holds the files in memory and reports them —\n * uploading is the application's business.\n *\n * @element kt-drag-drop\n *\n * @csspart base - The dashed drop zone.\n * @csspart list - The list of chosen files.\n *\n * @fires kt-files-change - The selection changed. `detail: { files }`.\n * @fires kt-files-rejected - Files were refused. `detail: { files, reason }`.\n *\n * @example\n * ```html\n * <kt-drag-drop accept=\"image/*\" recommended-size=\"800×400px\"></kt-drag-drop>\n * ```\n */\nexport class KtDragDrop extends KtElement {\n static readonly formAssociated = true;\n static override styles = [\n KtElement.styles,\n css`\n :host {\n display: block;\n user-select: none;\n }\n\n .zone {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--gap-drag-drop);\n padding: var(--padding-drag-drop);\n border: var(--border-width) dashed var(--color-dark-24);\n border-radius: var(--border-radius);\n cursor: pointer;\n transition:\n border-color var(--duration-instant),\n box-shadow var(--duration-instant);\n }\n\n .zone:hover {\n border-color: var(--text-muted);\n }\n\n .zone:focus-visible {\n outline: var(--outline-width) solid var(--color-primary-base);\n outline-offset: 2px;\n }\n\n .dragover {\n border-color: var(--color-primary-base);\n box-shadow: 0 0 0 6px var(--color-primary-soft);\n }\n\n .prompt {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--gap-button);\n text-align: center;\n }\n\n .prompt-icon {\n color: var(--text-muted);\n }\n\n .title {\n margin: 0;\n color: var(--color-primary-text);\n font: var(--font-normal-medium);\n }\n\n .hint {\n color: var(--text-muted);\n font: var(--font-normal-small);\n }\n\n input[type='file'] {\n display: none;\n }\n\n .list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n .item {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n padding: 12px;\n background: var(--surface-raised);\n border-radius: var(--border-radius);\n cursor: default;\n transition: background var(--duration-normal) var(--easing-standard);\n }\n .item:hover {\n background: var(--surface-hover);\n }\n\n .preview {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n overflow: hidden;\n color: var(--color-info-text);\n background: var(--color-dark-14);\n border-radius: 6px;\n }\n\n .info {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n }\n\n .name {\n overflow: hidden;\n color: var(--text-body);\n font: var(--font-normal-medium);\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n .details {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--text-muted);\n font: var(--font-normal-small);\n }\n `,\n ];\n\n @query('input[type=\"file\"]')\n private fileInput!: HTMLInputElement;\n\n private internals: UsableInternals | null = null;\n\n @state()\n private dragging = false;\n\n @state()\n private files: File[] = [];\n\n /**\n * The prompt above the browse link.\n *\n * Named `heading`, not `title`: an element already has a `title`, and\n * reusing it would put the prompt in a native tooltip on the whole zone.\n */\n @property({ type: String })\n heading = 'Drag and drop or';\n\n /** The clickable-looking part of the prompt. */\n @property({ type: String, attribute: 'link-text' })\n linkText = 'browse your files';\n\n /** Optional hint, e.g. `800×400px`. */\n @property({ type: String, attribute: 'recommended-size' })\n recommendedSize = '';\n\n @property({ type: Boolean })\n multiple = true;\n\n /** Same syntax as the native `accept`: `image/*`, `.pdf,.docx`. */\n @property({ type: String })\n accept = '';\n\n /** Largest accepted file, in bytes. Zero means no limit. */\n @property({ type: Number, attribute: 'max-size' })\n maxSize = 0;\n\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** The files are submitted under this name, as by `<input type=\"file\">`. */\n @property({ type: String })\n name = '';\n\n @property({ type: Boolean, reflect: true })\n required = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.internals ??= attachFormInternals(this);\n }\n\n // Untyped: `files` is private state, which PropertyValues<this> cannot name.\n override willUpdate(changed: PropertyValues): void {\n if (changed.has('files') || changed.has('name') || changed.has('required')) {\n this.syncFormValue();\n }\n }\n\n private syncFormValue(): void {\n // Like a native file input: nothing is submitted without a name or without\n // a file, and each file is its own entry under the one name.\n let value: FormData | null = null;\n if (this.name && this.files.length > 0) {\n value = new FormData();\n for (const file of this.files) value.append(this.name, file);\n }\n setFormValue(this.internals, value);\n\n const missing = this.required && this.files.length === 0;\n setValidity(this.internals, { valueMissing: missing }, missing ? 'Select a file.' : '');\n }\n\n formResetCallback(): void {\n this.files = [];\n this.dragging = false;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null): void {\n if (!(state instanceof FormData) || !this.name) return;\n this.files = state.getAll(this.name).filter((entry): entry is File => entry instanceof File);\n }\n\n /** The files currently held, newest last. */\n get selectedFiles(): readonly File[] {\n return this.files;\n }\n\n /** Empties the selection without firing a rejection. */\n clear(): void {\n this.files = [];\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n /**\n * `accept` is advisory on a native input — the OS dialog filters, but a\n * dropped file bypasses it entirely — so the same rules are enforced here.\n */\n private accepts(file: File): boolean {\n if (this.maxSize > 0 && file.size > this.maxSize) return false;\n if (!this.accept) return true;\n\n return this.accept\n .split(',')\n .map((pattern) => pattern.trim().toLowerCase())\n .filter(Boolean)\n .some((pattern) => {\n if (pattern.startsWith('.')) return file.name.toLowerCase().endsWith(pattern);\n if (pattern.endsWith('/*')) return file.type.startsWith(pattern.slice(0, -1));\n return file.type.toLowerCase() === pattern;\n });\n }\n\n private addFiles(incoming: FileList | null): void {\n if (!incoming || incoming.length === 0) return;\n\n const candidates = this.multiple ? [...incoming] : [...incoming].slice(0, 1);\n const accepted = candidates.filter((file) => this.accepts(file));\n const rejected = candidates.filter((file) => !this.accepts(file));\n\n if (rejected.length > 0) {\n emit(this, 'kt-files-rejected', {\n files: rejected,\n reason: this.maxSize > 0 && rejected.some((f) => f.size > this.maxSize) ? 'size' : 'type',\n });\n }\n if (accepted.length === 0) return;\n\n this.files = this.multiple ? [...this.files, ...accepted] : accepted;\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n private removeAt(index: number): void {\n this.files = this.files.filter((_, position) => position !== index);\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n private browse(): void {\n if (this.disabled) return;\n this.fileInput?.click();\n }\n\n private onZoneKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n this.browse();\n }\n\n private onDragOver(event: DragEvent): void {\n if (this.disabled) return;\n event.preventDefault();\n this.dragging = true;\n }\n\n private onDrop(event: DragEvent): void {\n if (this.disabled) return;\n event.preventDefault();\n this.dragging = false;\n this.addFiles(event.dataTransfer?.files ?? null);\n }\n\n override render(): TemplateResult {\n return html`<div\n part=\"base\"\n class=${classMap({ zone: true, dragover: this.dragging })}\n role=\"button\"\n tabindex=${this.disabled ? -1 : 0}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-label=${`${this.heading} ${this.linkText}`}\n @click=${this.browse}\n @keydown=${this.onZoneKeyDown}\n @dragover=${this.onDragOver}\n @dragleave=${() => {\n this.dragging = false;\n }}\n @drop=${this.onDrop}\n >\n <input\n type=\"file\"\n ?multiple=${this.multiple}\n accept=${this.accept || nothing}\n @click=${(event: Event) => event.stopPropagation()}\n @change=${(event: Event) => this.addFiles((event.target as HTMLInputElement).files)}\n />\n\n <div class=\"prompt\">\n <span class=\"prompt-icon\"><kt-icon name=\"file-up\" size=\"24\"></kt-icon></span>\n <div>\n <p class=\"title\">${this.heading} ${this.linkText}</p>\n ${\n this.recommendedSize\n ? html`<div class=\"hint\">Recommended image size: ${this.recommendedSize}</div>`\n : nothing\n }\n </div>\n </div>\n\n ${\n this.files.length > 0\n ? html`<ul\n part=\"list\"\n class=\"list\"\n aria-label=\"Selected files\"\n @click=${(event: Event) => event.stopPropagation()}\n >\n ${this.files.map(\n (file, index) =>\n html`<li class=\"item\">\n <span class=\"preview\"><kt-icon name=\"file\" size=\"24\"></kt-icon></span>\n <span class=\"info\">\n <span class=\"name\" title=${file.name}>${file.name}</span>\n <span class=\"details\">\n <span>${formatFileSize(file.size)}</span>\n ${file.type ? html`<span>•</span><span>${file.type}</span>` : nothing}\n </span>\n </span>\n <kt-button\n variant=\"danger\"\n size=\"small\"\n icon=\"trash-2\"\n label=${`Remove ${file.name}`}\n @click=${() => this.removeAt(index)}\n ></kt-button>\n </li>`,\n )}\n </ul>`\n : nothing\n }\n </div>`;\n }\n}\n\ndefineElement('kt-drag-drop', KtDragDrop);\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'kt-drag-drop': KtDragDrop;\n }\n}\n"],"names":["UNITS","formatFileSize","bytes","value","unit","KtDragDrop","KtElement","css","attachFormInternals","changed","file","setFormValue","missing","setValidity","state","entry","emit","pattern","incoming","candidates","accepted","rejected","f","index","_","position","event","html","classMap","nothing","__decorateClass","query","property","defineElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"kt-drag-drop.js","sources":["../../../../src/internal/format.ts","../../../../src/components/forms/kt-drag-drop/kt-drag-drop.ts"],"sourcesContent":["/** Formatting helpers shared by elements that display data. */\n\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * A human-readable file size.\n *\n * Uses 1024 steps, matching what every desktop file manager shows, and one\n * decimal above a kilobyte — \"1.4 MB\" reads; \"1468006 B\" does not.\n */\nexport function formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '';\n\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n\n const rounded = unit === 0 ? String(Math.round(value)) : value.toFixed(1);\n return `${rounded} ${UNITS[unit]}`;\n}\n","import { css, html, nothing, type PropertyValues, type TemplateResult } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport { KtElement, defineElement } from '#internal/kt-element';\nimport { emit } from '#internal/events';\nimport {\n attachFormInternals,\n setFormValue,\n setValidity,\n type UsableInternals,\n} from '#internal/form-control';\nimport { formatFileSize } from '#internal/format';\nimport { strings } from '#internal/strings';\nimport '../../core/kt-icon/kt-icon.js';\nimport '../../core/kt-button/kt-button.js';\n\n/**\n * A file drop zone.\n *\n * Accepts a drop or a click, lists what was picked with sizes and types, and\n * lets each entry be removed. It holds the files in memory and reports them —\n * uploading is the application's business.\n *\n * @element kt-drag-drop\n *\n * @csspart base - The dashed drop zone.\n * @csspart list - The list of chosen files.\n *\n * @fires kt-files-change - The selection changed. `detail: { files }`.\n * @fires kt-files-rejected - Files were refused. `detail: { files, reason }`.\n *\n * @example\n * ```html\n * <kt-drag-drop accept=\"image/*\" recommended-size=\"800×400px\"></kt-drag-drop>\n * ```\n */\nexport class KtDragDrop extends KtElement {\n static readonly formAssociated = true;\n static override styles = [\n KtElement.styles,\n css`\n :host {\n display: block;\n user-select: none;\n }\n\n .zone {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--gap-drag-drop);\n padding: var(--padding-drag-drop);\n border: var(--border-width) dashed var(--color-dark-24);\n border-radius: var(--border-radius);\n cursor: pointer;\n transition:\n border-color var(--duration-instant),\n box-shadow var(--duration-instant);\n }\n\n .zone:hover {\n border-color: var(--text-muted);\n }\n\n .zone:focus-visible {\n outline: var(--outline-width) solid var(--color-primary-base);\n outline-offset: 2px;\n }\n\n .dragover {\n border-color: var(--color-primary-base);\n box-shadow: 0 0 0 6px var(--color-primary-soft);\n }\n\n .prompt {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--gap-button);\n text-align: center;\n }\n\n .prompt-icon {\n color: var(--text-muted);\n }\n\n .title {\n margin: 0;\n color: var(--color-primary-text);\n font: var(--font-normal-medium);\n }\n\n .hint {\n color: var(--text-muted);\n font: var(--font-normal-small);\n }\n\n input[type='file'] {\n display: none;\n }\n\n .list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n .item {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n padding: 12px;\n background: var(--surface-raised);\n border-radius: var(--border-radius);\n cursor: default;\n transition: background var(--duration-normal) var(--easing-standard);\n }\n .item:hover {\n background: var(--surface-hover);\n }\n\n .preview {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n overflow: hidden;\n color: var(--color-info-text);\n background: var(--color-dark-14);\n border-radius: 6px;\n }\n\n .info {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n }\n\n .name {\n overflow: hidden;\n color: var(--text-body);\n font: var(--font-normal-medium);\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n .details {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--text-muted);\n font: var(--font-normal-small);\n }\n `,\n ];\n\n @query('input[type=\"file\"]')\n private fileInput!: HTMLInputElement;\n\n private internals: UsableInternals | null = null;\n\n @state()\n private dragging = false;\n\n @state()\n private files: File[] = [];\n\n /**\n * The prompt above the browse link.\n *\n * Named `heading`, not `title`: an element already has a `title`, and\n * reusing it would put the prompt in a native tooltip on the whole zone.\n */\n @property({ type: String })\n heading: string | undefined = undefined;\n\n /** The clickable-looking part of the prompt. */\n @property({ type: String, attribute: 'link-text' })\n linkText: string | undefined = undefined;\n\n /** Optional hint, e.g. `800×400px`. */\n @property({ type: String, attribute: 'recommended-size' })\n recommendedSize = '';\n\n @property({ type: Boolean })\n multiple = true;\n\n /** Same syntax as the native `accept`: `image/*`, `.pdf,.docx`. */\n @property({ type: String })\n accept = '';\n\n /** Largest accepted file, in bytes. Zero means no limit. */\n @property({ type: Number, attribute: 'max-size' })\n maxSize = 0;\n\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** The files are submitted under this name, as by `<input type=\"file\">`. */\n @property({ type: String })\n name = '';\n\n @property({ type: Boolean, reflect: true })\n required = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.internals ??= attachFormInternals(this);\n }\n\n // Untyped: `files` is private state, which PropertyValues<this> cannot name.\n override willUpdate(changed: PropertyValues): void {\n if (\n changed.has('files') ||\n changed.has('name') ||\n changed.has('required') ||\n this.stringsChanged(changed)\n ) {\n this.syncFormValue();\n }\n }\n\n private syncFormValue(): void {\n // Like a native file input: nothing is submitted without a name or without\n // a file, and each file is its own entry under the one name.\n let value: FormData | null = null;\n if (this.name && this.files.length > 0) {\n value = new FormData();\n for (const file of this.files) value.append(this.name, file);\n }\n setFormValue(this.internals, value);\n\n const missing = this.required && this.files.length === 0;\n setValidity(this.internals, { valueMissing: missing }, missing ? strings().selectFile : '');\n }\n\n formResetCallback(): void {\n this.files = [];\n this.dragging = false;\n }\n\n formStateRestoreCallback(state: string | File | FormData | null): void {\n if (!(state instanceof FormData) || !this.name) return;\n this.files = state.getAll(this.name).filter((entry): entry is File => entry instanceof File);\n }\n\n /** The files currently held, newest last. */\n get selectedFiles(): readonly File[] {\n return this.files;\n }\n\n /** Empties the selection without firing a rejection. */\n clear(): void {\n this.files = [];\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n /**\n * `accept` is advisory on a native input — the OS dialog filters, but a\n * dropped file bypasses it entirely — so the same rules are enforced here.\n */\n private accepts(file: File): boolean {\n if (this.maxSize > 0 && file.size > this.maxSize) return false;\n if (!this.accept) return true;\n\n return this.accept\n .split(',')\n .map((pattern) => pattern.trim().toLowerCase())\n .filter(Boolean)\n .some((pattern) => {\n if (pattern.startsWith('.')) return file.name.toLowerCase().endsWith(pattern);\n if (pattern.endsWith('/*')) return file.type.startsWith(pattern.slice(0, -1));\n return file.type.toLowerCase() === pattern;\n });\n }\n\n private addFiles(incoming: FileList | null): void {\n if (!incoming || incoming.length === 0) return;\n\n const candidates = this.multiple ? [...incoming] : [...incoming].slice(0, 1);\n const accepted = candidates.filter((file) => this.accepts(file));\n const rejected = candidates.filter((file) => !this.accepts(file));\n\n if (rejected.length > 0) {\n emit(this, 'kt-files-rejected', {\n files: rejected,\n reason: this.maxSize > 0 && rejected.some((f) => f.size > this.maxSize) ? 'size' : 'type',\n });\n }\n if (accepted.length === 0) return;\n\n this.files = this.multiple ? [...this.files, ...accepted] : accepted;\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n private removeAt(index: number): void {\n this.files = this.files.filter((_, position) => position !== index);\n emit(this, 'kt-files-change', { files: this.files });\n }\n\n private browse(): void {\n if (this.disabled) return;\n this.fileInput?.click();\n }\n\n private onZoneKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n this.browse();\n }\n\n private onDragOver(event: DragEvent): void {\n if (this.disabled) return;\n event.preventDefault();\n this.dragging = true;\n }\n\n private onDrop(event: DragEvent): void {\n if (this.disabled) return;\n event.preventDefault();\n this.dragging = false;\n this.addFiles(event.dataTransfer?.files ?? null);\n }\n\n override render(): TemplateResult {\n return html`<div\n part=\"base\"\n class=${classMap({ zone: true, dragover: this.dragging })}\n role=\"button\"\n tabindex=${this.disabled ? -1 : 0}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-label=${`${this.heading ?? strings().dragAndDrop} ${this.linkText ?? strings().browseFiles}`}\n @click=${this.browse}\n @keydown=${this.onZoneKeyDown}\n @dragover=${this.onDragOver}\n @dragleave=${() => {\n this.dragging = false;\n }}\n @drop=${this.onDrop}\n >\n <input\n type=\"file\"\n ?multiple=${this.multiple}\n accept=${this.accept || nothing}\n @click=${(event: Event) => event.stopPropagation()}\n @change=${(event: Event) => this.addFiles((event.target as HTMLInputElement).files)}\n />\n\n <div class=\"prompt\">\n <span class=\"prompt-icon\"><kt-icon name=\"file-up\" size=\"24\"></kt-icon></span>\n <div>\n <p class=\"title\">\n ${this.heading ?? strings().dragAndDrop} ${this.linkText ?? strings().browseFiles}\n </p>\n ${\n this.recommendedSize\n ? html`<div class=\"hint\">${strings().recommendedSize(this.recommendedSize)}</div>`\n : nothing\n }\n </div>\n </div>\n\n ${\n this.files.length > 0\n ? html`<ul\n part=\"list\"\n class=\"list\"\n aria-label=${strings().selectedFiles}\n @click=${(event: Event) => event.stopPropagation()}\n >\n ${this.files.map(\n (file, index) =>\n html`<li class=\"item\">\n <span class=\"preview\"><kt-icon name=\"file\" size=\"24\"></kt-icon></span>\n <span class=\"info\">\n <span class=\"name\" title=${file.name}>${file.name}</span>\n <span class=\"details\">\n <span>${formatFileSize(file.size)}</span>\n ${file.type ? html`<span>•</span><span>${file.type}</span>` : nothing}\n </span>\n </span>\n <kt-button\n variant=\"danger\"\n size=\"small\"\n icon=\"trash-2\"\n label=${strings().remove(file.name)}\n @click=${() => this.removeAt(index)}\n ></kt-button>\n </li>`,\n )}\n </ul>`\n : nothing\n }\n </div>`;\n }\n}\n\ndefineElement('kt-drag-drop', KtDragDrop);\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'kt-drag-drop': KtDragDrop;\n }\n}\n"],"names":["UNITS","formatFileSize","bytes","value","unit","KtDragDrop","KtElement","css","attachFormInternals","changed","file","setFormValue","missing","setValidity","strings","state","entry","emit","pattern","incoming","candidates","accepted","rejected","f","index","_","position","event","html","classMap","nothing","__decorateClass","query","property","defineElement"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { css as f, nothing as h, html as
|
|
1
|
+
import { css as f, nothing as h, html as d } from "lit";
|
|
2
2
|
import { queryAssignedElements as c, state as g, property as a } from "lit/decorators.js";
|
|
3
|
-
import { K as p, d as m } from "../../../chunks/kt-element-
|
|
3
|
+
import { K as p, d as m } from "../../../chunks/kt-element-E-tfJpGQ.js";
|
|
4
4
|
import { h as y } from "../../../chunks/slots-BPbIdxAJ.js";
|
|
5
|
+
import { s as b } from "../../../chunks/index-DSSLJirw.js";
|
|
5
6
|
import "../../feedback/kt-skeleton/kt-skeleton.js";
|
|
6
|
-
var
|
|
7
|
-
for (var i = void 0,
|
|
8
|
-
(n = l
|
|
9
|
-
return i &&
|
|
7
|
+
var u = Object.defineProperty, s = (r, e, o, v) => {
|
|
8
|
+
for (var i = void 0, l = r.length - 1, n; l >= 0; l--)
|
|
9
|
+
(n = r[l]) && (i = n(e, o, i) || i);
|
|
10
|
+
return i && u(e, o, i), i;
|
|
10
11
|
};
|
|
11
12
|
class t extends p {
|
|
12
13
|
constructor() {
|
|
@@ -133,19 +134,19 @@ class t extends p {
|
|
|
133
134
|
}
|
|
134
135
|
render() {
|
|
135
136
|
const e = !!this.heading || !!this.description;
|
|
136
|
-
return
|
|
137
|
+
return d`<fieldset part="base" ?disabled=${this.disabled}>
|
|
137
138
|
<legend class=${this.heading ? "" : "visually-hidden"}>
|
|
138
|
-
<slot name="heading">${this.heading ||
|
|
139
|
+
<slot name="heading">${this.heading || b().formSection}</slot>
|
|
139
140
|
</legend>
|
|
140
141
|
|
|
141
|
-
${e && this.description ?
|
|
142
|
+
${e && this.description ? d`<div part="header" class="header">
|
|
142
143
|
<p class="description">${this.description}</p>
|
|
143
144
|
</div>` : h}
|
|
144
145
|
|
|
145
146
|
<div part="fields" class="fields">
|
|
146
147
|
${this.loading ? Array.from(
|
|
147
148
|
{ length: this.loadingFields },
|
|
148
|
-
() =>
|
|
149
|
+
() => d`<div class="placeholder" aria-hidden="true">
|
|
149
150
|
<kt-skeleton width="30%" height="14px"></kt-skeleton>
|
|
150
151
|
<kt-skeleton variant="rect" height="40px"></kt-skeleton>
|
|
151
152
|
</div>`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kt-form.js","sources":["../../../../src/components/forms/kt-form/kt-form.ts"],"sourcesContent":["import { css, html, nothing, type PropertyValues, type TemplateResult } from 'lit';\nimport { property, queryAssignedElements, state } from 'lit/decorators.js';\nimport { KtElement, defineElement } from '#internal/kt-element';\nimport { hasAssignedContent } from '#internal/slots';\nimport '../../feedback/kt-skeleton/kt-skeleton.js';\n\n/** Anything the panel can switch off along with itself. */\ninterface Disableable extends HTMLElement {\n disabled?: boolean;\n}\n\n/**\n * A panel grouping related fields.\n *\n * It sits one step above the page on `--surface-form`, so the fields inside —\n * filled at `--surface-field` — read as wells recessed into it rather than as\n * boxes floating on it.\n *\n * **Put it inside your own `<form>`.** It renders a `<fieldset>`, not a form:\n * Kanto fields are form-associated custom elements, and a control finds its\n * form by walking its *own* tree. A `<form>` inside this element's shadow root\n * would not own anything slotted into it, and `new FormData(form)` would come\n * back empty. The `<form>` above stays in charge of submission, validation and\n * reset; this owns the grouping and the look.\n *\n * @element kt-form\n *\n * @slot - The fields.\n * @slot heading - Replaces the `heading` attribute, for markup.\n * @slot footer - Actions, right-aligned. Hidden when empty.\n *\n * @csspart base - The `<fieldset>`.\n * @csspart header - The heading and description.\n * @csspart fields - The field column.\n * @csspart footer - The action row.\n *\n * @example\n * ```html\n * <form>\n * <kt-form heading=\"Identity\" description=\"How we address you.\">\n * <kt-label-input label=\"Company\" required>\n * <kt-input name=\"company\" required></kt-input>\n * </kt-label-input>\n * <kt-button slot=\"footer\" type=\"submit\">Save</kt-button>\n * </kt-form>\n * </form>\n * ```\n */\nexport class KtForm extends KtElement {\n static override styles = [\n KtElement.styles,\n css`\n :host {\n display: block;\n }\n\n fieldset {\n display: flex;\n flex-direction: column;\n gap: var(--gap-form);\n min-width: 0;\n margin: 0;\n padding: var(--padding-card);\n background-color: var(--surface-form);\n border: var(--border-width) solid var(--border-subtle);\n border-radius: var(--border-radius-card);\n }\n\n .header {\n display: flex;\n flex-direction: column;\n gap: var(--gap-element);\n }\n\n /* A legend is the accessible name for the group, so it stays in the\n accessibility tree even when there is nothing to show. */\n legend {\n padding: 0;\n color: var(--text-body);\n font: var(--font-title-h6);\n }\n\n legend.visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n .description {\n margin: 0;\n color: var(--text-muted);\n font: var(--font-normal-regular);\n }\n\n .fields {\n display: flex;\n flex-direction: column;\n gap: var(--gap-form);\n min-width: 0;\n }\n\n .placeholder {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: var(--gap-button);\n }\n\n /* Collapsed from JS: a wrapper around a <slot> always has the slot as a\n child, so :has(*) cannot tell an empty slot from a filled one. */\n .footer.empty {\n display: none;\n }\n\n /* While loading, the real fields stay in the DOM — and keep their state\n — but step aside for the placeholders. */\n :host([loading]) ::slotted(*) {\n display: none;\n }\n\n :host([disabled]) fieldset {\n opacity: 0.6;\n }\n `,\n ];\n\n @queryAssignedElements()\n private fields!: HTMLElement[];\n\n @state()\n private hasFooter = false;\n\n /** Group heading. Also the fieldset's accessible name. */\n @property({ type: String })\n heading = '';\n\n @property({ type: String })\n description = '';\n\n /** Replaces the fields with placeholders. */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n /** Switches off every field in the panel. */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** How many placeholder fields `loading` draws. */\n @property({ type: Number, attribute: 'loading-fields' })\n loadingFields = 3;\n\n override updated(changed: PropertyValues<this>): void {\n if (changed.has('disabled')) this.syncDisabled();\n }\n\n /**\n * A native `<fieldset disabled>` switches off its descendants, but slotted\n * fields are not descendants of the fieldset in their own tree — the shadow\n * boundary sits between them — so the platform never reaches them. Walk the\n * slotted controls and set it directly.\n */\n /**\n * The initial `slotchange` is queued as a microtask and can land after the\n * first render has settled, so read the slots once directly as well.\n */\n override firstUpdated(): void {\n this.readSlots();\n this.syncDisabled();\n }\n\n private readSlots(): void {\n this.hasFooter = hasAssignedContent(\n this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"footer\"]'),\n );\n }\n\n private onFooterSlotChange(): void {\n this.readSlots();\n }\n\n private syncDisabled(): void {\n for (const field of this.fields ?? []) {\n if ('disabled' in field) (field as Disableable).disabled = this.disabled;\n for (const nested of field.querySelectorAll<Disableable>('*')) {\n if ('disabled' in nested) nested.disabled = this.disabled;\n }\n }\n }\n\n override render(): TemplateResult {\n const hasHeader = Boolean(this.heading) || Boolean(this.description);\n\n return html`<fieldset part=\"base\" ?disabled=${this.disabled}>\n <legend class=${this.heading ? '' : 'visually-hidden'}>\n <slot name=\"heading\">${this.heading ||
|
|
1
|
+
{"version":3,"file":"kt-form.js","sources":["../../../../src/components/forms/kt-form/kt-form.ts"],"sourcesContent":["import { css, html, nothing, type PropertyValues, type TemplateResult } from 'lit';\nimport { property, queryAssignedElements, state } from 'lit/decorators.js';\nimport { KtElement, defineElement } from '#internal/kt-element';\nimport { hasAssignedContent } from '#internal/slots';\nimport { strings } from '#internal/strings';\nimport '../../feedback/kt-skeleton/kt-skeleton.js';\n\n/** Anything the panel can switch off along with itself. */\ninterface Disableable extends HTMLElement {\n disabled?: boolean;\n}\n\n/**\n * A panel grouping related fields.\n *\n * It sits one step above the page on `--surface-form`, so the fields inside —\n * filled at `--surface-field` — read as wells recessed into it rather than as\n * boxes floating on it.\n *\n * **Put it inside your own `<form>`.** It renders a `<fieldset>`, not a form:\n * Kanto fields are form-associated custom elements, and a control finds its\n * form by walking its *own* tree. A `<form>` inside this element's shadow root\n * would not own anything slotted into it, and `new FormData(form)` would come\n * back empty. The `<form>` above stays in charge of submission, validation and\n * reset; this owns the grouping and the look.\n *\n * @element kt-form\n *\n * @slot - The fields.\n * @slot heading - Replaces the `heading` attribute, for markup.\n * @slot footer - Actions, right-aligned. Hidden when empty.\n *\n * @csspart base - The `<fieldset>`.\n * @csspart header - The heading and description.\n * @csspart fields - The field column.\n * @csspart footer - The action row.\n *\n * @example\n * ```html\n * <form>\n * <kt-form heading=\"Identity\" description=\"How we address you.\">\n * <kt-label-input label=\"Company\" required>\n * <kt-input name=\"company\" required></kt-input>\n * </kt-label-input>\n * <kt-button slot=\"footer\" type=\"submit\">Save</kt-button>\n * </kt-form>\n * </form>\n * ```\n */\nexport class KtForm extends KtElement {\n static override styles = [\n KtElement.styles,\n css`\n :host {\n display: block;\n }\n\n fieldset {\n display: flex;\n flex-direction: column;\n gap: var(--gap-form);\n min-width: 0;\n margin: 0;\n padding: var(--padding-card);\n background-color: var(--surface-form);\n border: var(--border-width) solid var(--border-subtle);\n border-radius: var(--border-radius-card);\n }\n\n .header {\n display: flex;\n flex-direction: column;\n gap: var(--gap-element);\n }\n\n /* A legend is the accessible name for the group, so it stays in the\n accessibility tree even when there is nothing to show. */\n legend {\n padding: 0;\n color: var(--text-body);\n font: var(--font-title-h6);\n }\n\n legend.visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n .description {\n margin: 0;\n color: var(--text-muted);\n font: var(--font-normal-regular);\n }\n\n .fields {\n display: flex;\n flex-direction: column;\n gap: var(--gap-form);\n min-width: 0;\n }\n\n .placeholder {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: var(--gap-button);\n }\n\n /* Collapsed from JS: a wrapper around a <slot> always has the slot as a\n child, so :has(*) cannot tell an empty slot from a filled one. */\n .footer.empty {\n display: none;\n }\n\n /* While loading, the real fields stay in the DOM — and keep their state\n — but step aside for the placeholders. */\n :host([loading]) ::slotted(*) {\n display: none;\n }\n\n :host([disabled]) fieldset {\n opacity: 0.6;\n }\n `,\n ];\n\n @queryAssignedElements()\n private fields!: HTMLElement[];\n\n @state()\n private hasFooter = false;\n\n /** Group heading. Also the fieldset's accessible name. */\n @property({ type: String })\n heading = '';\n\n @property({ type: String })\n description = '';\n\n /** Replaces the fields with placeholders. */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n /** Switches off every field in the panel. */\n @property({ type: Boolean, reflect: true })\n disabled = false;\n\n /** How many placeholder fields `loading` draws. */\n @property({ type: Number, attribute: 'loading-fields' })\n loadingFields = 3;\n\n override updated(changed: PropertyValues<this>): void {\n if (changed.has('disabled')) this.syncDisabled();\n }\n\n /**\n * A native `<fieldset disabled>` switches off its descendants, but slotted\n * fields are not descendants of the fieldset in their own tree — the shadow\n * boundary sits between them — so the platform never reaches them. Walk the\n * slotted controls and set it directly.\n */\n /**\n * The initial `slotchange` is queued as a microtask and can land after the\n * first render has settled, so read the slots once directly as well.\n */\n override firstUpdated(): void {\n this.readSlots();\n this.syncDisabled();\n }\n\n private readSlots(): void {\n this.hasFooter = hasAssignedContent(\n this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"footer\"]'),\n );\n }\n\n private onFooterSlotChange(): void {\n this.readSlots();\n }\n\n private syncDisabled(): void {\n for (const field of this.fields ?? []) {\n if ('disabled' in field) (field as Disableable).disabled = this.disabled;\n for (const nested of field.querySelectorAll<Disableable>('*')) {\n if ('disabled' in nested) nested.disabled = this.disabled;\n }\n }\n }\n\n override render(): TemplateResult {\n const hasHeader = Boolean(this.heading) || Boolean(this.description);\n\n return html`<fieldset part=\"base\" ?disabled=${this.disabled}>\n <legend class=${this.heading ? '' : 'visually-hidden'}>\n <slot name=\"heading\">${this.heading || strings().formSection}</slot>\n </legend>\n\n ${\n hasHeader && this.description\n ? html`<div part=\"header\" class=\"header\">\n <p class=\"description\">${this.description}</p>\n </div>`\n : nothing\n }\n\n <div part=\"fields\" class=\"fields\">\n ${\n this.loading\n ? Array.from(\n { length: this.loadingFields },\n () =>\n html`<div class=\"placeholder\" aria-hidden=\"true\">\n <kt-skeleton width=\"30%\" height=\"14px\"></kt-skeleton>\n <kt-skeleton variant=\"rect\" height=\"40px\"></kt-skeleton>\n </div>`,\n )\n : nothing\n }\n <slot @slotchange=${this.syncDisabled}></slot>\n </div>\n\n <div part=\"footer\" class=${this.hasFooter ? 'footer' : 'footer empty'}>\n <slot name=\"footer\" @slotchange=${this.onFooterSlotChange}></slot>\n </div>\n </fieldset>`;\n }\n}\n\ndefineElement('kt-form', KtForm);\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'kt-form': KtForm;\n }\n}\n"],"names":["KtForm","KtElement","css","changed","hasAssignedContent","field","nested","hasHeader","html","strings","nothing","__decorateClass","queryAssignedElements","state","property","defineElement"],"mappings":"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"}
|