@sky.ui/core 0.0.53 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom-elements.json +3785 -3679
- package/dist/helper/controllers/popover-controller.js +1 -1
- package/dist/helper/controllers/popover-controller.js.map +1 -1
- package/dist/helper/index.js +1 -1
- package/dist/helper/utils/index.d.ts +1 -0
- package/dist/helper/utils/index.js +1 -1
- package/dist/helper/utils/stable-array.d.ts +6 -0
- package/dist/helper/utils/stable-array.js +2 -0
- package/dist/helper/utils/stable-array.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/sky-avatar/sky-avatar.js +5 -5
- package/dist/sky-avatar/sky-avatar.js.map +1 -1
- package/dist/sky-chip/sky-chip.d.ts +8 -2
- package/dist/sky-chip/sky-chip.js +10 -10
- package/dist/sky-chip/sky-chip.js.map +1 -1
- package/dist/sky-combobox/sky-combobox.d.ts +2 -0
- package/dist/sky-combobox/sky-combobox.js +16 -16
- package/dist/sky-combobox/sky-combobox.js.map +1 -1
- package/dist/sky-flexbox/sky-flexbox.d.ts +2 -0
- package/dist/sky-flexbox/sky-flexbox.js +3 -3
- package/dist/sky-flexbox/sky-flexbox.js.map +1 -1
- package/dist/sky-form/sky-form.js +7 -7
- package/dist/sky-form/sky-form.js.map +1 -1
- package/dist/sky-image/sky-image.js +5 -5
- package/dist/sky-image/sky-image.js.map +1 -1
- package/dist/sky-input/sky-input.js +6 -6
- package/dist/sky-input/sky-input.js.map +1 -1
- package/dist/sky-list/sky-list.js +3 -3
- package/dist/sky-list/sky-list.js.map +1 -1
- package/dist/sky-listitem/sky-listitem.d.ts +1 -0
- package/dist/sky-listitem/sky-listitem.js +4 -2
- package/dist/sky-listitem/sky-listitem.js.map +1 -1
- package/dist/sky-radio/sky-radio.js +1 -1
- package/dist/sky-radio/sky-radio.js.map +1 -1
- package/dist/sky-simple-table/sky-simple-table.d.ts +7 -2
- package/dist/sky-simple-table/sky-simple-table.js +9 -9
- package/dist/sky-simple-table/sky-simple-table.js.map +1 -1
- package/dist/sky-theme-provider/sky-theme-provider.js +2 -2
- package/dist/sky-theme-provider/sky-theme-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -129,6 +129,7 @@ export declare class SkyFlexbox extends LitElement {
|
|
|
129
129
|
static styles: import("lit").CSSResult;
|
|
130
130
|
/** @private */
|
|
131
131
|
private _childMo?;
|
|
132
|
+
private _spansRaf;
|
|
132
133
|
/** @protected */
|
|
133
134
|
connectedCallback(): void;
|
|
134
135
|
/** @protected */
|
|
@@ -150,6 +151,7 @@ export declare class SkyFlexbox extends LitElement {
|
|
|
150
151
|
recalculateSpans(): void;
|
|
151
152
|
/** @protected */
|
|
152
153
|
render(): import("lit-html").TemplateResult<1>;
|
|
154
|
+
private _scheduleUpdateSpans;
|
|
153
155
|
/**
|
|
154
156
|
* Reads span attributes off slotted children and maps them to CSS vars:
|
|
155
157
|
* span, span-lg, span-md, span-sm, span-xs
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{css as
|
|
1
|
+
import{css as a,LitElement as t,html as s}from"lit";import{property as n,customElement as e}from"lit/decorators.js";import{PresetController as r}from"../helper/controllers/preset-controller.js";var p=Object.defineProperty,i=Object.getOwnPropertyDescriptor,o=(a,t,s,n)=>{for(var e,r=n>1?void 0:n?i(t,s):t,o=a.length-1;o>=0;o--)(e=a[o])&&(r=(n?e(t,s,r):e(r))||r);return n&&r&&p(t,s,r),r};let l=class extends t{constructor(){super(...arguments),this.direction="row",this.wrap="wrap",this.justify="flex-start",this.align="stretch",this.alignContent="normal",this.gap="16px",this.col=12,this.preset="",this._presets=new r(this),this._spansRaf=0,this._scheduleUpdateSpans=()=>{this._spansRaf||(this._spansRaf=requestAnimationFrame(()=>{this._spansRaf=0,this.updateSpans()}))},this.updateSpans=()=>{const a=this.shadowRoot?.querySelector("slot")?.assignedElements({flatten:!0})??[];for(const t of a){if(!(t instanceof HTMLElement))continue;this.applySpanVar(t,"--span",this.readSpanRaw(t,"span","span")),this.applySpanVar(t,"--span-lg",this.readSpanRaw(t,"span-lg","spanLg")),this.applySpanVar(t,"--span-md",this.readSpanRaw(t,"span-md","spanMd")),this.applySpanVar(t,"--span-sm",this.readSpanRaw(t,"span-sm","spanSm")),this.applySpanVar(t,"--span-xs",this.readSpanRaw(t,"span-xs","spanXs"));null!=this.readSpanRaw(t,"span","span")||null!=this.readSpanRaw(t,"span-lg","spanLg")||null!=this.readSpanRaw(t,"span-md","spanMd")||null!=this.readSpanRaw(t,"span-sm","spanSm")||null!=this.readSpanRaw(t,"span-xs","spanXs")?t.dataset.flexSpan="":delete t.dataset.flexSpan}}}connectedCallback(){super.connectedCallback(),this._childMo=new MutationObserver(()=>this._scheduleUpdateSpans()),this._childMo.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["span","span-lg","span-md","span-sm","span-xs"]})}disconnectedCallback(){super.disconnectedCallback(),this._spansRaf&&(cancelAnimationFrame(this._spansRaf),this._spansRaf=0),this._childMo?.disconnect()}firstUpdated(){this.syncVars(),this.updateSpans()}updated(a){(a.has("wrap")||a.has("gap")||a.has("gapX")||a.has("gapY")||a.has("col")||a.has("justify")||a.has("direction")||a.has("align")||a.has("alignContent"))&&this.syncVars(),a.has("col")&&this.updateSpans()}syncVars(){const a={"--wrap":this.wrap,"--gap":this.gap,"--gap-x":this.gapX??this.gap,"--gap-y":this.gapY??this.gap,"--columns":String(this.col),"--justify":this.justify,"--direction":this.direction,"--align":this.align,"--align-content":this.alignContent};Object.entries(a).forEach(([a,t])=>this.style.setProperty(a,t))}recalculateSpans(){this.updateSpans()}render(){const a="column"===this.direction||"column-reverse"===this.direction;return s`
|
|
2
2
|
<div
|
|
3
|
-
class="flex-container ${
|
|
3
|
+
class="flex-container ${a?"is-column":"is-row"}"
|
|
4
4
|
part="container"
|
|
5
5
|
>
|
|
6
6
|
<slot @slotchange=${this.updateSpans}></slot>
|
|
7
7
|
</div>
|
|
8
|
-
`}readSpanRaw(t,
|
|
8
|
+
`}readSpanRaw(a,t,s){const n=a.getAttribute(t);if(null!=n&&""!==n)return n;const e=a[s];return null!=e&&""!==e?String(e):null}applySpanVar(a,t,s){const n=this.clampSpan(s);n?a.style.setProperty(t,String(n)):a.style.removeProperty(t)}clampSpan(a){if(!a)return null;return Math.max(1,Math.min(this.col,Number(a)||1))}};l.styles=a`
|
|
9
9
|
:host {
|
|
10
10
|
display: block;
|
|
11
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-flexbox.js","sources":["../../src/sky-flexbox/sky-flexbox.ts"],"sourcesContent":["import { LitElement, html, css } from \"lit\";\r\nimport { customElement, property } from \"lit/decorators.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyFlexboxDirection = \"row\" | \"row-reverse\" | \"column\" | \"column-reverse\";\r\nexport type SkyFlexboxWrap = \"nowrap\" | \"wrap\" | \"wrap-reverse\";\r\nexport type SkyFlexboxJustify =\r\n | \"flex-start\"\r\n | \"center\"\r\n | \"flex-end\"\r\n | \"space-between\"\r\n | \"space-around\"\r\n | \"space-evenly\";\r\nexport type SkyFlexboxAlign = \"stretch\" | \"flex-start\" | \"center\" | \"flex-end\" | \"baseline\";\r\nexport type SkyFlexboxAlignContent =\r\n | \"normal\"\r\n | \"stretch\"\r\n | \"center\"\r\n | \"start\"\r\n | \"end\"\r\n | \"flex-start\"\r\n | \"flex-end\"\r\n | \"space-between\"\r\n | \"space-around\"\r\n | \"space-evenly\";\r\n\r\n/**\r\n * @element sky-flexbox\r\n *\r\n * @summary Flexbox container with grid-like span support for slotted children.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/flexbox\r\n *\r\n * @slot - Default slot for flex items (`<sky-flexcol>` or plain elements with `span` attributes).\r\n *\r\n * @property {SkyFlexboxDirection} direction - Flex direction.\r\n * @property {SkyFlexboxWrap} wrap - Flex wrap behavior.\r\n * @property {SkyFlexboxJustify} justify - Main-axis alignment.\r\n * @property {SkyFlexboxAlign} align - Cross-axis alignment.\r\n * @property {SkyFlexboxAlignContent} alignContent - Multi-line cross-axis distribution.\r\n * @property {string} gap - Shared row/column gap value.\r\n * @property {string | undefined} gapX - Horizontal gap override.\r\n * @property {string | undefined} gapY - Vertical gap override.\r\n * @property {number} col - Total column count used for span calculations.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n *\r\n * @fires {Event} slotchange - Fired when slotted children change.\r\n * @method recalculateSpans Recomputes slotted child span CSS variables.\r\n *\r\n * @csspart container - The flex container element.\r\n *\r\n * @Behavior\r\n * - Implements CSS Flexbox with custom property-based configuration\r\n * - Supports responsive span calculations based on column grid system\r\n * - Automatically manages CSS custom properties for child elements\r\n * - Handles responsive breakpoints through span attributes\r\n *\r\n * - Uses a configurable column count (default: 12)\r\n * - Calculates item widths based on span values (1-col through total columns)\r\n * - Supports responsive spans: `span-lg`, `span-md`, `span-sm`, `span-xs`\r\n * - Clamps span values to valid range (1 to column count)\r\n *\r\n * - Supports unified `gap` property\r\n * - Optional `gapX` and `gapY` for separate horizontal/vertical spacing\r\n * - Gap values can use any CSS unit (px, rem, em, %, etc.)\r\n *\r\n * @example\r\n * ```html\r\n * <sky-flexbox col=\"12\" gap=\"16px\">\r\n * <sky-flexcol span=\"8\" span-md=\"12\">\r\n * Main content\r\n * </sky-flexcol>\r\n * <sky-flexcol span=\"4\" span-md=\"12\">\r\n * Sidebar\r\n * </sky-flexcol>\r\n * </sky-flexbox>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-flexbox col=\"12\" gap=\"16px\">\r\n * <sky-flexcol span=\"8\" span-md=\"12\">Main content</sky-flexcol>\r\n * <sky-flexcol span=\"4\" span-md=\"12\">Sidebar</sky-flexcol>\r\n * </sky-flexbox>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-flexbox col=\"12\" gap=\"16px\">\r\n * <sky-flexcol span=\"8\" span-md=\"12\">Main content</sky-flexcol>\r\n * <sky-flexcol span=\"4\" span-md=\"12\">Sidebar</sky-flexcol>\r\n * </sky-flexbox>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-flexbox\")\r\nexport class SkyFlexbox extends LitElement {\r\n /**\r\n * Flex direction.\r\n * @public\r\n */\r\n @property({ type: String })\r\n direction: SkyFlexboxDirection = \"row\";\r\n\r\n /**\r\n * Wrap behavior.\r\n * @public\r\n */\r\n @property({ type: String })\r\n wrap: SkyFlexboxWrap = \"wrap\";\r\n\r\n /**\r\n * Main axis justification.\r\n * @public\r\n */\r\n @property({ type: String })\r\n justify: SkyFlexboxJustify = \"flex-start\";\r\n\r\n /**\r\n * Cross axis alignment.\r\n * @public\r\n */\r\n @property({ type: String })\r\n align: SkyFlexboxAlign = \"stretch\";\r\n\r\n /**\r\n * Multi-line alignment (when wrapping).\r\n * @public\r\n */\r\n @property({ type: String })\r\n alignContent: SkyFlexboxAlignContent = \"normal\";\r\n\r\n /**\r\n * Shorthand gap; used if gapX/gapY not provided.\r\n * @public\r\n */\r\n @property({ type: String }) gap: string = \"16px\";\r\n\r\n /**\r\n * Column gap.\r\n * @public\r\n */\r\n @property({ type: String })\r\n gapX?: string;\r\n\r\n /**\r\n * Row gap.\r\n * @public\r\n */\r\n @property({ type: String })\r\n gapY?: string;\r\n\r\n /**\r\n * Total columns used for span calculation.\r\n * @public\r\n */\r\n @property({ type: Number }) col: number = 12;\r\n\r\n\r\n /** Named prop preset from nearest `sky-config-provider`. */\r\n @property({ type: String, reflect: true }) preset = \"\";\r\n\r\n private _presets = new PresetController(this);\r\n\r\n static override styles = css`\r\n :host {\r\n display: block;\r\n }\r\n\r\n .flex-container {\r\n display: flex;\r\n box-sizing: border-box;\r\n flex-wrap: var(--wrap, wrap);\r\n gap: var(--gap-y, var(--gap, 16px)) var(--gap-x, var(--gap, 16px));\r\n justify-content: var(--justify, flex-start);\r\n align-items: var(--align, stretch);\r\n align-content: var(--align-content, normal);\r\n flex-direction: var(--direction, row);\r\n }\r\n\r\n /* Default behaviour for any slotted child: */\r\n ::slotted(*) {\r\n box-sizing: border-box;\r\n min-width: 0;\r\n }\r\n\r\n .flex-container.is-row ::slotted(*) {\r\n flex-grow: 0;\r\n flex-shrink: 1;\r\n flex-basis: auto;\r\n max-width: none;\r\n }\r\n\r\n .flex-container.is-row ::slotted([data-flex-span]) {\r\n flex-shrink: 0;\r\n --span-current: var(--span, 1);\r\n --span-width: calc(\r\n 100% * var(--span-current) / var(--columns, 12) -\r\n var(--gap-x, var(--gap, 16px)) *\r\n (var(--columns, 12) - var(--span-current)) / var(--columns, 12)\r\n );\r\n\r\n flex-basis: var(--span-width);\r\n width: var(--span-width);\r\n max-width: var(--span-width);\r\n }\r\n\r\n .flex-container.is-column ::slotted(*) {\r\n width: 100%;\r\n flex-basis: auto;\r\n max-width: none;\r\n flex-shrink: 0;\r\n }\r\n\r\n @media (max-width: 1200px) {\r\n .flex-container.is-row ::slotted([data-flex-span]) {\r\n --span-current: var(--span-lg, var(--span-current));\r\n }\r\n }\r\n\r\n @media (max-width: 992px) {\r\n .flex-container.is-row ::slotted([data-flex-span]) {\r\n --span-current: var(--span-md, var(--span-current));\r\n }\r\n }\r\n\r\n @media (max-width: 768px) {\r\n .flex-container.is-row ::slotted([data-flex-span]) {\r\n --span-current: var(--span-sm, var(--span-current));\r\n }\r\n }\r\n\r\n @media (max-width: 576px) {\r\n .flex-container.is-row ::slotted([data-flex-span]) {\r\n --span-current: var(--span-xs, var(--span-current));\r\n }\r\n }\r\n `;\r\n\r\n /** @private */\r\n private _childMo?: MutationObserver;\r\n\r\n /** @protected */\r\n override connectedCallback() {\r\n super.connectedCallback();\r\n this._childMo = new MutationObserver(() => this.updateSpans());\r\n this._childMo.observe(this, {\r\n childList: true,\r\n subtree: true,\r\n attributes: true,\r\n attributeFilter: [\"span\", \"span-lg\", \"span-md\", \"span-sm\", \"span-xs\"],\r\n });\r\n }\r\n\r\n /** @protected */\r\n override disconnectedCallback() {\r\n super.disconnectedCallback();\r\n this._childMo?.disconnect();\r\n }\r\n\r\n /** @protected */\r\n override firstUpdated() {\r\n this.syncVars();\r\n this.updateSpans(); // initial\r\n }\r\n\r\n /** @protected */\r\n override updated(changed: Map<string | number | symbol, unknown>) {\r\n if (\r\n changed.has(\"wrap\") ||\r\n changed.has(\"gap\") ||\r\n changed.has(\"gapX\") ||\r\n changed.has(\"gapY\") ||\r\n changed.has(\"col\") ||\r\n changed.has(\"justify\") ||\r\n changed.has(\"direction\") ||\r\n changed.has(\"align\") ||\r\n changed.has(\"alignContent\")\r\n ) {\r\n this.syncVars();\r\n }\r\n if (changed.has(\"col\")) {\r\n this.updateSpans(); // re-validate span bounds when column count changes\r\n }\r\n }\r\n\r\n /**\r\n * Syncs component properties to CSS custom properties.\r\n * @private\r\n */\r\n private syncVars() {\r\n const styles = {\r\n \"--wrap\": this.wrap,\r\n \"--gap\": this.gap,\r\n \"--gap-x\": this.gapX ?? this.gap,\r\n \"--gap-y\": this.gapY ?? this.gap,\r\n \"--columns\": String(this.col),\r\n \"--justify\": this.justify,\r\n \"--direction\": this.direction,\r\n \"--align\": this.align,\r\n \"--align-content\": this.alignContent,\r\n } as Record<string, string>;\r\n Object.entries(styles).forEach(([k, v]) => this.style.setProperty(k, v));\r\n }\r\n\r\n /**\r\n * Forces recalculation of spans for all child elements.\r\n * Useful when dynamically adding/removing items.\r\n * @public\r\n */\r\n public recalculateSpans() {\r\n this.updateSpans();\r\n }\r\n\r\n /** @protected */\r\n override render() {\r\n const isColumn =\r\n this.direction === \"column\" || this.direction === \"column-reverse\";\r\n\r\n return html`\r\n <div\r\n class=\"flex-container ${isColumn ? \"is-column\" : \"is-row\"}\"\r\n part=\"container\"\r\n >\r\n <slot @slotchange=${this.updateSpans}></slot>\r\n </div>\r\n `;\r\n }\r\n\r\n /**\r\n * Reads span attributes off slotted children and maps them to CSS vars:\r\n * span, span-lg, span-md, span-sm, span-xs\r\n * @private\r\n */\r\n private updateSpans = () => {\r\n const nodes =\r\n this.shadowRoot\r\n ?.querySelector(\"slot\")\r\n ?.assignedElements({ flatten: true }) ?? [];\r\n for (const el of nodes) {\r\n if (!(el instanceof HTMLElement)) continue;\r\n\r\n this.applySpanVar(el, \"--span\", this.readSpanRaw(el, \"span\", \"span\"));\r\n this.applySpanVar(el, \"--span-lg\", this.readSpanRaw(el, \"span-lg\", \"spanLg\"));\r\n this.applySpanVar(el, \"--span-md\", this.readSpanRaw(el, \"span-md\", \"spanMd\"));\r\n this.applySpanVar(el, \"--span-sm\", this.readSpanRaw(el, \"span-sm\", \"spanSm\"));\r\n this.applySpanVar(el, \"--span-xs\", this.readSpanRaw(el, \"span-xs\", \"spanXs\"));\r\n\r\n const hasSpan =\r\n this.readSpanRaw(el, \"span\", \"span\") != null ||\r\n this.readSpanRaw(el, \"span-lg\", \"spanLg\") != null ||\r\n this.readSpanRaw(el, \"span-md\", \"spanMd\") != null ||\r\n this.readSpanRaw(el, \"span-sm\", \"spanSm\") != null ||\r\n this.readSpanRaw(el, \"span-xs\", \"spanXs\") != null;\r\n\r\n if (hasSpan) el.dataset.flexSpan = \"\";\r\n else delete el.dataset.flexSpan;\r\n }\r\n };\r\n\r\n /**\r\n * Reads a span value from an HTML attribute or element property.\r\n * @private\r\n */\r\n private readSpanRaw(\r\n el: HTMLElement,\r\n attrName: string,\r\n propName: string,\r\n ): string | null {\r\n const fromAttr = el.getAttribute(attrName);\r\n if (fromAttr != null && fromAttr !== \"\") return fromAttr;\r\n const fromProp = (el as unknown as Record<string, unknown>)[propName];\r\n if (fromProp != null && fromProp !== \"\") return String(fromProp);\r\n return null;\r\n }\r\n\r\n /**\r\n * Sets or clears a span CSS variable on a slotted child.\r\n * @private\r\n */\r\n private applySpanVar(\r\n el: HTMLElement,\r\n cssVar: string,\r\n raw: string | null,\r\n ): void {\r\n const value = this.clampSpan(raw);\r\n if (value) el.style.setProperty(cssVar, String(value));\r\n else el.style.removeProperty(cssVar);\r\n }\r\n\r\n /**\r\n * Clamps span value to valid range (1 to column count).\r\n * @param raw - Raw span value from attribute\r\n * @returns Clamped number or null if invalid\r\n * @private\r\n */\r\n private clampSpan(raw: string | null): number | null {\r\n if (!raw) return null;\r\n const n = Math.max(1, Math.min(this.col, Number(raw) || 1));\r\n return n;\r\n }\r\n}\r\n"],"names":["SkyFlexbox","LitElement","constructor","super","arguments","this","direction","wrap","justify","align","alignContent","gap","col","preset","_presets","PresetController","updateSpans","nodes","shadowRoot","querySelector","assignedElements","flatten","el","HTMLElement","applySpanVar","readSpanRaw","dataset","flexSpan","connectedCallback","_childMo","MutationObserver","observe","childList","subtree","attributes","attributeFilter","disconnectedCallback","disconnect","firstUpdated","syncVars","updated","changed","has","styles","gapX","gapY","String","Object","entries","forEach","k","v","style","setProperty","recalculateSpans","render","isColumn","html","attrName","propName","fromAttr","getAttribute","fromProp","cssVar","raw","value","clampSpan","removeProperty","Math","max","min","Number","css","__decorateClass","property","type","prototype","reflect","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-flexbox.js","sources":["../../src/sky-flexbox/sky-flexbox.ts"],"sourcesContent":["import { LitElement, html, css } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { PresetController } from \"../helper/controllers/preset-controller\";\n\nexport type SkyFlexboxDirection = \"row\" | \"row-reverse\" | \"column\" | \"column-reverse\";\nexport type SkyFlexboxWrap = \"nowrap\" | \"wrap\" | \"wrap-reverse\";\nexport type SkyFlexboxJustify =\n | \"flex-start\"\n | \"center\"\n | \"flex-end\"\n | \"space-between\"\n | \"space-around\"\n | \"space-evenly\";\nexport type SkyFlexboxAlign = \"stretch\" | \"flex-start\" | \"center\" | \"flex-end\" | \"baseline\";\nexport type SkyFlexboxAlignContent =\n | \"normal\"\n | \"stretch\"\n | \"center\"\n | \"start\"\n | \"end\"\n | \"flex-start\"\n | \"flex-end\"\n | \"space-between\"\n | \"space-around\"\n | \"space-evenly\";\n\n/**\n * @element sky-flexbox\n *\n * @summary Flexbox container with grid-like span support for slotted children.\n *\n * @status stable\n * @since 1.0.0\n *\n * @documentation https://sky-ui.com/components/flexbox\n *\n * @slot - Default slot for flex items (`<sky-flexcol>` or plain elements with `span` attributes).\n *\n * @property {SkyFlexboxDirection} direction - Flex direction.\n * @property {SkyFlexboxWrap} wrap - Flex wrap behavior.\n * @property {SkyFlexboxJustify} justify - Main-axis alignment.\n * @property {SkyFlexboxAlign} align - Cross-axis alignment.\n * @property {SkyFlexboxAlignContent} alignContent - Multi-line cross-axis distribution.\n * @property {string} gap - Shared row/column gap value.\n * @property {string | undefined} gapX - Horizontal gap override.\n * @property {string | undefined} gapY - Vertical gap override.\n * @property {number} col - Total column count used for span calculations.\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\n *\n * @fires {Event} slotchange - Fired when slotted children change.\n * @method recalculateSpans Recomputes slotted child span CSS variables.\n *\n * @csspart container - The flex container element.\n *\n * @Behavior\n * - Implements CSS Flexbox with custom property-based configuration\n * - Supports responsive span calculations based on column grid system\n * - Automatically manages CSS custom properties for child elements\n * - Handles responsive breakpoints through span attributes\n *\n * - Uses a configurable column count (default: 12)\n * - Calculates item widths based on span values (1-col through total columns)\n * - Supports responsive spans: `span-lg`, `span-md`, `span-sm`, `span-xs`\n * - Clamps span values to valid range (1 to column count)\n *\n * - Supports unified `gap` property\n * - Optional `gapX` and `gapY` for separate horizontal/vertical spacing\n * - Gap values can use any CSS unit (px, rem, em, %, etc.)\n *\n * @example\n * ```html\n * <sky-flexbox col=\"12\" gap=\"16px\">\n * <sky-flexcol span=\"8\" span-md=\"12\">\n * Main content\n * </sky-flexcol>\n * <sky-flexcol span=\"4\" span-md=\"12\">\n * Sidebar\n * </sky-flexcol>\n * </sky-flexbox>\n * ```\n * ```vue\n * <template>\n * <sky-flexbox col=\"12\" gap=\"16px\">\n * <sky-flexcol span=\"8\" span-md=\"12\">Main content</sky-flexcol>\n * <sky-flexcol span=\"4\" span-md=\"12\">Sidebar</sky-flexcol>\n * </sky-flexbox>\n * </template>\n * ```\n * ```jsx\n * export default function Demo() {\n * return (\n * <sky-flexbox col=\"12\" gap=\"16px\">\n * <sky-flexcol span=\"8\" span-md=\"12\">Main content</sky-flexcol>\n * <sky-flexcol span=\"4\" span-md=\"12\">Sidebar</sky-flexcol>\n * </sky-flexbox>\n * );\n * }\n * ```\n */\n@customElement(\"sky-flexbox\")\nexport class SkyFlexbox extends LitElement {\n /**\n * Flex direction.\n * @public\n */\n @property({ type: String })\n direction: SkyFlexboxDirection = \"row\";\n\n /**\n * Wrap behavior.\n * @public\n */\n @property({ type: String })\n wrap: SkyFlexboxWrap = \"wrap\";\n\n /**\n * Main axis justification.\n * @public\n */\n @property({ type: String })\n justify: SkyFlexboxJustify = \"flex-start\";\n\n /**\n * Cross axis alignment.\n * @public\n */\n @property({ type: String })\n align: SkyFlexboxAlign = \"stretch\";\n\n /**\n * Multi-line alignment (when wrapping).\n * @public\n */\n @property({ type: String })\n alignContent: SkyFlexboxAlignContent = \"normal\";\n\n /**\n * Shorthand gap; used if gapX/gapY not provided.\n * @public\n */\n @property({ type: String }) gap: string = \"16px\";\n\n /**\n * Column gap.\n * @public\n */\n @property({ type: String })\n gapX?: string;\n\n /**\n * Row gap.\n * @public\n */\n @property({ type: String })\n gapY?: string;\n\n /**\n * Total columns used for span calculation.\n * @public\n */\n @property({ type: Number }) col: number = 12;\n\n\n /** Named prop preset from nearest `sky-config-provider`. */\n @property({ type: String, reflect: true }) preset = \"\";\n\n private _presets = new PresetController(this);\n\n static override styles = css`\n :host {\n display: block;\n }\n\n .flex-container {\n display: flex;\n box-sizing: border-box;\n flex-wrap: var(--wrap, wrap);\n gap: var(--gap-y, var(--gap, 16px)) var(--gap-x, var(--gap, 16px));\n justify-content: var(--justify, flex-start);\n align-items: var(--align, stretch);\n align-content: var(--align-content, normal);\n flex-direction: var(--direction, row);\n }\n\n /* Default behaviour for any slotted child: */\n ::slotted(*) {\n box-sizing: border-box;\n min-width: 0;\n }\n\n .flex-container.is-row ::slotted(*) {\n flex-grow: 0;\n flex-shrink: 1;\n flex-basis: auto;\n max-width: none;\n }\n\n .flex-container.is-row ::slotted([data-flex-span]) {\n flex-shrink: 0;\n --span-current: var(--span, 1);\n --span-width: calc(\n 100% * var(--span-current) / var(--columns, 12) -\n var(--gap-x, var(--gap, 16px)) *\n (var(--columns, 12) - var(--span-current)) / var(--columns, 12)\n );\n\n flex-basis: var(--span-width);\n width: var(--span-width);\n max-width: var(--span-width);\n }\n\n .flex-container.is-column ::slotted(*) {\n width: 100%;\n flex-basis: auto;\n max-width: none;\n flex-shrink: 0;\n }\n\n @media (max-width: 1200px) {\n .flex-container.is-row ::slotted([data-flex-span]) {\n --span-current: var(--span-lg, var(--span-current));\n }\n }\n\n @media (max-width: 992px) {\n .flex-container.is-row ::slotted([data-flex-span]) {\n --span-current: var(--span-md, var(--span-current));\n }\n }\n\n @media (max-width: 768px) {\n .flex-container.is-row ::slotted([data-flex-span]) {\n --span-current: var(--span-sm, var(--span-current));\n }\n }\n\n @media (max-width: 576px) {\n .flex-container.is-row ::slotted([data-flex-span]) {\n --span-current: var(--span-xs, var(--span-current));\n }\n }\n `;\n\n /** @private */\n private _childMo?: MutationObserver;\n private _spansRaf = 0;\n\n /** @protected */\n override connectedCallback() {\n super.connectedCallback();\n this._childMo = new MutationObserver(() => this._scheduleUpdateSpans());\n this._childMo.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"span\", \"span-lg\", \"span-md\", \"span-sm\", \"span-xs\"],\n });\n }\n\n /** @protected */\n override disconnectedCallback() {\n super.disconnectedCallback();\n if (this._spansRaf) {\n cancelAnimationFrame(this._spansRaf);\n this._spansRaf = 0;\n }\n this._childMo?.disconnect();\n }\n\n /** @protected */\n override firstUpdated() {\n this.syncVars();\n this.updateSpans(); // initial\n }\n\n /** @protected */\n override updated(changed: Map<string | number | symbol, unknown>) {\n if (\n changed.has(\"wrap\") ||\n changed.has(\"gap\") ||\n changed.has(\"gapX\") ||\n changed.has(\"gapY\") ||\n changed.has(\"col\") ||\n changed.has(\"justify\") ||\n changed.has(\"direction\") ||\n changed.has(\"align\") ||\n changed.has(\"alignContent\")\n ) {\n this.syncVars();\n }\n if (changed.has(\"col\")) {\n this.updateSpans(); // re-validate span bounds when column count changes\n }\n }\n\n /**\n * Syncs component properties to CSS custom properties.\n * @private\n */\n private syncVars() {\n const styles = {\n \"--wrap\": this.wrap,\n \"--gap\": this.gap,\n \"--gap-x\": this.gapX ?? this.gap,\n \"--gap-y\": this.gapY ?? this.gap,\n \"--columns\": String(this.col),\n \"--justify\": this.justify,\n \"--direction\": this.direction,\n \"--align\": this.align,\n \"--align-content\": this.alignContent,\n } as Record<string, string>;\n Object.entries(styles).forEach(([k, v]) => this.style.setProperty(k, v));\n }\n\n /**\n * Forces recalculation of spans for all child elements.\n * Useful when dynamically adding/removing items.\n * @public\n */\n public recalculateSpans() {\n this.updateSpans();\n }\n\n /** @protected */\n override render() {\n const isColumn =\n this.direction === \"column\" || this.direction === \"column-reverse\";\n\n return html`\n <div\n class=\"flex-container ${isColumn ? \"is-column\" : \"is-row\"}\"\n part=\"container\"\n >\n <slot @slotchange=${this.updateSpans}></slot>\n </div>\n `;\n }\n\n private _scheduleUpdateSpans = () => {\n if (this._spansRaf) return;\n this._spansRaf = requestAnimationFrame(() => {\n this._spansRaf = 0;\n this.updateSpans();\n });\n };\n\n /**\n * Reads span attributes off slotted children and maps them to CSS vars:\n * span, span-lg, span-md, span-sm, span-xs\n * @private\n */\n private updateSpans = () => {\n const nodes =\n this.shadowRoot\n ?.querySelector(\"slot\")\n ?.assignedElements({ flatten: true }) ?? [];\n for (const el of nodes) {\n if (!(el instanceof HTMLElement)) continue;\n\n this.applySpanVar(el, \"--span\", this.readSpanRaw(el, \"span\", \"span\"));\n this.applySpanVar(el, \"--span-lg\", this.readSpanRaw(el, \"span-lg\", \"spanLg\"));\n this.applySpanVar(el, \"--span-md\", this.readSpanRaw(el, \"span-md\", \"spanMd\"));\n this.applySpanVar(el, \"--span-sm\", this.readSpanRaw(el, \"span-sm\", \"spanSm\"));\n this.applySpanVar(el, \"--span-xs\", this.readSpanRaw(el, \"span-xs\", \"spanXs\"));\n\n const hasSpan =\n this.readSpanRaw(el, \"span\", \"span\") != null ||\n this.readSpanRaw(el, \"span-lg\", \"spanLg\") != null ||\n this.readSpanRaw(el, \"span-md\", \"spanMd\") != null ||\n this.readSpanRaw(el, \"span-sm\", \"spanSm\") != null ||\n this.readSpanRaw(el, \"span-xs\", \"spanXs\") != null;\n\n if (hasSpan) el.dataset.flexSpan = \"\";\n else delete el.dataset.flexSpan;\n }\n };\n\n /**\n * Reads a span value from an HTML attribute or element property.\n * @private\n */\n private readSpanRaw(\n el: HTMLElement,\n attrName: string,\n propName: string,\n ): string | null {\n const fromAttr = el.getAttribute(attrName);\n if (fromAttr != null && fromAttr !== \"\") return fromAttr;\n const fromProp = (el as unknown as Record<string, unknown>)[propName];\n if (fromProp != null && fromProp !== \"\") return String(fromProp);\n return null;\n }\n\n /**\n * Sets or clears a span CSS variable on a slotted child.\n * @private\n */\n private applySpanVar(\n el: HTMLElement,\n cssVar: string,\n raw: string | null,\n ): void {\n const value = this.clampSpan(raw);\n if (value) el.style.setProperty(cssVar, String(value));\n else el.style.removeProperty(cssVar);\n }\n\n /**\n * Clamps span value to valid range (1 to column count).\n * @param raw - Raw span value from attribute\n * @returns Clamped number or null if invalid\n * @private\n */\n private clampSpan(raw: string | null): number | null {\n if (!raw) return null;\n const n = Math.max(1, Math.min(this.col, Number(raw) || 1));\n return n;\n }\n}\n"],"names":["SkyFlexbox","LitElement","constructor","super","arguments","this","direction","wrap","justify","align","alignContent","gap","col","preset","_presets","PresetController","_spansRaf","_scheduleUpdateSpans","requestAnimationFrame","updateSpans","nodes","shadowRoot","querySelector","assignedElements","flatten","el","HTMLElement","applySpanVar","readSpanRaw","dataset","flexSpan","connectedCallback","_childMo","MutationObserver","observe","childList","subtree","attributes","attributeFilter","disconnectedCallback","cancelAnimationFrame","disconnect","firstUpdated","syncVars","updated","changed","has","styles","gapX","gapY","String","Object","entries","forEach","k","v","style","setProperty","recalculateSpans","render","isColumn","html","attrName","propName","fromAttr","getAttribute","fromProp","cssVar","raw","value","clampSpan","removeProperty","Math","max","min","Number","css","__decorateClass","property","type","prototype","reflect","customElement"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{LitElement as t,nothing as e,html as i}from"lit";import{property as r,state as s,query as o,customElement as
|
|
1
|
+
import{LitElement as t,nothing as e,html as i}from"lit";import{property as r,state as s,query as o,customElement as n}from"lit/decorators.js";import{ifDefined as a}from"lit/directives/if-defined.js";import{PresetController as l}from"../helper/controllers/preset-controller.js";var d=Object.defineProperty,h=Object.getOwnPropertyDescriptor,c=(t,e,i,r)=>{for(var s,o=r>1?void 0:r?h(e,i):e,n=t.length-1;n>=0;n--)(s=t[n])&&(o=(r?s(e,i,o):s(o))||o);return r&&o&&d(e,i,o),o};let m=class extends t{constructor(){super(...arguments),this.liveValidation=!1,this.showErrors=!0,this.errorBox=!1,this.errorBoxMessage="Fix These Issues Before Submitting",this.mode="client",this.noValidate=!1,this.enterSubmit=!1,this.resetOnSubmit=!1,this.csrfToken=null,this.validationErrors=[],this.submitSuccess=!1,this.validationActive=!1,this._syncQueued=!1,this._handlingSubmit=!1,this._handlingInvalid=!1,this.preset="",this._presets=new l(this),this._onEnterKey=t=>{if(!this.enterSubmit)return;if("Enter"!==t.key)return;const e=t.target;e instanceof HTMLTextAreaElement||e.closest('button[type="submit"], input[type="submit"]')||(t.preventDefault(),this.requestSubmit())},this._handleValueChanged=t=>{if(!this.liveValidation)return;const e=t.target;if(!e.matches("sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch, input, select, textarea"))return;const i=e;"function"==typeof i.validateInput&&i.validateInput(),this._refreshAggregates(!1),this._emitFormChange()},this._onFormDataCapture=t=>{t.target instanceof HTMLFormElement&&t.target===this._formEl&&t.stopImmediatePropagation()},this._onInvalidCapture=t=>{const e=t.target;e?.form&&e.form!==this._formEl||(t.preventDefault(),t.stopImmediatePropagation(),this._activateValidation(),this._focusFirstInvalid())},this._onHostSubmitCapture=t=>{if(t.target instanceof HTMLFormElement&&t.target===this._formEl&&!this._handlingSubmit){this._handlingSubmit=!0;try{this._syncNow();if(!this._formEl.checkValidity())return t.preventDefault(),t.stopImmediatePropagation(),this.submitSuccess=!1,void this._focusFirstInvalid();if("server"===this.mode)return this.submitSuccess=!0,void(this.resetOnSubmit&&queueMicrotask(()=>this.reset()));t.preventDefault(),t.stopImmediatePropagation();const e=new FormData(this._formEl);let i=!1;try{const t=new FormDataEvent("formdata",{formData:e,bubbles:!0,composed:!0});this.dispatchEvent(t),i=!0}catch{}i||this.dispatchEvent(new CustomEvent("formdata",{detail:{formData:e},bubbles:!0,composed:!0}));const r=this._formDataToJSON(e);this.dispatchEvent(new CustomEvent("form-submit",{detail:{values:r,formData:e,success:this._formEl.checkValidity()},bubbles:!0,composed:!0})),this.submitSuccess=!0,this.resetOnSubmit&&queueMicrotask(()=>this.reset())}finally{this._handlingSubmit=!1}}}}createRenderRoot(){return this}get formElement(){return this._formEl}connectedCallback(){super.connectedCallback(),this._queueSync(),this.addEventListener("submit",this._onHostSubmitCapture,!0),this.addEventListener("formdata",this._onFormDataCapture,!0),this.addEventListener("value-changed",this._handleValueChanged),this.addEventListener("keydown",this._onEnterKey,!0),this.addEventListener("invalid",this._onInvalidCapture,!0),this._mo=new MutationObserver(t=>{t.some(t=>{for(const e of t.addedNodes)if(e.nodeType===Node.ELEMENT_NODE)return!0;for(const e of t.removedNodes)if(e.nodeType===Node.ELEMENT_NODE)return!0;return!1})&&this._queueSync(!1)}),this._mo.observe(this,{childList:!0,subtree:!0})}disconnectedCallback(){this.removeEventListener("submit",this._onHostSubmitCapture,!0),this.removeEventListener("formdata",this._onFormDataCapture,!0),this.removeEventListener("value-changed",this._handleValueChanged),this.removeEventListener("keydown",this._onEnterKey,!0),this.removeEventListener("invalid",this._onInvalidCapture,!0),this._mo?.disconnect(),super.disconnectedCallback()}_queueSync(t=!1){if(this._syncQueued)return;this._syncQueued=!0;const e=t;queueMicrotask(()=>{this._syncQueued=!1,this._moveHostIdToNativeForm(),this._ensureNativeFormHasId(),this._assignFormOwnerToChildren(),this._refreshAggregates(e)})}_moveHostIdToNativeForm(){const t=this.id?.trim();t&&this._formEl&&(this._formEl.id!==t&&(this._formEl.id=t),this.id===t&&this.removeAttribute("id"))}_ensureNativeFormHasId(){if(!this._formEl?.id){const t="undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).slice(2,9)}`;this._formEl.id=`sky-form-${t}`}}_formValidQuiet(){const t=this._formEl;if(!t)return!0;for(const e of Array.from(t.elements)){const t=e;if("validity"in t&&t.validity&&!("willValidate"in t&&!1===t.willValidate||t.validity.valid))return!1}return!0}_emitFormChange(){if(!this._formEl)return;const t=new FormData(this._formEl),e=this._formDataToJSON(t);this.dispatchEvent(new CustomEvent("form-change",{detail:{values:e,formData:t,success:this._formValidQuiet()},bubbles:!0,composed:!0}))}_assignFormOwnerToChildren(){const t=this._formEl?.id;if(!t)return;const e=Array.from(this.querySelectorAll(["input","select","textarea","button","sky-button","sky-input","sky-combobox","sky-datepicker","sky-checkbox","sky-radio-group","sky-otp","sky-serial","sky-counter","sky-switch"].join(",")));for(const i of e)i.closest("form")!==this._formEl&&i.form?.id!==t&&i.setAttribute("form",t)}_getSkyInputs(){return Array.from(this.querySelectorAll("sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch"))}_emitValidationErrors(){this.dispatchEvent(new CustomEvent("validation-errors",{detail:{errors:[...this.validationErrors]},bubbles:!0,composed:!0}))}_refreshAggregates(t=!0){const e=[],i=this._getSkyInputs();for(const r of i)t&&!this._handlingInvalid&&"function"==typeof r.validateInput&&r.validateInput(),Array.isArray(r.validationErrors)&&e.push(...r.validationErrors);const r=this._formEl;if(r){const t=Array.from(r.elements);for(const i of t){if(!("willValidate"in i)||!i.willValidate)continue;if(!(!("validity"in i)||!i.validity||i.validity.valid)){const t=i.validationMessage||`${i.name||i.id||"Field"} is invalid`;e.push(t)}}}const s=[...new Set(e)];s.join("|")!==this.validationErrors.join("|")&&(this.validationErrors=s,this._emitValidationErrors())}_focusFirstInvalid(){const t=this._getSkyInputs().find(t=>!0===t.invalid);t&&this._focusElement(t)}_focusElement(t){const e="function"==typeof matchMedia&&matchMedia("(prefers-reduced-motion: reduce)").matches;t.scrollIntoView({behavior:e?"auto":"smooth",block:"center"}),setTimeout(()=>{t.focus({preventScroll:!0})},100)}_syncNow(){this._moveHostIdToNativeForm(),this._ensureNativeFormHasId(),this._assignFormOwnerToChildren(),this._refreshAggregates()}_formDataToJSON(t){const e={};return t.forEach((t,i)=>{i in e?Array.isArray(e[i])?e[i].push(t):e[i]=[e[i],t]:e[i]=t}),e}_activateValidation(){this.validationActive=!0;const t=this._getSkyInputs();for(const e of t)e.validationActive=!0,e.showErrors=!0;queueMicrotask(()=>this._refreshAggregates())}checkValidity(){return this._syncNow(),this._formEl?.checkValidity?.()??!0}reportValidity(){return this._syncNow(),this._formEl?.reportValidity?.()??!0}requestSubmit(t){this._syncNow(),this._activateValidation(),this._formEl?.requestSubmit?.(t)}reset(){this._queueSync(),this._formEl?.reset()}resetForm(){this._queueSync(),this._formEl?.reset()}submit(){if(this._activateValidation(),"client"===this.mode){const t=new SubmitEvent("submit",{bubbles:!0,cancelable:!0});this._formEl?.dispatchEvent(t)}else this._formEl?.submit()}render(){return i`
|
|
2
2
|
<style>
|
|
3
3
|
/* Scoped to the sky-form tag: this is a light-DOM component, so
|
|
4
4
|
unscoped class selectors would leak into the whole document. */
|
|
@@ -39,12 +39,12 @@ import{LitElement as t,nothing as e,html as i}from"lit";import{property as r,sta
|
|
|
39
39
|
|
|
40
40
|
<form
|
|
41
41
|
part="form"
|
|
42
|
-
method=${
|
|
43
|
-
action=${
|
|
44
|
-
enctype=${
|
|
45
|
-
autocomplete=${
|
|
42
|
+
method=${a(this.method)}
|
|
43
|
+
action=${a(this.action)}
|
|
44
|
+
enctype=${a(this.enctype)}
|
|
45
|
+
autocomplete=${a(this.autocomplete)}
|
|
46
46
|
?novalidate=${this.noValidate}
|
|
47
|
-
target=${
|
|
47
|
+
target=${a(this.target||void 0)}
|
|
48
48
|
>
|
|
49
49
|
${this.csrfToken?i`<input
|
|
50
50
|
type="hidden"
|
|
@@ -64,5 +64,5 @@ import{LitElement as t,nothing as e,html as i}from"lit";import{property as r,sta
|
|
|
64
64
|
</div>
|
|
65
65
|
</div>
|
|
66
66
|
`:e}
|
|
67
|
-
`}};c([r({type:Boolean})],m.prototype,"liveValidation",2),c([r({type:Boolean})],m.prototype,"showErrors",2),c([r({type:Boolean})],m.prototype,"errorBox",2),c([r({type:String})],m.prototype,"errorBoxMessage",2),c([r({type:String})],m.prototype,"mode",2),c([r({type:String})],m.prototype,"method",2),c([r({type:String})],m.prototype,"action",2),c([r({type:String})],m.prototype,"target",2),c([r({type:String})],m.prototype,"enctype",2),c([r({type:String})],m.prototype,"autocomplete",2),c([r({type:Boolean})],m.prototype,"noValidate",2),c([r({type:Boolean})],m.prototype,"enterSubmit",2),c([r({type:Boolean})],m.prototype,"resetOnSubmit",2),c([r({type:String})],m.prototype,"csrfToken",2),c([s()],m.prototype,"validationErrors",2),c([s()],m.prototype,"submitSuccess",2),c([s()],m.prototype,"validationActive",2),c([o("form")],m.prototype,"_formEl",2),c([r({type:String,reflect:!0})],m.prototype,"preset",2),m=c([
|
|
67
|
+
`}};c([r({type:Boolean})],m.prototype,"liveValidation",2),c([r({type:Boolean})],m.prototype,"showErrors",2),c([r({type:Boolean})],m.prototype,"errorBox",2),c([r({type:String})],m.prototype,"errorBoxMessage",2),c([r({type:String})],m.prototype,"mode",2),c([r({type:String})],m.prototype,"method",2),c([r({type:String})],m.prototype,"action",2),c([r({type:String})],m.prototype,"target",2),c([r({type:String})],m.prototype,"enctype",2),c([r({type:String})],m.prototype,"autocomplete",2),c([r({type:Boolean})],m.prototype,"noValidate",2),c([r({type:Boolean})],m.prototype,"enterSubmit",2),c([r({type:Boolean})],m.prototype,"resetOnSubmit",2),c([r({type:String})],m.prototype,"csrfToken",2),c([s()],m.prototype,"validationErrors",2),c([s()],m.prototype,"submitSuccess",2),c([s()],m.prototype,"validationActive",2),c([o("form")],m.prototype,"_formEl",2),c([r({type:String,reflect:!0})],m.prototype,"preset",2),m=c([n("sky-form")],m);export{m as SkyForm};
|
|
68
68
|
//# sourceMappingURL=sky-form.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-form.js","sources":["../../src/sky-form/sky-form.ts"],"sourcesContent":["import { LitElement, html, nothing, css } from \"lit\";\r\nimport { customElement, property, query, state } from \"lit/decorators.js\";\r\nimport { ifDefined } from \"lit/directives/if-defined.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyFormMode = \"client\" | \"server\";\r\nexport type SkyFormMethod = \"get\" | \"post\";\r\nexport type SkyFormEnctype =\r\n | \"application/x-www-form-urlencoded\"\r\n | \"multipart/form-data\"\r\n | \"text/plain\";\r\nexport type SkyFormAutocomplete = \"on\" | \"off\";\r\nexport type SkyFormValues = Record<string, FormDataEntryValue | FormDataEntryValue[]>;\r\nexport type SkyFormChangeDetail = { values: SkyFormValues; formData: FormData; success: boolean };\r\nexport type SkyFormValidationErrorsDetail = { errors: string[] };\r\nexport type SkyFormSubmitDetail = { values: SkyFormValues; formData: FormData; success: boolean };\r\nexport type SkyFormFormDataFallbackDetail = { formData: FormData };\r\n\r\n/**\r\n * @element sky-form\r\n *\r\n * @summary Form wrapper with unified validation, error aggregation, and client/server submit flows.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/form\r\n *\r\n * @slot - Form elements: inputs, buttons, and other form controls.\r\n *\r\n * @property {boolean} liveValidation - Validates and emits `form-change` on value changes.\r\n * @property {boolean} showErrors - Shows validation errors in child controls when available.\r\n * @property {boolean} errorBox - Shows consolidated validation messages above the form.\r\n * @property {string} errorBoxMessage - Header message shown in the consolidated error box.\r\n * @property {SkyFormMode} mode - Submission mode (`client` or `server`).\r\n * @property {SkyFormMethod | undefined} method - Native form method in server mode.\r\n * @property {string | undefined} action - Native form action in server mode.\r\n * @property {string | undefined} target - Native form target in server mode.\r\n * @property {SkyFormEnctype | undefined} enctype - Native form encoding type.\r\n * @property {SkyFormAutocomplete | undefined} autocomplete - Native form autocomplete behavior.\r\n * @property {boolean} noValidate - Disables native HTML constraint UI.\r\n * @property {boolean} enterSubmit - Submits on Enter for non-textarea controls.\r\n * @property {boolean} resetOnSubmit - Resets form after successful submit.\r\n * @property {string | null} csrfToken - Optional CSRF token value for app-level submit handling.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n *\r\n * @fires {CustomEvent<SkyFormChangeDetail>} form-change - Fired when form values change in live validation mode.\r\n * @fires {CustomEvent<SkyFormValidationErrorsDetail>} validation-errors - Fired when aggregated validation errors change.\r\n * @fires {CustomEvent<SkyFormFormDataFallbackDetail> | FormDataEvent} formdata - Fired before client-mode submit with form data.\r\n * @fires {CustomEvent<SkyFormSubmitDetail>} form-submit - Fired after successful client-mode submit handling.\r\n *\r\n * @method checkValidity Returns native form validity without UI reporting.\r\n * @method reportValidity Returns native form validity and reports native UI messages.\r\n * @method requestSubmit Requests submission using optional submitter.\r\n * @method reset Resets native form and values.\r\n * @method resetForm Alias for `reset`.\r\n * @method submit Submits form using mode-specific client/server flow.\r\n *\r\n * @csspart error-box - Container for the consolidated error messages.\r\n * @csspart error-list - List element containing validation errors.\r\n *\r\n * @Behavior\r\n * - Provides seamless integration of custom form controls with native HTML form behavior\r\n * - Supports both client-side AJAX submissions and traditional server-side submissions\r\n * - Automatic validation aggregation from both native and custom form controls\r\n * - Light DOM rendering for proper browser form association\r\n *\r\n * The component automatically associates form controls (including custom elements) with the native form\r\n * using the `form` attribute, ensuring proper browser validation and FormData collection.\r\n *\r\n * - **Live Validation**: Validates on every input change (when liveValidation=true)\r\n * - **Submit Validation**: Validates only on form submission\r\n * - **Mixed Validation**: Combines both native HTML5 validation and custom component validation\r\n *\r\n * - **Client Mode**: Prevents page refresh, emits formdata/form-submit events for JavaScript handling\r\n * - **Server Mode**: Allows traditional form submission with page navigation/refresh\r\n *\r\n * @example\r\n * ```html\r\n * <sky-form id=\"login-form\" live-validation error-box>\r\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\r\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\r\n * <sky-button type=\"submit\">Sign In</sky-button>\r\n * </sky-form>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-form id=\"login-form\" live-validation error-box @form-submit=\"onSubmit\">\r\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\r\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\r\n * <sky-button type=\"submit\">Sign In</sky-button>\r\n * </sky-form>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-form id=\"login-form\" live-validation error-box>\r\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\r\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\r\n * <sky-button type=\"submit\">Sign In</sky-button>\r\n * </sky-form>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-form\")\r\nexport class SkyForm extends LitElement {\r\n\r\n /**\r\n * When true, validates form on every value change.\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n liveValidation = false;\r\n\r\n /**\r\n * Controls whether validation errors are displayed.\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n showErrors = true;\r\n\r\n /**\r\n * When true, displays a consolidated error box with all validation errors.\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n errorBox = false;\r\n\r\n /**\r\n * Custom message displayed in the error box header.\r\n * @public\r\n */\r\n @property({ type: String })\r\n errorBoxMessage = \"Fix These Issues Before Submitting\";\r\n\r\n /**\r\n * Submission mode: 'client' prevents page refresh, 'server' allows native form submission.\r\n * @public\r\n */\r\n @property({ type: String })\r\n mode: SkyFormMode = \"client\";\r\n\r\n /**\r\n * HTTP method for form submission (when mode='server').\r\n * @public\r\n */\r\n @property({ type: String })\r\n method?: SkyFormMethod;\r\n\r\n /**\r\n * URL for form submission (when mode='server').\r\n * @public\r\n */\r\n @property({ type: String })\r\n action?: string;\r\n\r\n /**\r\n * Target window/frame for form submission.\r\n * @public\r\n */\r\n @property({ type: String })\r\n target?: string;\r\n\r\n /**\r\n * Form encoding type.\r\n * @public\r\n */\r\n @property({ type: String })\r\n enctype?: SkyFormEnctype;\r\n\r\n /**\r\n * HTML autocomplete attribute.\r\n * @public\r\n */\r\n @property({ type: String })\r\n autocomplete?: SkyFormAutocomplete;\r\n\r\n /**\r\n * When true, disables native HTML5 validation.\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n noValidate = false;\r\n\r\n /**\r\n * When true, pressing Enter in any input submits the form (except textarea).\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n enterSubmit = false;\r\n\r\n /**\r\n * When true, resets the form after successful submission.\r\n * @public\r\n */\r\n @property({ type: Boolean })\r\n resetOnSubmit = false;\r\n\r\n /**\r\n * CSRF token for form submission protection.\r\n * @public\r\n */\r\n @property({ type: String })\r\n csrfToken: string | null = null;\r\n\r\n /**\r\n * Array of current validation error messages.\r\n * @private\r\n */\r\n @state()\r\n private validationErrors: string[] = [];\r\n\r\n /**\r\n * Indicates whether the last form submission was successful.\r\n * @private\r\n */\r\n @state()\r\n private submitSuccess = false;\r\n\r\n /**\r\n * Indicates whether validation is currently active (errors shown).\r\n * @private\r\n */\r\n @state()\r\n private validationActive = false;\r\n\r\n\r\n\r\n /** Reference to the native form element. */\r\n @query(\"form\")\r\n private _formEl!: HTMLFormElement;\r\n\r\n private _mo?: MutationObserver;\r\n private _syncQueued = false;\r\n private _handlingSubmit = false;\r\n private _handlingInvalid = false;\r\n\r\n /** MUST be light DOM for button[form=\"...\"] + native id lookup */\r\n\r\n /** Named prop preset from nearest `sky-config-provider`. */\r\n @property({ type: String, reflect: true }) preset = \"\";\r\n\r\n private _presets = new PresetController(this);\r\n\r\n protected override createRenderRoot(): this {\r\n return this;\r\n }\r\n\r\n /**\r\n * Returns the native HTML form element.\r\n * @public\r\n */\r\n public get formElement(): HTMLFormElement {\r\n return this._formEl;\r\n }\r\n\r\n override connectedCallback(): void {\r\n super.connectedCallback();\r\n this._queueSync();\r\n\r\n // Capture submit on the host before it reaches the inner <form> target.\r\n this.addEventListener(\"submit\", this._onHostSubmitCapture as any, true);\r\n this.addEventListener(\"formdata\", this._onFormDataCapture as any, true);\r\n this.addEventListener(\"value-changed\", this._handleValueChanged as any);\r\n this.addEventListener(\"keydown\", this._onEnterKey as any, true);\r\n this.addEventListener(\"invalid\", this._onInvalidCapture as any, true);\r\n this._mo = new MutationObserver(() => this._queueSync());\r\n this._mo.observe(this, { childList: true, subtree: true });\r\n }\r\n\r\n override disconnectedCallback(): void {\r\n this.removeEventListener(\"submit\", this._onHostSubmitCapture as any, true);\r\n this.removeEventListener(\"formdata\", this._onFormDataCapture as any, true);\r\n this.removeEventListener(\"value-changed\", this._handleValueChanged as any);\r\n this.removeEventListener(\"keydown\", this._onEnterKey as any, true);\r\n this.removeEventListener(\"invalid\", this._onInvalidCapture as any, true);\r\n this._mo?.disconnect();\r\n super.disconnectedCallback();\r\n }\r\n\r\n private _queueSync(): void {\r\n if (this._syncQueued) return;\r\n this._syncQueued = true;\r\n\r\n queueMicrotask(() => {\r\n this._syncQueued = false;\r\n this._moveHostIdToNativeForm();\r\n this._ensureNativeFormHasId();\r\n this._assignFormOwnerToChildren();\r\n this._refreshAggregates();\r\n });\r\n }\r\n\r\n private _onEnterKey = (e: KeyboardEvent) => {\r\n if (!this.enterSubmit) return;\r\n if (e.key !== \"Enter\") return;\r\n\r\n const target = e.target as HTMLElement;\r\n\r\n // Allow Enter inside textarea.\r\n if (target instanceof HTMLTextAreaElement) return;\r\n\r\n if (target.closest('button[type=\"submit\"], input[type=\"submit\"]')) {\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n this.requestSubmit();\r\n };\r\n\r\n /**\r\n * Make `document.getElementById(hostId)` return the native form.\r\n * Copies host id => native form id and removes id from host to avoid duplicate ids.\r\n */\r\n private _moveHostIdToNativeForm() {\r\n const hostId = this.id?.trim();\r\n if (!hostId || !this._formEl) return;\r\n\r\n // set native form id\r\n if (this._formEl.id !== hostId) this._formEl.id = hostId;\r\n\r\n // remove host id so DOM lookups hit <form>\r\n if (this.id === hostId) this.removeAttribute(\"id\");\r\n }\r\n\r\n /**\r\n * Slotted controls remain in the light DOM under `<sky-form>`, not under the inner `<form>`.\r\n * `_assignFormOwnerToChildren` needs a stable `id` on that `<form>` to set `form=\"…\"` on\r\n * associated controls. When the host has no `id`, generate one so association still works.\r\n */\r\n private _ensureNativeFormHasId(): void {\r\n if (!this._formEl?.id) {\r\n const suffix =\r\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\r\n ? crypto.randomUUID()\r\n : `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\r\n this._formEl.id = `sky-form-${suffix}`;\r\n }\r\n }\r\n\r\n private _handleValueChanged = (e: Event) => {\r\n if (!this.liveValidation) return;\r\n\r\n // Only process if it's coming from our form elements\r\n const target = e.target as HTMLElement;\r\n if (\r\n !target.matches(\r\n \"sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch, input, select, textarea\",\r\n )\r\n ) {\r\n return;\r\n }\r\n\r\n // Queue a sync to update validation errors\r\n this._queueSync();\r\n\r\n // Emit form-change event with current form data\r\n this._emitFormChange();\r\n };\r\n\r\n /**\r\n * Whether the form passes constraint validation, without calling `form.checkValidity()`.\r\n * `HTMLFormElement.checkValidity()` dispatches `invalid` on each invalid control; during\r\n * liveValidation that runs on every keystroke and our capture listener would focus the first\r\n * invalid field, stealing focus from the control the user is editing.\r\n */\r\n private _formValidQuiet(): boolean {\r\n const form = this._formEl;\r\n if (!form) return true;\r\n\r\n for (const el of Array.from(form.elements)) {\r\n const h = el as HTMLElement & {\r\n willValidate?: boolean;\r\n validity?: ValidityState;\r\n };\r\n if (!(\"validity\" in h) || !h.validity) continue;\r\n if (\"willValidate\" in h && h.willValidate === false) continue;\r\n if (!h.validity.valid) return false;\r\n }\r\n return true;\r\n }\r\n\r\n private _emitFormChange(): void {\r\n if (!this._formEl) return;\r\n\r\n const fd = new FormData(this._formEl);\r\n const json = this._formDataToJSON(fd);\r\n\r\n this.dispatchEvent(\r\n new CustomEvent<SkyFormChangeDetail>(\"form-change\", {\r\n detail: {\r\n values: json,\r\n formData: fd,\r\n success: this._formValidQuiet(),\r\n },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * Assigns form ownership to elements not inside the native <form> element.\r\n * Ensures browser association for native submit + validation + FormData.\r\n */\r\n private _assignFormOwnerToChildren() {\r\n const formId = this._formEl?.id;\r\n if (!formId) return;\r\n\r\n const candidates = Array.from(\r\n this.querySelectorAll<HTMLElement>(\r\n [\r\n \"input\",\r\n \"select\",\r\n \"textarea\",\r\n \"button\",\r\n \"sky-button\",\r\n \"sky-input\",\r\n \"sky-combobox\",\r\n \"sky-datepicker\",\r\n \"sky-checkbox\",\r\n \"sky-radio-group\",\r\n \"sky-otp\",\r\n \"sky-serial\",\r\n \"sky-counter\",\r\n \"sky-switch\",\r\n ].join(\",\"),\r\n ),\r\n );\r\n\r\n for (const el of candidates) {\r\n // already inside the real form = skip\r\n if (el.closest(\"form\") === this._formEl) continue;\r\n\r\n // assign ownership\r\n if ((el as { form?: HTMLFormElement }).form?.id !== formId) el.setAttribute(\"form\", formId);\r\n }\r\n }\r\n\r\n private _getSkyInputs() {\r\n return Array.from(\r\n this.querySelectorAll<any>(\r\n \"sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch\",\r\n ),\r\n );\r\n }\r\n\r\n private _emitValidationErrors(): void {\r\n this.dispatchEvent(\r\n new CustomEvent<SkyFormValidationErrorsDetail>(\"validation-errors\", {\r\n detail: { errors: [...this.validationErrors] },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n private _onFormDataCapture = (e: Event) => {\r\n // Only intercept native form's event\r\n if (!(e.target instanceof HTMLFormElement)) return;\r\n if (e.target !== this._formEl) return;\r\n\r\n // Stop native bubbling so Vue does not handle the inner form event.\r\n e.stopImmediatePropagation();\r\n // formdata is informational; do not preventDefault.\r\n };\r\n\r\n private _onInvalidCapture = (e: Event) => {\r\n const t = e.target as any;\r\n\r\n // only react to invalids belonging to THIS form\r\n if (t?.form && t.form !== this._formEl) return;\r\n\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n\r\n // invalid handler activates validation and focuses the first invalid field.\r\n this._activateValidation();\r\n\r\n this._focusFirstInvalid();\r\n };\r\n\r\n private _refreshAggregates() {\r\n const errs: string[] = [];\r\n\r\n // 1) sky-* component errors (if they provide them)\r\n const skyInputs = this._getSkyInputs();\r\n for (const input of skyInputs) {\r\n // Skip validateInput during invalid-event recursion.\r\n if (!this._handlingInvalid && typeof input.validateInput === \"function\") {\r\n input.validateInput();\r\n }\r\n\r\n if (Array.isArray(input.validationErrors)) {\r\n errs.push(...input.validationErrors);\r\n }\r\n }\r\n\r\n // 2) native constraint validation messages from associated elements\r\n const elements = Array.from(this._formEl.elements) as any[];\r\n for (const el of elements) {\r\n // Skip buttons etc\r\n if (!(\"willValidate\" in el) || !el.willValidate) continue;\r\n\r\n const isValid =\r\n \"validity\" in el && el.validity ? el.validity.valid : true;\r\n\r\n if (!isValid) {\r\n const msg =\r\n el.validationMessage || `${el.name || el.id || \"Field\"} is invalid`;\r\n errs.push(msg);\r\n }\r\n }\r\n\r\n const next = [...new Set(errs)];\r\n if (next.join(\"|\") !== this.validationErrors.join(\"|\")) {\r\n this.validationErrors = next;\r\n this._emitValidationErrors();\r\n }\r\n }\r\n\r\n private _focusFirstInvalid() {\r\n const inputs = this._getSkyInputs();\r\n const firstInvalid = inputs.find(input => input.invalid === true);\r\n\r\n if (firstInvalid) {\r\n this._focusElement(firstInvalid);\r\n }\r\n }\r\n\r\n private _focusElement(element: HTMLElement) {\r\n const prefersReducedMotion =\r\n typeof matchMedia === \"function\" &&\r\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\r\n element.scrollIntoView({\r\n behavior: prefersReducedMotion ? \"auto\" : \"smooth\",\r\n block: \"center\",\r\n });\r\n setTimeout(() => {\r\n element.focus({ preventScroll: true });\r\n }, 100);\r\n }\r\n\r\n private _syncNow() {\r\n this._moveHostIdToNativeForm();\r\n this._ensureNativeFormHasId();\r\n this._assignFormOwnerToChildren();\r\n this._refreshAggregates();\r\n }\r\n\r\n private _onHostSubmitCapture = (e: Event) => {\r\n // only intercept submits coming from the inner native form\r\n if (!(e.target instanceof HTMLFormElement)) return;\r\n if (e.target !== this._formEl) return;\r\n\r\n // avoid recursion when we re-dispatch\r\n if (this._handlingSubmit) return;\r\n this._handlingSubmit = true;\r\n\r\n try {\r\n this._syncNow();\r\n const ok = this._formEl.checkValidity();\r\n if (!ok) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n this.submitSuccess = false;\r\n this._focusFirstInvalid();\r\n return;\r\n }\r\n\r\n // Server mode: allow native form submission (page refresh/navigation).\r\n if (this.mode === \"server\") {\r\n this.submitSuccess = true;\r\n if (this.resetOnSubmit) {\r\n queueMicrotask(() => this.reset());\r\n }\r\n return;\r\n }\r\n\r\n // Always prevent default for client mode to avoid page refresh\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n\r\n const fd = new FormData(this._formEl);\r\n\r\n // Emit a real FormDataEvent when available (Vue handlers use e.formData).\r\n let dispatched = false;\r\n try {\r\n // @ts-ignore\r\n const fde = new FormDataEvent(\"formdata\", {\r\n formData: fd,\r\n bubbles: true,\r\n composed: true,\r\n });\r\n this.dispatchEvent(fde);\r\n dispatched = true;\r\n } catch { }\r\n\r\n // fallback for older browsers\r\n if (!dispatched) {\r\n this.dispatchEvent(\r\n new CustomEvent<SkyFormFormDataFallbackDetail>(\"formdata\", {\r\n detail: { formData: fd },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n // optional perk event (JSON)\r\n const json = this._formDataToJSON(fd);\r\n this.dispatchEvent(\r\n new CustomEvent<SkyFormSubmitDetail>(\"form-submit\", {\r\n detail: {\r\n values: json,\r\n formData: fd,\r\n success: this._formEl.checkValidity(),\r\n },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n\r\n this.submitSuccess = true;\r\n if (this.resetOnSubmit) {\r\n queueMicrotask(() => this.reset());\r\n }\r\n } finally {\r\n this._handlingSubmit = false;\r\n }\r\n };\r\n\r\n private _formDataToJSON(fd: FormData): SkyFormValues {\r\n const obj: SkyFormValues = {};\r\n\r\n fd.forEach((value, key) => {\r\n if (key in obj) {\r\n // convert to array if duplicate keys\r\n if (Array.isArray(obj[key])) {\r\n obj[key].push(value);\r\n } else {\r\n obj[key] = [obj[key], value];\r\n }\r\n } else {\r\n obj[key] = value;\r\n }\r\n });\r\n\r\n return obj;\r\n }\r\n\r\n /** Activates validation mode and refreshes error aggregates. */\r\n private _activateValidation(): void {\r\n this.validationActive = true;\r\n\r\n const skyInputs = this._getSkyInputs();\r\n for (const input of skyInputs) {\r\n input.validationActive = true;\r\n input.showErrors = true;\r\n }\r\n\r\n // Defer aggregation until validationActive is set on child controls.\r\n queueMicrotask(() => this._refreshAggregates());\r\n }\r\n\r\n /**\r\n * Checks form validity without displaying validation messages.\r\n * @returns {boolean} True if the form is valid, false otherwise.\r\n * @public\r\n */\r\n public checkValidity(): boolean {\r\n // Sync synchronously: child controls must have their `form` association set\r\n // before form.checkValidity() runs, otherwise they are excluded from\r\n // form.elements and validity would be reported incorrectly.\r\n this._syncNow();\r\n return this._formEl?.checkValidity?.() ?? true;\r\n }\r\n\r\n /**\r\n * Checks form validity and displays validation messages.\r\n * @returns {boolean} True if the form is valid, false otherwise.\r\n * @public\r\n */\r\n public reportValidity(): boolean {\r\n this._syncNow();\r\n return this._formEl?.reportValidity?.() ?? true;\r\n }\r\n\r\n /**\r\n * Requests form submission (compatible with native requestSubmit).\r\n * @param {HTMLElement} submitter - Optional submitter element.\r\n * @public\r\n */\r\n public requestSubmit(submitter?: HTMLElement): void {\r\n this._syncNow();\r\n this._activateValidation();\r\n // @ts-ignore\r\n this._formEl?.requestSubmit?.(submitter);\r\n }\r\n\r\n /**\r\n * Resets the form to its initial state.\r\n * @public\r\n */\r\n public reset(): void {\r\n this._queueSync();\r\n this._formEl?.reset();\r\n }\r\n\r\n /**\r\n * Resets the form to its initial state (alias for reset()).\r\n * @public\r\n */\r\n public resetForm(): void {\r\n this._queueSync();\r\n this._formEl?.reset();\r\n }\r\n\r\n /**\r\n * Manually submits the form, respecting the current mode setting.\r\n * In client mode, triggers the client-side submission flow.\r\n * In server mode, performs native form submission.\r\n * @public\r\n */\r\n public submit(): void {\r\n this._activateValidation();\r\n if (this.mode === \"client\") {\r\n // Trigger the client-side submission flow\r\n const submitEvent = new SubmitEvent(\"submit\", {\r\n bubbles: true,\r\n cancelable: true,\r\n });\r\n this._formEl?.dispatchEvent(submitEvent);\r\n } else {\r\n // In server mode, use native form submission\r\n this._formEl?.submit();\r\n }\r\n }\r\n\r\n override render() {\r\n return html`\r\n <style>\r\n /* Scoped to the sky-form tag: this is a light-DOM component, so\r\n unscoped class selectors would leak into the whole document. */\r\n sky-form .error-wrapper {\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n }\r\n sky-form .error-container {\r\n margin: var(--sky-space-3, 12px) 0;\r\n padding: var(--sky-space-2, 8px) var(--sky-space-3, 12px);\r\n width: 100%;\r\n border-radius: var(--sky-radius-secondary);\r\n border: 1px solid var(--sky-danger-primary);\r\n background: var(--sky-glass-solid);\r\n box-shadow: var(--sky-box-shadow-primary);\r\n position: relative;\r\n z-index: inherit;\r\n }\r\n sky-form .error-title {\r\n font-size: var(--sky-font-size-sm, 14px);\r\n color: var(--sky-danger-primary);\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n }\r\n sky-form .error-list {\r\n list-style-position: inside;\r\n padding: var(--sky-space-1, 4px);\r\n margin: 0;\r\n text-align: start;\r\n }\r\n sky-form .error-item {\r\n font-size: var(--sky-font-size-xs, 12px);\r\n color: var(--sky-text-primary);\r\n }\r\n </style>\r\n\r\n <form\r\n part=\"form\"\r\n method=${ifDefined(this.method)}\r\n action=${ifDefined(this.action)}\r\n enctype=${ifDefined(this.enctype)}\r\n autocomplete=${ifDefined(this.autocomplete)}\r\n ?novalidate=${this.noValidate}\r\n target=${ifDefined(this.target || undefined)}\r\n >\r\n ${this.csrfToken\r\n ? html`<input\r\n type=\"hidden\"\r\n name=\"csrfmiddlewaretoken\"\r\n .value=${this.csrfToken}\r\n />`\r\n : nothing}\r\n <slot></slot>\r\n </form>\r\n\r\n ${this.errorBox &&\r\n this.validationErrors.length > 0 &&\r\n this.showErrors &&\r\n this.validationActive\r\n ? html`\r\n <div class=\"error-wrapper\" part=\"error-box\">\r\n <div class=\"error-container\">\r\n <div class=\"error-title\">${this.errorBoxMessage}</div>\r\n <ul class=\"error-list\" part=\"error-list\">\r\n ${this.validationErrors.map(\r\n (err) => html`<li class=\"error-item\">${err}</li>`,\r\n )}\r\n </ul>\r\n </div>\r\n </div>\r\n `\r\n : nothing}\r\n `;\r\n }\r\n}\r\n"],"names":["SkyForm","LitElement","constructor","super","arguments","this","liveValidation","showErrors","errorBox","errorBoxMessage","mode","noValidate","enterSubmit","resetOnSubmit","csrfToken","validationErrors","submitSuccess","validationActive","_syncQueued","_handlingSubmit","_handlingInvalid","preset","_presets","PresetController","_onEnterKey","e","key","target","HTMLTextAreaElement","closest","preventDefault","requestSubmit","_handleValueChanged","matches","_queueSync","_emitFormChange","_onFormDataCapture","HTMLFormElement","_formEl","stopImmediatePropagation","_onInvalidCapture","t","form","_activateValidation","_focusFirstInvalid","_onHostSubmitCapture","_syncNow","checkValidity","queueMicrotask","reset","fd","FormData","dispatched","fde","FormDataEvent","formData","bubbles","composed","dispatchEvent","CustomEvent","detail","json","_formDataToJSON","values","success","createRenderRoot","formElement","connectedCallback","addEventListener","_mo","MutationObserver","observe","childList","subtree","disconnectedCallback","removeEventListener","disconnect","_moveHostIdToNativeForm","_ensureNativeFormHasId","_assignFormOwnerToChildren","_refreshAggregates","hostId","id","trim","removeAttribute","suffix","crypto","randomUUID","Date","now","Math","random","toString","slice","_formValidQuiet","el","Array","from","elements","h","validity","willValidate","valid","formId","candidates","querySelectorAll","join","setAttribute","_getSkyInputs","_emitValidationErrors","errors","errs","skyInputs","input","validateInput","isArray","push","msg","validationMessage","name","next","Set","firstInvalid","find","invalid","_focusElement","element","prefersReducedMotion","matchMedia","scrollIntoView","behavior","block","setTimeout","focus","preventScroll","obj","forEach","value","reportValidity","submitter","resetForm","submit","submitEvent","SubmitEvent","cancelable","render","html","ifDefined","method","action","enctype","autocomplete","nothing","length","map","err","__decorateClass","property","type","Boolean","prototype","String","state","query","reflect","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-form.js","sources":["../../src/sky-form/sky-form.ts"],"sourcesContent":["import { LitElement, html, nothing, css } from \"lit\";\nimport { customElement, property, query, state } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\nimport { PresetController } from \"../helper/controllers/preset-controller\";\n\nexport type SkyFormMode = \"client\" | \"server\";\nexport type SkyFormMethod = \"get\" | \"post\";\nexport type SkyFormEnctype =\n | \"application/x-www-form-urlencoded\"\n | \"multipart/form-data\"\n | \"text/plain\";\nexport type SkyFormAutocomplete = \"on\" | \"off\";\nexport type SkyFormValues = Record<string, FormDataEntryValue | FormDataEntryValue[]>;\nexport type SkyFormChangeDetail = { values: SkyFormValues; formData: FormData; success: boolean };\nexport type SkyFormValidationErrorsDetail = { errors: string[] };\nexport type SkyFormSubmitDetail = { values: SkyFormValues; formData: FormData; success: boolean };\nexport type SkyFormFormDataFallbackDetail = { formData: FormData };\n\n/**\n * @element sky-form\n *\n * @summary Form wrapper with unified validation, error aggregation, and client/server submit flows.\n *\n * @status stable\n * @since 1.0.0\n *\n * @documentation https://sky-ui.com/components/form\n *\n * @slot - Form elements: inputs, buttons, and other form controls.\n *\n * @property {boolean} liveValidation - Validates and emits `form-change` on value changes.\n * @property {boolean} showErrors - Shows validation errors in child controls when available.\n * @property {boolean} errorBox - Shows consolidated validation messages above the form.\n * @property {string} errorBoxMessage - Header message shown in the consolidated error box.\n * @property {SkyFormMode} mode - Submission mode (`client` or `server`).\n * @property {SkyFormMethod | undefined} method - Native form method in server mode.\n * @property {string | undefined} action - Native form action in server mode.\n * @property {string | undefined} target - Native form target in server mode.\n * @property {SkyFormEnctype | undefined} enctype - Native form encoding type.\n * @property {SkyFormAutocomplete | undefined} autocomplete - Native form autocomplete behavior.\n * @property {boolean} noValidate - Disables native HTML constraint UI.\n * @property {boolean} enterSubmit - Submits on Enter for non-textarea controls.\n * @property {boolean} resetOnSubmit - Resets form after successful submit.\n * @property {string | null} csrfToken - Optional CSRF token value for app-level submit handling.\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\n *\n * @fires {CustomEvent<SkyFormChangeDetail>} form-change - Fired when form values change in live validation mode.\n * @fires {CustomEvent<SkyFormValidationErrorsDetail>} validation-errors - Fired when aggregated validation errors change.\n * @fires {CustomEvent<SkyFormFormDataFallbackDetail> | FormDataEvent} formdata - Fired before client-mode submit with form data.\n * @fires {CustomEvent<SkyFormSubmitDetail>} form-submit - Fired after successful client-mode submit handling.\n *\n * @method checkValidity Returns native form validity without UI reporting.\n * @method reportValidity Returns native form validity and reports native UI messages.\n * @method requestSubmit Requests submission using optional submitter.\n * @method reset Resets native form and values.\n * @method resetForm Alias for `reset`.\n * @method submit Submits form using mode-specific client/server flow.\n *\n * @csspart error-box - Container for the consolidated error messages.\n * @csspart error-list - List element containing validation errors.\n *\n * @Behavior\n * - Provides seamless integration of custom form controls with native HTML form behavior\n * - Supports both client-side AJAX submissions and traditional server-side submissions\n * - Automatic validation aggregation from both native and custom form controls\n * - Light DOM rendering for proper browser form association\n *\n * The component automatically associates form controls (including custom elements) with the native form\n * using the `form` attribute, ensuring proper browser validation and FormData collection.\n *\n * - **Live Validation**: Validates on every input change (when liveValidation=true)\n * - **Submit Validation**: Validates only on form submission\n * - **Mixed Validation**: Combines both native HTML5 validation and custom component validation\n *\n * - **Client Mode**: Prevents page refresh, emits formdata/form-submit events for JavaScript handling\n * - **Server Mode**: Allows traditional form submission with page navigation/refresh\n *\n * @example\n * ```html\n * <sky-form id=\"login-form\" live-validation error-box>\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\n * <sky-button type=\"submit\">Sign In</sky-button>\n * </sky-form>\n * ```\n * ```vue\n * <template>\n * <sky-form id=\"login-form\" live-validation error-box @form-submit=\"onSubmit\">\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\n * <sky-button type=\"submit\">Sign In</sky-button>\n * </sky-form>\n * </template>\n * ```\n * ```jsx\n * export default function Demo() {\n * return (\n * <sky-form id=\"login-form\" live-validation error-box>\n * <sky-input name=\"email\" type=\"email\" required label=\"Email\"></sky-input>\n * <sky-input name=\"password\" type=\"password\" required label=\"Password\"></sky-input>\n * <sky-button type=\"submit\">Sign In</sky-button>\n * </sky-form>\n * );\n * }\n * ```\n */\n@customElement(\"sky-form\")\nexport class SkyForm extends LitElement {\n\n /**\n * When true, validates form on every value change.\n * @public\n */\n @property({ type: Boolean })\n liveValidation = false;\n\n /**\n * Controls whether validation errors are displayed.\n * @public\n */\n @property({ type: Boolean })\n showErrors = true;\n\n /**\n * When true, displays a consolidated error box with all validation errors.\n * @public\n */\n @property({ type: Boolean })\n errorBox = false;\n\n /**\n * Custom message displayed in the error box header.\n * @public\n */\n @property({ type: String })\n errorBoxMessage = \"Fix These Issues Before Submitting\";\n\n /**\n * Submission mode: 'client' prevents page refresh, 'server' allows native form submission.\n * @public\n */\n @property({ type: String })\n mode: SkyFormMode = \"client\";\n\n /**\n * HTTP method for form submission (when mode='server').\n * @public\n */\n @property({ type: String })\n method?: SkyFormMethod;\n\n /**\n * URL for form submission (when mode='server').\n * @public\n */\n @property({ type: String })\n action?: string;\n\n /**\n * Target window/frame for form submission.\n * @public\n */\n @property({ type: String })\n target?: string;\n\n /**\n * Form encoding type.\n * @public\n */\n @property({ type: String })\n enctype?: SkyFormEnctype;\n\n /**\n * HTML autocomplete attribute.\n * @public\n */\n @property({ type: String })\n autocomplete?: SkyFormAutocomplete;\n\n /**\n * When true, disables native HTML5 validation.\n * @public\n */\n @property({ type: Boolean })\n noValidate = false;\n\n /**\n * When true, pressing Enter in any input submits the form (except textarea).\n * @public\n */\n @property({ type: Boolean })\n enterSubmit = false;\n\n /**\n * When true, resets the form after successful submission.\n * @public\n */\n @property({ type: Boolean })\n resetOnSubmit = false;\n\n /**\n * CSRF token for form submission protection.\n * @public\n */\n @property({ type: String })\n csrfToken: string | null = null;\n\n /**\n * Array of current validation error messages.\n * @private\n */\n @state()\n private validationErrors: string[] = [];\n\n /**\n * Indicates whether the last form submission was successful.\n * @private\n */\n @state()\n private submitSuccess = false;\n\n /**\n * Indicates whether validation is currently active (errors shown).\n * @private\n */\n @state()\n private validationActive = false;\n\n\n\n /** Reference to the native form element. */\n @query(\"form\")\n private _formEl!: HTMLFormElement;\n\n private _mo?: MutationObserver;\n private _syncQueued = false;\n private _handlingSubmit = false;\n private _handlingInvalid = false;\n\n /** MUST be light DOM for button[form=\"...\"] + native id lookup */\n\n /** Named prop preset from nearest `sky-config-provider`. */\n @property({ type: String, reflect: true }) preset = \"\";\n\n private _presets = new PresetController(this);\n\n protected override createRenderRoot(): this {\n return this;\n }\n\n /**\n * Returns the native HTML form element.\n * @public\n */\n public get formElement(): HTMLFormElement {\n return this._formEl;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._queueSync();\n\n // Capture submit on the host before it reaches the inner <form> target.\n this.addEventListener(\"submit\", this._onHostSubmitCapture as any, true);\n this.addEventListener(\"formdata\", this._onFormDataCapture as any, true);\n this.addEventListener(\"value-changed\", this._handleValueChanged as any);\n this.addEventListener(\"keydown\", this._onEnterKey as any, true);\n this.addEventListener(\"invalid\", this._onInvalidCapture as any, true);\n this._mo = new MutationObserver((mutations) => {\n const relevant = mutations.some((m) => {\n for (const node of m.addedNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) return true;\n }\n for (const node of m.removedNodes) {\n if (node.nodeType === Node.ELEMENT_NODE) return true;\n }\n return false;\n });\n if (relevant) this._queueSync(false);\n });\n this._mo.observe(this, { childList: true, subtree: true });\n }\n\n override disconnectedCallback(): void {\n this.removeEventListener(\"submit\", this._onHostSubmitCapture as any, true);\n this.removeEventListener(\"formdata\", this._onFormDataCapture as any, true);\n this.removeEventListener(\"value-changed\", this._handleValueChanged as any);\n this.removeEventListener(\"keydown\", this._onEnterKey as any, true);\n this.removeEventListener(\"invalid\", this._onInvalidCapture as any, true);\n this._mo?.disconnect();\n super.disconnectedCallback();\n }\n\n private _queueSync(revalidate = false): void {\n if (this._syncQueued) return;\n this._syncQueued = true;\n const shouldRevalidate = revalidate;\n\n queueMicrotask(() => {\n this._syncQueued = false;\n this._moveHostIdToNativeForm();\n this._ensureNativeFormHasId();\n this._assignFormOwnerToChildren();\n this._refreshAggregates(shouldRevalidate);\n });\n }\n\n private _onEnterKey = (e: KeyboardEvent) => {\n if (!this.enterSubmit) return;\n if (e.key !== \"Enter\") return;\n\n const target = e.target as HTMLElement;\n\n // Allow Enter inside textarea.\n if (target instanceof HTMLTextAreaElement) return;\n\n if (target.closest('button[type=\"submit\"], input[type=\"submit\"]')) {\n return;\n }\n\n e.preventDefault();\n this.requestSubmit();\n };\n\n /**\n * Make `document.getElementById(hostId)` return the native form.\n * Copies host id => native form id and removes id from host to avoid duplicate ids.\n */\n private _moveHostIdToNativeForm() {\n const hostId = this.id?.trim();\n if (!hostId || !this._formEl) return;\n\n // set native form id\n if (this._formEl.id !== hostId) this._formEl.id = hostId;\n\n // remove host id so DOM lookups hit <form>\n if (this.id === hostId) this.removeAttribute(\"id\");\n }\n\n /**\n * Slotted controls remain in the light DOM under `<sky-form>`, not under the inner `<form>`.\n * `_assignFormOwnerToChildren` needs a stable `id` on that `<form>` to set `form=\"…\"` on\n * associated controls. When the host has no `id`, generate one so association still works.\n */\n private _ensureNativeFormHasId(): void {\n if (!this._formEl?.id) {\n const suffix =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n this._formEl.id = `sky-form-${suffix}`;\n }\n }\n\n private _handleValueChanged = (e: Event) => {\n if (!this.liveValidation) return;\n\n // Only process if it's coming from our form elements\n const target = e.target as HTMLElement;\n if (\n !target.matches(\n \"sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch, input, select, textarea\",\n )\n ) {\n return;\n }\n\n // Queue a sync to update validation errors\n const sky = target as { validateInput?: () => unknown };\n if (typeof sky.validateInput === \"function\") {\n sky.validateInput();\n }\n this._refreshAggregates(false);\n\n // Emit form-change event with current form data\n this._emitFormChange();\n };\n\n /**\n * Whether the form passes constraint validation, without calling `form.checkValidity()`.\n * `HTMLFormElement.checkValidity()` dispatches `invalid` on each invalid control; during\n * liveValidation that runs on every keystroke and our capture listener would focus the first\n * invalid field, stealing focus from the control the user is editing.\n */\n private _formValidQuiet(): boolean {\n const form = this._formEl;\n if (!form) return true;\n\n for (const el of Array.from(form.elements)) {\n const h = el as HTMLElement & {\n willValidate?: boolean;\n validity?: ValidityState;\n };\n if (!(\"validity\" in h) || !h.validity) continue;\n if (\"willValidate\" in h && h.willValidate === false) continue;\n if (!h.validity.valid) return false;\n }\n return true;\n }\n\n private _emitFormChange(): void {\n if (!this._formEl) return;\n\n const fd = new FormData(this._formEl);\n const json = this._formDataToJSON(fd);\n\n this.dispatchEvent(\n new CustomEvent<SkyFormChangeDetail>(\"form-change\", {\n detail: {\n values: json,\n formData: fd,\n success: this._formValidQuiet(),\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n /**\n * Assigns form ownership to elements not inside the native <form> element.\n * Ensures browser association for native submit + validation + FormData.\n */\n private _assignFormOwnerToChildren() {\n const formId = this._formEl?.id;\n if (!formId) return;\n\n const candidates = Array.from(\n this.querySelectorAll<HTMLElement>(\n [\n \"input\",\n \"select\",\n \"textarea\",\n \"button\",\n \"sky-button\",\n \"sky-input\",\n \"sky-combobox\",\n \"sky-datepicker\",\n \"sky-checkbox\",\n \"sky-radio-group\",\n \"sky-otp\",\n \"sky-serial\",\n \"sky-counter\",\n \"sky-switch\",\n ].join(\",\"),\n ),\n );\n\n for (const el of candidates) {\n // already inside the real form = skip\n if (el.closest(\"form\") === this._formEl) continue;\n\n // assign ownership\n if ((el as { form?: HTMLFormElement }).form?.id !== formId) el.setAttribute(\"form\", formId);\n }\n }\n\n private _getSkyInputs() {\n return Array.from(\n this.querySelectorAll<any>(\n \"sky-input, sky-combobox, sky-datepicker, sky-checkbox, sky-radio-group, sky-otp, sky-serial, sky-counter, sky-switch\",\n ),\n );\n }\n\n private _emitValidationErrors(): void {\n this.dispatchEvent(\n new CustomEvent<SkyFormValidationErrorsDetail>(\"validation-errors\", {\n detail: { errors: [...this.validationErrors] },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private _onFormDataCapture = (e: Event) => {\n // Only intercept native form's event\n if (!(e.target instanceof HTMLFormElement)) return;\n if (e.target !== this._formEl) return;\n\n // Stop native bubbling so Vue does not handle the inner form event.\n e.stopImmediatePropagation();\n // formdata is informational; do not preventDefault.\n };\n\n private _onInvalidCapture = (e: Event) => {\n const t = e.target as any;\n\n // only react to invalids belonging to THIS form\n if (t?.form && t.form !== this._formEl) return;\n\n e.preventDefault();\n e.stopImmediatePropagation();\n\n // invalid handler activates validation and focuses the first invalid field.\n this._activateValidation();\n\n this._focusFirstInvalid();\n };\n\n private _refreshAggregates(revalidate = true) {\n const errs: string[] = [];\n\n // 1) sky-* component errors (if they provide them)\n const skyInputs = this._getSkyInputs();\n for (const input of skyInputs) {\n // Skip validateInput during invalid-event recursion and on mutation/ownership sync.\n if (\n revalidate &&\n !this._handlingInvalid &&\n typeof input.validateInput === \"function\"\n ) {\n input.validateInput();\n }\n\n if (Array.isArray(input.validationErrors)) {\n errs.push(...input.validationErrors);\n }\n }\n\n // 2) native constraint validation messages from associated elements\n const form = this._formEl;\n if (form) {\n const elements = Array.from(form.elements) as any[];\n for (const el of elements) {\n // Skip buttons etc\n if (!(\"willValidate\" in el) || !el.willValidate) continue;\n\n const isValid =\n \"validity\" in el && el.validity ? el.validity.valid : true;\n\n if (!isValid) {\n const msg =\n el.validationMessage || `${el.name || el.id || \"Field\"} is invalid`;\n errs.push(msg);\n }\n }\n }\n\n const next = [...new Set(errs)];\n if (next.join(\"|\") !== this.validationErrors.join(\"|\")) {\n this.validationErrors = next;\n this._emitValidationErrors();\n }\n }\n\n private _focusFirstInvalid() {\n const inputs = this._getSkyInputs();\n const firstInvalid = inputs.find(input => input.invalid === true);\n\n if (firstInvalid) {\n this._focusElement(firstInvalid);\n }\n }\n\n private _focusElement(element: HTMLElement) {\n const prefersReducedMotion =\n typeof matchMedia === \"function\" &&\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n element.scrollIntoView({\n behavior: prefersReducedMotion ? \"auto\" : \"smooth\",\n block: \"center\",\n });\n setTimeout(() => {\n element.focus({ preventScroll: true });\n }, 100);\n }\n\n private _syncNow() {\n this._moveHostIdToNativeForm();\n this._ensureNativeFormHasId();\n this._assignFormOwnerToChildren();\n this._refreshAggregates();\n }\n\n private _onHostSubmitCapture = (e: Event) => {\n // only intercept submits coming from the inner native form\n if (!(e.target instanceof HTMLFormElement)) return;\n if (e.target !== this._formEl) return;\n\n // avoid recursion when we re-dispatch\n if (this._handlingSubmit) return;\n this._handlingSubmit = true;\n\n try {\n this._syncNow();\n const ok = this._formEl.checkValidity();\n if (!ok) {\n e.preventDefault();\n e.stopImmediatePropagation();\n this.submitSuccess = false;\n this._focusFirstInvalid();\n return;\n }\n\n // Server mode: allow native form submission (page refresh/navigation).\n if (this.mode === \"server\") {\n this.submitSuccess = true;\n if (this.resetOnSubmit) {\n queueMicrotask(() => this.reset());\n }\n return;\n }\n\n // Always prevent default for client mode to avoid page refresh\n e.preventDefault();\n e.stopImmediatePropagation();\n\n const fd = new FormData(this._formEl);\n\n // Emit a real FormDataEvent when available (Vue handlers use e.formData).\n let dispatched = false;\n try {\n // @ts-ignore\n const fde = new FormDataEvent(\"formdata\", {\n formData: fd,\n bubbles: true,\n composed: true,\n });\n this.dispatchEvent(fde);\n dispatched = true;\n } catch { }\n\n // fallback for older browsers\n if (!dispatched) {\n this.dispatchEvent(\n new CustomEvent<SkyFormFormDataFallbackDetail>(\"formdata\", {\n detail: { formData: fd },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n // optional perk event (JSON)\n const json = this._formDataToJSON(fd);\n this.dispatchEvent(\n new CustomEvent<SkyFormSubmitDetail>(\"form-submit\", {\n detail: {\n values: json,\n formData: fd,\n success: this._formEl.checkValidity(),\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n this.submitSuccess = true;\n if (this.resetOnSubmit) {\n queueMicrotask(() => this.reset());\n }\n } finally {\n this._handlingSubmit = false;\n }\n };\n\n private _formDataToJSON(fd: FormData): SkyFormValues {\n const obj: SkyFormValues = {};\n\n fd.forEach((value, key) => {\n if (key in obj) {\n // convert to array if duplicate keys\n if (Array.isArray(obj[key])) {\n obj[key].push(value);\n } else {\n obj[key] = [obj[key], value];\n }\n } else {\n obj[key] = value;\n }\n });\n\n return obj;\n }\n\n /** Activates validation mode and refreshes error aggregates. */\n private _activateValidation(): void {\n this.validationActive = true;\n\n const skyInputs = this._getSkyInputs();\n for (const input of skyInputs) {\n input.validationActive = true;\n input.showErrors = true;\n }\n\n // Defer aggregation until validationActive is set on child controls.\n queueMicrotask(() => this._refreshAggregates());\n }\n\n /**\n * Checks form validity without displaying validation messages.\n * @returns {boolean} True if the form is valid, false otherwise.\n * @public\n */\n public checkValidity(): boolean {\n // Sync synchronously: child controls must have their `form` association set\n // before form.checkValidity() runs, otherwise they are excluded from\n // form.elements and validity would be reported incorrectly.\n this._syncNow();\n return this._formEl?.checkValidity?.() ?? true;\n }\n\n /**\n * Checks form validity and displays validation messages.\n * @returns {boolean} True if the form is valid, false otherwise.\n * @public\n */\n public reportValidity(): boolean {\n this._syncNow();\n return this._formEl?.reportValidity?.() ?? true;\n }\n\n /**\n * Requests form submission (compatible with native requestSubmit).\n * @param {HTMLElement} submitter - Optional submitter element.\n * @public\n */\n public requestSubmit(submitter?: HTMLElement): void {\n this._syncNow();\n this._activateValidation();\n // @ts-ignore\n this._formEl?.requestSubmit?.(submitter);\n }\n\n /**\n * Resets the form to its initial state.\n * @public\n */\n public reset(): void {\n this._queueSync();\n this._formEl?.reset();\n }\n\n /**\n * Resets the form to its initial state (alias for reset()).\n * @public\n */\n public resetForm(): void {\n this._queueSync();\n this._formEl?.reset();\n }\n\n /**\n * Manually submits the form, respecting the current mode setting.\n * In client mode, triggers the client-side submission flow.\n * In server mode, performs native form submission.\n * @public\n */\n public submit(): void {\n this._activateValidation();\n if (this.mode === \"client\") {\n // Trigger the client-side submission flow\n const submitEvent = new SubmitEvent(\"submit\", {\n bubbles: true,\n cancelable: true,\n });\n this._formEl?.dispatchEvent(submitEvent);\n } else {\n // In server mode, use native form submission\n this._formEl?.submit();\n }\n }\n\n override render() {\n return html`\n <style>\n /* Scoped to the sky-form tag: this is a light-DOM component, so\n unscoped class selectors would leak into the whole document. */\n sky-form .error-wrapper {\n width: 100%;\n display: flex;\n justify-content: center;\n }\n sky-form .error-container {\n margin: var(--sky-space-3, 12px) 0;\n padding: var(--sky-space-2, 8px) var(--sky-space-3, 12px);\n width: 100%;\n border-radius: var(--sky-radius-secondary);\n border: 1px solid var(--sky-danger-primary);\n background: var(--sky-glass-solid);\n box-shadow: var(--sky-box-shadow-primary);\n position: relative;\n z-index: inherit;\n }\n sky-form .error-title {\n font-size: var(--sky-font-size-sm, 14px);\n color: var(--sky-danger-primary);\n width: 100%;\n display: flex;\n justify-content: center;\n }\n sky-form .error-list {\n list-style-position: inside;\n padding: var(--sky-space-1, 4px);\n margin: 0;\n text-align: start;\n }\n sky-form .error-item {\n font-size: var(--sky-font-size-xs, 12px);\n color: var(--sky-text-primary);\n }\n </style>\n\n <form\n part=\"form\"\n method=${ifDefined(this.method)}\n action=${ifDefined(this.action)}\n enctype=${ifDefined(this.enctype)}\n autocomplete=${ifDefined(this.autocomplete)}\n ?novalidate=${this.noValidate}\n target=${ifDefined(this.target || undefined)}\n >\n ${this.csrfToken\n ? html`<input\n type=\"hidden\"\n name=\"csrfmiddlewaretoken\"\n .value=${this.csrfToken}\n />`\n : nothing}\n <slot></slot>\n </form>\n\n ${this.errorBox &&\n this.validationErrors.length > 0 &&\n this.showErrors &&\n this.validationActive\n ? html`\n <div class=\"error-wrapper\" part=\"error-box\">\n <div class=\"error-container\">\n <div class=\"error-title\">${this.errorBoxMessage}</div>\n <ul class=\"error-list\" part=\"error-list\">\n ${this.validationErrors.map(\n (err) => html`<li class=\"error-item\">${err}</li>`,\n )}\n </ul>\n </div>\n </div>\n `\n : nothing}\n `;\n }\n}\n"],"names":["SkyForm","LitElement","constructor","super","arguments","this","liveValidation","showErrors","errorBox","errorBoxMessage","mode","noValidate","enterSubmit","resetOnSubmit","csrfToken","validationErrors","submitSuccess","validationActive","_syncQueued","_handlingSubmit","_handlingInvalid","preset","_presets","PresetController","_onEnterKey","e","key","target","HTMLTextAreaElement","closest","preventDefault","requestSubmit","_handleValueChanged","matches","sky","validateInput","_refreshAggregates","_emitFormChange","_onFormDataCapture","HTMLFormElement","_formEl","stopImmediatePropagation","_onInvalidCapture","t","form","_activateValidation","_focusFirstInvalid","_onHostSubmitCapture","_syncNow","checkValidity","queueMicrotask","reset","fd","FormData","dispatched","fde","FormDataEvent","formData","bubbles","composed","dispatchEvent","CustomEvent","detail","json","_formDataToJSON","values","success","createRenderRoot","formElement","connectedCallback","_queueSync","addEventListener","_mo","MutationObserver","mutations","some","m","node","addedNodes","nodeType","Node","ELEMENT_NODE","removedNodes","observe","childList","subtree","disconnectedCallback","removeEventListener","disconnect","revalidate","shouldRevalidate","_moveHostIdToNativeForm","_ensureNativeFormHasId","_assignFormOwnerToChildren","hostId","id","trim","removeAttribute","suffix","crypto","randomUUID","Date","now","Math","random","toString","slice","_formValidQuiet","el","Array","from","elements","h","validity","willValidate","valid","formId","candidates","querySelectorAll","join","setAttribute","_getSkyInputs","_emitValidationErrors","errors","errs","skyInputs","input","isArray","push","msg","validationMessage","name","next","Set","firstInvalid","find","invalid","_focusElement","element","prefersReducedMotion","matchMedia","scrollIntoView","behavior","block","setTimeout","focus","preventScroll","obj","forEach","value","reportValidity","submitter","resetForm","submit","submitEvent","SubmitEvent","cancelable","render","html","ifDefined","method","action","enctype","autocomplete","nothing","length","map","err","__decorateClass","property","type","Boolean","prototype","String","state","query","reflect","customElement"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{css as t,LitElement as e,html as s,nothing as i}from"lit";import{property as r,
|
|
1
|
+
import{css as t,LitElement as e,html as s,nothing as i}from"lit";import{property as r,customElement as o}from"lit/decorators.js";import{styleMap as c}from"lit/directives/style-map.js";import{resolveImageFallbackSrc as h}from"./srcset-utils.js";import{PresetController as a}from"../helper/controllers/preset-controller.js";var n=Object.defineProperty,d=Object.getOwnPropertyDescriptor,p=(t,e,s,i)=>{for(var r,o=i>1?void 0:i?d(e,s):e,c=t.length-1;c>=0;c--)(r=t[c])&&(o=(i?r(e,s,o):r(o))||o);return i&&o&&n(e,s,o),o};let l=class extends e{constructor(){super(...arguments),this.src="",this.alt="Image",this.width="auto",this.height="auto",this.aspectRatio=null,this.borderRadius="0",this.boxShadow="none",this.objectFit="cover",this.objectPosition="center",this.srcset="",this.sizes="",this.autoSizes=!1,this._computedSizes="",this.loading="lazy",this.decoding="async",this.preset="",this._presets=new a(this),this._sizesRafId=null,this._updateSizes=()=>{null==this._sizesRafId&&(this._sizesRafId=requestAnimationFrame(()=>{this._sizesRafId=null;const t=Math.ceil(this.getBoundingClientRect().width);if(t>0){const e=`${t}px`;if(this._computedSizes===e)return;this._computedSizes=e;const s=this.renderRoot.querySelector("img");s instanceof HTMLImageElement&&(s.sizes=e)}}))}}connectedCallback(){super.connectedCallback(),this._ensureAutoSizesObserver()}firstUpdated(){this._ensureAutoSizesObserver()}_ensureAutoSizesObserver(){this.autoSizes&&(this._ro||(this._ro=new ResizeObserver(this._updateSizes),this._ro.observe(this)),this._updateSizes())}updated(t){if(super.updated(t),t.has("autoSizes"))if(this.autoSizes)this._ensureAutoSizesObserver();else{this._ro?.disconnect(),this._ro=void 0,this._computedSizes="";const t=this.renderRoot.querySelector("img");t instanceof HTMLImageElement&&(this.sizes?t.sizes=this.sizes:t.removeAttribute("sizes"))}this._shouldResyncImageSource(t)&&this._resyncImageSource(t.has("srcset")&&void 0!==t.get("srcset"))}disconnectedCallback(){super.disconnectedCallback(),this._ro?.disconnect(),this._ro=void 0,null!=this._sizesRafId&&(cancelAnimationFrame(this._sizesRafId),this._sizesRafId=null)}_shouldResyncImageSource(t){return!(!this.src?.trim()&&!this.srcset?.trim())&&(!(!t.has("src")||void 0===t.get("src"))||(!(!t.has("srcset")||void 0===t.get("srcset"))||!(!t.has("sizes")||void 0===t.get("sizes"))))}_resolvedSizesAttr(){return this.autoSizes?this._computedSizes:this.sizes||""}_displaySrc(){const t=this.src?.trim()??"",e=this.srcset?.trim()??"";return t||e?e?h(t,e):t:""}_applyImgAttrs(t,e){const s=this.srcset?.trim()??"",i=this._resolvedSizesAttr(),r=this._displaySrc();s?t.srcset=s:t.removeAttribute("srcset"),i?t.sizes=i:t.removeAttribute("sizes"),e&&t.removeAttribute("src"),t.src=r}_resyncImageSource(t=!1){const e=this.renderRoot.querySelector("img");if(!(e instanceof HTMLImageElement))return;const s=e.cloneNode(!1);s.alt=this.alt,s.loading=this.loading,s.decoding=this.decoding;const i=e.getAttribute("style");i&&s.setAttribute("style",i),s.setAttribute("part","img"),this._applyImgAttrs(s,t),e.replaceWith(s)}render(){const t=this._resolvedSizesAttr(),e=this._displaySrc(),r=""!==e,o={width:this.width,height:this.height,borderRadius:this.borderRadius,boxShadow:this.boxShadow,objectFit:this.objectFit,objectPosition:this.objectPosition,aspectRatio:this.aspectRatio??"auto"};return r?s`
|
|
2
2
|
<img
|
|
3
3
|
src=${e}
|
|
4
4
|
alt=${this.alt}
|
|
@@ -6,18 +6,18 @@ import{css as t,LitElement as e,html as s,nothing as i}from"lit";import{property
|
|
|
6
6
|
sizes=${t||i}
|
|
7
7
|
loading=${this.loading}
|
|
8
8
|
decoding=${this.decoding}
|
|
9
|
-
style=${
|
|
9
|
+
style=${c(o)}
|
|
10
10
|
part="img"
|
|
11
11
|
/>
|
|
12
12
|
`:s`
|
|
13
13
|
<div
|
|
14
14
|
class="placeholder"
|
|
15
15
|
part="img"
|
|
16
|
-
style=${
|
|
16
|
+
style=${c(o)}
|
|
17
17
|
aria-label=${this.alt}
|
|
18
18
|
role="img"
|
|
19
19
|
></div>
|
|
20
|
-
`}};
|
|
20
|
+
`}};l.styles=t`
|
|
21
21
|
:host {
|
|
22
22
|
display: inline-block;
|
|
23
23
|
max-width: 100%;
|
|
@@ -32,5 +32,5 @@ import{css as t,LitElement as e,html as s,nothing as i}from"lit";import{property
|
|
|
32
32
|
.placeholder {
|
|
33
33
|
background: var(--sky-hover-tertiary);
|
|
34
34
|
}
|
|
35
|
-
`,
|
|
35
|
+
`,p([r({type:String})],l.prototype,"src",2),p([r({type:String})],l.prototype,"alt",2),p([r({type:String})],l.prototype,"width",2),p([r({type:String})],l.prototype,"height",2),p([r({type:String})],l.prototype,"aspectRatio",2),p([r({type:String})],l.prototype,"borderRadius",2),p([r({type:String})],l.prototype,"boxShadow",2),p([r({type:String})],l.prototype,"objectFit",2),p([r({type:String})],l.prototype,"objectPosition",2),p([r({type:String})],l.prototype,"srcset",2),p([r({type:String})],l.prototype,"sizes",2),p([r({type:Boolean})],l.prototype,"autoSizes",2),p([r({type:String})],l.prototype,"loading",2),p([r({type:String})],l.prototype,"decoding",2),p([r({type:String,reflect:!0})],l.prototype,"preset",2),l=p([o("sky-image")],l);export{l as SkyImage};
|
|
36
36
|
//# sourceMappingURL=sky-image.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-image.js","sources":["../../src/sky-image/sky-image.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from \"lit\";\r\nimport type { PropertyValues } from \"lit\";\r\nimport { customElement, property, state } from \"lit/decorators.js\";\r\nimport { styleMap } from \"lit/directives/style-map.js\";\r\nimport { resolveImageFallbackSrc } from \"./srcset-utils.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyImageLoading = \"lazy\" | \"eager\";\r\nexport type SkyImageDecoding = \"async\" | \"sync\" | \"auto\";\r\n\r\n/**\r\n * @element sky-image\r\n *\r\n * @summary Responsive image component with style controls, `srcset`, and optional auto `sizes`.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/image\r\n *\r\n * @slot - No slots are exposed; image content is rendered internally.\r\n * @property {string} src - Primary image source URL.\r\n * @property {string} alt - Alternative text for accessibility.\r\n * @property {string} width - CSS width for the rendered image.\r\n * @property {string} height - CSS height for the rendered image.\r\n * @property {string | null} aspectRatio - CSS `aspect-ratio` value.\r\n * @property {string} borderRadius - CSS border radius.\r\n * @property {string} boxShadow - CSS box shadow.\r\n * @property {string} objectFit - CSS object-fit value.\r\n * @property {string} objectPosition - CSS object-position value.\r\n * @property {string} srcset - Responsive source set string.\r\n * @property {string} sizes - Responsive sizes string.\r\n * @property {boolean} autoSizes - Computes `sizes` from host width when true.\r\n * @property {SkyImageLoading} loading - Native loading hint.\r\n * @property {SkyImageDecoding} decoding - Native decoding hint.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n *\r\n * @csspart img - The underlying `<img>` element.\r\n *\r\n * @Behavior\r\n * - Renders a fallback visual placeholder when `src` is empty\r\n * - Supports automatic `sizes` updates using ResizeObserver\r\n * - Forwards native image loading and decoding hints\r\n *\r\n * @example\r\n * ```html\r\n * <sky-image\r\n * src=\"/images/photo-1280.jpg\"\r\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\r\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\r\n * alt=\"Responsive photo\"\r\n * width=\"100%\"\r\n * aspect-ratio=\"4/3\"\r\n * border-radius=\"8px\"\r\n * loading=\"lazy\"\r\n * ></sky-image>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-image\r\n * src=\"/images/photo-1280.jpg\"\r\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\r\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\r\n * alt=\"Responsive photo\"\r\n * width=\"100%\"\r\n * aspect-ratio=\"4/3\"\r\n * border-radius=\"8px\"\r\n * ></sky-image>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-image\r\n * src=\"/images/photo-1280.jpg\"\r\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\r\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\r\n * alt=\"Responsive photo\"\r\n * width=\"100%\"\r\n * aspect-ratio=\"4/3\"\r\n * border-radius=\"8px\"\r\n * ></sky-image>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-image\")\r\nexport class SkyImage extends LitElement {\r\n @property({ type: String }) src = \"\";\r\n @property({ type: String }) alt = \"Image\";\r\n @property({ type: String }) width = \"auto\";\r\n @property({ type: String }) height = \"auto\";\r\n @property({ type: String }) aspectRatio: string | null = null;\r\n @property({ type: String }) borderRadius = \"0\";\r\n @property({ type: String }) boxShadow = \"none\";\r\n @property({ type: String }) objectFit = \"cover\";\r\n @property({ type: String }) objectPosition = \"center\";\r\n\r\n @property({ type: String }) srcset = \"\";\r\n @property({ type: String }) sizes = \"\";\r\n\r\n @property({ type: Boolean }) autoSizes = false;\r\n @state() private _computedSizes = \"\";\r\n\r\n @property({ type: String }) loading: SkyImageLoading = \"lazy\";\r\n @property({ type: String }) decoding: SkyImageDecoding = \"async\";\r\n\r\n\r\n /** Named prop preset from nearest `sky-config-provider`. */\r\n @property({ type: String, reflect: true }) preset = \"\";\r\n\r\n private _presets = new PresetController(this);\r\n\r\n static override styles = css`\r\n :host {\r\n display: inline-block;\r\n max-width: 100%;\r\n }\r\n\r\n img,\r\n .placeholder {\r\n display: block;\r\n max-width: 100%;\r\n }\r\n\r\n .placeholder {\r\n background: var(--sky-hover-tertiary);\r\n }\r\n `;\r\n\r\n private _ro?: ResizeObserver;\r\n private _sizesRafId: number | null = null;\r\n private _updateSizes = () => {\r\n if (this._sizesRafId != null) return;\r\n this._sizesRafId = requestAnimationFrame(() => {\r\n this._sizesRafId = null;\r\n const w = Math.ceil(this.getBoundingClientRect().width);\r\n if (w > 0) this._computedSizes = `${w}px`;\r\n });\r\n };\r\n\r\n override connectedCallback(): void {\r\n super.connectedCallback();\r\n // firstUpdated runs only once; re-create the observer when the element\r\n // is moved back into the document after a disconnect.\r\n this._ensureAutoSizesObserver();\r\n }\r\n\r\n override firstUpdated(): void {\r\n this._ensureAutoSizesObserver();\r\n }\r\n\r\n private _ensureAutoSizesObserver(): void {\r\n if (!this.autoSizes) return;\r\n if (!this._ro) {\r\n this._ro = new ResizeObserver(this._updateSizes);\r\n this._ro.observe(this);\r\n }\r\n this._updateSizes();\r\n }\r\n\r\n override updated(changed: PropertyValues<this>): void {\r\n super.updated(changed);\r\n\r\n if (changed.has(\"autoSizes\")) {\r\n if (this.autoSizes) {\r\n this._ensureAutoSizesObserver();\r\n } else {\r\n this._ro?.disconnect();\r\n this._ro = undefined;\r\n this._computedSizes = \"\";\r\n }\r\n }\r\n\r\n if (!this._shouldResyncImageSource(changed)) return;\r\n this._resyncImageSource(\r\n changed.has(\"srcset\") && changed.get(\"srcset\") !== undefined,\r\n );\r\n }\r\n\r\n override disconnectedCallback(): void {\r\n super.disconnectedCallback();\r\n this._ro?.disconnect();\r\n this._ro = undefined;\r\n if (this._sizesRafId != null) {\r\n cancelAnimationFrame(this._sizesRafId);\r\n this._sizesRafId = null;\r\n }\r\n }\r\n\r\n /** Browsers keep the resolved resource when only srcset/sizes attrs change on a live <img>. */\r\n private _shouldResyncImageSource(changed: PropertyValues<this>): boolean {\r\n // Lit's PropertyValues<this> excludes private reactive state from keyof this,\r\n // although @state changes are present in the runtime map.\r\n const allChanged = changed as unknown as Map<PropertyKey, unknown>;\r\n if (!this.src?.trim() && !this.srcset?.trim()) return false;\r\n if (changed.has(\"src\") && changed.get(\"src\") !== undefined) return true;\r\n if (changed.has(\"srcset\") && changed.get(\"srcset\") !== undefined) return true;\r\n if (changed.has(\"sizes\") && changed.get(\"sizes\") !== undefined) return true;\r\n if (\r\n this.autoSizes &&\r\n allChanged.has(\"_computedSizes\") &&\r\n allChanged.get(\"_computedSizes\") !== undefined\r\n ) {\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n private _resolvedSizesAttr(): string {\r\n return this.autoSizes ? this._computedSizes : this.sizes || \"\";\r\n }\r\n\r\n /** `src` used on <img> — must stay in sync with `srcset` when the set changes. */\r\n private _displaySrc(): string {\r\n const explicit = this.src?.trim() ?? \"\";\r\n const srcset = this.srcset?.trim() ?? \"\";\r\n if (!explicit && !srcset) return \"\";\r\n if (!srcset) return explicit;\r\n return resolveImageFallbackSrc(explicit, srcset);\r\n }\r\n\r\n private _applyImgAttrs(img: HTMLImageElement, srcsetChanged: boolean): void {\r\n const srcset = this.srcset?.trim() ?? \"\";\r\n const sizesAttr = this._resolvedSizesAttr();\r\n const src = this._displaySrc();\r\n\r\n if (srcset) img.srcset = srcset;\r\n else img.removeAttribute(\"srcset\");\r\n\r\n if (sizesAttr) img.sizes = sizesAttr;\r\n else img.removeAttribute(\"sizes\");\r\n\r\n // When the candidate list changes, drop `src` first so the UA cannot keep the old resource.\r\n if (srcsetChanged) img.removeAttribute(\"src\");\r\n img.src = src;\r\n }\r\n\r\n private _resyncImageSource(srcsetChanged = false): void {\r\n const img = this.renderRoot.querySelector(\"img\");\r\n if (!(img instanceof HTMLImageElement)) return;\r\n\r\n const replacement = img.cloneNode(false) as HTMLImageElement;\r\n replacement.alt = this.alt;\r\n replacement.loading = this.loading;\r\n replacement.decoding = this.decoding;\r\n const style = img.getAttribute(\"style\");\r\n if (style) replacement.setAttribute(\"style\", style);\r\n replacement.setAttribute(\"part\", \"img\");\r\n\r\n this._applyImgAttrs(replacement, srcsetChanged);\r\n img.replaceWith(replacement);\r\n }\r\n\r\n override render() {\r\n const sizesAttr = this._resolvedSizesAttr();\r\n const displaySrc = this._displaySrc();\r\n const hasSrc = displaySrc !== \"\";\r\n\r\n const styleVars = {\r\n width: this.width,\r\n height: this.height,\r\n borderRadius: this.borderRadius,\r\n boxShadow: this.boxShadow,\r\n objectFit: this.objectFit,\r\n objectPosition: this.objectPosition,\r\n aspectRatio: this.aspectRatio ?? \"auto\",\r\n };\r\n\r\n if (!hasSrc) {\r\n return html`\r\n <div\r\n class=\"placeholder\"\r\n part=\"img\"\r\n style=${styleMap(styleVars)}\r\n aria-label=${this.alt}\r\n role=\"img\"\r\n ></div>\r\n `;\r\n }\r\n\r\n return html`\r\n <img\r\n src=${displaySrc}\r\n alt=${this.alt}\r\n srcset=${this.srcset || nothing}\r\n sizes=${sizesAttr || nothing}\r\n loading=${this.loading}\r\n decoding=${this.decoding}\r\n style=${styleMap(styleVars)}\r\n part=\"img\"\r\n />\r\n `;\r\n }\r\n}\r\n"],"names":["SkyImage","LitElement","constructor","super","arguments","this","src","alt","width","height","aspectRatio","borderRadius","boxShadow","objectFit","objectPosition","srcset","sizes","autoSizes","_computedSizes","loading","decoding","preset","_presets","PresetController","_sizesRafId","_updateSizes","requestAnimationFrame","w","Math","ceil","getBoundingClientRect","connectedCallback","_ensureAutoSizesObserver","firstUpdated","_ro","ResizeObserver","observe","updated","changed","has","disconnect","_shouldResyncImageSource","_resyncImageSource","get","disconnectedCallback","cancelAnimationFrame","allChanged","trim","_resolvedSizesAttr","_displaySrc","explicit","resolveImageFallbackSrc","_applyImgAttrs","img","srcsetChanged","sizesAttr","removeAttribute","renderRoot","querySelector","HTMLImageElement","replacement","cloneNode","style","getAttribute","setAttribute","replaceWith","render","displaySrc","hasSrc","styleVars","html","nothing","styleMap","styles","css","__decorateClass","property","type","String","prototype","Boolean","state","reflect","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-image.js","sources":["../../src/sky-image/sky-image.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from \"lit\";\nimport type { PropertyValues } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { resolveImageFallbackSrc } from \"./srcset-utils.js\";\nimport { PresetController } from \"../helper/controllers/preset-controller\";\n\nexport type SkyImageLoading = \"lazy\" | \"eager\";\nexport type SkyImageDecoding = \"async\" | \"sync\" | \"auto\";\n\n/**\n * @element sky-image\n *\n * @summary Responsive image component with style controls, `srcset`, and optional auto `sizes`.\n *\n * @status stable\n * @since 1.0.0\n *\n * @documentation https://sky-ui.com/components/image\n *\n * @slot - No slots are exposed; image content is rendered internally.\n * @property {string} src - Primary image source URL.\n * @property {string} alt - Alternative text for accessibility.\n * @property {string} width - CSS width for the rendered image.\n * @property {string} height - CSS height for the rendered image.\n * @property {string | null} aspectRatio - CSS `aspect-ratio` value.\n * @property {string} borderRadius - CSS border radius.\n * @property {string} boxShadow - CSS box shadow.\n * @property {string} objectFit - CSS object-fit value.\n * @property {string} objectPosition - CSS object-position value.\n * @property {string} srcset - Responsive source set string.\n * @property {string} sizes - Responsive sizes string.\n * @property {boolean} autoSizes - Computes `sizes` from host width when true.\n * @property {SkyImageLoading} loading - Native loading hint.\n * @property {SkyImageDecoding} decoding - Native decoding hint.\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\n *\n * @csspart img - The underlying `<img>` element.\n *\n * @Behavior\n * - Renders a fallback visual placeholder when `src` is empty\n * - Supports automatic `sizes` updates using ResizeObserver\n * - Forwards native image loading and decoding hints\n *\n * @example\n * ```html\n * <sky-image\n * src=\"/images/photo-1280.jpg\"\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\n * alt=\"Responsive photo\"\n * width=\"100%\"\n * aspect-ratio=\"4/3\"\n * border-radius=\"8px\"\n * loading=\"lazy\"\n * ></sky-image>\n * ```\n * ```vue\n * <template>\n * <sky-image\n * src=\"/images/photo-1280.jpg\"\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\n * alt=\"Responsive photo\"\n * width=\"100%\"\n * aspect-ratio=\"4/3\"\n * border-radius=\"8px\"\n * ></sky-image>\n * </template>\n * ```\n * ```jsx\n * export default function Demo() {\n * return (\n * <sky-image\n * src=\"/images/photo-1280.jpg\"\n * srcset=\"/images/photo-640.jpg 640w, /images/photo-960.jpg 960w, /images/photo-1280.jpg 1280w\"\n * sizes=\"(max-width: 768px) 100vw, 50vw\"\n * alt=\"Responsive photo\"\n * width=\"100%\"\n * aspect-ratio=\"4/3\"\n * border-radius=\"8px\"\n * ></sky-image>\n * );\n * }\n * ```\n */\n@customElement(\"sky-image\")\nexport class SkyImage extends LitElement {\n @property({ type: String }) src = \"\";\n @property({ type: String }) alt = \"Image\";\n @property({ type: String }) width = \"auto\";\n @property({ type: String }) height = \"auto\";\n @property({ type: String }) aspectRatio: string | null = null;\n @property({ type: String }) borderRadius = \"0\";\n @property({ type: String }) boxShadow = \"none\";\n @property({ type: String }) objectFit = \"cover\";\n @property({ type: String }) objectPosition = \"center\";\n\n @property({ type: String }) srcset = \"\";\n @property({ type: String }) sizes = \"\";\n\n @property({ type: Boolean }) autoSizes = false;\n private _computedSizes = \"\";\n\n @property({ type: String }) loading: SkyImageLoading = \"lazy\";\n @property({ type: String }) decoding: SkyImageDecoding = \"async\";\n\n\n /** Named prop preset from nearest `sky-config-provider`. */\n @property({ type: String, reflect: true }) preset = \"\";\n\n private _presets = new PresetController(this);\n\n static override styles = css`\n :host {\n display: inline-block;\n max-width: 100%;\n }\n\n img,\n .placeholder {\n display: block;\n max-width: 100%;\n }\n\n .placeholder {\n background: var(--sky-hover-tertiary);\n }\n `;\n\n private _ro?: ResizeObserver;\n private _sizesRafId: number | null = null;\n private _updateSizes = () => {\n if (this._sizesRafId != null) return;\n this._sizesRafId = requestAnimationFrame(() => {\n this._sizesRafId = null;\n const w = Math.ceil(this.getBoundingClientRect().width);\n if (w > 0) {\n const next = `${w}px`;\n if (this._computedSizes === next) return;\n this._computedSizes = next;\n const img = this.renderRoot.querySelector(\"img\");\n if (img instanceof HTMLImageElement) img.sizes = next;\n }\n });\n };\n\n override connectedCallback(): void {\n super.connectedCallback();\n // firstUpdated runs only once; re-create the observer when the element\n // is moved back into the document after a disconnect.\n this._ensureAutoSizesObserver();\n }\n\n override firstUpdated(): void {\n this._ensureAutoSizesObserver();\n }\n\n private _ensureAutoSizesObserver(): void {\n if (!this.autoSizes) return;\n if (!this._ro) {\n this._ro = new ResizeObserver(this._updateSizes);\n this._ro.observe(this);\n }\n this._updateSizes();\n }\n\n override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n\n if (changed.has(\"autoSizes\")) {\n if (this.autoSizes) {\n this._ensureAutoSizesObserver();\n } else {\n this._ro?.disconnect();\n this._ro = undefined;\n this._computedSizes = \"\";\n const img = this.renderRoot.querySelector(\"img\");\n if (img instanceof HTMLImageElement) {\n if (this.sizes) img.sizes = this.sizes;\n else img.removeAttribute(\"sizes\");\n }\n }\n }\n\n if (!this._shouldResyncImageSource(changed)) return;\n this._resyncImageSource(\n changed.has(\"srcset\") && changed.get(\"srcset\") !== undefined,\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._ro?.disconnect();\n this._ro = undefined;\n if (this._sizesRafId != null) {\n cancelAnimationFrame(this._sizesRafId);\n this._sizesRafId = null;\n }\n }\n\n /** Browsers keep the resolved resource when only srcset/sizes attrs change on a live <img>. */\n private _shouldResyncImageSource(changed: PropertyValues<this>): boolean {\n if (!this.src?.trim() && !this.srcset?.trim()) return false;\n if (changed.has(\"src\") && changed.get(\"src\") !== undefined) return true;\n if (changed.has(\"srcset\") && changed.get(\"srcset\") !== undefined) return true;\n if (changed.has(\"sizes\") && changed.get(\"sizes\") !== undefined) return true;\n return false;\n }\n\n private _resolvedSizesAttr(): string {\n return this.autoSizes ? this._computedSizes : this.sizes || \"\";\n }\n\n /** `src` used on <img> — must stay in sync with `srcset` when the set changes. */\n private _displaySrc(): string {\n const explicit = this.src?.trim() ?? \"\";\n const srcset = this.srcset?.trim() ?? \"\";\n if (!explicit && !srcset) return \"\";\n if (!srcset) return explicit;\n return resolveImageFallbackSrc(explicit, srcset);\n }\n\n private _applyImgAttrs(img: HTMLImageElement, srcsetChanged: boolean): void {\n const srcset = this.srcset?.trim() ?? \"\";\n const sizesAttr = this._resolvedSizesAttr();\n const src = this._displaySrc();\n\n if (srcset) img.srcset = srcset;\n else img.removeAttribute(\"srcset\");\n\n if (sizesAttr) img.sizes = sizesAttr;\n else img.removeAttribute(\"sizes\");\n\n // When the candidate list changes, drop `src` first so the UA cannot keep the old resource.\n if (srcsetChanged) img.removeAttribute(\"src\");\n img.src = src;\n }\n\n private _resyncImageSource(srcsetChanged = false): void {\n const img = this.renderRoot.querySelector(\"img\");\n if (!(img instanceof HTMLImageElement)) return;\n\n const replacement = img.cloneNode(false) as HTMLImageElement;\n replacement.alt = this.alt;\n replacement.loading = this.loading;\n replacement.decoding = this.decoding;\n const style = img.getAttribute(\"style\");\n if (style) replacement.setAttribute(\"style\", style);\n replacement.setAttribute(\"part\", \"img\");\n\n this._applyImgAttrs(replacement, srcsetChanged);\n img.replaceWith(replacement);\n }\n\n override render() {\n const sizesAttr = this._resolvedSizesAttr();\n const displaySrc = this._displaySrc();\n const hasSrc = displaySrc !== \"\";\n\n const styleVars = {\n width: this.width,\n height: this.height,\n borderRadius: this.borderRadius,\n boxShadow: this.boxShadow,\n objectFit: this.objectFit,\n objectPosition: this.objectPosition,\n aspectRatio: this.aspectRatio ?? \"auto\",\n };\n\n if (!hasSrc) {\n return html`\n <div\n class=\"placeholder\"\n part=\"img\"\n style=${styleMap(styleVars)}\n aria-label=${this.alt}\n role=\"img\"\n ></div>\n `;\n }\n\n return html`\n <img\n src=${displaySrc}\n alt=${this.alt}\n srcset=${this.srcset || nothing}\n sizes=${sizesAttr || nothing}\n loading=${this.loading}\n decoding=${this.decoding}\n style=${styleMap(styleVars)}\n part=\"img\"\n />\n `;\n }\n}\n"],"names":["SkyImage","LitElement","constructor","super","arguments","this","src","alt","width","height","aspectRatio","borderRadius","boxShadow","objectFit","objectPosition","srcset","sizes","autoSizes","_computedSizes","loading","decoding","preset","_presets","PresetController","_sizesRafId","_updateSizes","requestAnimationFrame","w","Math","ceil","getBoundingClientRect","next","img","renderRoot","querySelector","HTMLImageElement","connectedCallback","_ensureAutoSizesObserver","firstUpdated","_ro","ResizeObserver","observe","updated","changed","has","disconnect","removeAttribute","_shouldResyncImageSource","_resyncImageSource","get","disconnectedCallback","cancelAnimationFrame","trim","_resolvedSizesAttr","_displaySrc","explicit","resolveImageFallbackSrc","_applyImgAttrs","srcsetChanged","sizesAttr","replacement","cloneNode","style","getAttribute","setAttribute","replaceWith","render","displaySrc","hasSrc","styleVars","html","nothing","styleMap","styles","css","__decorateClass","property","type","String","prototype","Boolean","reflect","customElement"],"mappings":"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"}
|