kanto-ds 1.3.0 → 1.5.1
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 +10 -10
- package/dist/chunks/defaults-CFrjqae2.js +59 -0
- package/dist/chunks/{defaults-DKzFsNbv.js.map → defaults-CFrjqae2.js.map} +1 -1
- package/dist/chunks/events-r51msXNf.js +22 -0
- package/dist/chunks/{events-Bjl5Zmmq.js.map → events-r51msXNf.js.map} +1 -1
- package/dist/chunks/floating-B7_Dahpn.js +54 -0
- package/dist/chunks/floating-B7_Dahpn.js.map +1 -0
- package/dist/chunks/{form-control-CxjBTt1c.js → form-control-B74_EHtG.js} +7 -7
- package/dist/chunks/form-control-B74_EHtG.js.map +1 -0
- package/dist/chunks/{index-fzPIgxe2.js → index-STEEvEP7.js} +33 -16
- package/dist/chunks/index-STEEvEP7.js.map +1 -0
- package/dist/chunks/kt-calendar-B_hFdD9V.js +640 -0
- package/dist/chunks/kt-calendar-B_hFdD9V.js.map +1 -0
- package/dist/chunks/{kt-element-mdd5cXZX.js → kt-element-Djx2JcGl.js} +2 -2
- package/dist/chunks/{kt-element-mdd5cXZX.js.map → kt-element-Djx2JcGl.js.map} +1 -1
- package/dist/chunks/{listbox-Sm3nvsC6.js → listbox-llcUu_V-.js} +18 -14
- package/dist/chunks/{listbox-Sm3nvsC6.js.map → listbox-llcUu_V-.js.map} +1 -1
- package/dist/chunks/locale-C5xLkGx5.js +14 -0
- package/dist/chunks/locale-C5xLkGx5.js.map +1 -0
- package/dist/chunks/position-Cx_ZH5zh.js +39 -0
- package/dist/chunks/position-Cx_ZH5zh.js.map +1 -0
- package/dist/components/core/kt-avatar/kt-avatar.js +2 -2
- package/dist/components/core/kt-badge/kt-badge.js +3 -3
- package/dist/components/core/kt-button/kt-button.js +1 -1
- package/dist/components/core/kt-card/kt-card.js +2 -2
- package/dist/components/core/kt-code/kt-code.js +3 -3
- 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.js +4 -4
- package/dist/components/data/kt-chart/kt-chart.js +52 -52
- 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.js +3 -3
- package/dist/components/data/kt-stat/kt-stat.js +1 -1
- package/dist/components/data/kt-table/kt-table.d.ts +53 -3
- package/dist/components/data/kt-table/kt-table.js +286 -131
- package/dist/components/data/kt-table/kt-table.js.map +1 -1
- package/dist/components/data/kt-table/window.d.ts +19 -0
- package/dist/components/data/kt-timeline/kt-timeline.js +1 -1
- package/dist/components/feedback/kt-alert/kt-alert.js +3 -3
- package/dist/components/feedback/kt-empty-state/kt-empty-state.js +1 -1
- package/dist/components/feedback/kt-progress-bar/kt-progress-bar.js +10 -10
- package/dist/components/feedback/kt-progress-bar/kt-progress-bar.js.map +1 -1
- package/dist/components/feedback/kt-skeleton/kt-skeleton.js +1 -1
- package/dist/components/feedback/kt-toast/kt-toast.js +58 -42
- 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.d.ts +20 -0
- package/dist/components/feedback/kt-tooltip/kt-tooltip.js +95 -51
- package/dist/components/feedback/kt-tooltip/kt-tooltip.js.map +1 -1
- package/dist/components/forms/kt-calendar/kt-calendar.d.ts +96 -0
- package/dist/components/forms/kt-calendar/kt-calendar.js +13 -0
- package/dist/components/forms/kt-calendar/kt-calendar.js.map +1 -0
- package/dist/components/forms/kt-checkbox/kt-checkbox.d.ts +75 -0
- package/dist/components/forms/kt-checkbox/kt-checkbox.js +212 -0
- package/dist/components/forms/kt-checkbox/kt-checkbox.js.map +1 -0
- package/dist/components/forms/kt-date-picker/kt-date-picker.d.ts +105 -0
- package/dist/components/forms/kt-date-picker/kt-date-picker.js +382 -0
- package/dist/components/forms/kt-date-picker/kt-date-picker.js.map +1 -0
- package/dist/components/forms/kt-drag-drop/kt-drag-drop.d.ts +9 -0
- package/dist/components/forms/kt-drag-drop/kt-drag-drop.js +36 -22
- 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.d.ts +37 -1
- package/dist/components/forms/kt-input/kt-input.js +380 -194
- package/dist/components/forms/kt-input/kt-input.js.map +1 -1
- package/dist/components/forms/kt-input-menu/kt-input-menu.d.ts +13 -0
- package/dist/components/forms/kt-input-menu/kt-input-menu.js +73 -42
- 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 +112 -0
- package/dist/components/forms/kt-radio-group/kt-radio-group.js +279 -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 +13 -0
- package/dist/components/forms/kt-select/kt-select.js +87 -56
- package/dist/components/forms/kt-select/kt-select.js.map +1 -1
- package/dist/components/forms/kt-textarea/kt-textarea.d.ts +9 -0
- package/dist/components/forms/kt-textarea/kt-textarea.js +28 -14
- package/dist/components/forms/kt-textarea/kt-textarea.js.map +1 -1
- package/dist/components/forms/kt-toggle/kt-toggle.d.ts +9 -0
- package/dist/components/forms/kt-toggle/kt-toggle.js +43 -29
- package/dist/components/forms/kt-toggle/kt-toggle.js.map +1 -1
- package/dist/components/navigation/kt-breadcrumb/kt-breadcrumb.js +3 -3
- package/dist/components/navigation/kt-header/kt-header.js +3 -3
- package/dist/components/navigation/kt-page-header/kt-page-header.js +1 -1
- package/dist/components/navigation/kt-segmented-control/kt-segmented-control.d.ts +9 -0
- package/dist/components/navigation/kt-segmented-control/kt-segmented-control.js +40 -22
- 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.js +3 -3
- package/dist/components/navigation/kt-tabs/kt-tabs.js +2 -2
- package/dist/components/navigation/kt-toggle-button/kt-toggle-button.js +2 -2
- package/dist/components/navigation/kt-toggle-button-group/kt-toggle-button-group.js +13 -7
- 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 +2 -2
- package/dist/components/overlays/kt-confirm-dialog/kt-confirm-dialog.js +3 -3
- package/dist/components/overlays/kt-dropdown/kt-dropdown.d.ts +7 -5
- package/dist/components/overlays/kt-dropdown/kt-dropdown.js +67 -57
- package/dist/components/overlays/kt-dropdown/kt-dropdown.js.map +1 -1
- package/dist/components/overlays/kt-modal/kt-modal.js +3 -3
- package/dist/components/overlays/kt-side-panel/kt-side-panel.js +3 -3
- package/dist/custom-elements.json +3526 -741
- package/dist/icons/defaults.d.ts +2 -0
- package/dist/icons/index.js +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +99 -90
- package/dist/index.js.map +1 -1
- package/dist/internal/countries.d.ts +24 -9
- package/dist/internal/date.d.ts +53 -0
- package/dist/internal/events.d.ts +5 -0
- package/dist/internal/floating.d.ts +48 -0
- package/dist/internal/form-control.d.ts +5 -1
- package/dist/internal/locale.d.ts +15 -0
- package/dist/internal/position.d.ts +27 -0
- package/dist/internal/strings.d.ts +25 -0
- package/dist/react/index.d.ts +25 -0
- package/dist/react/index.js +189 -156
- package/dist/react/index.js.map +1 -1
- package/dist/react/jsx.d.ts +22 -1
- package/dist/strings/index.js +1 -1
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/responsive.css +1 -0
- package/dist/tokens/spacing.css +1 -0
- package/dist/vscode.css-custom-data.json +20 -0
- package/dist/vscode.html-custom-data.json +221 -9
- package/dist/vue/index.d.ts +10 -0
- package/dist/vue/index.js +11 -7
- package/dist/vue/index.js.map +1 -1
- package/dist/web-types.json +514 -12
- package/package.json +18 -1
- package/dist/chunks/defaults-DKzFsNbv.js +0 -57
- package/dist/chunks/events-Bjl5Zmmq.js +0 -18
- package/dist/chunks/form-control-CxjBTt1c.js.map +0 -1
- package/dist/chunks/index-fzPIgxe2.js.map +0 -1
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import { css as v, nothing as p, html as c } from "lit";
|
|
2
|
-
import { query as y, state as
|
|
2
|
+
import { query as y, state as f, property as n } from "lit/decorators.js";
|
|
3
3
|
import { classMap as b } from "lit/directives/class-map.js";
|
|
4
|
-
import { K as
|
|
5
|
-
import { e as h } from "../../../chunks/events-
|
|
6
|
-
import { a as k, s as $, b as w } from "../../../chunks/form-control-
|
|
7
|
-
import { s as l } from "../../../chunks/index-
|
|
4
|
+
import { K as u, d as x } from "../../../chunks/kt-element-Djx2JcGl.js";
|
|
5
|
+
import { e as h } from "../../../chunks/events-r51msXNf.js";
|
|
6
|
+
import { a as k, s as $, b as w } from "../../../chunks/form-control-B74_EHtG.js";
|
|
7
|
+
import { s as l } from "../../../chunks/index-STEEvEP7.js";
|
|
8
8
|
import "../../core/kt-icon/kt-icon.js";
|
|
9
9
|
import "../../core/kt-button/kt-button.js";
|
|
10
|
-
const
|
|
10
|
+
const g = ["B", "KB", "MB", "GB", "TB"];
|
|
11
11
|
function z(d) {
|
|
12
12
|
if (!Number.isFinite(d) || d < 0) return "";
|
|
13
13
|
let e = d, t = 0;
|
|
14
|
-
for (; e >= 1024 && t <
|
|
14
|
+
for (; e >= 1024 && t < g.length - 1; )
|
|
15
15
|
e /= 1024, t += 1;
|
|
16
|
-
return `${t === 0 ? String(Math.round(e)) : e.toFixed(1)} ${
|
|
16
|
+
return `${t === 0 ? String(Math.round(e)) : e.toFixed(1)} ${g[t]}`;
|
|
17
17
|
}
|
|
18
|
-
var
|
|
19
|
-
for (var s = void 0, o = d.length - 1,
|
|
20
|
-
(
|
|
21
|
-
return s &&
|
|
18
|
+
var D = Object.defineProperty, r = (d, e, t, a) => {
|
|
19
|
+
for (var s = void 0, o = d.length - 1, m; o >= 0; o--)
|
|
20
|
+
(m = d[o]) && (s = m(e, t, s) || s);
|
|
21
|
+
return s && D(e, t, s), s;
|
|
22
22
|
};
|
|
23
|
-
class i extends
|
|
23
|
+
class i extends u {
|
|
24
24
|
constructor() {
|
|
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
|
+
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.formDisabled = !1, this.name = "", this.required = !1;
|
|
26
26
|
}
|
|
27
27
|
static {
|
|
28
28
|
this.formAssociated = !0;
|
|
29
29
|
}
|
|
30
30
|
static {
|
|
31
31
|
this.styles = [
|
|
32
|
-
|
|
32
|
+
u.styles,
|
|
33
33
|
v`
|
|
34
34
|
:host {
|
|
35
35
|
display: block;
|
|
@@ -156,6 +156,10 @@ class i extends m {
|
|
|
156
156
|
`
|
|
157
157
|
];
|
|
158
158
|
}
|
|
159
|
+
/** Whether the control is off, by its own `disabled` or by its form. */
|
|
160
|
+
get inactive() {
|
|
161
|
+
return this.disabled || this.formDisabled;
|
|
162
|
+
}
|
|
159
163
|
connectedCallback() {
|
|
160
164
|
super.connectedCallback(), this.internals ??= k(this);
|
|
161
165
|
}
|
|
@@ -176,6 +180,13 @@ class i extends m {
|
|
|
176
180
|
formResetCallback() {
|
|
177
181
|
this.files = [], this.dragging = !1;
|
|
178
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Called by the platform when the control's disabled state changes — its
|
|
185
|
+
* own `disabled`, or an ancestor `<fieldset disabled>` it cannot see.
|
|
186
|
+
*/
|
|
187
|
+
formDisabledCallback(e) {
|
|
188
|
+
this.formDisabled = e;
|
|
189
|
+
}
|
|
179
190
|
formStateRestoreCallback(e) {
|
|
180
191
|
!(e instanceof FormData) || !this.name || (this.files = e.getAll(this.name).filter((t) => t instanceof File));
|
|
181
192
|
}
|
|
@@ -206,24 +217,24 @@ class i extends m {
|
|
|
206
217
|
this.files = this.files.filter((t, a) => a !== e), h(this, "kt-files-change", { files: this.files });
|
|
207
218
|
}
|
|
208
219
|
browse() {
|
|
209
|
-
this.
|
|
220
|
+
this.inactive || this.fileInput?.click();
|
|
210
221
|
}
|
|
211
222
|
onZoneKeyDown(e) {
|
|
212
223
|
e.key !== "Enter" && e.key !== " " || (e.preventDefault(), this.browse());
|
|
213
224
|
}
|
|
214
225
|
onDragOver(e) {
|
|
215
|
-
this.
|
|
226
|
+
this.inactive || (e.preventDefault(), this.dragging = !0);
|
|
216
227
|
}
|
|
217
228
|
onDrop(e) {
|
|
218
|
-
this.
|
|
229
|
+
this.inactive || (e.preventDefault(), this.dragging = !1, this.addFiles(e.dataTransfer?.files ?? null));
|
|
219
230
|
}
|
|
220
231
|
render() {
|
|
221
232
|
return c`<div
|
|
222
233
|
part="base"
|
|
223
234
|
class=${b({ zone: !0, dragover: this.dragging })}
|
|
224
235
|
role="button"
|
|
225
|
-
tabindex=${this.
|
|
226
|
-
aria-disabled=${this.
|
|
236
|
+
tabindex=${this.inactive ? -1 : 0}
|
|
237
|
+
aria-disabled=${this.inactive ? "true" : p}
|
|
227
238
|
aria-label=${`${this.heading ?? l().dragAndDrop} ${this.linkText ?? l().browseFiles}`}
|
|
228
239
|
@click=${this.browse}
|
|
229
240
|
@keydown=${this.onZoneKeyDown}
|
|
@@ -284,10 +295,10 @@ r([
|
|
|
284
295
|
y('input[type="file"]')
|
|
285
296
|
], i.prototype, "fileInput");
|
|
286
297
|
r([
|
|
287
|
-
|
|
298
|
+
f()
|
|
288
299
|
], i.prototype, "dragging");
|
|
289
300
|
r([
|
|
290
|
-
|
|
301
|
+
f()
|
|
291
302
|
], i.prototype, "files");
|
|
292
303
|
r([
|
|
293
304
|
n({ type: String })
|
|
@@ -310,6 +321,9 @@ r([
|
|
|
310
321
|
r([
|
|
311
322
|
n({ type: Boolean, reflect: !0 })
|
|
312
323
|
], i.prototype, "disabled");
|
|
324
|
+
r([
|
|
325
|
+
f()
|
|
326
|
+
], i.prototype, "formDisabled");
|
|
313
327
|
r([
|
|
314
328
|
n({ type: String })
|
|
315
329
|
], i.prototype, "name");
|
|
@@ -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 { 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
|
+
{"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 /** Disabled by an enclosing `<fieldset>`, which leaves `disabled` alone. */\n @state()\n private formDisabled = false;\n\n /** Whether the control is off, by its own `disabled` or by its form. */\n private get inactive(): boolean {\n return this.disabled || this.formDisabled;\n }\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 /**\n * Called by the platform when the control's disabled state changes — its\n * own `disabled`, or an ancestor `<fieldset disabled>` it cannot see.\n */\n formDisabledCallback(disabled: boolean): void {\n this.formDisabled = disabled;\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.inactive) 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.inactive) return;\n event.preventDefault();\n this.dragging = true;\n }\n\n private onDrop(event: DragEvent): void {\n if (this.inactive) 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.inactive ? -1 : 0}\n aria-disabled=${this.inactive ? '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","disabled","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-Djx2JcGl.js";
|
|
4
4
|
import { h as y } from "../../../chunks/slots-BPbIdxAJ.js";
|
|
5
|
+
import { s as b } from "../../../chunks/index-STEEvEP7.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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"}
|
|
@@ -2,6 +2,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
2
2
|
import { KtElement } from '#internal/kt-element';
|
|
3
3
|
import { type KtCountry } from '#internal/countries';
|
|
4
4
|
import '../../core/kt-icon/kt-icon.js';
|
|
5
|
+
import '../../feedback/kt-tooltip/kt-tooltip.js';
|
|
5
6
|
export type KtInputSize = 'small' | 'medium' | 'large';
|
|
6
7
|
/**
|
|
7
8
|
* A single-line text field.
|
|
@@ -39,6 +40,11 @@ export declare class KtInput extends KtElement {
|
|
|
39
40
|
private passwordVisible;
|
|
40
41
|
private countryOpen;
|
|
41
42
|
private countryQuery;
|
|
43
|
+
/** The field has focus: the error tooltip shows for keyboard users too. */
|
|
44
|
+
private focused;
|
|
45
|
+
/** Escape put the error tooltip away until the field is focused again. */
|
|
46
|
+
private errorDismissed;
|
|
47
|
+
private floating;
|
|
42
48
|
/** The field's value. The `value` attribute seeds it and acts as the reset value. */
|
|
43
49
|
value: string;
|
|
44
50
|
name: string;
|
|
@@ -48,11 +54,18 @@ export declare class KtInput extends KtElement {
|
|
|
48
54
|
/** Field height: 32 / 40 / 48px at the desktop scale. */
|
|
49
55
|
size: KtInputSize;
|
|
50
56
|
disabled: boolean;
|
|
57
|
+
/** Disabled by an enclosing `<fieldset>`, which leaves `disabled` alone. */
|
|
58
|
+
private formDisabled;
|
|
59
|
+
/** Whether the control is off, by its own `disabled` or by its form. */
|
|
60
|
+
private get inactive();
|
|
51
61
|
readonly: boolean;
|
|
52
62
|
required: boolean;
|
|
53
63
|
/** Trailing Lucide icon, kebab-case. */
|
|
54
64
|
icon: string;
|
|
55
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Error message. A non-empty value puts the field in its error state, and
|
|
67
|
+
* wins over the field's own checks.
|
|
68
|
+
*/
|
|
56
69
|
error: string;
|
|
57
70
|
/** Shows the clear button once there is something to clear. */
|
|
58
71
|
clearable: boolean;
|
|
@@ -69,11 +82,17 @@ export declare class KtInput extends KtElement {
|
|
|
69
82
|
/** The value the enclosing form resets to. */
|
|
70
83
|
private defaultValue;
|
|
71
84
|
connectedCallback(): void;
|
|
85
|
+
updated(changed: PropertyValues): void;
|
|
72
86
|
willUpdate(changed: PropertyValues<this>): void;
|
|
73
87
|
private refreshValidity;
|
|
74
88
|
disconnectedCallback(): void;
|
|
75
89
|
/** Called by the platform when the enclosing form is reset. */
|
|
76
90
|
formResetCallback(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Called by the platform when the control's disabled state changes — its
|
|
93
|
+
* own `disabled`, or an ancestor `<fieldset disabled>` it cannot see.
|
|
94
|
+
*/
|
|
95
|
+
formDisabledCallback(disabled: boolean): void;
|
|
77
96
|
/** Called by the platform when the browser restores a session. */
|
|
78
97
|
formStateRestoreCallback(state: string): void;
|
|
79
98
|
focus(options?: FocusOptions): void;
|
|
@@ -89,6 +108,16 @@ export declare class KtInput extends KtElement {
|
|
|
89
108
|
* the user picked.
|
|
90
109
|
*/
|
|
91
110
|
private get formValue();
|
|
111
|
+
/**
|
|
112
|
+
* What the browser saves for a restore. The international number cannot be
|
|
113
|
+
* split back into country and digits — `+1` is both the US and Canada — so
|
|
114
|
+
* phone mode saves the two apart.
|
|
115
|
+
*/
|
|
116
|
+
private get formState();
|
|
117
|
+
/** Phone mode: a number typed with the prefix only dialled at home. */
|
|
118
|
+
private get phoneError();
|
|
119
|
+
/** The message on show: the application's first, then the field's own. */
|
|
120
|
+
private get shownError();
|
|
92
121
|
private get displayValue();
|
|
93
122
|
private get showClear();
|
|
94
123
|
private get resolvedType();
|
|
@@ -98,11 +127,18 @@ export declare class KtInput extends KtElement {
|
|
|
98
127
|
private closeCountryPanel;
|
|
99
128
|
private onCountryKeyDown;
|
|
100
129
|
private renderCountryPicker;
|
|
130
|
+
private onFocus;
|
|
131
|
+
private onBlur;
|
|
132
|
+
/** WCAG 1.4.13: content shown on focus can be dismissed without moving it. */
|
|
133
|
+
private onControlKeyDown;
|
|
134
|
+
private get errorTooltipOpen();
|
|
101
135
|
private onChange;
|
|
102
136
|
private clear;
|
|
103
137
|
private togglePassword;
|
|
104
138
|
/** Clicking the padding around the input should focus it, as on a native field. */
|
|
105
139
|
private onFieldPointerDown;
|
|
140
|
+
/** Both buttons are out of the tab order, so their tooltips are for the pointer. */
|
|
141
|
+
private renderPasswordToggle;
|
|
106
142
|
render(): TemplateResult;
|
|
107
143
|
}
|
|
108
144
|
declare global {
|