@sky.ui/core 0.0.57 → 0.0.58
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 +4322 -4322
- package/dist/helper/controllers/animation-controller.js.map +1 -1
- package/dist/helper/controllers/preset-controller.js.map +1 -1
- package/dist/helper/iconify-offline.js.map +1 -1
- package/dist/helper/styles/focus-ring.js.map +1 -1
- package/dist/helper/utils/component-presets.js.map +1 -1
- package/dist/helper/utils/size-resolver.js.map +1 -1
- package/dist/sky-avatar/sky-avatar.js.map +1 -1
- package/dist/sky-chip/sky-chip.js.map +1 -1
- package/dist/sky-flexbox/sky-flexbox.js.map +1 -1
- package/dist/sky-form/sky-form.js.map +1 -1
- package/dist/sky-image/sky-image.js.map +1 -1
- package/dist/sky-image/srcset-utils.js.map +1 -1
- package/dist/sky-input/sky-input.js.map +1 -1
- package/dist/sky-list/sky-list.js.map +1 -1
- package/dist/sky-listitem/sky-listitem.js.map +1 -1
- package/dist/sky-simple-table/sky-simple-table.js.map +1 -1
- package/dist/sky-theme-provider/sky-theme-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animation-controller.js","sources":["../../../src/helper/controllers/animation-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\r\n\r\n/** Fill mode for WAAPI (KeyframeEffectOptions). */\r\nexport type FillMode = 'none' | 'forwards' | 'backwards' | 'both';\r\n\r\n/** Playback direction for WAAPI (KeyframeEffectOptions). */\r\nexport type AnimationDirection =\r\n 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';\r\n\r\n/**\r\n * Options for a single animation run (Web Animations API).\r\n * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect#options\r\n */\r\nexport interface AnimationOptions {\r\n /** Duration in milliseconds. */\r\n duration?: number;\r\n /** Delay before start in milliseconds. */\r\n delay?: number;\r\n /** Easing function (CSS easing or cubic-bezier). */\r\n easing?: string;\r\n /** Fill mode: 'none' | 'forwards' | 'backwards' | 'both'. */\r\n fill?: FillMode;\r\n /** Number of iterations (default 1). */\r\n iterations?: number;\r\n /** Playback direction. */\r\n direction?: AnimationDirection;\r\n /** End delay in milliseconds. */\r\n endDelay?: number;\r\n /** Unique id for the animation (useful for canceling by id). */\r\n id?: string;\r\n}\r\n\r\n/**\r\n * Keyframes format accepted by Element.animate().\r\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats\r\n */\r\nexport type AnimationKeyframes = Keyframe[] | PropertyIndexedKeyframes;\r\n\r\n/**\r\n * Configuration for the controller defaults (used when not overridden per call).\r\n */\r\nexport interface AnimationControllerConfig {\r\n defaultDuration?: number;\r\n defaultEasing?: string;\r\n defaultFill?: FillMode;\r\n}\r\n\r\n/** Direction for slide presets. */\r\nexport type SlideDirection = 'up' | 'down' | 'left' | 'right';\r\n\r\n/**\r\n * AnimationController – consistent animations via the Web Animations API (WAAPI).\r\n *\r\n * Use as a Lit ReactiveController so components get:\r\n * - Shared defaults (duration, easing, fill)\r\n * - Preset helpers: fadeIn, fadeOut, slideIn, slideOut, scaleIn, scaleOut\r\n * - Low-level animate() for custom keyframes\r\n * - Automatic cancellation of running animations when the host disconnects\r\n *\r\n * @example\r\n * ```ts\r\n * class MyPanel extends LitElement {\r\n * animation = new AnimationController(this, { defaultDuration: 200, defaultEasing: 'ease-out' });\r\n *\r\n * async open() {\r\n * await this.animation.fadeIn(this.panelEl);\r\n * }\r\n *\r\n * async close() {\r\n * await this.animation.fadeOut(this.panelEl);\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class AnimationController implements ReactiveController {\r\n readonly host: ReactiveControllerHost;\r\n private readonly config: Required<AnimationControllerConfig>;\r\n private readonly running = new Set<Animation>();\r\n\r\n constructor(\r\n host: ReactiveControllerHost,\r\n config: AnimationControllerConfig = {}\r\n ) {\r\n this.host = host;\r\n this.config = {\r\n defaultDuration: config.defaultDuration ?? 200,\r\n defaultEasing: config.defaultEasing ?? 'ease-out',\r\n defaultFill: config.defaultFill ?? 'forwards',\r\n };\r\n host.addController(this);\r\n }\r\n\r\n hostDisconnected(): void {\r\n this.running.forEach((a) => {\r\n try {\r\n a.cancel();\r\n } catch {\r\n // ignore\r\n }\r\n });\r\n this.running.clear();\r\n }\r\n\r\n /**\r\n * Run a WAAPI animation on an element. Returns the Animation for chaining or await .finished.\r\n * Running animations are tracked and cancelled when the host disconnects.\r\n */\r\n animate(\r\n element: Element | null | undefined,\r\n keyframes: AnimationKeyframes,\r\n options: AnimationOptions = {}\r\n ): Animation | null {\r\n if (!element) return null;\r\n\r\n const duration = options.duration ?? this.config.defaultDuration;\r\n const easing = options.easing ?? this.config.defaultEasing;\r\n const fill = options.fill ?? this.config.defaultFill;\r\n\r\n const opts: KeyframeAnimationOptions = {\r\n duration,\r\n delay: options.delay ?? 0,\r\n easing,\r\n fill,\r\n iterations: options.iterations ?? 1,\r\n direction: options.direction ?? 'normal',\r\n endDelay: options.endDelay ?? 0,\r\n id: options.id,\r\n };\r\n\r\n const animation = element.animate(keyframes as Keyframe[], opts);\r\n this.running.add(animation);\r\n animation.finished\r\n .then(() => this.running.delete(animation))\r\n .catch(() => this.running.delete(animation));\r\n return animation;\r\n }\r\n\r\n /**\r\n * Fade in: opacity 0 → 1.\r\n */\r\n fadeIn(\r\n element: Element | null | undefined,\r\n options: AnimationOptions = {}\r\n ): Animation | null {\r\n return this.animate(element, [{ opacity: 0 }, { opacity: 1 }], options);\r\n }\r\n\r\n /**\r\n * Fade out: opacity 1 → 0.\r\n */\r\n fadeOut(\r\n element: Element | null | undefined,\r\n options: AnimationOptions = {}\r\n ): Animation | null {\r\n return this.animate(element, [{ opacity: 1 }, { opacity: 0 }], options);\r\n }\r\n\r\n /**\r\n * Slide in from the given direction (translate from off-screen to 0).\r\n */\r\n slideIn(\r\n element: Element | null | undefined,\r\n options: AnimationOptions & { direction?: SlideDirection } = {}\r\n ): Animation | null {\r\n const dir = options.direction ?? 'down';\r\n const [fromX, fromY] = slideOffset(dir, true);\r\n const keyframes: Keyframe[] = [\r\n { transform: `translate(${fromX}, ${fromY})` },\r\n { transform: 'translate(0, 0)' },\r\n ];\r\n return this.animate(element, keyframes, options);\r\n }\r\n\r\n /**\r\n * Slide out toward the given direction (translate from 0 to off-screen).\r\n */\r\n slideOut(\r\n element: Element | null | undefined,\r\n options: AnimationOptions & { direction?: SlideDirection } = {}\r\n ): Animation | null {\r\n const dir = options.direction ?? 'up';\r\n const [toX, toY] = slideOffset(dir, false);\r\n const keyframes: Keyframe[] = [\r\n { transform: 'translate(0, 0)' },\r\n { transform: `translate(${toX}, ${toY})` },\r\n ];\r\n return this.animate(element, keyframes, options);\r\n }\r\n\r\n /**\r\n * Scale in: scale(0) or scale(0.9) → scale(1). Optionally combine with opacity.\r\n */\r\n scaleIn(\r\n element: Element | null | undefined,\r\n options: AnimationOptions & { fromScale?: number; opacity?: boolean } = {}\r\n ): Animation | null {\r\n const from = options.fromScale ?? 0.95;\r\n const withOpacity = options.opacity !== false;\r\n const keyframes: Keyframe[] = withOpacity\r\n ? [\r\n { transform: `scale(${from})`, opacity: 0 },\r\n { transform: 'scale(1)', opacity: 1 },\r\n ]\r\n : [{ transform: `scale(${from})` }, { transform: 'scale(1)' }];\r\n return this.animate(element, keyframes, options);\r\n }\r\n\r\n /**\r\n * Scale out: scale(1) → scale(0.95) or custom. Optionally combine with opacity.\r\n */\r\n scaleOut(\r\n element: Element | null | undefined,\r\n options: AnimationOptions & { toScale?: number; opacity?: boolean } = {}\r\n ): Animation | null {\r\n const to = options.toScale ?? 0.95;\r\n const withOpacity = options.opacity !== false;\r\n const keyframes: Keyframe[] = withOpacity\r\n ? [\r\n { transform: 'scale(1)', opacity: 1 },\r\n { transform: `scale(${to})`, opacity: 0 },\r\n ]\r\n : [{ transform: 'scale(1)' }, { transform: `scale(${to})` }];\r\n return this.animate(element, keyframes, options);\r\n }\r\n\r\n /**\r\n * Cancel a specific animation (e.g. one you stored from animate() or a preset).\r\n */\r\n cancel(animation: Animation): void {\r\n animation.cancel();\r\n this.running.delete(animation);\r\n }\r\n\r\n /**\r\n * Cancel all animations currently running for this controller.\r\n */\r\n cancelAll(): void {\r\n this.running.forEach((a) => {\r\n try {\r\n a.cancel();\r\n } catch {\r\n // ignore\r\n }\r\n });\r\n this.running.clear();\r\n }\r\n}\r\n\r\nfunction slideOffset(\r\n direction: SlideDirection,\r\n isIn: boolean\r\n): [string, string] {\r\n const pct = '100%';\r\n switch (direction) {\r\n case 'up':\r\n return ['0', isIn ? pct : `-${pct}`];\r\n case 'down':\r\n return ['0', isIn ? `-${pct}` : pct];\r\n case 'left':\r\n return [isIn ? pct : `-${pct}`, '0'];\r\n case 'right':\r\n return [isIn ? `-${pct}` : pct, '0'];\r\n default:\r\n return ['0', isIn ? `-${pct}` : pct];\r\n }\r\n}\r\n"],"names":["AnimationController","constructor","host","config","this","running","Set","defaultDuration","defaultEasing","defaultFill","addController","hostDisconnected","forEach","a","cancel","clear","animate","element","keyframes","options","duration","easing","fill","opts","delay","iterations","direction","endDelay","id","animation","add","finished","then","delete","catch","fadeIn","opacity","fadeOut","slideIn","dir","fromX","fromY","slideOffset","transform","slideOut","toX","toY","scaleIn","from","fromScale","scaleOut","to","toScale","cancelAll","isIn","pct"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"animation-controller.js","sources":["../../../src/helper/controllers/animation-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/** Fill mode for WAAPI (KeyframeEffectOptions). */\nexport type FillMode = 'none' | 'forwards' | 'backwards' | 'both';\n\n/** Playback direction for WAAPI (KeyframeEffectOptions). */\nexport type AnimationDirection =\n 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';\n\n/**\n * Options for a single animation run (Web Animations API).\n * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect#options\n */\nexport interface AnimationOptions {\n /** Duration in milliseconds. */\n duration?: number;\n /** Delay before start in milliseconds. */\n delay?: number;\n /** Easing function (CSS easing or cubic-bezier). */\n easing?: string;\n /** Fill mode: 'none' | 'forwards' | 'backwards' | 'both'. */\n fill?: FillMode;\n /** Number of iterations (default 1). */\n iterations?: number;\n /** Playback direction. */\n direction?: AnimationDirection;\n /** End delay in milliseconds. */\n endDelay?: number;\n /** Unique id for the animation (useful for canceling by id). */\n id?: string;\n}\n\n/**\n * Keyframes format accepted by Element.animate().\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats\n */\nexport type AnimationKeyframes = Keyframe[] | PropertyIndexedKeyframes;\n\n/**\n * Configuration for the controller defaults (used when not overridden per call).\n */\nexport interface AnimationControllerConfig {\n defaultDuration?: number;\n defaultEasing?: string;\n defaultFill?: FillMode;\n}\n\n/** Direction for slide presets. */\nexport type SlideDirection = 'up' | 'down' | 'left' | 'right';\n\n/**\n * AnimationController – consistent animations via the Web Animations API (WAAPI).\n *\n * Use as a Lit ReactiveController so components get:\n * - Shared defaults (duration, easing, fill)\n * - Preset helpers: fadeIn, fadeOut, slideIn, slideOut, scaleIn, scaleOut\n * - Low-level animate() for custom keyframes\n * - Automatic cancellation of running animations when the host disconnects\n *\n * @example\n * ```ts\n * class MyPanel extends LitElement {\n * animation = new AnimationController(this, { defaultDuration: 200, defaultEasing: 'ease-out' });\n *\n * async open() {\n * await this.animation.fadeIn(this.panelEl);\n * }\n *\n * async close() {\n * await this.animation.fadeOut(this.panelEl);\n * }\n * }\n * ```\n */\nexport class AnimationController implements ReactiveController {\n readonly host: ReactiveControllerHost;\n private readonly config: Required<AnimationControllerConfig>;\n private readonly running = new Set<Animation>();\n\n constructor(\n host: ReactiveControllerHost,\n config: AnimationControllerConfig = {}\n ) {\n this.host = host;\n this.config = {\n defaultDuration: config.defaultDuration ?? 200,\n defaultEasing: config.defaultEasing ?? 'ease-out',\n defaultFill: config.defaultFill ?? 'forwards',\n };\n host.addController(this);\n }\n\n hostDisconnected(): void {\n this.running.forEach((a) => {\n try {\n a.cancel();\n } catch {\n // ignore\n }\n });\n this.running.clear();\n }\n\n /**\n * Run a WAAPI animation on an element. Returns the Animation for chaining or await .finished.\n * Running animations are tracked and cancelled when the host disconnects.\n */\n animate(\n element: Element | null | undefined,\n keyframes: AnimationKeyframes,\n options: AnimationOptions = {}\n ): Animation | null {\n if (!element) return null;\n\n const duration = options.duration ?? this.config.defaultDuration;\n const easing = options.easing ?? this.config.defaultEasing;\n const fill = options.fill ?? this.config.defaultFill;\n\n const opts: KeyframeAnimationOptions = {\n duration,\n delay: options.delay ?? 0,\n easing,\n fill,\n iterations: options.iterations ?? 1,\n direction: options.direction ?? 'normal',\n endDelay: options.endDelay ?? 0,\n id: options.id,\n };\n\n const animation = element.animate(keyframes as Keyframe[], opts);\n this.running.add(animation);\n animation.finished\n .then(() => this.running.delete(animation))\n .catch(() => this.running.delete(animation));\n return animation;\n }\n\n /**\n * Fade in: opacity 0 → 1.\n */\n fadeIn(\n element: Element | null | undefined,\n options: AnimationOptions = {}\n ): Animation | null {\n return this.animate(element, [{ opacity: 0 }, { opacity: 1 }], options);\n }\n\n /**\n * Fade out: opacity 1 → 0.\n */\n fadeOut(\n element: Element | null | undefined,\n options: AnimationOptions = {}\n ): Animation | null {\n return this.animate(element, [{ opacity: 1 }, { opacity: 0 }], options);\n }\n\n /**\n * Slide in from the given direction (translate from off-screen to 0).\n */\n slideIn(\n element: Element | null | undefined,\n options: AnimationOptions & { direction?: SlideDirection } = {}\n ): Animation | null {\n const dir = options.direction ?? 'down';\n const [fromX, fromY] = slideOffset(dir, true);\n const keyframes: Keyframe[] = [\n { transform: `translate(${fromX}, ${fromY})` },\n { transform: 'translate(0, 0)' },\n ];\n return this.animate(element, keyframes, options);\n }\n\n /**\n * Slide out toward the given direction (translate from 0 to off-screen).\n */\n slideOut(\n element: Element | null | undefined,\n options: AnimationOptions & { direction?: SlideDirection } = {}\n ): Animation | null {\n const dir = options.direction ?? 'up';\n const [toX, toY] = slideOffset(dir, false);\n const keyframes: Keyframe[] = [\n { transform: 'translate(0, 0)' },\n { transform: `translate(${toX}, ${toY})` },\n ];\n return this.animate(element, keyframes, options);\n }\n\n /**\n * Scale in: scale(0) or scale(0.9) → scale(1). Optionally combine with opacity.\n */\n scaleIn(\n element: Element | null | undefined,\n options: AnimationOptions & { fromScale?: number; opacity?: boolean } = {}\n ): Animation | null {\n const from = options.fromScale ?? 0.95;\n const withOpacity = options.opacity !== false;\n const keyframes: Keyframe[] = withOpacity\n ? [\n { transform: `scale(${from})`, opacity: 0 },\n { transform: 'scale(1)', opacity: 1 },\n ]\n : [{ transform: `scale(${from})` }, { transform: 'scale(1)' }];\n return this.animate(element, keyframes, options);\n }\n\n /**\n * Scale out: scale(1) → scale(0.95) or custom. Optionally combine with opacity.\n */\n scaleOut(\n element: Element | null | undefined,\n options: AnimationOptions & { toScale?: number; opacity?: boolean } = {}\n ): Animation | null {\n const to = options.toScale ?? 0.95;\n const withOpacity = options.opacity !== false;\n const keyframes: Keyframe[] = withOpacity\n ? [\n { transform: 'scale(1)', opacity: 1 },\n { transform: `scale(${to})`, opacity: 0 },\n ]\n : [{ transform: 'scale(1)' }, { transform: `scale(${to})` }];\n return this.animate(element, keyframes, options);\n }\n\n /**\n * Cancel a specific animation (e.g. one you stored from animate() or a preset).\n */\n cancel(animation: Animation): void {\n animation.cancel();\n this.running.delete(animation);\n }\n\n /**\n * Cancel all animations currently running for this controller.\n */\n cancelAll(): void {\n this.running.forEach((a) => {\n try {\n a.cancel();\n } catch {\n // ignore\n }\n });\n this.running.clear();\n }\n}\n\nfunction slideOffset(\n direction: SlideDirection,\n isIn: boolean\n): [string, string] {\n const pct = '100%';\n switch (direction) {\n case 'up':\n return ['0', isIn ? pct : `-${pct}`];\n case 'down':\n return ['0', isIn ? `-${pct}` : pct];\n case 'left':\n return [isIn ? pct : `-${pct}`, '0'];\n case 'right':\n return [isIn ? `-${pct}` : pct, '0'];\n default:\n return ['0', isIn ? `-${pct}` : pct];\n }\n}\n"],"names":["AnimationController","constructor","host","config","this","running","Set","defaultDuration","defaultEasing","defaultFill","addController","hostDisconnected","forEach","a","cancel","clear","animate","element","keyframes","options","duration","easing","fill","opts","delay","iterations","direction","endDelay","id","animation","add","finished","then","delete","catch","fadeIn","opacity","fadeOut","slideIn","dir","fromX","fromY","slideOffset","transform","slideOut","toX","toY","scaleIn","from","fromScale","scaleOut","to","toScale","cancelAll","isIn","pct"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-controller.js","sources":["../../../src/helper/controllers/preset-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { ContextConsumer } from \"@lit/context\";\nimport {\n applyPresetLayer,\n captureBuiltInPropSnapshot,\n captureInitialUserAttributes,\n clearAppliedPresetLayer,\n configContext,\n finalizeBuiltInPropSnapshot,\n findNearestPresetsConfig,\n getPresetLayer,\n isSkyCompositeInternal,\n type ComponentPresetKey,\n type ComponentPresetsConfig,\n} from \"../utils/component-presets.js\";\n\ntype PresetHost = ReactiveControllerHost &\n HTMLElement & {\n preset?: string;\n };\n\nexport type PresetControllerOptions = {\n /**\n * Config key under `defaults` / `presets`.\n * Prefer the custom element tag (`sky-button`). Defaults to `host.localName`.\n * Short aliases (`button`) resolve to the same bag.\n */\n componentKey?: ComponentPresetKey;\n /**\n * When true, clear preset-applied props (e.g. `sky-button` inside a group\n * that owns visual knobs).\n */\n isPaused?: () => boolean;\n /** Called after props are applied / cleared (e.g. mark style cache dirty). */\n onChange?: () => void;\n /** Prop names never written from presets. */\n skipKeys?: string[];\n};\n\n/**\n * Consumes nearest `<sky-config-provider>` and **assigns every prop** from the\n * active defaults + named `preset` bag onto the host (unless the user set that\n * attribute). Changing the provider config or `preset=\"…\"` re-applies the bag.\n *\n * Hosts rendered inside another `sky-*` shadow tree (composite internals) skip\n * presets entirely so app defaults do not restyle library chrome. Slotted /\n * light-DOM instances still receive them. See `isSkyCompositeInternal`.\n *\n * Timing note (Lit reactive-element ≥2): controller `hostUpdate` runs *after*\n * the host's `willUpdate` and *before* `update`/render. Components that derive\n * paint from props must rebuild in `update()`/`updated()` (not `willUpdate`),\n * or rely on the follow-up `requestUpdate` scheduled when props change.\n */\nexport class PresetController implements ReactiveController {\n private readonly _consumer: ContextConsumer<\n typeof configContext,\n PresetHost\n >;\n\n /** Bumps when context / applied props change. */\n revision = 0;\n\n private _lastPresetName = \"\";\n private _lastPaused = false;\n private _applyQueued = false;\n\n constructor(\n private readonly host: PresetHost,\n private readonly options: PresetControllerOptions = {},\n ) {\n this._consumer = new ContextConsumer(this.host, {\n context: configContext,\n subscribe: true,\n callback: () => {\n this._scheduleApply();\n },\n });\n this.host.addController(this);\n // Snapshot @property initials now (before Vue/React assign host props).\n // Keep this field initializer after presettable props on the host class.\n captureBuiltInPropSnapshot(this.host);\n }\n\n /** Tag name (or explicit override) used to look up config bags. */\n get componentKey(): ComponentPresetKey {\n return this.options.componentKey || this.host.localName || \"\";\n }\n\n /** Merged presets from context, or nearest provider via DOM walk. */\n get config(): ComponentPresetsConfig | null {\n const fromCtx = this._consumer.value;\n if (fromCtx && (fromCtx.defaults || fromCtx.presets)) {\n return fromCtx;\n }\n return findNearestPresetsConfig(this.host) ?? fromCtx ?? null;\n }\n\n hostConnected(): void {\n finalizeBuiltInPropSnapshot(this.host);\n captureInitialUserAttributes(this.host);\n this._scheduleApply();\n }\n\n hostUpdate(): void {\n const presetName = String(this.host.preset ?? \"\");\n const paused = this._isPaused();\n if (\n presetName !== this._lastPresetName ||\n paused !== this._lastPaused ||\n this._applyQueued\n ) {\n this._applyNow();\n }\n }\n\n hostDisconnected(): void {\n // ContextConsumer cleans up with the host.\n }\n\n /** Force re-apply (e.g. after imperative config tweaks). */\n reapply(): void {\n this._scheduleApply();\n }\n\n /**\n * Skip applying when `isPaused` says so, or when this host is sky composite\n * chrome (inside another `sky-*` shadow root).\n */\n private _isPaused(): boolean {\n return (\n Boolean(this.options.isPaused?.()) || isSkyCompositeInternal(this.host)\n );\n }\n\n private _scheduleApply(): void {\n this._applyQueued = true;\n this.revision++;\n this.host.requestUpdate();\n }\n\n private _applyNow(): void {\n this._applyQueued = false;\n const presetName = String(this.host.preset ?? \"\");\n const paused = this._isPaused();\n this._lastPresetName = presetName;\n this._lastPaused = paused;\n\n let changed = false;\n if (paused) {\n changed = clearAppliedPresetLayer(this.host);\n } else {\n const layer = getPresetLayer(\n this.config,\n this.componentKey,\n presetName || null,\n );\n changed = applyPresetLayer(this.host, layer, {\n skipKeys: this.options.skipKeys,\n });\n }\n\n if (changed) {\n this.revision++;\n this.options.onChange?.();\n // Props were written during hostUpdate (after willUpdate). Lit will not\n // enqueue another update while one is pending, and the mid-update writes\n // are wiped from the pending-change map at end of update — so schedule a\n // follow-up pass for hosts that still resolve styles in willUpdate.\n queueMicrotask(() => this.host.requestUpdate());\n }\n }\n}\n"],"names":["PresetController","constructor","host","options","this","revision","_lastPresetName","_lastPaused","_applyQueued","_consumer","ContextConsumer","context","configContext","subscribe","callback","_scheduleApply","addController","captureBuiltInPropSnapshot","componentKey","localName","config","fromCtx","value","defaults","presets","findNearestPresetsConfig","hostConnected","finalizeBuiltInPropSnapshot","captureInitialUserAttributes","hostUpdate","presetName","String","preset","paused","_isPaused","_applyNow","hostDisconnected","reapply","Boolean","isPaused","isSkyCompositeInternal","requestUpdate","changed","clearAppliedPresetLayer","layer","getPresetLayer","applyPresetLayer","skipKeys","onChange","queueMicrotask"],"mappings":"iVAqDO,MAAMA,EAaX,WAAAC,CACmBC,EACAC,EAAmC,IADnCC,KAAAF,KAAAA,EACAE,KAAAD,QAAAA,EARnBC,KAAAC,SAAW,EAEXD,KAAQE,gBAAkB,GAC1BF,KAAQG,aAAc,EACtBH,KAAQI,cAAe,EAMrBJ,KAAKK,UAAY,IAAIC,EAAgBN,KAAKF,KAAM,CAC9CS,QAASC,EACTC,WAAW,EACXC,SAAU,KACRV,KAAKW,oBAGTX,KAAKF,KAAKc,cAAcZ,MAGxBa,EAA2Bb,KAAKF,KAClC,CAGA,gBAAIgB,GACF,OAAOd,KAAKD,QAAQe,cAAgBd,KAAKF,KAAKiB,WAAa,EAC7D,CAGA,UAAIC,GACF,MAAMC,EAAUjB,KAAKK,UAAUa,MAC/B,OAAID,IAAYA,EAAQE,UAAYF,EAAQG,SACnCH,EAEFI,EAAyBrB,KAAKF,OAASmB,GAAW,IAC3D,CAEA,aAAAK,GACEC,EAA4BvB,KAAKF,MACjC0B,EAA6BxB,KAAKF,MAClCE,KAAKW,gBACP,CAEA,UAAAc,GACE,MAAMC,EAAaC,OAAO3B,KAAKF,KAAK8B,QAAU,IACxCC,EAAS7B,KAAK8B,aAElBJ,IAAe1B,KAAKE,iBACpB2B,IAAW7B,KAAKG,aAChBH,KAAKI,eAELJ,KAAK+B,WAET,CAEA,gBAAAC,GAEA,CAGA,OAAAC,GACEjC,KAAKW,gBACP,CAMQ,SAAAmB,GACN,OACEI,QAAQlC,KAAKD,QAAQoC,eAAiBC,EAAuBpC,KAAKF,KAEtE,CAEQ,cAAAa,GACNX,KAAKI,cAAe,EACpBJ,KAAKC,WACLD,KAAKF,KAAKuC,eACZ,CAEQ,SAAAN,GACN/B,KAAKI,cAAe,EACpB,MAAMsB,EAAaC,OAAO3B,KAAKF,KAAK8B,QAAU,IACxCC,EAAS7B,KAAK8B,YACpB9B,KAAKE,gBAAkBwB,EACvB1B,KAAKG,YAAc0B,EAEnB,IAAIS,GAAU,EACd,GAAIT,EACFS,EAAUC,EAAwBvC,KAAKF,UAClC,CACL,MAAM0C,EAAQC,EACZzC,KAAKgB,OACLhB,KAAKc,aACLY,GAAc,MAEhBY,EAAUI,EAAiB1C,KAAKF,KAAM0C,EAAO,CAC3CG,SAAU3C,KAAKD,QAAQ4C,UAE3B,CAEIL,IACFtC,KAAKC,WACLD,KAAKD,QAAQ6C,aAKbC,eAAe,IAAM7C,KAAKF,KAAKuC,iBAEnC"}
|
|
1
|
+
{"version":3,"file":"preset-controller.js","sources":["../../../src/helper/controllers/preset-controller.ts"],"sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from \"lit\";\r\nimport { ContextConsumer } from \"@lit/context\";\r\nimport {\r\n applyPresetLayer,\r\n captureBuiltInPropSnapshot,\r\n captureInitialUserAttributes,\r\n clearAppliedPresetLayer,\r\n configContext,\r\n finalizeBuiltInPropSnapshot,\r\n findNearestPresetsConfig,\r\n getPresetLayer,\r\n isSkyCompositeInternal,\r\n type ComponentPresetKey,\r\n type ComponentPresetsConfig,\r\n} from \"../utils/component-presets.js\";\r\n\r\ntype PresetHost = ReactiveControllerHost &\r\n HTMLElement & {\r\n preset?: string;\r\n };\r\n\r\nexport type PresetControllerOptions = {\r\n /**\r\n * Config key under `defaults` / `presets`.\r\n * Prefer the custom element tag (`sky-button`). Defaults to `host.localName`.\r\n * Short aliases (`button`) resolve to the same bag.\r\n */\r\n componentKey?: ComponentPresetKey;\r\n /**\r\n * When true, clear preset-applied props (e.g. `sky-button` inside a group\r\n * that owns visual knobs).\r\n */\r\n isPaused?: () => boolean;\r\n /** Called after props are applied / cleared (e.g. mark style cache dirty). */\r\n onChange?: () => void;\r\n /** Prop names never written from presets. */\r\n skipKeys?: string[];\r\n};\r\n\r\n/**\r\n * Consumes nearest `<sky-config-provider>` and **assigns every prop** from the\r\n * active defaults + named `preset` bag onto the host (unless the user set that\r\n * attribute). Changing the provider config or `preset=\"…\"` re-applies the bag.\r\n *\r\n * Hosts rendered inside another `sky-*` shadow tree (composite internals) skip\r\n * presets entirely so app defaults do not restyle library chrome. Slotted /\r\n * light-DOM instances still receive them. See `isSkyCompositeInternal`.\r\n *\r\n * Timing note (Lit reactive-element ≥2): controller `hostUpdate` runs *after*\r\n * the host's `willUpdate` and *before* `update`/render. Components that derive\r\n * paint from props must rebuild in `update()`/`updated()` (not `willUpdate`),\r\n * or rely on the follow-up `requestUpdate` scheduled when props change.\r\n */\r\nexport class PresetController implements ReactiveController {\r\n private readonly _consumer: ContextConsumer<\r\n typeof configContext,\r\n PresetHost\r\n >;\r\n\r\n /** Bumps when context / applied props change. */\r\n revision = 0;\r\n\r\n private _lastPresetName = \"\";\r\n private _lastPaused = false;\r\n private _applyQueued = false;\r\n\r\n constructor(\r\n private readonly host: PresetHost,\r\n private readonly options: PresetControllerOptions = {},\r\n ) {\r\n this._consumer = new ContextConsumer(this.host, {\r\n context: configContext,\r\n subscribe: true,\r\n callback: () => {\r\n this._scheduleApply();\r\n },\r\n });\r\n this.host.addController(this);\r\n // Snapshot @property initials now (before Vue/React assign host props).\r\n // Keep this field initializer after presettable props on the host class.\r\n captureBuiltInPropSnapshot(this.host);\r\n }\r\n\r\n /** Tag name (or explicit override) used to look up config bags. */\r\n get componentKey(): ComponentPresetKey {\r\n return this.options.componentKey || this.host.localName || \"\";\r\n }\r\n\r\n /** Merged presets from context, or nearest provider via DOM walk. */\r\n get config(): ComponentPresetsConfig | null {\r\n const fromCtx = this._consumer.value;\r\n if (fromCtx && (fromCtx.defaults || fromCtx.presets)) {\r\n return fromCtx;\r\n }\r\n return findNearestPresetsConfig(this.host) ?? fromCtx ?? null;\r\n }\r\n\r\n hostConnected(): void {\r\n finalizeBuiltInPropSnapshot(this.host);\r\n captureInitialUserAttributes(this.host);\r\n this._scheduleApply();\r\n }\r\n\r\n hostUpdate(): void {\r\n const presetName = String(this.host.preset ?? \"\");\r\n const paused = this._isPaused();\r\n if (\r\n presetName !== this._lastPresetName ||\r\n paused !== this._lastPaused ||\r\n this._applyQueued\r\n ) {\r\n this._applyNow();\r\n }\r\n }\r\n\r\n hostDisconnected(): void {\r\n // ContextConsumer cleans up with the host.\r\n }\r\n\r\n /** Force re-apply (e.g. after imperative config tweaks). */\r\n reapply(): void {\r\n this._scheduleApply();\r\n }\r\n\r\n /**\r\n * Skip applying when `isPaused` says so, or when this host is sky composite\r\n * chrome (inside another `sky-*` shadow root).\r\n */\r\n private _isPaused(): boolean {\r\n return (\r\n Boolean(this.options.isPaused?.()) || isSkyCompositeInternal(this.host)\r\n );\r\n }\r\n\r\n private _scheduleApply(): void {\r\n this._applyQueued = true;\r\n this.revision++;\r\n this.host.requestUpdate();\r\n }\r\n\r\n private _applyNow(): void {\r\n this._applyQueued = false;\r\n const presetName = String(this.host.preset ?? \"\");\r\n const paused = this._isPaused();\r\n this._lastPresetName = presetName;\r\n this._lastPaused = paused;\r\n\r\n let changed = false;\r\n if (paused) {\r\n changed = clearAppliedPresetLayer(this.host);\r\n } else {\r\n const layer = getPresetLayer(\r\n this.config,\r\n this.componentKey,\r\n presetName || null,\r\n );\r\n changed = applyPresetLayer(this.host, layer, {\r\n skipKeys: this.options.skipKeys,\r\n });\r\n }\r\n\r\n if (changed) {\r\n this.revision++;\r\n this.options.onChange?.();\r\n // Props were written during hostUpdate (after willUpdate). Lit will not\r\n // enqueue another update while one is pending, and the mid-update writes\r\n // are wiped from the pending-change map at end of update — so schedule a\r\n // follow-up pass for hosts that still resolve styles in willUpdate.\r\n queueMicrotask(() => this.host.requestUpdate());\r\n }\r\n }\r\n}\r\n"],"names":["PresetController","constructor","host","options","this","revision","_lastPresetName","_lastPaused","_applyQueued","_consumer","ContextConsumer","context","configContext","subscribe","callback","_scheduleApply","addController","captureBuiltInPropSnapshot","componentKey","localName","config","fromCtx","value","defaults","presets","findNearestPresetsConfig","hostConnected","finalizeBuiltInPropSnapshot","captureInitialUserAttributes","hostUpdate","presetName","String","preset","paused","_isPaused","_applyNow","hostDisconnected","reapply","Boolean","isPaused","isSkyCompositeInternal","requestUpdate","changed","clearAppliedPresetLayer","layer","getPresetLayer","applyPresetLayer","skipKeys","onChange","queueMicrotask"],"mappings":"iVAqDO,MAAMA,EAaX,WAAAC,CACmBC,EACAC,EAAmC,IADnCC,KAAAF,KAAAA,EACAE,KAAAD,QAAAA,EARnBC,KAAAC,SAAW,EAEXD,KAAQE,gBAAkB,GAC1BF,KAAQG,aAAc,EACtBH,KAAQI,cAAe,EAMrBJ,KAAKK,UAAY,IAAIC,EAAgBN,KAAKF,KAAM,CAC9CS,QAASC,EACTC,WAAW,EACXC,SAAU,KACRV,KAAKW,oBAGTX,KAAKF,KAAKc,cAAcZ,MAGxBa,EAA2Bb,KAAKF,KAClC,CAGA,gBAAIgB,GACF,OAAOd,KAAKD,QAAQe,cAAgBd,KAAKF,KAAKiB,WAAa,EAC7D,CAGA,UAAIC,GACF,MAAMC,EAAUjB,KAAKK,UAAUa,MAC/B,OAAID,IAAYA,EAAQE,UAAYF,EAAQG,SACnCH,EAEFI,EAAyBrB,KAAKF,OAASmB,GAAW,IAC3D,CAEA,aAAAK,GACEC,EAA4BvB,KAAKF,MACjC0B,EAA6BxB,KAAKF,MAClCE,KAAKW,gBACP,CAEA,UAAAc,GACE,MAAMC,EAAaC,OAAO3B,KAAKF,KAAK8B,QAAU,IACxCC,EAAS7B,KAAK8B,aAElBJ,IAAe1B,KAAKE,iBACpB2B,IAAW7B,KAAKG,aAChBH,KAAKI,eAELJ,KAAK+B,WAET,CAEA,gBAAAC,GAEA,CAGA,OAAAC,GACEjC,KAAKW,gBACP,CAMQ,SAAAmB,GACN,OACEI,QAAQlC,KAAKD,QAAQoC,eAAiBC,EAAuBpC,KAAKF,KAEtE,CAEQ,cAAAa,GACNX,KAAKI,cAAe,EACpBJ,KAAKC,WACLD,KAAKF,KAAKuC,eACZ,CAEQ,SAAAN,GACN/B,KAAKI,cAAe,EACpB,MAAMsB,EAAaC,OAAO3B,KAAKF,KAAK8B,QAAU,IACxCC,EAAS7B,KAAK8B,YACpB9B,KAAKE,gBAAkBwB,EACvB1B,KAAKG,YAAc0B,EAEnB,IAAIS,GAAU,EACd,GAAIT,EACFS,EAAUC,EAAwBvC,KAAKF,UAClC,CACL,MAAM0C,EAAQC,EACZzC,KAAKgB,OACLhB,KAAKc,aACLY,GAAc,MAEhBY,EAAUI,EAAiB1C,KAAKF,KAAM0C,EAAO,CAC3CG,SAAU3C,KAAKD,QAAQ4C,UAE3B,CAEIL,IACFtC,KAAKC,WACLD,KAAKD,QAAQ6C,aAKbC,eAAe,IAAM7C,KAAKF,KAAKuC,iBAEnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iconify-offline.js","sources":["../../src/helper/iconify-offline.ts"],"sourcesContent":["/**\
|
|
1
|
+
{"version":3,"file":"iconify-offline.js","sources":["../../src/helper/iconify-offline.ts"],"sourcesContent":["/**\n * Iconify/sky-icon helpers: extract icon names from code and fetch icon data for offline use.\n *\n * Usage (browser or Node):\n * import { extractIconNames, fetchIconsForOffline, registerOfflineIcons } from '~/components/helper/iconify-offline';\n * import { SkyIcon } from '~/components/sky-icon/sky-icon';\n *\n * // Extract icon names from a code page string\n * const names = extractIconNames('<sky-icon icon=\"ion:home\"></sky-icon>'); // ['ion:home']\n *\n * // Fetch and then register in SkyIcon (directly pass generated JSON)\n * const combined = await fetchIconsForOffline(['ion:home', 'mdi:bell']);\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\n *\n * // Or from saved JSON string:\n * const combined = JSON.parse(savedJson);\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\n */\n\nconst ICONIFY_API_BASE = 'https://api.iconify.design';\n\n/** Matches quoted \"prefix:icon-name\" (prefix must start with a letter so \"1:3\" etc. are ignored). */\nconst QUOTED_ICON_PATTERN = /([\"'`])([a-z][a-z0-9-]*:[a-zA-Z0-9_.-]+)\\1/g;\n\nexport interface IconifyIconData {\n body?: string;\n left?: number;\n top?: number;\n width?: number;\n height?: number;\n viewBox?: string;\n hidden?: boolean;\n}\n\nexport interface IconifySetData {\n prefix?: string;\n icons: Record<string, IconifyIconData>;\n width?: number;\n height?: number;\n left?: number;\n top?: number;\n /** Icon names requested but not in API response; add manually if needed. Omit when registering with SkyIcon. */\n _notFound?: string[];\n}\n\n/** Combined offline data: prefix -> Iconify set data (ready for SkyIcon.registerLocalSet). Skip keys starting with _ when registering. */\nexport type CombinedOfflineIcons = Record<string, IconifySetData>;\n\nexport interface FetchIconsForOfflineOptions {\n /** Iconify API base URL (default: https://api.iconify.design) */\n baseUrl?: string;\n}\n\n/**\n * Extracts all icon names in \"prefix:icon-name\" format from a code page string.\n * Looks for icon names inside single, double, or backtick quotes (e.g. in attributes\n * like icon=\"ion:home\", iconL='mdi:bell', or :icon=\"`ion:person`\").\n *\n * @param codePageString - Source code string (HTML, Vue, TS, JS, etc.)\n * @returns Array of unique icon names (e.g. [\"ion:home\", \"mdi:bell\"])\n */\nexport function extractIconNames(codePageString: string): string[] {\n if (!codePageString || typeof codePageString !== 'string') {\n return [];\n }\n const seen = new Set<string>();\n const result: string[] = [];\n let match: RegExpExecArray | null;\n QUOTED_ICON_PATTERN.lastIndex = 0;\n while ((match = QUOTED_ICON_PATTERN.exec(codePageString)) !== null) {\n const full = match[2];\n if (full && full.includes(':') && !seen.has(full)) {\n seen.add(full);\n result.push(full);\n }\n }\n return result;\n}\n\n/**\n * Fetches icon data from the Iconify API for the given icon names, groups by prefix,\n * and returns a combined JSON object keyed by prefix for offline use.\n *\n * @param iconNames - List of icon names in \"prefix:icon-name\" format (e.g. [\"ion:home\", \"mdi:bell\"])\n * @param options - Optional baseUrl for the Iconify API\n * @returns Combined object: { \"ion\": { prefix, icons, width, height }, \"mdi\": { ... }, ... }\n * Use with SkyIcon.registerLocalSet(prefix, combined[prefix]) for each prefix.\n */\nexport async function fetchIconsForOffline(\n iconNames: string[],\n options: FetchIconsForOfflineOptions = {}\n): Promise<CombinedOfflineIcons> {\n const baseUrl = (options.baseUrl || ICONIFY_API_BASE).replace(/\\/$/, '');\n const byPrefix = new Map<string, string[]>();\n\n for (const full of iconNames) {\n const trimmed = full.trim();\n if (!trimmed || !trimmed.includes(':')) continue;\n const [prefix, name] = trimmed.split(':');\n const iconName = name?.trim();\n if (!prefix || !iconName) continue;\n // Ignore non-icon patterns (e.g. \"1:3\"); Iconify prefixes start with a letter\n if (!/^[a-z]/.test(prefix)) continue;\n if (!byPrefix.has(prefix)) {\n byPrefix.set(prefix, []);\n }\n const list = byPrefix.get(prefix)!;\n if (!list.includes(iconName)) {\n list.push(iconName);\n }\n }\n\n const combined: CombinedOfflineIcons = {};\n\n await Promise.all(\n Array.from(byPrefix.entries()).map(async ([prefix, names]) => {\n const iconsParam = names.join(',');\n const url = `${baseUrl}/${prefix}.json?icons=${encodeURIComponent(iconsParam)}`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(\n `Iconify API error for ${prefix}: ${res.status} ${res.statusText}`\n );\n }\n const json = (await res.json()) as IconifySetData & {\n icons?: Record<string, IconifyIconData>;\n };\n const returnedIcons = json.icons || {};\n const notFound = names.filter((name) => !returnedIcons[name]);\n const setData: IconifySetData = {\n prefix,\n icons: returnedIcons,\n width: json.width,\n height: json.height,\n left: json.left,\n top: json.top,\n };\n if (notFound.length > 0) {\n setData._notFound = notFound;\n }\n combined[prefix] = setData;\n })\n );\n\n const hasAnyNotFound = Object.values(combined).some(\n (set) => set._notFound && set._notFound.length > 0\n );\n if (hasAnyNotFound) {\n (combined as Record<string, unknown>)['_comment_notFound'] =\n 'Icons listed in _notFound (per set) were not in the API response; add manually if needed.';\n }\n\n return combined;\n}\n\n/** Set data without _notFound / _comment (suitable for SkyIcon.registerLocalSet). */\ntype SetDataForRegister = Omit<IconifySetData, '_notFound'>;\n\n/**\n * Registers the generated offline JSON with SkyIcon. Pass the object returned by fetchIconsForOffline\n * (or parsed from saved JSON). Skips top-level keys starting with \"_\" and strips _notFound from each set.\n *\n * @param combined - The combined object (from fetchIconsForOffline or JSON.parse of saved output)\n * @param register - Callback to register each set, e.g. (prefix, data) => SkyIcon.registerLocalSet(prefix, data)\n */\nexport function registerOfflineIcons(\n combined: CombinedOfflineIcons & Record<string, unknown>,\n register: (prefix: string, data: SetDataForRegister) => void\n): void {\n for (const [key, set] of Object.entries(combined)) {\n if (key.startsWith('_')) continue;\n if (!set || typeof set !== 'object' || !('icons' in set)) continue;\n const { _notFound: _unused, ...data } = set as IconifySetData;\n register(key, data as SetDataForRegister);\n }\n}\n"],"names":["QUOTED_ICON_PATTERN","extractIconNames","codePageString","seen","Set","result","match","lastIndex","exec","full","includes","has","add","push","async","fetchIconsForOffline","iconNames","options","baseUrl","replace","byPrefix","Map","trimmed","trim","prefix","name","split","iconName","test","set","list","get","combined","Promise","all","Array","from","entries","map","names","iconsParam","join","url","encodeURIComponent","res","fetch","ok","Error","status","statusText","json","returnedIcons","icons","notFound","filter","setData","width","height","left","top","length","_notFound","Object","values","some","registerOfflineIcons","register","key","startsWith","_unused","data"],"mappings":"AAmBA,MAGMA,EAAsB,8CAuCrB,SAASC,EAAiBC,GAC/B,IAAKA,GAA4C,iBAAnBA,EAC5B,MAAO,GAET,MAAMC,MAAWC,IACXC,EAAmB,GACzB,IAAIC,EAEJ,IADAN,EAAoBO,UAAY,EAC8B,QAAtDD,EAAQN,EAAoBQ,KAAKN,KAA2B,CAClE,MAAMO,EAAOH,EAAM,GACfG,GAAQA,EAAKC,SAAS,OAASP,EAAKQ,IAAIF,KAC1CN,EAAKS,IAAIH,GACTJ,EAAOQ,KAAKJ,GAEhB,CACA,OAAOJ,CACT,CAWAS,eAAsBC,EACpBC,EACAC,EAAuC,IAEvC,MAAMC,GAAWD,EAAQC,SAzEF,8BAyE+BC,QAAQ,MAAO,IAC/DC,MAAeC,IAErB,IAAA,MAAWZ,KAAQO,EAAW,CAC5B,MAAMM,EAAUb,EAAKc,OACrB,IAAKD,IAAYA,EAAQZ,SAAS,KAAM,SACxC,MAAOc,EAAQC,GAAQH,EAAQI,MAAM,KAC/BC,EAAWF,GAAMF,OACvB,IAAKC,IAAWG,EAAU,SAE1B,IAAK,SAASC,KAAKJ,GAAS,SACvBJ,EAAST,IAAIa,IAChBJ,EAASS,IAAIL,EAAQ,IAEvB,MAAMM,EAAOV,EAASW,IAAIP,GACrBM,EAAKpB,SAASiB,IACjBG,EAAKjB,KAAKc,EAEd,CAEA,MAAMK,EAAiC,CAAA,QAEjCC,QAAQC,IACZC,MAAMC,KAAKhB,EAASiB,WAAWC,IAAIxB,OAAQU,EAAQe,MACjD,MAAMC,EAAaD,EAAME,KAAK,KACxBC,EAAM,GAAGxB,KAAWM,gBAAqBmB,mBAAmBH,KAC5DI,QAAYC,MAAMH,GACxB,IAAKE,EAAIE,GACP,MAAM,IAAIC,MACR,yBAAyBvB,MAAWoB,EAAII,UAAUJ,EAAIK,cAG1D,MAAMC,QAAcN,EAAIM,OAGlBC,EAAgBD,EAAKE,OAAS,CAAA,EAC9BC,EAAWd,EAAMe,OAAQ7B,IAAU0B,EAAc1B,IACjD8B,EAA0B,CAC9B/B,SACA4B,MAAOD,EACPK,MAAON,EAAKM,MACZC,OAAQP,EAAKO,OACbC,KAAMR,EAAKQ,KACXC,IAAKT,EAAKS,KAERN,EAASO,OAAS,IACpBL,EAAQM,UAAYR,GAEtBrB,EAASR,GAAU+B,KAYvB,OARuBO,OAAOC,OAAO/B,GAAUgC,KAC5CnC,GAAQA,EAAIgC,WAAahC,EAAIgC,UAAUD,OAAS,KAGhD5B,EAAwD,kBACvD,6FAGGA,CACT,CAYO,SAASiC,EACdjC,EACAkC,GAEA,IAAA,MAAYC,EAAKtC,KAAQiC,OAAOzB,QAAQL,GAAW,CACjD,GAAImC,EAAIC,WAAW,KAAM,SACzB,IAAKvC,GAAsB,iBAARA,KAAsB,UAAWA,GAAM,SAC1D,MAAQgC,UAAWQ,KAAYC,GAASzC,EACxCqC,EAASC,EAAKG,EAChB,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"focus-ring.js","sources":["../../../src/helper/styles/focus-ring.ts"],"sourcesContent":["import { css } from \"lit\";\n\n/**\n * Focus ring box-shadow using `--sky-focus-ring-color` (falls back to active-primary).\n *\n * Theme-level `--sky-focus-ring` resolves nested `var()` on the theme host before\n * inheritance, so local color overrides do not retint an inherited ring. Redeclare\n * this formula on the same element that sets `--sky-focus-ring-color`.\n */\nexport const SKY_FOCUS_RING_SHADOW =\n \"0 0 0 3px color-mix(in oklch, var(--sky-focus-ring-color, var(--sky-active-primary)) 42%, transparent)\";\n\n/** Redeclare `--sky-focus-ring` on `:host` so local `--sky-focus-ring-color` applies. */\nexport const skyFocusRingHostStyles = css`\n :host {\n --sky-focus-ring: 0 0 0 3px color-mix(\n in oklch,\n var(--sky-focus-ring-color, var(--sky-active-primary)) 42%,\n transparent\n );\n }\n`;\n"],"names":["SKY_FOCUS_RING_SHADOW","skyFocusRingHostStyles","css"],"mappings":"0BASO,MAAMA,EACX,yGAGWC,EAAyBC,CAAA;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"focus-ring.js","sources":["../../../src/helper/styles/focus-ring.ts"],"sourcesContent":["import { css } from \"lit\";\r\n\r\n/**\r\n * Focus ring box-shadow using `--sky-focus-ring-color` (falls back to active-primary).\r\n *\r\n * Theme-level `--sky-focus-ring` resolves nested `var()` on the theme host before\r\n * inheritance, so local color overrides do not retint an inherited ring. Redeclare\r\n * this formula on the same element that sets `--sky-focus-ring-color`.\r\n */\r\nexport const SKY_FOCUS_RING_SHADOW =\r\n \"0 0 0 3px color-mix(in oklch, var(--sky-focus-ring-color, var(--sky-active-primary)) 42%, transparent)\";\r\n\r\n/** Redeclare `--sky-focus-ring` on `:host` so local `--sky-focus-ring-color` applies. */\r\nexport const skyFocusRingHostStyles = css`\r\n :host {\r\n --sky-focus-ring: 0 0 0 3px color-mix(\r\n in oklch,\r\n var(--sky-focus-ring-color, var(--sky-active-primary)) 42%,\r\n transparent\r\n );\r\n }\r\n`;\r\n"],"names":["SKY_FOCUS_RING_SHADOW","skyFocusRingHostStyles","css"],"mappings":"0BASO,MAAMA,EACX,yGAGWC,EAAyBC,CAAA;;;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-presets.js","sources":["../../../src/helper/utils/component-presets.ts"],"sourcesContent":["/**\n * Component prop presets — global defaults + named bundles for sky-* CEs.\n *\n * Precedence (highest wins):\n * explicit user prop (attribute, framework property, or marked/diverged) →\n * named `preset` → provider `defaults` → built-in class default\n *\n * A named preset overlays `defaults` unless it sets `$inheritDefaults: false`,\n * in which case it is used standalone (keys it omits fall to the built-in).\n *\n * User-owned detection:\n * - attributes present before Lit's first reflect (and any attrs added later)\n * - props marked via `markUserSetProp`\n * - props the consumer changed after presets applied (divergence)\n * - props a framework set before first apply (current value ≠ constructor\n * snapshot from `captureBuiltInPropSnapshot` — covers Vue/React CE property\n * bindings that never create attributes). Overriding *back* to the exact\n * built-in default still needs an attribute or `markUserSetProp`.\n *\n * @see sky-config-provider\n */\n\nimport { createContext } from \"@lit/context\";\n\n/** Prop bag applied to a single component instance. */\nexport type ComponentPropBag = Record<string, unknown> & {\n /**\n * Named presets only: set `false` to ignore `defaults[component]` and use\n * this bag standalone. Omitted / `true` keeps the default layering.\n */\n $inheritDefaults?: boolean;\n};\n\n/** Reserved bag keys that configure resolution instead of naming a prop. */\nconst RESERVED_BAG_KEYS = new Set([\"$inheritDefaults\"]);\n\n/**\n * Config keys are custom element tag names (`sky-button`).\n * Short aliases without the `sky-` prefix (`button`) are also accepted.\n */\nexport type ComponentPresetKey = string;\n\nexport type ComponentPresetsConfig = {\n /** Applied when the instance does not set the prop (no matching attribute). */\n defaults?: Partial<Record<ComponentPresetKey, ComponentPropBag>>;\n /** Named bundles selected via `preset=\"name\"` on the instance. */\n presets?: Partial<\n Record<ComponentPresetKey, Record<string, ComponentPropBag>>\n >;\n};\n\n/**\n * Lookup aliases for a component key: tag name and short name.\n * `sky-button` ↔ `button`, `sky-button-group` ↔ `button-group`.\n */\nexport function presetKeyAliases(key: string): string[] {\n const k = String(key || \"\").trim();\n if (!k) return [];\n const aliases = new Set<string>([k]);\n if (k.startsWith(\"sky-\")) aliases.add(k.slice(4));\n else aliases.add(`sky-${k}`);\n return [...aliases];\n}\n\n/** First matching bag under `defaults` / `presets` for any alias of `key`. */\nexport function lookupPresetSection<T>(\n section: Partial<Record<string, T>> | null | undefined,\n key: string,\n): T | undefined {\n if (!section) return undefined;\n for (const alias of presetKeyAliases(key)) {\n if (Object.prototype.hasOwnProperty.call(section, alias)) {\n return section[alias];\n }\n }\n return undefined;\n}\n\n/** Merge all alias bags (short + tag) under a section key. Later aliases win. */\nfunction mergeAliasBags(\n section: Partial<Record<string, ComponentPropBag>> | null | undefined,\n key: string,\n): ComponentPropBag {\n if (!section) return {};\n let out: ComponentPropBag = {};\n for (const alias of presetKeyAliases(key)) {\n const bag = section[alias];\n if (bag) out = { ...out, ...bag };\n }\n return out;\n}\n\nfunction mergeAliasNamedMaps(\n section:\n | Partial<Record<string, Record<string, ComponentPropBag>>>\n | null\n | undefined,\n key: string,\n): Record<string, ComponentPropBag> {\n if (!section) return {};\n const out: Record<string, ComponentPropBag> = {};\n for (const alias of presetKeyAliases(key)) {\n const named = section[alias];\n if (!named) continue;\n for (const [name, bag] of Object.entries(named)) {\n out[name] = { ...(out[name] ?? {}), ...(bag ?? {}) };\n }\n }\n return out;\n}\n\n/**\n * Canonical tag form for storage/docs: `button` → `sky-button`.\n * Already-tagged names are unchanged.\n */\nexport function toPresetTagKey(key: string): string {\n const k = String(key || \"\").trim();\n if (!k) return k;\n return k.startsWith(\"sky-\") ? k : `sky-${k}`;\n}\n\n/** Lit context provided by `<sky-config-provider>`. */\nexport const configContext = createContext<ComponentPresetsConfig | null>(\n \"sky/component-presets\",\n);\n\n/** camelCase / PascalCase → kebab-case attribute name. */\nexport function camelToKebab(name: string): string {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/_/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Attributes present before Lit's first reflect (captured in `PresetController`).\n * Needed so reflected class defaults (e.g. `variant=\"solid\"`) do not look user-owned.\n */\nconst initialUserAttrs = new WeakMap<HTMLElement, Set<string>>();\n\n/** Props explicitly marked user-owned (property-only writes / divergence). */\nconst userOwnedProps = new WeakMap<HTMLElement, Set<string>>();\n\n/** Last values written by `applyPresetLayer` (for divergence detection). */\nconst lastPresetApplied = new WeakMap<HTMLElement, Map<string, unknown>>();\n\n/**\n * Prop values at CE construction (before Vue/React assign host props).\n * Call from `PresetController` construction so field initializers are visible.\n */\nconst builtInPropSnapshots = new WeakMap<HTMLElement, Map<string, unknown>>();\n\nconst PRESET_SKIP_KEYS = new Set([\"preset\", ...RESERVED_BAG_KEYS]);\n\n/**\n * Snapshot host attributes once (call from controller `hostConnected`, before first update).\n * Declarative HTML / early `setAttribute` calls are treated as user-owned thereafter.\n */\nexport function captureInitialUserAttributes(host: HTMLElement): void {\n if (initialUserAttrs.has(host)) return;\n initialUserAttrs.set(host, new Set(host.getAttributeNames()));\n}\n\n/**\n * Snapshot Lit `@property` values once during host construction (before frameworks\n * assign props). Later diffs against this map mark framework property bindings as\n * user-owned so presets do not overwrite them.\n *\n * Declare `PresetController` after the host's presettable `@property` fields so\n * those initials are already assigned when this runs. Keys still `undefined` are\n * filled by `finalizeBuiltInPropSnapshot` on connect (constructor-body defaults).\n */\nexport function captureBuiltInPropSnapshot(host: HTMLElement): void {\n if (builtInPropSnapshots.has(host)) return;\n const snap = new Map<string, unknown>();\n const record = host as HTMLElement & Record<string, unknown>;\n const meta = (\n host.constructor as { elementProperties?: Map<PropertyKey, unknown> }\n ).elementProperties;\n if (meta) {\n for (const prop of meta.keys()) {\n if (typeof prop !== \"string\" || PRESET_SKIP_KEYS.has(prop)) continue;\n snap.set(prop, record[prop]);\n }\n }\n builtInPropSnapshots.set(host, snap);\n}\n\n/**\n * Fill snapshot entries that were still `undefined` when the controller\n * constructed (e.g. defaults assigned in the host `constructor` body after the\n * controller field). Does not overwrite defined snapshot values — those are the\n * true built-ins Vue/React may have already overridden by connect time.\n */\nexport function finalizeBuiltInPropSnapshot(host: HTMLElement): void {\n if (!builtInPropSnapshots.has(host)) {\n captureBuiltInPropSnapshot(host);\n return;\n }\n const snap = builtInPropSnapshots.get(host)!;\n const record = host as HTMLElement & Record<string, unknown>;\n for (const [key, value] of snap) {\n if (value === undefined && record[key] !== undefined) {\n snap.set(key, record[key]);\n }\n }\n}\n\n/**\n * Merge any attributes that appeared after connect (late framework bindings)\n * into the user-owned attribute snapshot.\n */\nexport function refreshUserAttributes(host: HTMLElement): void {\n const set = initialUserAttrs.get(host) ?? new Set<string>();\n for (const name of host.getAttributeNames()) set.add(name);\n initialUserAttrs.set(host, set);\n}\n\n/**\n * Mark a prop as consumer-owned so presets will not overwrite it.\n * Use for property-only bindings (no attribute) when needed.\n */\nexport function markUserSetProp(host: HTMLElement, prop: string): void {\n const set = userOwnedProps.get(host) ?? new Set<string>();\n set.add(prop);\n userOwnedProps.set(host, set);\n}\n\n/**\n * True when the host had an attribute for this prop before/at apply time,\n * the prop was marked user-owned (explicit mark or post-apply divergence),\n * or a framework assigned a property that differs from the constructor snapshot\n * before presets first wrote that key.\n */\nexport function isUserSetProp(host: HTMLElement, prop: string): boolean {\n if (userOwnedProps.get(host)?.has(prop)) return true;\n const kebab = camelToKebab(prop);\n const initial = initialUserAttrs.get(host);\n if (initial?.has(kebab) || (kebab !== prop && initial?.has(prop))) {\n return true;\n }\n if (\n !initial &&\n (host.hasAttribute(kebab) || (kebab !== prop && host.hasAttribute(prop)))\n ) {\n return true;\n }\n // Framework CE bindings (Vue/React) set properties, not attributes.\n if (!lastPresetApplied.get(host)?.has(prop)) {\n const snap = builtInPropSnapshots.get(host);\n if (snap?.has(prop)) {\n const record = host as HTMLElement & Record<string, unknown>;\n if (record[prop] !== snap.get(prop)) return true;\n }\n }\n return false;\n}\n\n/**\n * Permanently mark props a framework changed before our first write for that key.\n */\nfunction detectPreApplyFrameworkProps(\n host: HTMLElement,\n layer: ComponentPropBag,\n): void {\n const snap = builtInPropSnapshots.get(host);\n if (!snap?.size) return;\n const applied = lastPresetApplied.get(host);\n const record = host as HTMLElement & Record<string, unknown>;\n for (const key of Object.keys(layer)) {\n if (PRESET_SKIP_KEYS.has(key) || applied?.has(key)) continue;\n if (userOwnedProps.get(host)?.has(key)) continue;\n if (!snap.has(key)) continue;\n if (record[key] !== snap.get(key)) {\n markUserSetProp(host, key);\n }\n }\n}\n\n/**\n * If the consumer changed a prop after we applied a preset value, mark it\n * user-owned so later re-applies leave it alone.\n */\nfunction detectDivergedUserProps(host: HTMLElement): void {\n const applied = lastPresetApplied.get(host);\n if (!applied?.size) return;\n const record = host as HTMLElement & Record<string, unknown>;\n for (const [key, appliedValue] of applied) {\n if (isUserSetProp(host, key)) continue;\n if (record[key] !== appliedValue) {\n markUserSetProp(host, key);\n }\n }\n}\n\n/**\n * Deep-merge parent + child preset configs.\n * Inner provider wins per component key, then per prop / named preset.\n */\nexport function mergePresetsConfig(\n parent: ComponentPresetsConfig | null | undefined,\n child: ComponentPresetsConfig | null | undefined,\n): ComponentPresetsConfig {\n const p = parent ?? {};\n const c = child ?? {};\n\n const defaults: NonNullable<ComponentPresetsConfig[\"defaults\"]> = {};\n for (const source of [p.defaults, c.defaults]) {\n for (const [key, bag] of Object.entries(source ?? {})) {\n const tag = toPresetTagKey(key);\n defaults[tag] = { ...(defaults[tag] ?? {}), ...(bag ?? {}) };\n }\n }\n\n const presets: NonNullable<ComponentPresetsConfig[\"presets\"]> = {};\n for (const source of [p.presets, c.presets]) {\n for (const [comp, named] of Object.entries(source ?? {})) {\n const tag = toPresetTagKey(comp);\n const mergedNamed: Record<string, ComponentPropBag> = {\n ...(presets[tag] ?? {}),\n };\n for (const [name, bag] of Object.entries(named ?? {})) {\n mergedNamed[name] = { ...(mergedNamed[name] ?? {}), ...(bag ?? {}) };\n }\n presets[tag] = mergedNamed;\n }\n }\n\n return { defaults, presets };\n}\n\nexport type ResolveComponentPropsOptions<T extends Record<string, unknown>> = {\n host: HTMLElement;\n componentKey: ComponentPresetKey;\n /** Value of the instance `preset` attribute/prop. */\n presetName?: string | null;\n config?: ComponentPresetsConfig | null;\n /** Current instance values (class built-ins already applied). */\n values: T;\n /**\n * Keys eligible for preset / default substitution.\n * Omit to apply every key present in the preset layer.\n */\n keys?: Array<keyof T & string>;\n};\n\n/**\n * True when `host` is rendered inside another `sky-*` component's shadow tree\n * (library chrome), not as slotted / light-DOM content.\n *\n * Slotted children keep `getRootNode() === document` (or an outer root), so\n * provider defaults still apply. Internals like `sky-button` inside `sky-table`\n * must not pick up app-level `defaults['sky-button']`.\n */\nexport function isSkyCompositeInternal(host: HTMLElement): boolean {\n if (typeof ShadowRoot === \"undefined\") return false;\n const root = host.getRootNode();\n if (!(root instanceof ShadowRoot)) return false;\n const tag = root.host?.localName ?? \"\";\n // Config/theme providers are not composites that own chrome children.\n if (tag === \"sky-config-provider\" || tag === \"sky-theme-provider\") {\n return false;\n }\n return tag.startsWith(\"sky-\");\n}\n\n/**\n * Walk ancestors for `<sky-config-provider>` and deep-merge outer → inner.\n * Used when Lit context has not delivered a value yet (or as a reliable fallback).\n */\nexport function findNearestPresetsConfig(\n host: HTMLElement,\n): ComponentPresetsConfig | null {\n const layers: ComponentPresetsConfig[] = [];\n let el: HTMLElement | null = host.parentElement;\n while (el) {\n if (el.localName === \"sky-config-provider\") {\n const provider = el as HTMLElement & {\n presets?: ComponentPresetsConfig;\n mergedConfig?: ComponentPresetsConfig;\n };\n // Prefer live `presets` so we do not depend on context publish timing.\n if (provider.mergedConfig) {\n // Nearest provider already merged its ancestors — use it and stop.\n return provider.mergedConfig;\n }\n if (provider.presets) layers.push(provider.presets);\n }\n el = el.parentElement;\n }\n if (!layers.length) return null;\n // layers collected inner→outer while walking up; merge outer first, inner last.\n return layers.reduceRight(\n (acc, layer) => mergePresetsConfig(acc, layer),\n {} as ComponentPresetsConfig,\n );\n}\n\n/**\n * Resolve the active prop bag for a component: `defaults[key]` ← `presets[key][name]`.\n * Any prop key present in that bag can be applied to the instance.\n *\n * A named preset with `$inheritDefaults: false` is used standalone, so keys it\n * omits fall through to the component built-in instead of `defaults`.\n */\nexport function getPresetLayer(\n config: ComponentPresetsConfig | null | undefined,\n componentKey: ComponentPresetKey,\n presetName?: string | null,\n): ComponentPropBag {\n const defaults = mergeAliasBags(config?.defaults, componentKey);\n const namedMap = mergeAliasNamedMaps(config?.presets, componentKey);\n const named =\n presetName && namedMap[presetName] ? namedMap[presetName]! : null;\n\n const merged =\n named && named.$inheritDefaults === false\n ? { ...named }\n : { ...defaults, ...(named ?? {}) };\n\n for (const key of RESERVED_BAG_KEYS) delete merged[key];\n return merged;\n}\n\n/** Per-host map of prop → value before presets overwrote it (for revert). */\nconst presetRestoreValues = new WeakMap<HTMLElement, Map<string, unknown>>();\n\nexport type ApplyPresetLayerOptions = {\n /** Extra keys never written from presets. */\n skipKeys?: string[];\n};\n\n/**\n * Write every key from the preset layer onto the host (any component prop).\n * Skips user-owned attributes / props. Remembers prior values so removing a\n * key from the provider config restores the built-in / prior value.\n *\n * Before writing, detects divergence: if the host value no longer matches the\n * last preset-applied value, that prop is marked user-owned (property-only\n * overrides after first apply). Also locks in framework property bindings that\n * differ from the constructor snapshot before the first write for that key.\n *\n * @returns true if any host property changed\n */\nexport function applyPresetLayer(\n host: HTMLElement,\n layer: ComponentPropBag,\n options?: ApplyPresetLayerOptions,\n): boolean {\n refreshUserAttributes(host);\n detectPreApplyFrameworkProps(host, layer);\n detectDivergedUserProps(host);\n\n const skip = new Set([\n ...PRESET_SKIP_KEYS,\n ...(options?.skipKeys ?? []),\n ]);\n const restore = presetRestoreValues.get(host) ?? new Map<string, unknown>();\n const applied = lastPresetApplied.get(host) ?? new Map<string, unknown>();\n let changed = false;\n const record = host as HTMLElement & Record<string, unknown>;\n\n // Drop / revert keys no longer provided by the layer.\n for (const key of [...restore.keys()]) {\n if (\n Object.prototype.hasOwnProperty.call(layer, key) &&\n layer[key] !== undefined\n ) {\n continue;\n }\n if (!isUserSetProp(host, key) && restore.has(key)) {\n const prev = restore.get(key);\n if (record[key] !== prev) {\n record[key] = prev;\n changed = true;\n }\n }\n restore.delete(key);\n applied.delete(key);\n }\n\n // Apply all keys in the layer (full prop surface — not a whitelist).\n for (const [key, value] of Object.entries(layer)) {\n if (skip.has(key) || value === undefined) continue;\n if (isUserSetProp(host, key)) {\n applied.delete(key);\n continue;\n }\n if (!restore.has(key)) {\n restore.set(key, record[key]);\n }\n if (record[key] !== value) {\n record[key] = value;\n changed = true;\n }\n applied.set(key, value);\n }\n\n presetRestoreValues.set(host, restore);\n lastPresetApplied.set(host, applied);\n return changed;\n}\n\n/**\n * Clear preset-applied props and restore saved built-ins (e.g. when paused in a group).\n */\nexport function clearAppliedPresetLayer(host: HTMLElement): boolean {\n const restore = presetRestoreValues.get(host);\n if (!restore?.size) return false;\n const record = host as HTMLElement & Record<string, unknown>;\n let changed = false;\n for (const [key, prev] of restore) {\n if (isUserSetProp(host, key)) continue;\n if (record[key] !== prev) {\n record[key] = prev;\n changed = true;\n }\n }\n restore.clear();\n lastPresetApplied.get(host)?.clear();\n return changed;\n}\n\n/**\n * Resolve effective props for styling / behavior without mutating the host.\n * When `keys` is omitted, every key in the preset layer is considered.\n * Skips keys the user set via attributes.\n */\nexport function resolveComponentProps<T extends Record<string, unknown>>(\n options: ResolveComponentPropsOptions<T>,\n): T {\n const { host, componentKey, presetName, config, values, keys } = options;\n const layer = getPresetLayer(config, componentKey, presetName);\n const keysToApply =\n keys ?? (Object.keys(layer) as Array<keyof T & string>);\n\n const out = { ...values };\n for (const key of keysToApply) {\n const k = key as string;\n if (isUserSetProp(host, k)) continue;\n if (Object.prototype.hasOwnProperty.call(layer, k) && layer[k] !== undefined) {\n (out as Record<string, unknown>)[k] = layer[k];\n }\n }\n return out;\n}\n"],"names":["RESERVED_BAG_KEYS","Set","presetKeyAliases","key","k","String","trim","aliases","startsWith","add","slice","lookupPresetSection","section","alias","Object","prototype","hasOwnProperty","call","toPresetTagKey","configContext","createContext","camelToKebab","name","replace","toLowerCase","initialUserAttrs","WeakMap","userOwnedProps","lastPresetApplied","builtInPropSnapshots","PRESET_SKIP_KEYS","captureInitialUserAttributes","host","has","set","getAttributeNames","captureBuiltInPropSnapshot","snap","Map","record","meta","constructor","elementProperties","prop","keys","finalizeBuiltInPropSnapshot","get","value","refreshUserAttributes","markUserSetProp","isUserSetProp","kebab","initial","hasAttribute","mergePresetsConfig","parent","child","p","c","defaults","source","bag","entries","tag","presets","comp","named","mergedNamed","isSkyCompositeInternal","ShadowRoot","root","getRootNode","localName","findNearestPresetsConfig","layers","el","parentElement","provider","mergedConfig","push","length","reduceRight","acc","layer","getPresetLayer","config","componentKey","presetName","out","mergeAliasBags","namedMap","mergeAliasNamedMaps","merged","$inheritDefaults","presetRestoreValues","applyPresetLayer","options","size","applied","detectPreApplyFrameworkProps","appliedValue","detectDivergedUserProps","skip","skipKeys","restore","changed","prev","delete","clearAppliedPresetLayer","clear","resolveComponentProps","values","keysToApply"],"mappings":"6CAkCA,MAAMA,EAAoB,IAAIC,IAAI,CAAC,qBAqB5B,SAASC,EAAiBC,GAC/B,MAAMC,EAAIC,OAAOF,GAAO,IAAIG,OAC5B,IAAKF,EAAG,MAAO,GACf,MAAMG,EAAU,IAAIN,IAAY,CAACG,IAGjC,OAFIA,EAAEI,WAAW,UAAiBC,IAAIL,EAAEM,MAAM,IACzCH,EAAQE,IAAI,OAAOL,KACjB,IAAIG,EACb,CAGO,SAASI,EACdC,EACAT,GAEA,GAAKS,EACL,IAAA,MAAWC,KAASX,EAAiBC,GACnC,GAAIW,OAAOC,UAAUC,eAAeC,KAAKL,EAASC,GAChD,OAAOD,EAAQC,EAIrB,CAuCO,SAASK,EAAef,GAC7B,MAAMC,EAAIC,OAAOF,GAAO,IAAIG,OAC5B,OAAKF,EACEA,EAAEI,WAAW,QAAUJ,EAAI,OAAOA,IAD1BA,CAEjB,CAGO,MAAMe,EAAgBC,EAC3B,yBAIK,SAASC,EAAaC,GAC3B,OAAOA,EACJC,QAAQ,qBAAsB,SAC9BA,QAAQ,KAAM,KACdC,aACL,CAMA,MAAMC,MAAuBC,QAGvBC,MAAqBD,QAGrBE,MAAwBF,QAMxBG,MAA2BH,QAE3BI,EAAmB,IAAI7B,IAAI,CAAC,YAAaD,IAMxC,SAAS+B,EAA6BC,GACvCP,EAAiBQ,IAAID,IACzBP,EAAiBS,IAAIF,EAAM,IAAI/B,IAAI+B,EAAKG,qBAC1C,CAWO,SAASC,EAA2BJ,GACzC,GAAIH,EAAqBI,IAAID,GAAO,OACpC,MAAMK,MAAWC,IACXC,EAASP,EACTQ,EACJR,EAAKS,YACLC,kBACF,GAAIF,EACF,IAAA,MAAWG,KAAQH,EAAKI,OACF,iBAATD,GAAqBb,EAAiBG,IAAIU,IACrDN,EAAKH,IAAIS,EAAMJ,EAAOI,IAG1Bd,EAAqBK,IAAIF,EAAMK,EACjC,CAQO,SAASQ,EAA4Bb,GAC1C,IAAKH,EAAqBI,IAAID,GAE5B,YADAI,EAA2BJ,GAG7B,MAAMK,EAAOR,EAAqBiB,IAAId,GAChCO,EAASP,EACf,IAAA,MAAY7B,EAAK4C,KAAUV,OACX,IAAVU,QAAuC,IAAhBR,EAAOpC,IAChCkC,EAAKH,IAAI/B,EAAKoC,EAAOpC,GAG3B,CAMO,SAAS6C,EAAsBhB,GACpC,MAAME,EAAMT,EAAiBqB,IAAId,QAAa/B,IAC9C,IAAA,MAAWqB,KAAQU,EAAKG,oBAAqBD,EAAIzB,IAAIa,GACrDG,EAAiBS,IAAIF,EAAME,EAC7B,CAMO,SAASe,EAAgBjB,EAAmBW,GACjD,MAAMT,EAAMP,EAAemB,IAAId,QAAa/B,IAC5CiC,EAAIzB,IAAIkC,GACRhB,EAAeO,IAAIF,EAAME,EAC3B,CAQO,SAASgB,EAAclB,EAAmBW,GAC/C,GAAIhB,EAAemB,IAAId,IAAOC,IAAIU,GAAO,OAAO,EAChD,MAAMQ,EAAQ9B,EAAasB,GACrBS,EAAU3B,EAAiBqB,IAAId,GACrC,GAAIoB,GAASnB,IAAIkB,IAAWA,IAAUR,GAAQS,GAASnB,IAAIU,GACzD,OAAO,EAET,IACGS,IACApB,EAAKqB,aAAaF,IAAWA,IAAUR,GAAQX,EAAKqB,aAAaV,IAElE,OAAO,EAGT,IAAKf,EAAkBkB,IAAId,IAAOC,IAAIU,GAAO,CAC3C,MAAMN,EAAOR,EAAqBiB,IAAId,GACtC,GAAIK,GAAMJ,IAAIU,GAAO,CAEnB,GADeX,EACJW,KAAUN,EAAKS,IAAIH,GAAO,OAAO,CAC9C,CACF,CACA,OAAO,CACT,CA2CO,SAASW,EACdC,EACAC,GAEA,MAAMC,EAAIF,GAAU,CAAA,EACdG,EAAIF,GAAS,CAAA,EAEbG,EAA4D,CAAA,EAClE,IAAA,MAAWC,IAAU,CAACH,EAAEE,SAAUD,EAAEC,UAClC,IAAA,MAAYxD,EAAK0D,KAAQ/C,OAAOgD,QAAQF,GAAU,CAAA,GAAK,CACrD,MAAMG,EAAM7C,EAAef,GAC3BwD,EAASI,GAAO,IAAMJ,EAASI,IAAQ,CAAA,KAASF,GAAO,GACzD,CAGF,MAAMG,EAA0D,CAAA,EAChE,IAAA,MAAWJ,IAAU,CAACH,EAAEO,QAASN,EAAEM,SACjC,IAAA,MAAYC,EAAMC,KAAUpD,OAAOgD,QAAQF,GAAU,CAAA,GAAK,CACxD,MAAMG,EAAM7C,EAAe+C,GACrBE,EAAgD,IAChDH,EAAQD,IAAQ,CAAA,GAEtB,IAAA,MAAYzC,EAAMuC,KAAQ/C,OAAOgD,QAAQI,GAAS,CAAA,GAChDC,EAAY7C,GAAQ,IAAM6C,EAAY7C,IAAS,CAAA,KAASuC,GAAO,IAEjEG,EAAQD,GAAOI,CACjB,CAGF,MAAO,CAAER,WAAUK,UACrB,CAyBO,SAASI,EAAuBpC,GACrC,GAA0B,oBAAfqC,WAA4B,OAAO,EAC9C,MAAMC,EAAOtC,EAAKuC,cAClB,KAAMD,aAAgBD,YAAa,OAAO,EAC1C,MAAMN,EAAMO,EAAKtC,MAAMwC,WAAa,GAEpC,MAAY,wBAART,GAAyC,uBAARA,GAG9BA,EAAIvD,WAAW,OACxB,CAMO,SAASiE,EACdzC,GAEA,MAAM0C,EAAmC,GACzC,IAAIC,EAAyB3C,EAAK4C,cAClC,KAAOD,GAAI,CACT,GAAqB,wBAAjBA,EAAGH,UAAqC,CAC1C,MAAMK,EAAWF,EAKjB,GAAIE,EAASC,aAEX,OAAOD,EAASC,aAEdD,EAASb,SAASU,EAAOK,KAAKF,EAASb,QAC7C,CACAW,EAAKA,EAAGC,aACV,CACA,OAAKF,EAAOM,OAELN,EAAOO,YACZ,CAACC,EAAKC,IAAU7B,EAAmB4B,EAAKC,GACxC,CAAA,GAJyB,IAM7B,CASO,SAASC,EACdC,EACAC,EACAC,GAEA,MAAM5B,EA3UR,SACE/C,EACAT,GAEA,IAAKS,EAAS,MAAO,CAAA,EACrB,IAAI4E,EAAwB,CAAA,EAC5B,IAAA,MAAW3E,KAASX,EAAiBC,GAAM,CACzC,MAAM0D,EAAMjD,EAAQC,GAChBgD,IAAK2B,EAAM,IAAKA,KAAQ3B,GAC9B,CACA,OAAO2B,CACT,CAgUmBC,CAAeJ,GAAQ1B,SAAU2B,GAC5CI,EA/TR,SACE9E,EAIAT,GAEA,IAAKS,EAAS,MAAO,CAAA,EACrB,MAAM4E,EAAwC,CAAA,EAC9C,IAAA,MAAW3E,KAASX,EAAiBC,GAAM,CACzC,MAAM+D,EAAQtD,EAAQC,GACtB,GAAKqD,EACL,IAAA,MAAY5C,EAAMuC,KAAQ/C,OAAOgD,QAAQI,GACvCsB,EAAIlE,GAAQ,IAAMkE,EAAIlE,IAAS,CAAA,KAASuC,GAAO,GAEnD,CACA,OAAO2B,CACT,CA8SmBG,CAAoBN,GAAQrB,QAASsB,GAChDpB,EACJqB,GAAcG,EAASH,GAAcG,EAASH,GAAe,KAEzDK,EACJ1B,IAAoC,IAA3BA,EAAM2B,iBACX,IAAK3B,GACL,IAAKP,KAAcO,GAAS,CAAA,GAElC,IAAA,MAAW/D,KAAOH,SAA0B4F,EAAOzF,GACnD,OAAOyF,CACT,CAGA,MAAME,MAA0BpE,QAmBzB,SAASqE,EACd/D,EACAmD,EACAa,GAEAhD,EAAsBhB,GA5LxB,SACEA,EACAmD,GAEA,MAAM9C,EAAOR,EAAqBiB,IAAId,GACtC,IAAKK,GAAM4D,KAAM,OACjB,MAAMC,EAAUtE,EAAkBkB,IAAId,GAChCO,EAASP,EACf,IAAA,MAAW7B,KAAOW,OAAO8B,KAAKuC,GACxBrD,EAAiBG,IAAI9B,IAAQ+F,GAASjE,IAAI9B,IAC1CwB,EAAemB,IAAId,IAAOC,IAAI9B,IAC7BkC,EAAKJ,IAAI9B,IACVoC,EAAOpC,KAASkC,EAAKS,IAAI3C,IAC3B8C,EAAgBjB,EAAM7B,EAG5B,CA6KEgG,CAA6BnE,EAAMmD,GAvKrC,SAAiCnD,GAC/B,MAAMkE,EAAUtE,EAAkBkB,IAAId,GACtC,IAAKkE,GAASD,KAAM,OACpB,MAAM1D,EAASP,EACf,IAAA,MAAY7B,EAAKiG,KAAiBF,EAC5BhD,EAAclB,EAAM7B,IACpBoC,EAAOpC,KAASiG,GAClBnD,EAAgBjB,EAAM7B,EAG5B,CA8JEkG,CAAwBrE,GAExB,MAAMsE,MAAWrG,IAAI,IAChB6B,KACCkE,GAASO,UAAY,KAErBC,EAAUV,EAAoBhD,IAAId,QAAaM,IAC/C4D,EAAUtE,EAAkBkB,IAAId,QAAaM,IACnD,IAAImE,GAAU,EACd,MAAMlE,EAASP,EAGf,IAAA,MAAW7B,IAAO,IAAIqG,EAAQ5D,QAC5B,IACE9B,OAAOC,UAAUC,eAAeC,KAAKkE,EAAOhF,aAC5CgF,EAAMhF,GAFR,CAMA,IAAK+C,EAAclB,EAAM7B,IAAQqG,EAAQvE,IAAI9B,GAAM,CACjD,MAAMuG,EAAOF,EAAQ1D,IAAI3C,GACrBoC,EAAOpC,KAASuG,IAClBnE,EAAOpC,GAAOuG,EACdD,GAAU,EAEd,CACAD,EAAQG,OAAOxG,GACf+F,EAAQS,OAAOxG,EATf,CAaF,IAAA,MAAYA,EAAK4C,KAAUjC,OAAOgD,QAAQqB,GACpCmB,EAAKrE,IAAI9B,SAAkB,IAAV4C,IACjBG,EAAclB,EAAM7B,GACtB+F,EAAQS,OAAOxG,IAGZqG,EAAQvE,IAAI9B,IACfqG,EAAQtE,IAAI/B,EAAKoC,EAAOpC,IAEtBoC,EAAOpC,KAAS4C,IAClBR,EAAOpC,GAAO4C,EACd0D,GAAU,GAEZP,EAAQhE,IAAI/B,EAAK4C,KAKnB,OAFA+C,EAAoB5D,IAAIF,EAAMwE,GAC9B5E,EAAkBM,IAAIF,EAAMkE,GACrBO,CACT,CAKO,SAASG,EAAwB5E,GACtC,MAAMwE,EAAUV,EAAoBhD,IAAId,GACxC,IAAKwE,GAASP,KAAM,OAAO,EAC3B,MAAM1D,EAASP,EACf,IAAIyE,GAAU,EACd,IAAA,MAAYtG,EAAKuG,KAASF,EACpBtD,EAAclB,EAAM7B,IACpBoC,EAAOpC,KAASuG,IAClBnE,EAAOpC,GAAOuG,EACdD,GAAU,GAKd,OAFAD,EAAQK,QACRjF,EAAkBkB,IAAId,IAAO6E,QACtBJ,CACT,CAOO,SAASK,EACdd,GAEA,MAAMhE,KAAEA,EAAAsD,aAAMA,EAAAC,WAAcA,SAAYF,EAAA0B,OAAQA,EAAAnE,KAAQA,GAASoD,EAC3Db,EAAQC,EAAeC,EAAQC,EAAcC,GAC7CyB,EACJpE,GAAS9B,OAAO8B,KAAKuC,GAEjBK,EAAM,IAAKuB,GACjB,IAAA,MAAW5G,KAAO6G,EAAa,CAC7B,MAAM5G,EAAID,EACN+C,EAAclB,EAAM5B,IACpBU,OAAOC,UAAUC,eAAeC,KAAKkE,EAAO/E,aAAM+E,EAAM/E,KACzDoF,EAAgCpF,GAAK+E,EAAM/E,GAEhD,CACA,OAAOoF,CACT"}
|
|
1
|
+
{"version":3,"file":"component-presets.js","sources":["../../../src/helper/utils/component-presets.ts"],"sourcesContent":["/**\r\n * Component prop presets — global defaults + named bundles for sky-* CEs.\r\n *\r\n * Precedence (highest wins):\r\n * explicit user prop (attribute, framework property, or marked/diverged) →\r\n * named `preset` → provider `defaults` → built-in class default\r\n *\r\n * A named preset overlays `defaults` unless it sets `$inheritDefaults: false`,\r\n * in which case it is used standalone (keys it omits fall to the built-in).\r\n *\r\n * User-owned detection:\r\n * - attributes present before Lit's first reflect (and any attrs added later)\r\n * - props marked via `markUserSetProp`\r\n * - props the consumer changed after presets applied (divergence)\r\n * - props a framework set before first apply (current value ≠ constructor\r\n * snapshot from `captureBuiltInPropSnapshot` — covers Vue/React CE property\r\n * bindings that never create attributes). Overriding *back* to the exact\r\n * built-in default still needs an attribute or `markUserSetProp`.\r\n *\r\n * @see sky-config-provider\r\n */\r\n\r\nimport { createContext } from \"@lit/context\";\r\n\r\n/** Prop bag applied to a single component instance. */\r\nexport type ComponentPropBag = Record<string, unknown> & {\r\n /**\r\n * Named presets only: set `false` to ignore `defaults[component]` and use\r\n * this bag standalone. Omitted / `true` keeps the default layering.\r\n */\r\n $inheritDefaults?: boolean;\r\n};\r\n\r\n/** Reserved bag keys that configure resolution instead of naming a prop. */\r\nconst RESERVED_BAG_KEYS = new Set([\"$inheritDefaults\"]);\r\n\r\n/**\r\n * Config keys are custom element tag names (`sky-button`).\r\n * Short aliases without the `sky-` prefix (`button`) are also accepted.\r\n */\r\nexport type ComponentPresetKey = string;\r\n\r\nexport type ComponentPresetsConfig = {\r\n /** Applied when the instance does not set the prop (no matching attribute). */\r\n defaults?: Partial<Record<ComponentPresetKey, ComponentPropBag>>;\r\n /** Named bundles selected via `preset=\"name\"` on the instance. */\r\n presets?: Partial<\r\n Record<ComponentPresetKey, Record<string, ComponentPropBag>>\r\n >;\r\n};\r\n\r\n/**\r\n * Lookup aliases for a component key: tag name and short name.\r\n * `sky-button` ↔ `button`, `sky-button-group` ↔ `button-group`.\r\n */\r\nexport function presetKeyAliases(key: string): string[] {\r\n const k = String(key || \"\").trim();\r\n if (!k) return [];\r\n const aliases = new Set<string>([k]);\r\n if (k.startsWith(\"sky-\")) aliases.add(k.slice(4));\r\n else aliases.add(`sky-${k}`);\r\n return [...aliases];\r\n}\r\n\r\n/** First matching bag under `defaults` / `presets` for any alias of `key`. */\r\nexport function lookupPresetSection<T>(\r\n section: Partial<Record<string, T>> | null | undefined,\r\n key: string,\r\n): T | undefined {\r\n if (!section) return undefined;\r\n for (const alias of presetKeyAliases(key)) {\r\n if (Object.prototype.hasOwnProperty.call(section, alias)) {\r\n return section[alias];\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** Merge all alias bags (short + tag) under a section key. Later aliases win. */\r\nfunction mergeAliasBags(\r\n section: Partial<Record<string, ComponentPropBag>> | null | undefined,\r\n key: string,\r\n): ComponentPropBag {\r\n if (!section) return {};\r\n let out: ComponentPropBag = {};\r\n for (const alias of presetKeyAliases(key)) {\r\n const bag = section[alias];\r\n if (bag) out = { ...out, ...bag };\r\n }\r\n return out;\r\n}\r\n\r\nfunction mergeAliasNamedMaps(\r\n section:\r\n | Partial<Record<string, Record<string, ComponentPropBag>>>\r\n | null\r\n | undefined,\r\n key: string,\r\n): Record<string, ComponentPropBag> {\r\n if (!section) return {};\r\n const out: Record<string, ComponentPropBag> = {};\r\n for (const alias of presetKeyAliases(key)) {\r\n const named = section[alias];\r\n if (!named) continue;\r\n for (const [name, bag] of Object.entries(named)) {\r\n out[name] = { ...(out[name] ?? {}), ...(bag ?? {}) };\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Canonical tag form for storage/docs: `button` → `sky-button`.\r\n * Already-tagged names are unchanged.\r\n */\r\nexport function toPresetTagKey(key: string): string {\r\n const k = String(key || \"\").trim();\r\n if (!k) return k;\r\n return k.startsWith(\"sky-\") ? k : `sky-${k}`;\r\n}\r\n\r\n/** Lit context provided by `<sky-config-provider>`. */\r\nexport const configContext = createContext<ComponentPresetsConfig | null>(\r\n \"sky/component-presets\",\r\n);\r\n\r\n/** camelCase / PascalCase → kebab-case attribute name. */\r\nexport function camelToKebab(name: string): string {\r\n return name\r\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\r\n .replace(/_/g, \"-\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * Attributes present before Lit's first reflect (captured in `PresetController`).\r\n * Needed so reflected class defaults (e.g. `variant=\"solid\"`) do not look user-owned.\r\n */\r\nconst initialUserAttrs = new WeakMap<HTMLElement, Set<string>>();\r\n\r\n/** Props explicitly marked user-owned (property-only writes / divergence). */\r\nconst userOwnedProps = new WeakMap<HTMLElement, Set<string>>();\r\n\r\n/** Last values written by `applyPresetLayer` (for divergence detection). */\r\nconst lastPresetApplied = new WeakMap<HTMLElement, Map<string, unknown>>();\r\n\r\n/**\r\n * Prop values at CE construction (before Vue/React assign host props).\r\n * Call from `PresetController` construction so field initializers are visible.\r\n */\r\nconst builtInPropSnapshots = new WeakMap<HTMLElement, Map<string, unknown>>();\r\n\r\nconst PRESET_SKIP_KEYS = new Set([\"preset\", ...RESERVED_BAG_KEYS]);\r\n\r\n/**\r\n * Snapshot host attributes once (call from controller `hostConnected`, before first update).\r\n * Declarative HTML / early `setAttribute` calls are treated as user-owned thereafter.\r\n */\r\nexport function captureInitialUserAttributes(host: HTMLElement): void {\r\n if (initialUserAttrs.has(host)) return;\r\n initialUserAttrs.set(host, new Set(host.getAttributeNames()));\r\n}\r\n\r\n/**\r\n * Snapshot Lit `@property` values once during host construction (before frameworks\r\n * assign props). Later diffs against this map mark framework property bindings as\r\n * user-owned so presets do not overwrite them.\r\n *\r\n * Declare `PresetController` after the host's presettable `@property` fields so\r\n * those initials are already assigned when this runs. Keys still `undefined` are\r\n * filled by `finalizeBuiltInPropSnapshot` on connect (constructor-body defaults).\r\n */\r\nexport function captureBuiltInPropSnapshot(host: HTMLElement): void {\r\n if (builtInPropSnapshots.has(host)) return;\r\n const snap = new Map<string, unknown>();\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n const meta = (\r\n host.constructor as { elementProperties?: Map<PropertyKey, unknown> }\r\n ).elementProperties;\r\n if (meta) {\r\n for (const prop of meta.keys()) {\r\n if (typeof prop !== \"string\" || PRESET_SKIP_KEYS.has(prop)) continue;\r\n snap.set(prop, record[prop]);\r\n }\r\n }\r\n builtInPropSnapshots.set(host, snap);\r\n}\r\n\r\n/**\r\n * Fill snapshot entries that were still `undefined` when the controller\r\n * constructed (e.g. defaults assigned in the host `constructor` body after the\r\n * controller field). Does not overwrite defined snapshot values — those are the\r\n * true built-ins Vue/React may have already overridden by connect time.\r\n */\r\nexport function finalizeBuiltInPropSnapshot(host: HTMLElement): void {\r\n if (!builtInPropSnapshots.has(host)) {\r\n captureBuiltInPropSnapshot(host);\r\n return;\r\n }\r\n const snap = builtInPropSnapshots.get(host)!;\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n for (const [key, value] of snap) {\r\n if (value === undefined && record[key] !== undefined) {\r\n snap.set(key, record[key]);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Merge any attributes that appeared after connect (late framework bindings)\r\n * into the user-owned attribute snapshot.\r\n */\r\nexport function refreshUserAttributes(host: HTMLElement): void {\r\n const set = initialUserAttrs.get(host) ?? new Set<string>();\r\n for (const name of host.getAttributeNames()) set.add(name);\r\n initialUserAttrs.set(host, set);\r\n}\r\n\r\n/**\r\n * Mark a prop as consumer-owned so presets will not overwrite it.\r\n * Use for property-only bindings (no attribute) when needed.\r\n */\r\nexport function markUserSetProp(host: HTMLElement, prop: string): void {\r\n const set = userOwnedProps.get(host) ?? new Set<string>();\r\n set.add(prop);\r\n userOwnedProps.set(host, set);\r\n}\r\n\r\n/**\r\n * True when the host had an attribute for this prop before/at apply time,\r\n * the prop was marked user-owned (explicit mark or post-apply divergence),\r\n * or a framework assigned a property that differs from the constructor snapshot\r\n * before presets first wrote that key.\r\n */\r\nexport function isUserSetProp(host: HTMLElement, prop: string): boolean {\r\n if (userOwnedProps.get(host)?.has(prop)) return true;\r\n const kebab = camelToKebab(prop);\r\n const initial = initialUserAttrs.get(host);\r\n if (initial?.has(kebab) || (kebab !== prop && initial?.has(prop))) {\r\n return true;\r\n }\r\n if (\r\n !initial &&\r\n (host.hasAttribute(kebab) || (kebab !== prop && host.hasAttribute(prop)))\r\n ) {\r\n return true;\r\n }\r\n // Framework CE bindings (Vue/React) set properties, not attributes.\r\n if (!lastPresetApplied.get(host)?.has(prop)) {\r\n const snap = builtInPropSnapshots.get(host);\r\n if (snap?.has(prop)) {\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n if (record[prop] !== snap.get(prop)) return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Permanently mark props a framework changed before our first write for that key.\r\n */\r\nfunction detectPreApplyFrameworkProps(\r\n host: HTMLElement,\r\n layer: ComponentPropBag,\r\n): void {\r\n const snap = builtInPropSnapshots.get(host);\r\n if (!snap?.size) return;\r\n const applied = lastPresetApplied.get(host);\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n for (const key of Object.keys(layer)) {\r\n if (PRESET_SKIP_KEYS.has(key) || applied?.has(key)) continue;\r\n if (userOwnedProps.get(host)?.has(key)) continue;\r\n if (!snap.has(key)) continue;\r\n if (record[key] !== snap.get(key)) {\r\n markUserSetProp(host, key);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * If the consumer changed a prop after we applied a preset value, mark it\r\n * user-owned so later re-applies leave it alone.\r\n */\r\nfunction detectDivergedUserProps(host: HTMLElement): void {\r\n const applied = lastPresetApplied.get(host);\r\n if (!applied?.size) return;\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n for (const [key, appliedValue] of applied) {\r\n if (isUserSetProp(host, key)) continue;\r\n if (record[key] !== appliedValue) {\r\n markUserSetProp(host, key);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Deep-merge parent + child preset configs.\r\n * Inner provider wins per component key, then per prop / named preset.\r\n */\r\nexport function mergePresetsConfig(\r\n parent: ComponentPresetsConfig | null | undefined,\r\n child: ComponentPresetsConfig | null | undefined,\r\n): ComponentPresetsConfig {\r\n const p = parent ?? {};\r\n const c = child ?? {};\r\n\r\n const defaults: NonNullable<ComponentPresetsConfig[\"defaults\"]> = {};\r\n for (const source of [p.defaults, c.defaults]) {\r\n for (const [key, bag] of Object.entries(source ?? {})) {\r\n const tag = toPresetTagKey(key);\r\n defaults[tag] = { ...(defaults[tag] ?? {}), ...(bag ?? {}) };\r\n }\r\n }\r\n\r\n const presets: NonNullable<ComponentPresetsConfig[\"presets\"]> = {};\r\n for (const source of [p.presets, c.presets]) {\r\n for (const [comp, named] of Object.entries(source ?? {})) {\r\n const tag = toPresetTagKey(comp);\r\n const mergedNamed: Record<string, ComponentPropBag> = {\r\n ...(presets[tag] ?? {}),\r\n };\r\n for (const [name, bag] of Object.entries(named ?? {})) {\r\n mergedNamed[name] = { ...(mergedNamed[name] ?? {}), ...(bag ?? {}) };\r\n }\r\n presets[tag] = mergedNamed;\r\n }\r\n }\r\n\r\n return { defaults, presets };\r\n}\r\n\r\nexport type ResolveComponentPropsOptions<T extends Record<string, unknown>> = {\r\n host: HTMLElement;\r\n componentKey: ComponentPresetKey;\r\n /** Value of the instance `preset` attribute/prop. */\r\n presetName?: string | null;\r\n config?: ComponentPresetsConfig | null;\r\n /** Current instance values (class built-ins already applied). */\r\n values: T;\r\n /**\r\n * Keys eligible for preset / default substitution.\r\n * Omit to apply every key present in the preset layer.\r\n */\r\n keys?: Array<keyof T & string>;\r\n};\r\n\r\n/**\r\n * True when `host` is rendered inside another `sky-*` component's shadow tree\r\n * (library chrome), not as slotted / light-DOM content.\r\n *\r\n * Slotted children keep `getRootNode() === document` (or an outer root), so\r\n * provider defaults still apply. Internals like `sky-button` inside `sky-table`\r\n * must not pick up app-level `defaults['sky-button']`.\r\n */\r\nexport function isSkyCompositeInternal(host: HTMLElement): boolean {\r\n if (typeof ShadowRoot === \"undefined\") return false;\r\n const root = host.getRootNode();\r\n if (!(root instanceof ShadowRoot)) return false;\r\n const tag = root.host?.localName ?? \"\";\r\n // Config/theme providers are not composites that own chrome children.\r\n if (tag === \"sky-config-provider\" || tag === \"sky-theme-provider\") {\r\n return false;\r\n }\r\n return tag.startsWith(\"sky-\");\r\n}\r\n\r\n/**\r\n * Walk ancestors for `<sky-config-provider>` and deep-merge outer → inner.\r\n * Used when Lit context has not delivered a value yet (or as a reliable fallback).\r\n */\r\nexport function findNearestPresetsConfig(\r\n host: HTMLElement,\r\n): ComponentPresetsConfig | null {\r\n const layers: ComponentPresetsConfig[] = [];\r\n let el: HTMLElement | null = host.parentElement;\r\n while (el) {\r\n if (el.localName === \"sky-config-provider\") {\r\n const provider = el as HTMLElement & {\r\n presets?: ComponentPresetsConfig;\r\n mergedConfig?: ComponentPresetsConfig;\r\n };\r\n // Prefer live `presets` so we do not depend on context publish timing.\r\n if (provider.mergedConfig) {\r\n // Nearest provider already merged its ancestors — use it and stop.\r\n return provider.mergedConfig;\r\n }\r\n if (provider.presets) layers.push(provider.presets);\r\n }\r\n el = el.parentElement;\r\n }\r\n if (!layers.length) return null;\r\n // layers collected inner→outer while walking up; merge outer first, inner last.\r\n return layers.reduceRight(\r\n (acc, layer) => mergePresetsConfig(acc, layer),\r\n {} as ComponentPresetsConfig,\r\n );\r\n}\r\n\r\n/**\r\n * Resolve the active prop bag for a component: `defaults[key]` ← `presets[key][name]`.\r\n * Any prop key present in that bag can be applied to the instance.\r\n *\r\n * A named preset with `$inheritDefaults: false` is used standalone, so keys it\r\n * omits fall through to the component built-in instead of `defaults`.\r\n */\r\nexport function getPresetLayer(\r\n config: ComponentPresetsConfig | null | undefined,\r\n componentKey: ComponentPresetKey,\r\n presetName?: string | null,\r\n): ComponentPropBag {\r\n const defaults = mergeAliasBags(config?.defaults, componentKey);\r\n const namedMap = mergeAliasNamedMaps(config?.presets, componentKey);\r\n const named =\r\n presetName && namedMap[presetName] ? namedMap[presetName]! : null;\r\n\r\n const merged =\r\n named && named.$inheritDefaults === false\r\n ? { ...named }\r\n : { ...defaults, ...(named ?? {}) };\r\n\r\n for (const key of RESERVED_BAG_KEYS) delete merged[key];\r\n return merged;\r\n}\r\n\r\n/** Per-host map of prop → value before presets overwrote it (for revert). */\r\nconst presetRestoreValues = new WeakMap<HTMLElement, Map<string, unknown>>();\r\n\r\nexport type ApplyPresetLayerOptions = {\r\n /** Extra keys never written from presets. */\r\n skipKeys?: string[];\r\n};\r\n\r\n/**\r\n * Write every key from the preset layer onto the host (any component prop).\r\n * Skips user-owned attributes / props. Remembers prior values so removing a\r\n * key from the provider config restores the built-in / prior value.\r\n *\r\n * Before writing, detects divergence: if the host value no longer matches the\r\n * last preset-applied value, that prop is marked user-owned (property-only\r\n * overrides after first apply). Also locks in framework property bindings that\r\n * differ from the constructor snapshot before the first write for that key.\r\n *\r\n * @returns true if any host property changed\r\n */\r\nexport function applyPresetLayer(\r\n host: HTMLElement,\r\n layer: ComponentPropBag,\r\n options?: ApplyPresetLayerOptions,\r\n): boolean {\r\n refreshUserAttributes(host);\r\n detectPreApplyFrameworkProps(host, layer);\r\n detectDivergedUserProps(host);\r\n\r\n const skip = new Set([\r\n ...PRESET_SKIP_KEYS,\r\n ...(options?.skipKeys ?? []),\r\n ]);\r\n const restore = presetRestoreValues.get(host) ?? new Map<string, unknown>();\r\n const applied = lastPresetApplied.get(host) ?? new Map<string, unknown>();\r\n let changed = false;\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n\r\n // Drop / revert keys no longer provided by the layer.\r\n for (const key of [...restore.keys()]) {\r\n if (\r\n Object.prototype.hasOwnProperty.call(layer, key) &&\r\n layer[key] !== undefined\r\n ) {\r\n continue;\r\n }\r\n if (!isUserSetProp(host, key) && restore.has(key)) {\r\n const prev = restore.get(key);\r\n if (record[key] !== prev) {\r\n record[key] = prev;\r\n changed = true;\r\n }\r\n }\r\n restore.delete(key);\r\n applied.delete(key);\r\n }\r\n\r\n // Apply all keys in the layer (full prop surface — not a whitelist).\r\n for (const [key, value] of Object.entries(layer)) {\r\n if (skip.has(key) || value === undefined) continue;\r\n if (isUserSetProp(host, key)) {\r\n applied.delete(key);\r\n continue;\r\n }\r\n if (!restore.has(key)) {\r\n restore.set(key, record[key]);\r\n }\r\n if (record[key] !== value) {\r\n record[key] = value;\r\n changed = true;\r\n }\r\n applied.set(key, value);\r\n }\r\n\r\n presetRestoreValues.set(host, restore);\r\n lastPresetApplied.set(host, applied);\r\n return changed;\r\n}\r\n\r\n/**\r\n * Clear preset-applied props and restore saved built-ins (e.g. when paused in a group).\r\n */\r\nexport function clearAppliedPresetLayer(host: HTMLElement): boolean {\r\n const restore = presetRestoreValues.get(host);\r\n if (!restore?.size) return false;\r\n const record = host as HTMLElement & Record<string, unknown>;\r\n let changed = false;\r\n for (const [key, prev] of restore) {\r\n if (isUserSetProp(host, key)) continue;\r\n if (record[key] !== prev) {\r\n record[key] = prev;\r\n changed = true;\r\n }\r\n }\r\n restore.clear();\r\n lastPresetApplied.get(host)?.clear();\r\n return changed;\r\n}\r\n\r\n/**\r\n * Resolve effective props for styling / behavior without mutating the host.\r\n * When `keys` is omitted, every key in the preset layer is considered.\r\n * Skips keys the user set via attributes.\r\n */\r\nexport function resolveComponentProps<T extends Record<string, unknown>>(\r\n options: ResolveComponentPropsOptions<T>,\r\n): T {\r\n const { host, componentKey, presetName, config, values, keys } = options;\r\n const layer = getPresetLayer(config, componentKey, presetName);\r\n const keysToApply =\r\n keys ?? (Object.keys(layer) as Array<keyof T & string>);\r\n\r\n const out = { ...values };\r\n for (const key of keysToApply) {\r\n const k = key as string;\r\n if (isUserSetProp(host, k)) continue;\r\n if (Object.prototype.hasOwnProperty.call(layer, k) && layer[k] !== undefined) {\r\n (out as Record<string, unknown>)[k] = layer[k];\r\n }\r\n }\r\n return out;\r\n}\r\n"],"names":["RESERVED_BAG_KEYS","Set","presetKeyAliases","key","k","String","trim","aliases","startsWith","add","slice","lookupPresetSection","section","alias","Object","prototype","hasOwnProperty","call","toPresetTagKey","configContext","createContext","camelToKebab","name","replace","toLowerCase","initialUserAttrs","WeakMap","userOwnedProps","lastPresetApplied","builtInPropSnapshots","PRESET_SKIP_KEYS","captureInitialUserAttributes","host","has","set","getAttributeNames","captureBuiltInPropSnapshot","snap","Map","record","meta","constructor","elementProperties","prop","keys","finalizeBuiltInPropSnapshot","get","value","refreshUserAttributes","markUserSetProp","isUserSetProp","kebab","initial","hasAttribute","mergePresetsConfig","parent","child","p","c","defaults","source","bag","entries","tag","presets","comp","named","mergedNamed","isSkyCompositeInternal","ShadowRoot","root","getRootNode","localName","findNearestPresetsConfig","layers","el","parentElement","provider","mergedConfig","push","length","reduceRight","acc","layer","getPresetLayer","config","componentKey","presetName","out","mergeAliasBags","namedMap","mergeAliasNamedMaps","merged","$inheritDefaults","presetRestoreValues","applyPresetLayer","options","size","applied","detectPreApplyFrameworkProps","appliedValue","detectDivergedUserProps","skip","skipKeys","restore","changed","prev","delete","clearAppliedPresetLayer","clear","resolveComponentProps","values","keysToApply"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"size-resolver.js","sources":["../../../src/helper/utils/size-resolver.ts"],"sourcesContent":["/**\
|
|
1
|
+
{"version":3,"file":"size-resolver.js","sources":["../../../src/helper/utils/size-resolver.ts"],"sourcesContent":["/**\n * Resolves a radius input into a valid CSS radius value.\n * @param input - The radius input (`sm`, `md`, `lg`, `full`, `none`, or CSS length).\n * @returns The resolved CSS radius value.\n */\nexport function resolveRadius(input: string) {\n const map: Record<string, string> = {\n none: '0px',\n sm: 'var(--sky-radius-primary)',\n md: 'var(--sky-radius-secondary)',\n lg: 'var(--sky-radius-tertiary)',\n full: '9999px',\n };\n return Object.prototype.hasOwnProperty.call(map, input) ? map[input] : input;\n}\n"],"names":["resolveRadius","input","map","none","sm","md","lg","full","Object","prototype","hasOwnProperty","call"],"mappings":"AAKO,SAASA,EAAcC,GAC5B,MAAMC,EAA8B,CAClCC,KAAM,MACNC,GAAI,4BACJC,GAAI,8BACJC,GAAI,6BACJC,KAAM,UAER,OAAOC,OAAOC,UAAUC,eAAeC,KAAKT,EAAKD,GAASC,EAAID,GAASA,CACzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-avatar.js","sources":["../../src/sky-avatar/sky-avatar.ts"],"sourcesContent":["import { LitElement, html, css, PropertyValues, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { when } from \"lit/directives/when.js\";\nimport { keyed } from \"lit/directives/keyed.js\";\nimport { resolveRadius } from \"../helper/utils/size-resolver\";\nimport { resolveColor } from \"../helper/utils/color-resolver\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport { PresetController } from \"../helper/controllers/preset-controller\";\n\nexport type SkyAvatarFit = \"cover\" | \"contain\";\n\nconst SIZE_PRESETS: Record<string, string> = {\n sm: \"var(--sky-space-8, 2rem)\",\n md: \"var(--sky-space-10, 2.5rem)\",\n lg: \"var(--sky-space-12, 3rem)\",\n};\n\nconst SIZE_PX: Record<string, number> = { sm: 32, md: 40, lg: 48 };\n\n\n/**\n * @element sky-avatar\n *\n * @summary Avatar that renders image content or fallback slot content with configurable size, radius, outline, and loading state.\n *\n * @status stable\n * @since 1.0.0\n *\n * @documentation https://sky-ui.com/components/avatar\n *\n * @slot - Fallback content (e.g., initials or an icon) rendered when `image` is empty.\n *\n * @property {string} size - Avatar size: preset `sm` | `md` | `lg` or CSS length (for example `50px`).\n * @property {string} image - Image URL; fallback slot is shown on load error.\n * @property {SkyAvatarFit} fit - Image fill mode: `cover` (default) or `contain`.\n * @property {string} radius - Border radius token or CSS value.\n * @property {string} color - Token or CSS color applied to fallback slot content.\n * @property {string} alt - Accessible name when image content conveys meaning.\n * @property {string} outline - CSS `outline` value.\n * @property {string} outlineOffset - CSS `outline-offset` value.\n * @property {boolean} loading - Shows loading overlay when true.\n * @property {boolean} disabled - Dims and disables pointer interactions.\n * @property {boolean} interactive - Enables interactive behavior with keyboard/click activation.\n * @property {boolean} shadow - When true, applies solid surface, border ring, and elevation; no backdrop blur.\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\n *\n * @csspart avatar - The avatar container element.\n * @csspart loading-overlay - The loading overlay container (shown when `loading` is true).\n * @csspart spinner - The spinner element shown when loading.\n *\n * @Behavior\n * - Applies image content when available; otherwise renders fallback slot content.\n * - Uses keyed rendering to reset avatar content when image availability changes.\n * - Scales spinner dimensions from resolved avatar size for numeric values.\n * - Respects reduced-motion preference by disabling spinner animation.\n *\n * @example\n * ```html\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\n * JD\n * </sky-avatar>\n * ```\n * ```vue\n * <template>\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\n * JD\n * </sky-avatar>\n * </template>\n * ```\n * ```jsx\n * export default function Demo() {\n * return (\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\n * JD\n * </sky-avatar>\n * );\n * }\n * ```\n */\n@customElement(\"sky-avatar\")\nexport class SkyAvatar extends LitElement {\n\n /** Avatar size: preset sm|md|lg or CSS length. @default \"50px\" */\n @property({ type: String }) size: string = \"50px\";\n\n /** Image URL. On error, slot is shown. @default \"\" */\n @property({ type: String }) image: string = \"\";\n\n /** How the image fills the avatar. @default \"cover\" */\n @property({ type: String, reflect: true }) fit: SkyAvatarFit = \"cover\";\n\n /** Border radius token or CSS value. @default \"md\" */\n @property({ type: String }) radius: string = \"md\";\n\n /** Accessible name when image is set. @default \"\" */\n @property({ type: String }) alt: string = \"\";\n\n /** Slot text/icon color token/CSS color. @default \"dark\" */\n @property({ type: String }) color: string = \"dark\";\n\n /** Outline string. @default \"\" */\n @property({ type: String }) outline: string = \"\";\n\n /** Outline offset value. @default \"2px\" */\n @property({ type: String }) outlineOffset: string = \"2px\";\n\n /** Loading state. @default false */\n @property({ type: Boolean, reflect: true }) loading: boolean = false;\n\n /** Disables the avatar. @default false */\n @property({ type: Boolean, reflect: true }) disabled: boolean = false;\n\n /** When true, focusable with focus ring and click. @default false */\n @property({ type: Boolean }) interactive: boolean = false;\n\n /**\n * Elevated ring styling (`border`, inset shadow). No backdrop blur on avatar media.\n * `false` (default) keeps the avatar minimal for inline use.\n */\n @property({ type: Boolean, reflect: true })\n shadow = false;\n\n /** True when image failed to load; show slot instead. */\n @state() private _imageError = false;\n\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-flex;\n line-height: 0;\n justify-content: center;\n align-items: center;\n }\n\n :host([disabled]) {\n opacity: var(--sky-opacity-disabled, 0.5);\n pointer-events: none;\n }\n\n .avatar {\n width: var(--avatar-size);\n height: var(--avatar-size);\n position: relative;\n background-size: var(--avatar-bg-size, cover);\n background-position: center;\n background-repeat: no-repeat;\n border-radius: var(--avatar-border-radius, var(--sky-radius-secondary));\n display: flex;\n color: var(--slot-text-color);\n justify-content: center;\n align-items: center;\n background-clip: padding-box;\n -webkit-background-clip: padding-box;\n box-sizing: border-box;\n overflow: hidden;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n }\n\n :host([shadow]) .avatar {\n background-color: var(--sky-glass-solid);\n box-shadow:\n var(--sky-box-shadow-inset),\n var(--sky-box-shadow-primary);\n border: var(--sky-border-primary);\n }\n\n :host([loading]) .avatar {\n will-change: transform;\n transform: translateZ(0);\n }\n\n .avatar:focus-visible {\n outline: none;\n box-shadow: var(--sky-focus-ring);\n }\n\n :host([shadow]) .avatar:focus-visible {\n box-shadow:\n var(--sky-focus-ring),\n var(--sky-box-shadow-inset),\n var(--sky-box-shadow-primary);\n }\n\n .avatar--interactive {\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n\n .image-probe {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n pointer-events: none;\n }\n\n ::slotted(*) {\n font-family: var(--sky-font);\n font-weight: var(--sky-font-weight-semibold, 600);\n font-size: calc(var(--avatar-size) * 0.38);\n line-height: 1;\n letter-spacing: var(--sky-letter-spacing-normal, 0);\n text-align: center;\n display: flex;\n justify-content: center;\n align-items: center;\n object-fit: contain;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n }\n\n .loading-overlay {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: var(--sky-overlay-secondary);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1;\n visibility: hidden;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n }\n :host([loading]) .loading-overlay {\n visibility: visible;\n }\n\n .spinner {\n border-width: 2px;\n border-style: solid;\n border-color: var(--sky-border-light);\n border-top-color: var(--slot-text-color, var(--sky-text-primary));\n border-radius: var(--sky-radius-full);\n width: 40px;\n height: 40px;\n animation: spin var(--sky-duration-slow, 1s) linear infinite;\n }\n\n @keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host([loading]) .avatar {\n will-change: auto;\n transform: none;\n }\n\n .spinner {\n animation: none;\n border-top-color: var(--slot-text-color, var(--sky-text-primary));\n opacity: var(--sky-opacity-muted, 0.7);\n }\n }\n `;\n\n override updated(changed: PropertyValues): void {\n super.updated(changed);\n\n if (changed.has(\"image\")) {\n this._imageError = false;\n }\n\n if (\n changed.has(\"size\") ||\n changed.has(\"fit\") ||\n changed.has(\"radius\") ||\n changed.has(\"color\")\n ) {\n const resolvedSize = SIZE_PRESETS[this.size] ?? this.size;\n this.style.setProperty(\"--avatar-size\", resolvedSize);\n\n this.style.setProperty(\n \"--avatar-bg-size\",\n this.fit === \"contain\" ? \"contain\" : \"cover\",\n );\n\n this.style.setProperty(\n \"--avatar-border-radius\",\n resolveRadius(this.radius),\n );\n\n if (this.color) {\n this.style.setProperty(\"--slot-text-color\", resolveColor(this.color));\n }\n }\n\n if (changed.has(\"size\") || changed.has(\"loading\")) {\n this.adjustSpinnerSize();\n }\n }\n\n /** Marks image load success and keeps image rendering active. */\n private _onImageLoad = (): void => {\n this._imageError = false;\n };\n\n /** Marks image load failure and enables fallback slot rendering. */\n private _onImageError = (): void => {\n this._imageError = true;\n };\n\n /** Re-dispatches click in interactive mode for host-level consumers. */\n private _handleInteractiveClick(e: Event): void {\n if (!this.interactive || this.disabled) return;\n e.stopPropagation();\n this.dispatchEvent(new Event(\"click\", { bubbles: true, composed: true }));\n }\n\n /** Handles keyboard activation in interactive mode. */\n private _handleInteractiveKeydown(e: KeyboardEvent): void {\n if (!this.interactive || this.disabled) return;\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n this.dispatchEvent(new Event(\"click\", { bubbles: true, composed: true }));\n }\n }\n\n\n\n /** Derives spinner dimensions from current avatar size. */\n private adjustSpinnerSize(): void {\n const avatarSizePx =\n SIZE_PX[this.size] ?? parseFloat(this.size);\n if (Number.isFinite(avatarSizePx)) {\n const spinnerSize = avatarSizePx * 0.25;\n const spinner = this.shadowRoot?.querySelector<HTMLElement>(\".spinner\");\n if (spinner) {\n spinner.style.width = `${spinnerSize}px`;\n spinner.style.height = `${spinnerSize}px`;\n spinner.style.borderWidth = `${Math.max(1, spinnerSize / 10)}px`;\n }\n }\n }\n\n override render() {\n const showImage = this.image && !this._imageError;\n const avatarStyle: Record<string, string> = {};\n if (showImage) {\n avatarStyle[\"backgroundImage\"] = `url(${encodeURI(this.image)})`;\n }\n if (this.outline) {\n avatarStyle[\"outline\"] = this.outline;\n }\n if (this.outlineOffset) {\n avatarStyle[\"outlineOffset\"] = this.outlineOffset;\n }\n\n const role = this.interactive\n ? \"button\"\n : showImage && this.alt\n ? \"img\"\n : nothing;\n const ariaLabel =\n this.interactive && this.alt\n ? this.alt\n : showImage && this.alt\n ? this.alt\n : nothing;\n\n return html`\n <div\n class=${classMap({\n avatar: true,\n \"avatar--interactive\": !!this.interactive,\n })}\n part=\"avatar\"\n style=${styleMap(avatarStyle)}\n role=${role}\n aria-label=${ariaLabel}\n tabindex=${this.interactive && !this.disabled ? \"0\" : nothing}\n @click=${this._handleInteractiveClick}\n @keydown=${this._handleInteractiveKeydown}\n >\n ${this.image\n ? html`<img\n class=\"image-probe\"\n src=${this.image}\n alt=\"\"\n @load=${this._onImageLoad}\n @error=${this._onImageError}\n />`\n : nothing}\n <div class=\"loading-overlay\" part=\"loading-overlay\">\n <div class=\"spinner\" part=\"spinner\"></div>\n </div>\n ${keyed(\n showImage ? \"_img_\" : \"_slot_\",\n when(!showImage, () => html`<slot></slot>`, () => nothing),\n )}\n </div>\n `;\n }\n}\n"],"names":["SIZE_PRESETS","sm","md","lg","SIZE_PX","SkyAvatar","LitElement","constructor","super","arguments","this","size","image","fit","radius","alt","color","outline","outlineOffset","loading","disabled","interactive","shadow","_imageError","preset","_presets","PresetController","_onImageLoad","_onImageError","updated","changed","has","resolvedSize","style","setProperty","resolveRadius","resolveColor","adjustSpinnerSize","_handleInteractiveClick","e","stopPropagation","dispatchEvent","Event","bubbles","composed","_handleInteractiveKeydown","key","preventDefault","avatarSizePx","parseFloat","Number","isFinite","spinnerSize","spinner","shadowRoot","querySelector","width","height","borderWidth","Math","max","render","showImage","avatarStyle","encodeURI","role","nothing","ariaLabel","html","classMap","avatar","styleMap","keyed","when","styles","css","__decorateClass","property","type","String","prototype","reflect","Boolean","state","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-avatar.js","sources":["../../src/sky-avatar/sky-avatar.ts"],"sourcesContent":["import { LitElement, html, css, PropertyValues, nothing } from \"lit\";\r\nimport { customElement, property, state } from \"lit/decorators.js\";\r\nimport { when } from \"lit/directives/when.js\";\r\nimport { keyed } from \"lit/directives/keyed.js\";\r\nimport { resolveRadius } from \"../helper/utils/size-resolver\";\r\nimport { resolveColor } from \"../helper/utils/color-resolver\";\r\nimport { styleMap } from \"lit/directives/style-map.js\";\r\nimport { classMap } from \"lit/directives/class-map.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyAvatarFit = \"cover\" | \"contain\";\r\n\r\nconst SIZE_PRESETS: Record<string, string> = {\r\n sm: \"var(--sky-space-8, 2rem)\",\r\n md: \"var(--sky-space-10, 2.5rem)\",\r\n lg: \"var(--sky-space-12, 3rem)\",\r\n};\r\n\r\nconst SIZE_PX: Record<string, number> = { sm: 32, md: 40, lg: 48 };\r\n\r\n\r\n/**\r\n * @element sky-avatar\r\n *\r\n * @summary Avatar that renders image content or fallback slot content with configurable size, radius, outline, and loading state.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/avatar\r\n *\r\n * @slot - Fallback content (e.g., initials or an icon) rendered when `image` is empty.\r\n *\r\n * @property {string} size - Avatar size: preset `sm` | `md` | `lg` or CSS length (for example `50px`).\r\n * @property {string} image - Image URL; fallback slot is shown on load error.\r\n * @property {SkyAvatarFit} fit - Image fill mode: `cover` (default) or `contain`.\r\n * @property {string} radius - Border radius token or CSS value.\r\n * @property {string} color - Token or CSS color applied to fallback slot content.\r\n * @property {string} alt - Accessible name when image content conveys meaning.\r\n * @property {string} outline - CSS `outline` value.\r\n * @property {string} outlineOffset - CSS `outline-offset` value.\r\n * @property {boolean} loading - Shows loading overlay when true.\r\n * @property {boolean} disabled - Dims and disables pointer interactions.\r\n * @property {boolean} interactive - Enables interactive behavior with keyboard/click activation.\r\n * @property {boolean} shadow - When true, applies solid surface, border ring, and elevation; no backdrop blur.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n *\r\n * @csspart avatar - The avatar container element.\r\n * @csspart loading-overlay - The loading overlay container (shown when `loading` is true).\r\n * @csspart spinner - The spinner element shown when loading.\r\n *\r\n * @Behavior\r\n * - Applies image content when available; otherwise renders fallback slot content.\r\n * - Uses keyed rendering to reset avatar content when image availability changes.\r\n * - Scales spinner dimensions from resolved avatar size for numeric values.\r\n * - Respects reduced-motion preference by disabling spinner animation.\r\n *\r\n * @example\r\n * ```html\r\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\r\n * JD\r\n * </sky-avatar>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\r\n * JD\r\n * </sky-avatar>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-avatar size=\"56px\" radius=\"full\" color=\"primary\">\r\n * JD\r\n * </sky-avatar>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-avatar\")\r\nexport class SkyAvatar extends LitElement {\r\n\r\n /** Avatar size: preset sm|md|lg or CSS length. @default \"50px\" */\r\n @property({ type: String }) size: string = \"50px\";\r\n\r\n /** Image URL. On error, slot is shown. @default \"\" */\r\n @property({ type: String }) image: string = \"\";\r\n\r\n /** How the image fills the avatar. @default \"cover\" */\r\n @property({ type: String, reflect: true }) fit: SkyAvatarFit = \"cover\";\r\n\r\n /** Border radius token or CSS value. @default \"md\" */\r\n @property({ type: String }) radius: string = \"md\";\r\n\r\n /** Accessible name when image is set. @default \"\" */\r\n @property({ type: String }) alt: string = \"\";\r\n\r\n /** Slot text/icon color token/CSS color. @default \"dark\" */\r\n @property({ type: String }) color: string = \"dark\";\r\n\r\n /** Outline string. @default \"\" */\r\n @property({ type: String }) outline: string = \"\";\r\n\r\n /** Outline offset value. @default \"2px\" */\r\n @property({ type: String }) outlineOffset: string = \"2px\";\r\n\r\n /** Loading state. @default false */\r\n @property({ type: Boolean, reflect: true }) loading: boolean = false;\r\n\r\n /** Disables the avatar. @default false */\r\n @property({ type: Boolean, reflect: true }) disabled: boolean = false;\r\n\r\n /** When true, focusable with focus ring and click. @default false */\r\n @property({ type: Boolean }) interactive: boolean = false;\r\n\r\n /**\r\n * Elevated ring styling (`border`, inset shadow). No backdrop blur on avatar media.\r\n * `false` (default) keeps the avatar minimal for inline use.\r\n */\r\n @property({ type: Boolean, reflect: true })\r\n shadow = false;\r\n\r\n /** True when image failed to load; show slot instead. */\r\n @state() private _imageError = false;\r\n\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-flex;\r\n line-height: 0;\r\n justify-content: center;\r\n align-items: center;\r\n }\r\n\r\n :host([disabled]) {\r\n opacity: var(--sky-opacity-disabled, 0.5);\r\n pointer-events: none;\r\n }\r\n\r\n .avatar {\r\n width: var(--avatar-size);\r\n height: var(--avatar-size);\r\n position: relative;\r\n background-size: var(--avatar-bg-size, cover);\r\n background-position: center;\r\n background-repeat: no-repeat;\r\n border-radius: var(--avatar-border-radius, var(--sky-radius-secondary));\r\n display: flex;\r\n color: var(--slot-text-color);\r\n justify-content: center;\r\n align-items: center;\r\n background-clip: padding-box;\r\n -webkit-background-clip: padding-box;\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n }\r\n\r\n :host([shadow]) .avatar {\r\n background-color: var(--sky-glass-solid);\r\n box-shadow:\r\n var(--sky-box-shadow-inset),\r\n var(--sky-box-shadow-primary);\r\n border: var(--sky-border-primary);\r\n }\r\n\r\n :host([loading]) .avatar {\r\n will-change: transform;\r\n transform: translateZ(0);\r\n }\r\n\r\n .avatar:focus-visible {\r\n outline: none;\r\n box-shadow: var(--sky-focus-ring);\r\n }\r\n\r\n :host([shadow]) .avatar:focus-visible {\r\n box-shadow:\r\n var(--sky-focus-ring),\r\n var(--sky-box-shadow-inset),\r\n var(--sky-box-shadow-primary);\r\n }\r\n\r\n .avatar--interactive {\r\n cursor: pointer;\r\n -webkit-tap-highlight-color: transparent;\r\n }\r\n\r\n .image-probe {\r\n position: absolute;\r\n width: 0;\r\n height: 0;\r\n opacity: 0;\r\n pointer-events: none;\r\n }\r\n\r\n ::slotted(*) {\r\n font-family: var(--sky-font);\r\n font-weight: var(--sky-font-weight-semibold, 600);\r\n font-size: calc(var(--avatar-size) * 0.38);\r\n line-height: 1;\r\n letter-spacing: var(--sky-letter-spacing-normal, 0);\r\n text-align: center;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n object-fit: contain;\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n }\r\n\r\n .loading-overlay {\r\n position: absolute;\r\n inset: 0;\r\n border-radius: inherit;\r\n background: var(--sky-overlay-secondary);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1;\r\n visibility: hidden;\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n }\r\n :host([loading]) .loading-overlay {\r\n visibility: visible;\r\n }\r\n\r\n .spinner {\r\n border-width: 2px;\r\n border-style: solid;\r\n border-color: var(--sky-border-light);\r\n border-top-color: var(--slot-text-color, var(--sky-text-primary));\r\n border-radius: var(--sky-radius-full);\r\n width: 40px;\r\n height: 40px;\r\n animation: spin var(--sky-duration-slow, 1s) linear infinite;\r\n }\r\n\r\n @keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n :host([loading]) .avatar {\r\n will-change: auto;\r\n transform: none;\r\n }\r\n\r\n .spinner {\r\n animation: none;\r\n border-top-color: var(--slot-text-color, var(--sky-text-primary));\r\n opacity: var(--sky-opacity-muted, 0.7);\r\n }\r\n }\r\n `;\r\n\r\n override updated(changed: PropertyValues): void {\r\n super.updated(changed);\r\n\r\n if (changed.has(\"image\")) {\r\n this._imageError = false;\r\n }\r\n\r\n if (\r\n changed.has(\"size\") ||\r\n changed.has(\"fit\") ||\r\n changed.has(\"radius\") ||\r\n changed.has(\"color\")\r\n ) {\r\n const resolvedSize = SIZE_PRESETS[this.size] ?? this.size;\r\n this.style.setProperty(\"--avatar-size\", resolvedSize);\r\n\r\n this.style.setProperty(\r\n \"--avatar-bg-size\",\r\n this.fit === \"contain\" ? \"contain\" : \"cover\",\r\n );\r\n\r\n this.style.setProperty(\r\n \"--avatar-border-radius\",\r\n resolveRadius(this.radius),\r\n );\r\n\r\n if (this.color) {\r\n this.style.setProperty(\"--slot-text-color\", resolveColor(this.color));\r\n }\r\n }\r\n\r\n if (changed.has(\"size\") || changed.has(\"loading\")) {\r\n this.adjustSpinnerSize();\r\n }\r\n }\r\n\r\n /** Marks image load success and keeps image rendering active. */\r\n private _onImageLoad = (): void => {\r\n this._imageError = false;\r\n };\r\n\r\n /** Marks image load failure and enables fallback slot rendering. */\r\n private _onImageError = (): void => {\r\n this._imageError = true;\r\n };\r\n\r\n /** Re-dispatches click in interactive mode for host-level consumers. */\r\n private _handleInteractiveClick(e: Event): void {\r\n if (!this.interactive || this.disabled) return;\r\n e.stopPropagation();\r\n this.dispatchEvent(new Event(\"click\", { bubbles: true, composed: true }));\r\n }\r\n\r\n /** Handles keyboard activation in interactive mode. */\r\n private _handleInteractiveKeydown(e: KeyboardEvent): void {\r\n if (!this.interactive || this.disabled) return;\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n this.dispatchEvent(new Event(\"click\", { bubbles: true, composed: true }));\r\n }\r\n }\r\n\r\n\r\n\r\n /** Derives spinner dimensions from current avatar size. */\r\n private adjustSpinnerSize(): void {\r\n const avatarSizePx =\r\n SIZE_PX[this.size] ?? parseFloat(this.size);\r\n if (Number.isFinite(avatarSizePx)) {\r\n const spinnerSize = avatarSizePx * 0.25;\r\n const spinner = this.shadowRoot?.querySelector<HTMLElement>(\".spinner\");\r\n if (spinner) {\r\n spinner.style.width = `${spinnerSize}px`;\r\n spinner.style.height = `${spinnerSize}px`;\r\n spinner.style.borderWidth = `${Math.max(1, spinnerSize / 10)}px`;\r\n }\r\n }\r\n }\r\n\r\n override render() {\r\n const showImage = this.image && !this._imageError;\r\n const avatarStyle: Record<string, string> = {};\r\n if (showImage) {\r\n avatarStyle[\"backgroundImage\"] = `url(${encodeURI(this.image)})`;\r\n }\r\n if (this.outline) {\r\n avatarStyle[\"outline\"] = this.outline;\r\n }\r\n if (this.outlineOffset) {\r\n avatarStyle[\"outlineOffset\"] = this.outlineOffset;\r\n }\r\n\r\n const role = this.interactive\r\n ? \"button\"\r\n : showImage && this.alt\r\n ? \"img\"\r\n : nothing;\r\n const ariaLabel =\r\n this.interactive && this.alt\r\n ? this.alt\r\n : showImage && this.alt\r\n ? this.alt\r\n : nothing;\r\n\r\n return html`\r\n <div\r\n class=${classMap({\r\n avatar: true,\r\n \"avatar--interactive\": !!this.interactive,\r\n })}\r\n part=\"avatar\"\r\n style=${styleMap(avatarStyle)}\r\n role=${role}\r\n aria-label=${ariaLabel}\r\n tabindex=${this.interactive && !this.disabled ? \"0\" : nothing}\r\n @click=${this._handleInteractiveClick}\r\n @keydown=${this._handleInteractiveKeydown}\r\n >\r\n ${this.image\r\n ? html`<img\r\n class=\"image-probe\"\r\n src=${this.image}\r\n alt=\"\"\r\n @load=${this._onImageLoad}\r\n @error=${this._onImageError}\r\n />`\r\n : nothing}\r\n <div class=\"loading-overlay\" part=\"loading-overlay\">\r\n <div class=\"spinner\" part=\"spinner\"></div>\r\n </div>\r\n ${keyed(\r\n showImage ? \"_img_\" : \"_slot_\",\r\n when(!showImage, () => html`<slot></slot>`, () => nothing),\r\n )}\r\n </div>\r\n `;\r\n }\r\n}\r\n"],"names":["SIZE_PRESETS","sm","md","lg","SIZE_PX","SkyAvatar","LitElement","constructor","super","arguments","this","size","image","fit","radius","alt","color","outline","outlineOffset","loading","disabled","interactive","shadow","_imageError","preset","_presets","PresetController","_onImageLoad","_onImageError","updated","changed","has","resolvedSize","style","setProperty","resolveRadius","resolveColor","adjustSpinnerSize","_handleInteractiveClick","e","stopPropagation","dispatchEvent","Event","bubbles","composed","_handleInteractiveKeydown","key","preventDefault","avatarSizePx","parseFloat","Number","isFinite","spinnerSize","spinner","shadowRoot","querySelector","width","height","borderWidth","Math","max","render","showImage","avatarStyle","encodeURI","role","nothing","ariaLabel","html","classMap","avatar","styleMap","keyed","when","styles","css","__decorateClass","property","type","String","prototype","reflect","Boolean","state","customElement"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-chip.js","sources":["../../src/sky-chip/sky-chip.ts"],"sourcesContent":["import { LitElement, html, css, nothing, unsafeCSS, PropertyValues } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { when } from \"lit/directives/when.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport {\n isDefaultColorToken,\n resolveAccentColorForToken,\n resolveColor,\n resolveInkColorForToken,\n} from \"../helper/utils/color-resolver\";\nimport { cssOnFillEnhance, ON_COLOR_FALLBACK_LIGHT } from \"../helper/utils/contrast-color\";\nimport { resolveRadius } from \"../helper/utils/size-resolver\";\nimport { PresetController } from \"../helper/controllers/preset-controller\";\nimport { SkyIcon } from \"../sky-icon/sky-icon.js\";\nimport { skyFocusRingHostStyles } from \"../helper/styles/focus-ring\";\n\nexport type SkyChipVariant =\n | \"solid\"\n | \"flat\"\n | \"shadow\"\n | \"bordered\"\n | \"faded\"\n | \"light\"\n | \"dot\";\nexport type SkyChipRemovedEventDetail = { label: string };\n\n/**\n * @element sky-chip\n *\n * @summary Customizable chip for tags or selections with icon support, removable action, loading state, and visual variants.\n *\n * @status stable\n * @since 1.0.0\n *\n * @documentation https://sky-ui.com/components/chip\n * @dependency sky-icon\n *\n * @slot - Default slot for the chip label or any other content.\n * @slot prefix - Optional prefix content that can be placed before the label.\n * @slot suffix - Optional suffix content that can be placed after the label.\n *\n * @csspart chip - The visual representation of the chip component.\n * @csspart icon - The icons displayed within the chip.\n * @csspart close-button - The button for removing the chip.\n * @csspart loading-spinner - The spinner that appears during loading.\n * @csspart error-message - The error message displayed for invalid chips.\n *\n * @property {string} label - The label displayed on the chip. Default: `\"\"`.\n * @property {boolean} removable - Whether the chip can be removed by the user. Default: `false`.\n * @property {string} color - The background color of the chip, can accept tokens or custom CSS color. Default: `\"primary\"`.\n * @property {string} radius - The border radius of the chip. Default: `\"full\"`.\n * @property {string} size - The size of the chip, which affects padding and font size. Default: `\"md\"`.\n * @property {string} iconL - Left icon; shown only when the `prefix` slot is empty. Default: `null`.\n * @property {string} iconR - Right icon; shown only when the `suffix` slot is empty. Default: `null`.\n * @property {boolean} loading - Whether the chip is in a loading state. Default: `false`.\n * @property {boolean} disabled - Whether the chip is disabled. Default: `false`.\n * @property {SkyChipVariant} variant - Visual style variant. Default: `\"flat\"`.\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\n * @method removeChip Dispatches `chip-removed` and removes the chip from the DOM.\n *\n * @fires {CustomEvent<SkyChipRemovedEventDetail>} chip-removed - Fired when the chip is removed by user action.\n *\n * @Behavior\n * - Uses default slot content when present, otherwise falls back to `label`.\n * - `iconL` / `iconR` render only when `prefix` / `suffix` slots have no meaningful content.\n * - Emits `chip-removed` and removes itself when removable close action is triggered.\n * - Resolves color tokens and radius/size presets to CSS values at render time.\n *\n * @example\n * ```html\n * <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>\n * ```\n * ```vue\n * <template>\n * <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>\n * </template>\n * ```\n * ```jsx\n * export default function Demo() {\n * return <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>;\n * }\n * ```\n */\n@customElement(\"sky-chip\")\nexport class SkyChip extends LitElement {\n\n static dependencies: Record<string, CustomElementConstructor> = {\n \"sky-icon\": SkyIcon,\n };\n\n @property({ type: String }) label: string = \"\";\n @property({ type: Boolean }) removable: boolean = false;\n\n /** Accepts preset tokens OR any valid CSS color (e.g., #0af, rgb(), var(--x)) */\n @property({ type: String }) color: string = \"primary\";\n\n /** Radius can be any CSS radius value */\n @property({ type: String }) radius: string = \"full\";\n\n /** Size controls padding + font-size (xs, sm, md, lg, xl or numeric like \"14px\") */\n @property({ type: String }) size: string = \"md\";\n\n @property({ type: String }) iconL: string | null = null;\n @property({ type: String }) iconR: string | null = null;\n @property({ type: Boolean }) loading: boolean = false;\n @property({ type: Boolean, reflect: true }) disabled: boolean = false;\n @property({ type: String, reflect: true }) variant: SkyChipVariant = \"flat\";\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 = [\n skyFocusRingHostStyles,\n css`\n :host {\n display: inline-block;\n font-family: var(--sky-font);\n z-index: 1;\n --sky-focus-ring-color: var(--chip-bg-color, var(--sky-active-primary));\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: var(--sky-opacity-disabled, 0.6);\n }\n\n .chip {\n position: relative;\n display: flex;\n gap: 2px;\n width: max-content;\n align-items: center;\n border-radius: var(--sky-radius-secondary);\n color: var(--chip-text-color);\n font-weight: var(--sky-font-weight-medium, 500);\n transition: background-color 0.3s ease, color 0.3s ease,\n box-shadow 0.2s ease, transform 0.2s ease;\n cursor: pointer;\n background-color: var(--chip-bg-color);\n }\n\n .close-button {\n position: relative;\n display: flex;\n height: 18px;\n width: 18px;\n justify-content: center;\n font-size: inherit;\n align-items: center;\n background-color: var(--sky-hover-primary);\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n border: none;\n border-radius: var(--sky-radius-full);\n cursor: pointer;\n color: var(--sky-text-primary);\n transition: color 0.2s ease, background-color 0.2s ease;\n }\n\n .close-button sky-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n transition: color 0.3s ease;\n }\n\n :dir(rtl) .close-button {\n margin-right: var(--sky-space-1, 5px);\n margin-left: unset;\n }\n\n .close-button:hover {\n color: var(--sky-danger-primary);\n }\n\n .close-button:focus-visible {\n outline: none;\n box-shadow: var(--sky-focus-ring);\n }\n\n .close-button:hover sky-icon,\n .close-button:focus-visible sky-icon{\n color: var(--sky-danger-primary);\n outline: none;\n }\n\n .icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--chip-text-color);\n }\n\n sky-icon {\n display: inline-block;\n width: 1em;\n height: 1em;\n }\n\n ::slotted([slot=\"prefix\"]),\n ::slotted([slot=\"suffix\"]) {\n display: inline-flex;\n align-items: center;\n }\n\n .loading-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--sky-overlay-primary);\n border-radius: inherit;\n }\n\n .loading-spinner {\n width: 14px;\n height: 14px;\n border: 2px solid var(--sky-border-light, var(--sky-text-tertiary));\n border-top-color: var(--chip-bg-color);\n border-radius: var(--sky-radius-full);\n animation: spin 0.8s linear infinite;\n }\n\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .chip,\n .close-button,\n .close-button sky-icon {\n transition: none;\n }\n .loading-spinner {\n animation: none;\n }\n }\n\n .chip.bordered {\n border: 1px solid var(--chip-ink-color, var(--chip-bg-color));\n color: var(--chip-ink-color, var(--chip-bg-color));\n background: transparent;\n }\n\n .chip.light {\n background: transparent;\n color: var(--chip-ink-color, var(--chip-bg-color));\n }\n\n .chip.flat {\n box-shadow: none;\n background: color-mix(\n in srgb,\n var(--chip-fill-color, var(--chip-bg-color)) 20%,\n transparent\n );\n color: var(--chip-ink-color, var(--chip-bg-color));\n }\n\n .chip.faded {\n background-color: var(--sky-glass-primary);\n border: 1px solid var(--sky-text-tertiary);\n color: var(--chip-ink-color, var(--chip-bg-color));\n }\n\n .chip.shadow {\n box-shadow: var(--sky-box-shadow-primary);\n }\n\n .chip.shadow:hover {\n box-shadow: var(--sky-box-shadow-hover);\n }\n\n .chip.faded .icon,\n .chip.flat .icon,\n .chip.light .icon,\n .chip.dot .icon,\n .chip.bordered .icon {\n color: var(--chip-ink-color, var(--chip-bg-color));\n }\n\n .chip.dot {\n padding-left: var(--sky-space-5, 20px) !important;\n position: relative;\n background-color: transparent;\n border: 1px solid var(--sky-text-tertiary);\n color: var(--sky-text-primary);\n }\n\n .chip.dot::before {\n content: \"\";\n position: absolute;\n left: var(--sky-space-1\\\\.5, 6px);\n top: 50%;\n transform: translateY(-50%);\n width: var(--sky-space-2, 8px);\n height: var(--sky-space-2, 8px);\n border-radius: var(--sky-radius-full);\n background-color: var(--chip-accent-color, var(--chip-bg-color));\n }\n\n /* Solid / shadow chips: enhance label + icon FG against fill. */\n ${unsafeCSS(\n cssOnFillEnhance([\n {\n selector: \".chip.solid, .chip.shadow\",\n fillVar: \"--chip-bg-color\",\n },\n {\n selector: \".chip.solid .icon, .chip.shadow .icon\",\n fillVar: \"--chip-bg-color\",\n },\n ]),\n )}\n `,\n ];\n\n private _prefixFilled = false;\n private _suffixFilled = false;\n private _defaultFilled = false;\n private _slotFlagsSeeded = false;\n\n /** Updates host CSS variables from current color token/value. */\n protected override updated(changed: PropertyValues): void {\n if (!changed.has(\"color\")) return;\n\n const bgColor = resolveColor(this.color);\n const textColor = isDefaultColorToken(this.color)\n ? \"var(--sky-text-primary)\"\n : ON_COLOR_FALLBACK_LIGHT;\n\n this.style.setProperty(\"--chip-bg-color\", bgColor);\n this.style.setProperty(\"--chip-text-color\", textColor);\n\n if (isDefaultColorToken(this.color)) {\n this.style.setProperty(\"--chip-ink-color\", resolveInkColorForToken(this.color));\n this.style.setProperty(\n \"--chip-accent-color\",\n resolveAccentColorForToken(this.color),\n );\n this.style.setProperty(\"--chip-fill-color\", \"var(--sky-hover-tertiary)\");\n } else {\n this.style.removeProperty(\"--chip-ink-color\");\n this.style.removeProperty(\"--chip-accent-color\");\n this.style.removeProperty(\"--chip-fill-color\");\n }\n }\n\n /** Size resolver similar to button: controls padding + font-size */\n private _resolveSize(\n input: string\n ): {\n padding: string;\n fontSize: string;\n } {\n const presets: Record<string, { padding: string; fontSize: string }> = {\n xs: { padding: \"2px 4px\", fontSize: \"11px\" },\n sm: { padding: \"3px 6px\", fontSize: \"12px\" },\n md: { padding: \"4px 8px\", fontSize: \"13px\" },\n lg: { padding: \"5px 10px\", fontSize: \"14px\" },\n xl: { padding: \"6px 12px\", fontSize: \"15px\" },\n };\n\n if (presets[input]) return presets[input];\n\n const m = String(input).trim().match(/^([\\d.]+)(px|rem|em)?$/i);\n const unit = m?.[2] || \"px\";\n const fsNum = m ? parseFloat(m[1]) : 13;\n const fs = `${fsNum}${unit}`;\n\n const clamp = (n: number, min: number, max: number) =>\n Math.max(min, Math.min(max, n));\n\n const py = clamp(fsNum * 0.4, 2, 10);\n const px = clamp(fsNum * 0.8, 6, 16);\n\n const to = (n: number) => `${Math.round(n * 100) / 100}${unit}`;\n\n return {\n padding: `${to(py)} ${to(px)}`,\n fontSize: fs,\n };\n }\n\n /**\n * Dispatches `chip-removed` and removes the chip from the DOM.\n *\n * @returns {void}\n */\n public removeChip(): void {\n const removeEvent = new CustomEvent<SkyChipRemovedEventDetail>(\"chip-removed\", {\n detail: { label: this.label },\n bubbles: true,\n composed: true,\n });\n this.dispatchEvent(removeEvent);\n this.remove();\n }\n\n private _slotHasMeaningfulContent(slotEl: HTMLSlotElement): boolean {\n const nodes = slotEl.assignedNodes({ flatten: true });\n return nodes.some((n) =>\n n.nodeType === Node.ELEMENT_NODE\n ? true\n : (n.textContent ?? \"\").trim().length > 0\n );\n }\n\n /** Light DOM check works before the shadow tree exists (first render). */\n private _hasPrefixSlotContent(): boolean {\n return this._prefixFilled;\n }\n\n private _hasSuffixSlotContent(): boolean {\n return this._suffixFilled;\n }\n\n private _hasDefaultSlotContent(): boolean {\n return this._defaultFilled;\n }\n\n private _seedSlotFlagsFromLightDom(): void {\n this._prefixFilled = !!this.querySelector('[slot=\"prefix\"]');\n this._suffixFilled = !!this.querySelector('[slot=\"suffix\"]');\n this._defaultFilled = false;\n for (const n of Array.from(this.childNodes)) {\n if (n.nodeType === Node.ELEMENT_NODE) {\n const slot = (n as Element).getAttribute(\"slot\");\n if (slot === \"prefix\" || slot === \"suffix\") continue;\n this._defaultFilled = true;\n break;\n }\n if ((n.textContent ?? \"\").trim().length > 0) {\n this._defaultFilled = true;\n break;\n }\n }\n this._slotFlagsSeeded = true;\n }\n\n override willUpdate(): void {\n if (!this._slotFlagsSeeded) this._seedSlotFlagsFromLightDom();\n }\n\n private _onSlotChange = (e: Event): void => {\n const slot = e.target as HTMLSlotElement | null;\n if (!slot || slot.tagName !== \"SLOT\") return;\n this._slotFlagsSeeded = true;\n const filled = this._slotHasMeaningfulContent(slot);\n const name = slot.name;\n if (name === \"prefix\") {\n if (this._prefixFilled === filled) return;\n this._prefixFilled = filled;\n } else if (name === \"suffix\") {\n if (this._suffixFilled === filled) return;\n this._suffixFilled = filled;\n } else {\n if (this._defaultFilled === filled) return;\n this._defaultFilled = filled;\n }\n this.requestUpdate();\n };\n\n override render() {\n const sizeDef = this._resolveSize(this.size);\n const chipStyle = {\n padding: sizeDef.padding,\n borderRadius: resolveRadius(this.radius),\n fontSize: sizeDef.fontSize,\n };\n\n return html`\n <div\n class=${classMap({\n chip: true,\n [this.variant]: true,\n loading: this.loading,\n })}\n style=${styleMap(chipStyle)}\n part=\"chip\"\n >\n <slot name=\"prefix\" @slotchange=${this._onSlotChange}></slot>\n ${when(!this._hasPrefixSlotContent() && this.iconL, () => html`\n <sky-icon class=\"icon\" part=\"icon\" icon=\"${this.iconL!}\"></sky-icon>\n `)}\n <slot @slotchange=${this._onSlotChange}></slot>\n ${this._hasDefaultSlotContent() ? nothing : this.label}\n <slot name=\"suffix\" @slotchange=${this._onSlotChange}></slot>\n ${when(!this._hasSuffixSlotContent() && this.iconR, () => html`\n <sky-icon class=\"icon icon-right\" part=\"icon\" icon=\"${this.iconR!}\"></sky-icon>\n `)}\n ${when(this.removable, () => html`\n <button\n class=\"close-button\"\n part=\"close-button\"\n type=\"button\"\n aria-label=\"Remove chip\"\n @click=${this.removeChip}\n >\n <sky-icon icon=\"ion:close\"></sky-icon>\n </button>\n `)}\n ${when(this.loading, () => html`\n <div class=\"loading-overlay\" part=\"loading-overlay\">\n <div class=\"loading-spinner\" part=\"loading-spinner\"></div>\n </div>\n `)}\n </div>\n `;\n }\n}"],"names":["SkyChip","LitElement","constructor","super","arguments","this","label","removable","color","radius","size","iconL","iconR","loading","disabled","variant","preset","_presets","PresetController","_prefixFilled","_suffixFilled","_defaultFilled","_slotFlagsSeeded","_onSlotChange","e","slot","target","tagName","filled","_slotHasMeaningfulContent","name","requestUpdate","updated","changed","has","bgColor","resolveColor","textColor","isDefaultColorToken","ON_COLOR_FALLBACK_LIGHT","style","setProperty","resolveInkColorForToken","resolveAccentColorForToken","removeProperty","_resolveSize","input","presets","xs","padding","fontSize","sm","md","lg","xl","m","String","trim","match","unit","fsNum","parseFloat","fs","clamp","n","min","max","Math","py","px","to","round","removeChip","removeEvent","CustomEvent","detail","bubbles","composed","dispatchEvent","remove","slotEl","assignedNodes","flatten","some","nodeType","Node","ELEMENT_NODE","textContent","length","_hasPrefixSlotContent","_hasSuffixSlotContent","_hasDefaultSlotContent","_seedSlotFlagsFromLightDom","querySelector","Array","from","childNodes","getAttribute","willUpdate","render","sizeDef","chipStyle","borderRadius","resolveRadius","html","classMap","chip","styleMap","when","nothing","dependencies","SkyIcon","styles","skyFocusRingHostStyles","css","unsafeCSS","cssOnFillEnhance","selector","fillVar","__decorateClass","property","type","prototype","Boolean","reflect","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-chip.js","sources":["../../src/sky-chip/sky-chip.ts"],"sourcesContent":["import { LitElement, html, css, nothing, unsafeCSS, PropertyValues } from \"lit\";\r\nimport { customElement, property } from \"lit/decorators.js\";\r\nimport { when } from \"lit/directives/when.js\";\r\nimport { styleMap } from \"lit/directives/style-map.js\";\r\nimport { classMap } from \"lit/directives/class-map.js\";\r\nimport {\r\n isDefaultColorToken,\r\n resolveAccentColorForToken,\r\n resolveColor,\r\n resolveInkColorForToken,\r\n} from \"../helper/utils/color-resolver\";\r\nimport { cssOnFillEnhance, ON_COLOR_FALLBACK_LIGHT } from \"../helper/utils/contrast-color\";\r\nimport { resolveRadius } from \"../helper/utils/size-resolver\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\nimport { SkyIcon } from \"../sky-icon/sky-icon.js\";\r\nimport { skyFocusRingHostStyles } from \"../helper/styles/focus-ring\";\r\n\r\nexport type SkyChipVariant =\r\n | \"solid\"\r\n | \"flat\"\r\n | \"shadow\"\r\n | \"bordered\"\r\n | \"faded\"\r\n | \"light\"\r\n | \"dot\";\r\nexport type SkyChipRemovedEventDetail = { label: string };\r\n\r\n/**\r\n * @element sky-chip\r\n *\r\n * @summary Customizable chip for tags or selections with icon support, removable action, loading state, and visual variants.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/chip\r\n * @dependency sky-icon\r\n *\r\n * @slot - Default slot for the chip label or any other content.\r\n * @slot prefix - Optional prefix content that can be placed before the label.\r\n * @slot suffix - Optional suffix content that can be placed after the label.\r\n *\r\n * @csspart chip - The visual representation of the chip component.\r\n * @csspart icon - The icons displayed within the chip.\r\n * @csspart close-button - The button for removing the chip.\r\n * @csspart loading-spinner - The spinner that appears during loading.\r\n * @csspart error-message - The error message displayed for invalid chips.\r\n *\r\n * @property {string} label - The label displayed on the chip. Default: `\"\"`.\r\n * @property {boolean} removable - Whether the chip can be removed by the user. Default: `false`.\r\n * @property {string} color - The background color of the chip, can accept tokens or custom CSS color. Default: `\"primary\"`.\r\n * @property {string} radius - The border radius of the chip. Default: `\"full\"`.\r\n * @property {string} size - The size of the chip, which affects padding and font size. Default: `\"md\"`.\r\n * @property {string} iconL - Left icon; shown only when the `prefix` slot is empty. Default: `null`.\r\n * @property {string} iconR - Right icon; shown only when the `suffix` slot is empty. Default: `null`.\r\n * @property {boolean} loading - Whether the chip is in a loading state. Default: `false`.\r\n * @property {boolean} disabled - Whether the chip is disabled. Default: `false`.\r\n * @property {SkyChipVariant} variant - Visual style variant. Default: `\"flat\"`.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n * @method removeChip Dispatches `chip-removed` and removes the chip from the DOM.\r\n *\r\n * @fires {CustomEvent<SkyChipRemovedEventDetail>} chip-removed - Fired when the chip is removed by user action.\r\n *\r\n * @Behavior\r\n * - Uses default slot content when present, otherwise falls back to `label`.\r\n * - `iconL` / `iconR` render only when `prefix` / `suffix` slots have no meaningful content.\r\n * - Emits `chip-removed` and removes itself when removable close action is triggered.\r\n * - Resolves color tokens and radius/size presets to CSS values at render time.\r\n *\r\n * @example\r\n * ```html\r\n * <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return <sky-chip label=\"Active Filter\" removable iconl=\"ion:funnel\" variant=\"light\"></sky-chip>;\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-chip\")\r\nexport class SkyChip extends LitElement {\r\n\r\n static dependencies: Record<string, CustomElementConstructor> = {\r\n \"sky-icon\": SkyIcon,\r\n };\r\n\r\n @property({ type: String }) label: string = \"\";\r\n @property({ type: Boolean }) removable: boolean = false;\r\n\r\n /** Accepts preset tokens OR any valid CSS color (e.g., #0af, rgb(), var(--x)) */\r\n @property({ type: String }) color: string = \"primary\";\r\n\r\n /** Radius can be any CSS radius value */\r\n @property({ type: String }) radius: string = \"full\";\r\n\r\n /** Size controls padding + font-size (xs, sm, md, lg, xl or numeric like \"14px\") */\r\n @property({ type: String }) size: string = \"md\";\r\n\r\n @property({ type: String }) iconL: string | null = null;\r\n @property({ type: String }) iconR: string | null = null;\r\n @property({ type: Boolean }) loading: boolean = false;\r\n @property({ type: Boolean, reflect: true }) disabled: boolean = false;\r\n @property({ type: String, reflect: true }) variant: SkyChipVariant = \"flat\";\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 = [\r\n skyFocusRingHostStyles,\r\n css`\r\n :host {\r\n display: inline-block;\r\n font-family: var(--sky-font);\r\n z-index: 1;\r\n --sky-focus-ring-color: var(--chip-bg-color, var(--sky-active-primary));\r\n }\r\n\r\n :host([disabled]) {\r\n pointer-events: none;\r\n opacity: var(--sky-opacity-disabled, 0.6);\r\n }\r\n\r\n .chip {\r\n position: relative;\r\n display: flex;\r\n gap: 2px;\r\n width: max-content;\r\n align-items: center;\r\n border-radius: var(--sky-radius-secondary);\r\n color: var(--chip-text-color);\r\n font-weight: var(--sky-font-weight-medium, 500);\r\n transition: background-color 0.3s ease, color 0.3s ease,\r\n box-shadow 0.2s ease, transform 0.2s ease;\r\n cursor: pointer;\r\n background-color: var(--chip-bg-color);\r\n }\r\n\r\n .close-button {\r\n position: relative;\r\n display: flex;\r\n height: 18px;\r\n width: 18px;\r\n justify-content: center;\r\n font-size: inherit;\r\n align-items: center;\r\n background-color: var(--sky-hover-primary);\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n border: none;\r\n border-radius: var(--sky-radius-full);\r\n cursor: pointer;\r\n color: var(--sky-text-primary);\r\n transition: color 0.2s ease, background-color 0.2s ease;\r\n }\r\n\r\n .close-button sky-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n line-height: 1;\r\n transition: color 0.3s ease;\r\n }\r\n\r\n :dir(rtl) .close-button {\r\n margin-right: var(--sky-space-1, 5px);\r\n margin-left: unset;\r\n }\r\n\r\n .close-button:hover {\r\n color: var(--sky-danger-primary);\r\n }\r\n\r\n .close-button:focus-visible {\r\n outline: none;\r\n box-shadow: var(--sky-focus-ring);\r\n }\r\n\r\n .close-button:hover sky-icon,\r\n .close-button:focus-visible sky-icon{\r\n color: var(--sky-danger-primary);\r\n outline: none;\r\n }\r\n\r\n .icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: var(--chip-text-color);\r\n }\r\n\r\n sky-icon {\r\n display: inline-block;\r\n width: 1em;\r\n height: 1em;\r\n }\r\n\r\n ::slotted([slot=\"prefix\"]),\r\n ::slotted([slot=\"suffix\"]) {\r\n display: inline-flex;\r\n align-items: center;\r\n }\r\n\r\n .loading-overlay {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--sky-overlay-primary);\r\n border-radius: inherit;\r\n }\r\n\r\n .loading-spinner {\r\n width: 14px;\r\n height: 14px;\r\n border: 2px solid var(--sky-border-light, var(--sky-text-tertiary));\r\n border-top-color: var(--chip-bg-color);\r\n border-radius: var(--sky-radius-full);\r\n animation: spin 0.8s linear infinite;\r\n }\r\n\r\n @keyframes spin {\r\n from {\r\n transform: rotate(0deg);\r\n }\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n .chip,\r\n .close-button,\r\n .close-button sky-icon {\r\n transition: none;\r\n }\r\n .loading-spinner {\r\n animation: none;\r\n }\r\n }\r\n\r\n .chip.bordered {\r\n border: 1px solid var(--chip-ink-color, var(--chip-bg-color));\r\n color: var(--chip-ink-color, var(--chip-bg-color));\r\n background: transparent;\r\n }\r\n\r\n .chip.light {\r\n background: transparent;\r\n color: var(--chip-ink-color, var(--chip-bg-color));\r\n }\r\n\r\n .chip.flat {\r\n box-shadow: none;\r\n background: color-mix(\r\n in srgb,\r\n var(--chip-fill-color, var(--chip-bg-color)) 20%,\r\n transparent\r\n );\r\n color: var(--chip-ink-color, var(--chip-bg-color));\r\n }\r\n\r\n .chip.faded {\r\n background-color: var(--sky-glass-primary);\r\n border: 1px solid var(--sky-text-tertiary);\r\n color: var(--chip-ink-color, var(--chip-bg-color));\r\n }\r\n\r\n .chip.shadow {\r\n box-shadow: var(--sky-box-shadow-primary);\r\n }\r\n\r\n .chip.shadow:hover {\r\n box-shadow: var(--sky-box-shadow-hover);\r\n }\r\n\r\n .chip.faded .icon,\r\n .chip.flat .icon,\r\n .chip.light .icon,\r\n .chip.dot .icon,\r\n .chip.bordered .icon {\r\n color: var(--chip-ink-color, var(--chip-bg-color));\r\n }\r\n\r\n .chip.dot {\r\n padding-left: var(--sky-space-5, 20px) !important;\r\n position: relative;\r\n background-color: transparent;\r\n border: 1px solid var(--sky-text-tertiary);\r\n color: var(--sky-text-primary);\r\n }\r\n\r\n .chip.dot::before {\r\n content: \"\";\r\n position: absolute;\r\n left: var(--sky-space-1\\\\.5, 6px);\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: var(--sky-space-2, 8px);\r\n height: var(--sky-space-2, 8px);\r\n border-radius: var(--sky-radius-full);\r\n background-color: var(--chip-accent-color, var(--chip-bg-color));\r\n }\r\n\r\n /* Solid / shadow chips: enhance label + icon FG against fill. */\r\n ${unsafeCSS(\r\n cssOnFillEnhance([\r\n {\r\n selector: \".chip.solid, .chip.shadow\",\r\n fillVar: \"--chip-bg-color\",\r\n },\r\n {\r\n selector: \".chip.solid .icon, .chip.shadow .icon\",\r\n fillVar: \"--chip-bg-color\",\r\n },\r\n ]),\r\n )}\r\n `,\r\n ];\r\n\r\n private _prefixFilled = false;\r\n private _suffixFilled = false;\r\n private _defaultFilled = false;\r\n private _slotFlagsSeeded = false;\r\n\r\n /** Updates host CSS variables from current color token/value. */\r\n protected override updated(changed: PropertyValues): void {\r\n if (!changed.has(\"color\")) return;\r\n\r\n const bgColor = resolveColor(this.color);\r\n const textColor = isDefaultColorToken(this.color)\r\n ? \"var(--sky-text-primary)\"\r\n : ON_COLOR_FALLBACK_LIGHT;\r\n\r\n this.style.setProperty(\"--chip-bg-color\", bgColor);\r\n this.style.setProperty(\"--chip-text-color\", textColor);\r\n\r\n if (isDefaultColorToken(this.color)) {\r\n this.style.setProperty(\"--chip-ink-color\", resolveInkColorForToken(this.color));\r\n this.style.setProperty(\r\n \"--chip-accent-color\",\r\n resolveAccentColorForToken(this.color),\r\n );\r\n this.style.setProperty(\"--chip-fill-color\", \"var(--sky-hover-tertiary)\");\r\n } else {\r\n this.style.removeProperty(\"--chip-ink-color\");\r\n this.style.removeProperty(\"--chip-accent-color\");\r\n this.style.removeProperty(\"--chip-fill-color\");\r\n }\r\n }\r\n\r\n /** Size resolver similar to button: controls padding + font-size */\r\n private _resolveSize(\r\n input: string\r\n ): {\r\n padding: string;\r\n fontSize: string;\r\n } {\r\n const presets: Record<string, { padding: string; fontSize: string }> = {\r\n xs: { padding: \"2px 4px\", fontSize: \"11px\" },\r\n sm: { padding: \"3px 6px\", fontSize: \"12px\" },\r\n md: { padding: \"4px 8px\", fontSize: \"13px\" },\r\n lg: { padding: \"5px 10px\", fontSize: \"14px\" },\r\n xl: { padding: \"6px 12px\", fontSize: \"15px\" },\r\n };\r\n\r\n if (presets[input]) return presets[input];\r\n\r\n const m = String(input).trim().match(/^([\\d.]+)(px|rem|em)?$/i);\r\n const unit = m?.[2] || \"px\";\r\n const fsNum = m ? parseFloat(m[1]) : 13;\r\n const fs = `${fsNum}${unit}`;\r\n\r\n const clamp = (n: number, min: number, max: number) =>\r\n Math.max(min, Math.min(max, n));\r\n\r\n const py = clamp(fsNum * 0.4, 2, 10);\r\n const px = clamp(fsNum * 0.8, 6, 16);\r\n\r\n const to = (n: number) => `${Math.round(n * 100) / 100}${unit}`;\r\n\r\n return {\r\n padding: `${to(py)} ${to(px)}`,\r\n fontSize: fs,\r\n };\r\n }\r\n\r\n /**\r\n * Dispatches `chip-removed` and removes the chip from the DOM.\r\n *\r\n * @returns {void}\r\n */\r\n public removeChip(): void {\r\n const removeEvent = new CustomEvent<SkyChipRemovedEventDetail>(\"chip-removed\", {\r\n detail: { label: this.label },\r\n bubbles: true,\r\n composed: true,\r\n });\r\n this.dispatchEvent(removeEvent);\r\n this.remove();\r\n }\r\n\r\n private _slotHasMeaningfulContent(slotEl: HTMLSlotElement): boolean {\r\n const nodes = slotEl.assignedNodes({ flatten: true });\r\n return nodes.some((n) =>\r\n n.nodeType === Node.ELEMENT_NODE\r\n ? true\r\n : (n.textContent ?? \"\").trim().length > 0\r\n );\r\n }\r\n\r\n /** Light DOM check works before the shadow tree exists (first render). */\r\n private _hasPrefixSlotContent(): boolean {\r\n return this._prefixFilled;\r\n }\r\n\r\n private _hasSuffixSlotContent(): boolean {\r\n return this._suffixFilled;\r\n }\r\n\r\n private _hasDefaultSlotContent(): boolean {\r\n return this._defaultFilled;\r\n }\r\n\r\n private _seedSlotFlagsFromLightDom(): void {\r\n this._prefixFilled = !!this.querySelector('[slot=\"prefix\"]');\r\n this._suffixFilled = !!this.querySelector('[slot=\"suffix\"]');\r\n this._defaultFilled = false;\r\n for (const n of Array.from(this.childNodes)) {\r\n if (n.nodeType === Node.ELEMENT_NODE) {\r\n const slot = (n as Element).getAttribute(\"slot\");\r\n if (slot === \"prefix\" || slot === \"suffix\") continue;\r\n this._defaultFilled = true;\r\n break;\r\n }\r\n if ((n.textContent ?? \"\").trim().length > 0) {\r\n this._defaultFilled = true;\r\n break;\r\n }\r\n }\r\n this._slotFlagsSeeded = true;\r\n }\r\n\r\n override willUpdate(): void {\r\n if (!this._slotFlagsSeeded) this._seedSlotFlagsFromLightDom();\r\n }\r\n\r\n private _onSlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement | null;\r\n if (!slot || slot.tagName !== \"SLOT\") return;\r\n this._slotFlagsSeeded = true;\r\n const filled = this._slotHasMeaningfulContent(slot);\r\n const name = slot.name;\r\n if (name === \"prefix\") {\r\n if (this._prefixFilled === filled) return;\r\n this._prefixFilled = filled;\r\n } else if (name === \"suffix\") {\r\n if (this._suffixFilled === filled) return;\r\n this._suffixFilled = filled;\r\n } else {\r\n if (this._defaultFilled === filled) return;\r\n this._defaultFilled = filled;\r\n }\r\n this.requestUpdate();\r\n };\r\n\r\n override render() {\r\n const sizeDef = this._resolveSize(this.size);\r\n const chipStyle = {\r\n padding: sizeDef.padding,\r\n borderRadius: resolveRadius(this.radius),\r\n fontSize: sizeDef.fontSize,\r\n };\r\n\r\n return html`\r\n <div\r\n class=${classMap({\r\n chip: true,\r\n [this.variant]: true,\r\n loading: this.loading,\r\n })}\r\n style=${styleMap(chipStyle)}\r\n part=\"chip\"\r\n >\r\n <slot name=\"prefix\" @slotchange=${this._onSlotChange}></slot>\r\n ${when(!this._hasPrefixSlotContent() && this.iconL, () => html`\r\n <sky-icon class=\"icon\" part=\"icon\" icon=\"${this.iconL!}\"></sky-icon>\r\n `)}\r\n <slot @slotchange=${this._onSlotChange}></slot>\r\n ${this._hasDefaultSlotContent() ? nothing : this.label}\r\n <slot name=\"suffix\" @slotchange=${this._onSlotChange}></slot>\r\n ${when(!this._hasSuffixSlotContent() && this.iconR, () => html`\r\n <sky-icon class=\"icon icon-right\" part=\"icon\" icon=\"${this.iconR!}\"></sky-icon>\r\n `)}\r\n ${when(this.removable, () => html`\r\n <button\r\n class=\"close-button\"\r\n part=\"close-button\"\r\n type=\"button\"\r\n aria-label=\"Remove chip\"\r\n @click=${this.removeChip}\r\n >\r\n <sky-icon icon=\"ion:close\"></sky-icon>\r\n </button>\r\n `)}\r\n ${when(this.loading, () => html`\r\n <div class=\"loading-overlay\" part=\"loading-overlay\">\r\n <div class=\"loading-spinner\" part=\"loading-spinner\"></div>\r\n </div>\r\n `)}\r\n </div>\r\n `;\r\n }\r\n}"],"names":["SkyChip","LitElement","constructor","super","arguments","this","label","removable","color","radius","size","iconL","iconR","loading","disabled","variant","preset","_presets","PresetController","_prefixFilled","_suffixFilled","_defaultFilled","_slotFlagsSeeded","_onSlotChange","e","slot","target","tagName","filled","_slotHasMeaningfulContent","name","requestUpdate","updated","changed","has","bgColor","resolveColor","textColor","isDefaultColorToken","ON_COLOR_FALLBACK_LIGHT","style","setProperty","resolveInkColorForToken","resolveAccentColorForToken","removeProperty","_resolveSize","input","presets","xs","padding","fontSize","sm","md","lg","xl","m","String","trim","match","unit","fsNum","parseFloat","fs","clamp","n","min","max","Math","py","px","to","round","removeChip","removeEvent","CustomEvent","detail","bubbles","composed","dispatchEvent","remove","slotEl","assignedNodes","flatten","some","nodeType","Node","ELEMENT_NODE","textContent","length","_hasPrefixSlotContent","_hasSuffixSlotContent","_hasDefaultSlotContent","_seedSlotFlagsFromLightDom","querySelector","Array","from","childNodes","getAttribute","willUpdate","render","sizeDef","chipStyle","borderRadius","resolveRadius","html","classMap","chip","styleMap","when","nothing","dependencies","SkyIcon","styles","skyFocusRingHostStyles","css","unsafeCSS","cssOnFillEnhance","selector","fillVar","__decorateClass","property","type","prototype","Boolean","reflect","customElement"],"mappings":"8+BAqFO,IAAMA,EAAN,cAAsBC,EAAtB,WAAAC,GAAAC,SAAAC,WAMuBC,KAAAC,MAAgB,GACfD,KAAAE,WAAqB,EAGtBF,KAAAG,MAAgB,UAGhBH,KAAAI,OAAiB,OAGjBJ,KAAAK,KAAe,KAEfL,KAAAM,MAAuB,KACvBN,KAAAO,MAAuB,KACtBP,KAAAQ,SAAmB,EACJR,KAAAS,UAAoB,EACrBT,KAAAU,QAA0B,OAG1BV,KAAAW,OAAS,GAEpDX,KAAQY,SAAW,IAAIC,EAAiBb,MAuNxCA,KAAQc,eAAgB,EACxBd,KAAQe,eAAgB,EACxBf,KAAQgB,gBAAiB,EACzBhB,KAAQiB,kBAAmB,EA4H3BjB,KAAQkB,cAAiBC,IACvB,MAAMC,EAAOD,EAAEE,OACf,IAAKD,GAAyB,SAAjBA,EAAKE,QAAoB,OACtCtB,KAAKiB,kBAAmB,EACxB,MAAMM,EAASvB,KAAKwB,0BAA0BJ,GACxCK,EAAOL,EAAKK,KAClB,GAAa,WAATA,EAAmB,CACrB,GAAIzB,KAAKc,gBAAkBS,EAAQ,OACnCvB,KAAKc,cAAgBS,CACvB,MAAA,GAAoB,WAATE,EAAmB,CAC5B,GAAIzB,KAAKe,gBAAkBQ,EAAQ,OACnCvB,KAAKe,cAAgBQ,CACvB,KAAO,CACL,GAAIvB,KAAKgB,iBAAmBO,EAAQ,OACpCvB,KAAKgB,eAAiBO,CACxB,CACAvB,KAAK0B,gBACP,CA1ImB,OAAAC,CAAQC,GACzB,IAAKA,EAAQC,IAAI,SAAU,OAE3B,MAAMC,EAAUC,EAAa/B,KAAKG,OAC5B6B,EAAYC,EAAoBjC,KAAKG,OACvC,0BACA+B,EAEJlC,KAAKmC,MAAMC,YAAY,kBAAmBN,GAC1C9B,KAAKmC,MAAMC,YAAY,oBAAqBJ,GAExCC,EAAoBjC,KAAKG,QAC3BH,KAAKmC,MAAMC,YAAY,mBAAoBC,EAAwBrC,KAAKG,QACxEH,KAAKmC,MAAMC,YACT,sBACAE,EAA2BtC,KAAKG,QAElCH,KAAKmC,MAAMC,YAAY,oBAAqB,+BAE5CpC,KAAKmC,MAAMI,eAAe,oBAC1BvC,KAAKmC,MAAMI,eAAe,uBAC1BvC,KAAKmC,MAAMI,eAAe,qBAE9B,CAGQ,YAAAC,CACNC,GAKA,MAAMC,EAAiE,CACrEC,GAAI,CAAEC,QAAS,UAAWC,SAAU,QACpCC,GAAI,CAAEF,QAAS,UAAWC,SAAU,QACpCE,GAAI,CAAEH,QAAS,UAAWC,SAAU,QACpCG,GAAI,CAAEJ,QAAS,WAAYC,SAAU,QACrCI,GAAI,CAAEL,QAAS,WAAYC,SAAU,SAGvC,GAAIH,EAAQD,GAAQ,OAAOC,EAAQD,GAEnC,MAAMS,EAAIC,OAAOV,GAAOW,OAAOC,MAAM,2BAC/BC,EAAOJ,IAAI,IAAM,KACjBK,EAAQL,EAAIM,WAAWN,EAAE,IAAM,GAC/BO,EAAK,GAAGF,IAAQD,IAEhBI,EAAQ,CAACC,EAAWC,EAAaC,IACrCC,KAAKD,IAAID,EAAKE,KAAKF,IAAIC,EAAKF,IAExBI,EAAKL,EAAc,GAARH,EAAa,EAAG,IAC3BS,EAAKN,EAAc,GAARH,EAAa,EAAG,IAE3BU,EAAMN,GAAc,GAAGG,KAAKI,MAAU,IAAJP,GAAW,MAAML,IAEzD,MAAO,CACLV,QAAS,GAAGqB,EAAGF,MAAOE,EAAGD,KACzBnB,SAAUY,EAEd,CAOO,UAAAU,GACL,MAAMC,EAAc,IAAIC,YAAuC,eAAgB,CAC7EC,OAAQ,CAAErE,MAAOD,KAAKC,OACtBsE,SAAS,EACTC,UAAU,IAEZxE,KAAKyE,cAAcL,GACnBpE,KAAK0E,QACP,CAEQ,yBAAAlD,CAA0BmD,GAEhC,OADcA,EAAOC,cAAc,CAAEC,SAAS,IACjCC,KAAMnB,GACjBA,EAAEoB,WAAaC,KAAKC,eAEftB,EAAEuB,aAAe,IAAI9B,OAAO+B,OAAS,EAE9C,CAGQ,qBAAAC,GACN,OAAOpF,KAAKc,aACd,CAEQ,qBAAAuE,GACN,OAAOrF,KAAKe,aACd,CAEQ,sBAAAuE,GACN,OAAOtF,KAAKgB,cACd,CAEQ,0BAAAuE,GACNvF,KAAKc,gBAAkBd,KAAKwF,cAAc,mBAC1CxF,KAAKe,gBAAkBf,KAAKwF,cAAc,mBAC1CxF,KAAKgB,gBAAiB,EACtB,IAAA,MAAW2C,KAAK8B,MAAMC,KAAK1F,KAAK2F,YAAa,CAC3C,GAAIhC,EAAEoB,WAAaC,KAAKC,aAAc,CACpC,MAAM7D,EAAQuC,EAAciC,aAAa,QACzC,GAAa,WAATxE,GAA8B,WAATA,EAAmB,SAC5CpB,KAAKgB,gBAAiB,EACtB,KACF,CACA,IAAK2C,EAAEuB,aAAe,IAAI9B,OAAO+B,OAAS,EAAG,CAC3CnF,KAAKgB,gBAAiB,EACtB,KACF,CACF,CACAhB,KAAKiB,kBAAmB,CAC1B,CAES,UAAA4E,GACF7F,KAAKiB,kBAAkBjB,KAAKuF,4BACnC,CAqBS,MAAAO,GACP,MAAMC,EAAU/F,KAAKwC,aAAaxC,KAAKK,MACjC2F,EAAY,CAChBpD,QAASmD,EAAQnD,QACjBqD,aAAcC,EAAclG,KAAKI,QACjCyC,SAAUkD,EAAQlD,UAGpB,OAAOsD,CAAA;;gBAEKC,EAAS,CACfC,MAAM,EACN,CAACrG,KAAKU,UAAU,EAChBF,QAASR,KAAKQ;gBAER8F,EAASN;;;0CAGiBhG,KAAKkB;UACrCqF,GAAMvG,KAAKoF,yBAA2BpF,KAAKM,MAAO,IAAM6F,CAAA;qDACbnG,KAAKM;;4BAE9BN,KAAKkB;UACvBlB,KAAKsF,yBAA2BkB,EAAUxG,KAAKC;0CACfD,KAAKkB;UACrCqF,GAAMvG,KAAKqF,yBAA2BrF,KAAKO,MAAO,IAAM4F,CAAA;gEACFnG,KAAKO;;UAE3DgG,EAAKvG,KAAKE,UAAW,IAAMiG,CAAA;;;;;;qBAMhBnG,KAAKmE;;;;;UAKhBoC,EAAKvG,KAAKQ,QAAS,IAAM2F,CAAA;;;;;;KAOjC,GAlbWxG,EAEJ8G,aAAyD,CAC9D,WAAYC,GAHH/G,EA6BKgH,OAAS,CACvBC,EACAC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAoMEC,EACAC,EAAiB,CACf,CACEC,SAAU,4BACVC,QAAS,mBAEX,CACED,SAAU,wCACVC,QAAS;KArOWC,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UANPxD,EAMiB0H,UAAA,QAAA,GACCH,EAAA,CAA5BC,EAAS,CAAEC,KAAME,WAPP3H,EAOkB0H,UAAA,YAAA,GAGDH,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UAVPxD,EAUiB0H,UAAA,QAAA,GAGAH,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UAbPxD,EAaiB0H,UAAA,SAAA,GAGAH,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UAhBPxD,EAgBiB0H,UAAA,OAAA,GAEAH,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UAlBPxD,EAkBiB0H,UAAA,QAAA,GACAH,EAAA,CAA3BC,EAAS,CAAEC,KAAMjE,UAnBPxD,EAmBiB0H,UAAA,QAAA,GACCH,EAAA,CAA5BC,EAAS,CAAEC,KAAME,WApBP3H,EAoBkB0H,UAAA,UAAA,GACeH,EAAA,CAA3CC,EAAS,CAAEC,KAAME,QAASC,SAAS,KArBzB5H,EAqBiC0H,UAAA,WAAA,GACDH,EAAA,CAA1CC,EAAS,CAAEC,KAAMjE,OAAQoE,SAAS,KAtBxB5H,EAsBgC0H,UAAA,UAAA,GAGAH,EAAA,CAA1CC,EAAS,CAAEC,KAAMjE,OAAQoE,SAAS,KAzBxB5H,EAyBgC0H,UAAA,SAAA,GAzBhC1H,EAANuH,EAAA,CADNM,EAAc,aACF7H"}
|