@sky.ui/core 0.0.42 → 0.0.44

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.
Files changed (44) hide show
  1. package/dist/custom-elements.json +2248 -2248
  2. package/dist/helper/controllers/animation-controller.d.ts +4 -4
  3. package/dist/helper/controllers/animation-controller.js.map +1 -1
  4. package/dist/helper/controllers/preset-controller.js +1 -1
  5. package/dist/helper/controllers/preset-controller.js.map +1 -1
  6. package/dist/helper/iconify-offline.js.map +1 -1
  7. package/dist/helper/index.js +1 -1
  8. package/dist/helper/utils/color-resolver.js.map +1 -1
  9. package/dist/helper/utils/component-presets.d.ts +28 -4
  10. package/dist/helper/utils/component-presets.js +1 -1
  11. package/dist/helper/utils/component-presets.js.map +1 -1
  12. package/dist/helper/utils/contrast-color.js.map +1 -1
  13. package/dist/helper/utils/index.js +1 -1
  14. package/dist/helper/utils/size-resolver.js.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/sky-alert/sky-alert.js.map +1 -1
  17. package/dist/sky-appbar/sky-appbar.js.map +1 -1
  18. package/dist/sky-avatar/sky-avatar.js.map +1 -1
  19. package/dist/sky-badge/sky-badge.js.map +1 -1
  20. package/dist/sky-button/sky-button.js.map +1 -1
  21. package/dist/sky-card/sky-card.js.map +1 -1
  22. package/dist/sky-checkbox/sky-checkbox.js.map +1 -1
  23. package/dist/sky-chip/sky-chip.js.map +1 -1
  24. package/dist/sky-col/sky-col.js.map +1 -1
  25. package/dist/sky-combobox/sky-combobox.js +0 -5
  26. package/dist/sky-combobox/sky-combobox.js.map +1 -1
  27. package/dist/sky-dialog/sky-dialog.js.map +1 -1
  28. package/dist/sky-divider/sky-divider.js.map +1 -1
  29. package/dist/sky-flexbox/sky-flexbox.js.map +1 -1
  30. package/dist/sky-flexcol/sky-flexcol.js.map +1 -1
  31. package/dist/sky-grid/sky-grid.js.map +1 -1
  32. package/dist/sky-icon/sky-icon.js +1 -1
  33. package/dist/sky-icon/sky-icon.js.map +1 -1
  34. package/dist/sky-image/sky-image.js.map +1 -1
  35. package/dist/sky-image/srcset-utils.js.map +1 -1
  36. package/dist/sky-input/sky-input.js.map +1 -1
  37. package/dist/sky-list/sky-list.js.map +1 -1
  38. package/dist/sky-listitem/sky-listitem.js.map +1 -1
  39. package/dist/sky-radio/sky-radio.js.map +1 -1
  40. package/dist/sky-row/sky-row.js.map +1 -1
  41. package/dist/sky-sheet/sky-sheet.js.map +1 -1
  42. package/dist/sky-simple-table/sky-simple-table.js.map +1 -1
  43. package/dist/sky-theme-provider/sky-theme-provider.js.map +1 -1
  44. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import type { ReactiveController, ReactiveControllerHost } from "lit";
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
2
  /** Fill mode for WAAPI (KeyframeEffectOptions). */
3
- export type FillMode = "none" | "forwards" | "backwards" | "both";
3
+ export type FillMode = 'none' | 'forwards' | 'backwards' | 'both';
4
4
  /** Playback direction for WAAPI (KeyframeEffectOptions). */
5
- export type AnimationDirection = "normal" | "reverse" | "alternate" | "alternate-reverse";
5
+ export type AnimationDirection = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
6
6
  /**
7
7
  * Options for a single animation run (Web Animations API).
8
8
  * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect#options
@@ -39,7 +39,7 @@ export interface AnimationControllerConfig {
39
39
  defaultFill?: FillMode;
40
40
  }
41
41
  /** Direction for slide presets. */
42
- export type SlideDirection = "up" | "down" | "left" | "right";
42
+ export type SlideDirection = 'up' | 'down' | 'left' | 'right';
43
43
  /**
44
44
  * AnimationController – consistent animations via the Web Animations API (WAAPI).
45
45
  *
@@ -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\"\r\n | \"reverse\"\r\n | \"alternate\"\r\n | \"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 =\r\n | Keyframe[]\r\n | 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(\r\n element,\r\n [{ opacity: 0 }, { opacity: 1 }],\r\n options,\r\n );\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(\r\n element,\r\n [{ opacity: 1 }, { opacity: 0 }],\r\n options,\r\n );\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 : [\r\n { transform: `scale(${from})` },\r\n { transform: \"scale(1)\" },\r\n ];\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 : [\r\n { transform: \"scale(1)\" },\r\n { transform: `scale(${to})` },\r\n ];\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';\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,2 +1,2 @@
1
- import{ContextConsumer as s}from"@lit/context";import{configContext as t,findNearestPresetsConfig as e,captureInitialUserAttributes as i,clearAppliedPresetLayer as o,getPresetLayer as h,applyPresetLayer as n}from"../utils/component-presets.js";class p{constructor(e,i={}){this.host=e,this.options=i,this.revision=0,this._lastPresetName="",this._lastPaused=!1,this._applyQueued=!1,this._consumer=new s(this.host,{context:t,subscribe:!0,callback:()=>{this._scheduleApply()}}),this.host.addController(this)}get componentKey(){return this.options.componentKey||this.host.localName||""}get config(){const s=this._consumer.value;return s&&(s.defaults||s.presets)?s:e(this.host)??s??null}hostConnected(){i(this.host),this._scheduleApply()}hostUpdate(){const s=String(this.host.preset??""),t=Boolean(this.options.isPaused?.());(s!==this._lastPresetName||t!==this._lastPaused||this._applyQueued)&&this._applyNow()}hostDisconnected(){}reapply(){this._scheduleApply()}_scheduleApply(){this._applyQueued=!0,this.revision++,this.host.requestUpdate()}_applyNow(){this._applyQueued=!1;const s=String(this.host.preset??""),t=Boolean(this.options.isPaused?.());this._lastPresetName=s,this._lastPaused=t;let e=!1;if(t)e=o(this.host);else{const t=h(this.config,this.componentKey,s||null);e=n(this.host,t,{skipKeys:this.options.skipKeys})}e&&(this.revision++,this.options.onChange?.(),queueMicrotask(()=>this.host.requestUpdate()))}}export{p as PresetController};
1
+ import{ContextConsumer as s}from"@lit/context";import{configContext as t,captureBuiltInPropSnapshot as e,findNearestPresetsConfig as i,finalizeBuiltInPropSnapshot as o,captureInitialUserAttributes as h,clearAppliedPresetLayer as n,getPresetLayer as p,applyPresetLayer as l}from"../utils/component-presets.js";class a{constructor(i,o={}){this.host=i,this.options=o,this.revision=0,this._lastPresetName="",this._lastPaused=!1,this._applyQueued=!1,this._consumer=new s(this.host,{context:t,subscribe:!0,callback:()=>{this._scheduleApply()}}),this.host.addController(this),e(this.host)}get componentKey(){return this.options.componentKey||this.host.localName||""}get config(){const s=this._consumer.value;return s&&(s.defaults||s.presets)?s:i(this.host)??s??null}hostConnected(){o(this.host),h(this.host),this._scheduleApply()}hostUpdate(){const s=String(this.host.preset??""),t=Boolean(this.options.isPaused?.());(s!==this._lastPresetName||t!==this._lastPaused||this._applyQueued)&&this._applyNow()}hostDisconnected(){}reapply(){this._scheduleApply()}_scheduleApply(){this._applyQueued=!0,this.revision++,this.host.requestUpdate()}_applyNow(){this._applyQueued=!1;const s=String(this.host.preset??""),t=Boolean(this.options.isPaused?.());this._lastPresetName=s,this._lastPaused=t;let e=!1;if(t)e=n(this.host);else{const t=p(this.config,this.componentKey,s||null);e=l(this.host,t,{skipKeys:this.options.skipKeys})}e&&(this.revision++,this.options.onChange?.(),queueMicrotask(()=>this.host.requestUpdate()))}}export{a as PresetController};
2
2
  //# sourceMappingURL=preset-controller.js.map
@@ -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 captureInitialUserAttributes,\n clearAppliedPresetLayer,\n configContext,\n findNearestPresetsConfig,\n getPresetLayer,\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 * 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 }\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 captureInitialUserAttributes(this.host);\n this._scheduleApply();\n }\n\n hostUpdate(): void {\n const presetName = String(this.host.preset ?? \"\");\n const paused = Boolean(this.options.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 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 = Boolean(this.options.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","componentKey","localName","config","fromCtx","value","defaults","presets","findNearestPresetsConfig","hostConnected","captureInitialUserAttributes","hostUpdate","presetName","String","preset","paused","Boolean","isPaused","_applyNow","hostDisconnected","reapply","requestUpdate","changed","clearAppliedPresetLayer","layer","getPresetLayer","applyPresetLayer","skipKeys","onChange","queueMicrotask"],"mappings":"oPA8CO,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,KAC1B,CAGA,gBAAIa,GACF,OAAOb,KAAKD,QAAQc,cAAgBb,KAAKF,KAAKgB,WAAa,EAC7D,CAGA,UAAIC,GACF,MAAMC,EAAUhB,KAAKK,UAAUY,MAC/B,OAAID,IAAYA,EAAQE,UAAYF,EAAQG,SACnCH,EAEFI,EAAyBpB,KAAKF,OAASkB,GAAW,IAC3D,CAEA,aAAAK,GACEC,EAA6BtB,KAAKF,MAClCE,KAAKW,gBACP,CAEA,UAAAY,GACE,MAAMC,EAAaC,OAAOzB,KAAKF,KAAK4B,QAAU,IACxCC,EAASC,QAAQ5B,KAAKD,QAAQ8B,eAElCL,IAAexB,KAAKE,iBACpByB,IAAW3B,KAAKG,aAChBH,KAAKI,eAELJ,KAAK8B,WAET,CAEA,gBAAAC,GAEA,CAGA,OAAAC,GACEhC,KAAKW,gBACP,CAEQ,cAAAA,GACNX,KAAKI,cAAe,EACpBJ,KAAKC,WACLD,KAAKF,KAAKmC,eACZ,CAEQ,SAAAH,GACN9B,KAAKI,cAAe,EACpB,MAAMoB,EAAaC,OAAOzB,KAAKF,KAAK4B,QAAU,IACxCC,EAASC,QAAQ5B,KAAKD,QAAQ8B,cACpC7B,KAAKE,gBAAkBsB,EACvBxB,KAAKG,YAAcwB,EAEnB,IAAIO,GAAU,EACd,GAAIP,EACFO,EAAUC,EAAwBnC,KAAKF,UAClC,CACL,MAAMsC,EAAQC,EACZrC,KAAKe,OACLf,KAAKa,aACLW,GAAc,MAEhBU,EAAUI,EAAiBtC,KAAKF,KAAMsC,EAAO,CAC3CG,SAAUvC,KAAKD,QAAQwC,UAE3B,CAEIL,IACFlC,KAAKC,WACLD,KAAKD,QAAQyC,aAKbC,eAAe,IAAMzC,KAAKF,KAAKmC,iBAEnC"}
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 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 * 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 = Boolean(this.options.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 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 = Boolean(this.options.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","Boolean","isPaused","_applyNow","hostDisconnected","reapply","requestUpdate","changed","clearAppliedPresetLayer","layer","getPresetLayer","applyPresetLayer","skipKeys","onChange","queueMicrotask"],"mappings":"qTAgDO,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,EAASC,QAAQ9B,KAAKD,QAAQgC,eAElCL,IAAe1B,KAAKE,iBACpB2B,IAAW7B,KAAKG,aAChBH,KAAKI,eAELJ,KAAKgC,WAET,CAEA,gBAAAC,GAEA,CAGA,OAAAC,GACElC,KAAKW,gBACP,CAEQ,cAAAA,GACNX,KAAKI,cAAe,EACpBJ,KAAKC,WACLD,KAAKF,KAAKqC,eACZ,CAEQ,SAAAH,GACNhC,KAAKI,cAAe,EACpB,MAAMsB,EAAaC,OAAO3B,KAAKF,KAAK8B,QAAU,IACxCC,EAASC,QAAQ9B,KAAKD,QAAQgC,cACpC/B,KAAKE,gBAAkBwB,EACvB1B,KAAKG,YAAc0B,EAEnB,IAAIO,GAAU,EACd,GAAIP,EACFO,EAAUC,EAAwBrC,KAAKF,UAClC,CACL,MAAMwC,EAAQC,EACZvC,KAAKgB,OACLhB,KAAKc,aACLY,GAAc,MAEhBU,EAAUI,EAAiBxC,KAAKF,KAAMwC,EAAO,CAC3CG,SAAUzC,KAAKD,QAAQ0C,UAE3B,CAEIL,IACFpC,KAAKC,WACLD,KAAKD,QAAQ2C,aAKbC,eAAe,IAAM3C,KAAKF,KAAKqC,iBAEnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"iconify-offline.js","sources":["../../src/helper/iconify-offline.ts"],"sourcesContent":["/**\r\n * Iconify/sky-icon helpers: extract icon names from code and fetch icon data for offline use.\r\n *\r\n * Usage (browser or Node):\r\n * import { extractIconNames, fetchIconsForOffline, registerOfflineIcons } from '~/components/helper/iconify-offline';\r\n * import { SkyIcon } from '~/components/sky-icon/sky-icon';\r\n *\r\n * // Extract icon names from a code page string\r\n * const names = extractIconNames('<sky-icon icon=\"ion:home\"></sky-icon>'); // ['ion:home']\r\n *\r\n * // Fetch and then register in SkyIcon (directly pass generated JSON)\r\n * const combined = await fetchIconsForOffline(['ion:home', 'mdi:bell']);\r\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\r\n *\r\n * // Or from saved JSON string:\r\n * const combined = JSON.parse(savedJson);\r\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\r\n */\r\n\r\nconst ICONIFY_API_BASE = 'https://api.iconify.design';\r\n\r\n/** Matches quoted \"prefix:icon-name\" (prefix must start with a letter so \"1:3\" etc. are ignored). */\r\nconst QUOTED_ICON_PATTERN = /([\"'`])([a-z][a-z0-9-]*:[a-zA-Z0-9_.-]+)\\1/g;\r\n\r\nexport interface IconifyIconData {\r\n body?: string;\r\n left?: number;\r\n top?: number;\r\n width?: number;\r\n height?: number;\r\n viewBox?: string;\r\n hidden?: boolean;\r\n}\r\n\r\nexport interface IconifySetData {\r\n prefix?: string;\r\n icons: Record<string, IconifyIconData>;\r\n width?: number;\r\n height?: number;\r\n left?: number;\r\n top?: number;\r\n /** Icon names requested but not in API response; add manually if needed. Omit when registering with SkyIcon. */\r\n _notFound?: string[];\r\n}\r\n\r\n/** Combined offline data: prefix -> Iconify set data (ready for SkyIcon.registerLocalSet). Skip keys starting with _ when registering. */\r\nexport type CombinedOfflineIcons = Record<string, IconifySetData>;\r\n\r\nexport interface FetchIconsForOfflineOptions {\r\n /** Iconify API base URL (default: https://api.iconify.design) */\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * Extracts all icon names in \"prefix:icon-name\" format from a code page string.\r\n * Looks for icon names inside single, double, or backtick quotes (e.g. in attributes\r\n * like icon=\"ion:home\", iconL='mdi:bell', or :icon=\"`ion:person`\").\r\n *\r\n * @param codePageString - Source code string (HTML, Vue, TS, JS, etc.)\r\n * @returns Array of unique icon names (e.g. [\"ion:home\", \"mdi:bell\"])\r\n */\r\nexport function extractIconNames(codePageString: string): string[] {\r\n if (!codePageString || typeof codePageString !== 'string') {\r\n return [];\r\n }\r\n const seen = new Set<string>();\r\n const result: string[] = [];\r\n let match: RegExpExecArray | null;\r\n QUOTED_ICON_PATTERN.lastIndex = 0;\r\n while ((match = QUOTED_ICON_PATTERN.exec(codePageString)) !== null) {\r\n const full = match[2];\r\n if (full && full.includes(':') && !seen.has(full)) {\r\n seen.add(full);\r\n result.push(full);\r\n }\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Fetches icon data from the Iconify API for the given icon names, groups by prefix,\r\n * and returns a combined JSON object keyed by prefix for offline use.\r\n *\r\n * @param iconNames - List of icon names in \"prefix:icon-name\" format (e.g. [\"ion:home\", \"mdi:bell\"])\r\n * @param options - Optional baseUrl for the Iconify API\r\n * @returns Combined object: { \"ion\": { prefix, icons, width, height }, \"mdi\": { ... }, ... }\r\n * Use with SkyIcon.registerLocalSet(prefix, combined[prefix]) for each prefix.\r\n */\r\nexport async function fetchIconsForOffline(\r\n iconNames: string[],\r\n options: FetchIconsForOfflineOptions = {}\r\n): Promise<CombinedOfflineIcons> {\r\n const baseUrl = (options.baseUrl || ICONIFY_API_BASE).replace(/\\/$/, '');\r\n const byPrefix = new Map<string, string[]>();\r\n\r\n for (const full of iconNames) {\r\n const trimmed = full.trim();\r\n if (!trimmed || !trimmed.includes(':')) continue;\r\n const [prefix, name] = trimmed.split(':');\r\n const iconName = name?.trim();\r\n if (!prefix || !iconName) continue;\r\n // Ignore non-icon patterns (e.g. \"1:3\"); Iconify prefixes start with a letter\r\n if (!/^[a-z]/.test(prefix)) continue;\r\n if (!byPrefix.has(prefix)) {\r\n byPrefix.set(prefix, []);\r\n }\r\n const list = byPrefix.get(prefix)!;\r\n if (!list.includes(iconName)) {\r\n list.push(iconName);\r\n }\r\n }\r\n\r\n const combined: CombinedOfflineIcons = {};\r\n\r\n await Promise.all(\r\n Array.from(byPrefix.entries()).map(async ([prefix, names]) => {\r\n const iconsParam = names.join(',');\r\n const url = `${baseUrl}/${prefix}.json?icons=${encodeURIComponent(iconsParam)}`;\r\n const res = await fetch(url);\r\n if (!res.ok) {\r\n throw new Error(`Iconify API error for ${prefix}: ${res.status} ${res.statusText}`);\r\n }\r\n const json = (await res.json()) as IconifySetData & { icons?: Record<string, IconifyIconData> };\r\n const returnedIcons = json.icons || {};\r\n const notFound = names.filter((name) => !returnedIcons[name]);\r\n const setData: IconifySetData = {\r\n prefix,\r\n icons: returnedIcons,\r\n width: json.width,\r\n height: json.height,\r\n left: json.left,\r\n top: json.top,\r\n };\r\n if (notFound.length > 0) {\r\n setData._notFound = notFound;\r\n }\r\n combined[prefix] = setData;\r\n })\r\n );\r\n\r\n const hasAnyNotFound = Object.values(combined).some((set) => set._notFound && set._notFound.length > 0);\r\n if (hasAnyNotFound) {\r\n (combined as Record<string, unknown>)['_comment_notFound'] =\r\n 'Icons listed in _notFound (per set) were not in the API response; add manually if needed.';\r\n }\r\n\r\n return combined;\r\n}\r\n\r\n/** Set data without _notFound / _comment (suitable for SkyIcon.registerLocalSet). */\r\ntype SetDataForRegister = Omit<IconifySetData, '_notFound'>;\r\n\r\n/**\r\n * Registers the generated offline JSON with SkyIcon. Pass the object returned by fetchIconsForOffline\r\n * (or parsed from saved JSON). Skips top-level keys starting with \"_\" and strips _notFound from each set.\r\n *\r\n * @param combined - The combined object (from fetchIconsForOffline or JSON.parse of saved output)\r\n * @param register - Callback to register each set, e.g. (prefix, data) => SkyIcon.registerLocalSet(prefix, data)\r\n */\r\nexport function registerOfflineIcons(\r\n combined: CombinedOfflineIcons & Record<string, unknown>,\r\n register: (prefix: string, data: SetDataForRegister) => void\r\n): void {\r\n for (const [key, set] of Object.entries(combined)) {\r\n if (key.startsWith('_')) continue;\r\n if (!set || typeof set !== 'object' || !('icons' in set)) continue;\r\n const { _notFound: _unused, ...data } = set as IconifySetData;\r\n register(key, data as SetDataForRegister);\r\n }\r\n}\r\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,MAAM,yBAAyBvB,MAAWoB,EAAII,UAAUJ,EAAIK,cAExE,MAAMC,QAAcN,EAAIM,OAClBC,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,KAUvB,OANuBO,OAAOC,OAAO/B,GAAUgC,KAAMnC,GAAQA,EAAIgC,WAAahC,EAAIgC,UAAUD,OAAS,KAElG5B,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
+ {"version":3,"file":"iconify-offline.js","sources":["../../src/helper/iconify-offline.ts"],"sourcesContent":["/**\r\n * Iconify/sky-icon helpers: extract icon names from code and fetch icon data for offline use.\r\n *\r\n * Usage (browser or Node):\r\n * import { extractIconNames, fetchIconsForOffline, registerOfflineIcons } from '~/components/helper/iconify-offline';\r\n * import { SkyIcon } from '~/components/sky-icon/sky-icon';\r\n *\r\n * // Extract icon names from a code page string\r\n * const names = extractIconNames('<sky-icon icon=\"ion:home\"></sky-icon>'); // ['ion:home']\r\n *\r\n * // Fetch and then register in SkyIcon (directly pass generated JSON)\r\n * const combined = await fetchIconsForOffline(['ion:home', 'mdi:bell']);\r\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\r\n *\r\n * // Or from saved JSON string:\r\n * const combined = JSON.parse(savedJson);\r\n * registerOfflineIcons(combined, (prefix, data) => SkyIcon.registerLocalSet(prefix, data));\r\n */\r\n\r\nconst ICONIFY_API_BASE = 'https://api.iconify.design';\r\n\r\n/** Matches quoted \"prefix:icon-name\" (prefix must start with a letter so \"1:3\" etc. are ignored). */\r\nconst QUOTED_ICON_PATTERN = /([\"'`])([a-z][a-z0-9-]*:[a-zA-Z0-9_.-]+)\\1/g;\r\n\r\nexport interface IconifyIconData {\r\n body?: string;\r\n left?: number;\r\n top?: number;\r\n width?: number;\r\n height?: number;\r\n viewBox?: string;\r\n hidden?: boolean;\r\n}\r\n\r\nexport interface IconifySetData {\r\n prefix?: string;\r\n icons: Record<string, IconifyIconData>;\r\n width?: number;\r\n height?: number;\r\n left?: number;\r\n top?: number;\r\n /** Icon names requested but not in API response; add manually if needed. Omit when registering with SkyIcon. */\r\n _notFound?: string[];\r\n}\r\n\r\n/** Combined offline data: prefix -> Iconify set data (ready for SkyIcon.registerLocalSet). Skip keys starting with _ when registering. */\r\nexport type CombinedOfflineIcons = Record<string, IconifySetData>;\r\n\r\nexport interface FetchIconsForOfflineOptions {\r\n /** Iconify API base URL (default: https://api.iconify.design) */\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * Extracts all icon names in \"prefix:icon-name\" format from a code page string.\r\n * Looks for icon names inside single, double, or backtick quotes (e.g. in attributes\r\n * like icon=\"ion:home\", iconL='mdi:bell', or :icon=\"`ion:person`\").\r\n *\r\n * @param codePageString - Source code string (HTML, Vue, TS, JS, etc.)\r\n * @returns Array of unique icon names (e.g. [\"ion:home\", \"mdi:bell\"])\r\n */\r\nexport function extractIconNames(codePageString: string): string[] {\r\n if (!codePageString || typeof codePageString !== 'string') {\r\n return [];\r\n }\r\n const seen = new Set<string>();\r\n const result: string[] = [];\r\n let match: RegExpExecArray | null;\r\n QUOTED_ICON_PATTERN.lastIndex = 0;\r\n while ((match = QUOTED_ICON_PATTERN.exec(codePageString)) !== null) {\r\n const full = match[2];\r\n if (full && full.includes(':') && !seen.has(full)) {\r\n seen.add(full);\r\n result.push(full);\r\n }\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Fetches icon data from the Iconify API for the given icon names, groups by prefix,\r\n * and returns a combined JSON object keyed by prefix for offline use.\r\n *\r\n * @param iconNames - List of icon names in \"prefix:icon-name\" format (e.g. [\"ion:home\", \"mdi:bell\"])\r\n * @param options - Optional baseUrl for the Iconify API\r\n * @returns Combined object: { \"ion\": { prefix, icons, width, height }, \"mdi\": { ... }, ... }\r\n * Use with SkyIcon.registerLocalSet(prefix, combined[prefix]) for each prefix.\r\n */\r\nexport async function fetchIconsForOffline(\r\n iconNames: string[],\r\n options: FetchIconsForOfflineOptions = {}\r\n): Promise<CombinedOfflineIcons> {\r\n const baseUrl = (options.baseUrl || ICONIFY_API_BASE).replace(/\\/$/, '');\r\n const byPrefix = new Map<string, string[]>();\r\n\r\n for (const full of iconNames) {\r\n const trimmed = full.trim();\r\n if (!trimmed || !trimmed.includes(':')) continue;\r\n const [prefix, name] = trimmed.split(':');\r\n const iconName = name?.trim();\r\n if (!prefix || !iconName) continue;\r\n // Ignore non-icon patterns (e.g. \"1:3\"); Iconify prefixes start with a letter\r\n if (!/^[a-z]/.test(prefix)) continue;\r\n if (!byPrefix.has(prefix)) {\r\n byPrefix.set(prefix, []);\r\n }\r\n const list = byPrefix.get(prefix)!;\r\n if (!list.includes(iconName)) {\r\n list.push(iconName);\r\n }\r\n }\r\n\r\n const combined: CombinedOfflineIcons = {};\r\n\r\n await Promise.all(\r\n Array.from(byPrefix.entries()).map(async ([prefix, names]) => {\r\n const iconsParam = names.join(',');\r\n const url = `${baseUrl}/${prefix}.json?icons=${encodeURIComponent(iconsParam)}`;\r\n const res = await fetch(url);\r\n if (!res.ok) {\r\n throw new Error(\r\n `Iconify API error for ${prefix}: ${res.status} ${res.statusText}`\r\n );\r\n }\r\n const json = (await res.json()) as IconifySetData & {\r\n icons?: Record<string, IconifyIconData>;\r\n };\r\n const returnedIcons = json.icons || {};\r\n const notFound = names.filter((name) => !returnedIcons[name]);\r\n const setData: IconifySetData = {\r\n prefix,\r\n icons: returnedIcons,\r\n width: json.width,\r\n height: json.height,\r\n left: json.left,\r\n top: json.top,\r\n };\r\n if (notFound.length > 0) {\r\n setData._notFound = notFound;\r\n }\r\n combined[prefix] = setData;\r\n })\r\n );\r\n\r\n const hasAnyNotFound = Object.values(combined).some(\r\n (set) => set._notFound && set._notFound.length > 0\r\n );\r\n if (hasAnyNotFound) {\r\n (combined as Record<string, unknown>)['_comment_notFound'] =\r\n 'Icons listed in _notFound (per set) were not in the API response; add manually if needed.';\r\n }\r\n\r\n return combined;\r\n}\r\n\r\n/** Set data without _notFound / _comment (suitable for SkyIcon.registerLocalSet). */\r\ntype SetDataForRegister = Omit<IconifySetData, '_notFound'>;\r\n\r\n/**\r\n * Registers the generated offline JSON with SkyIcon. Pass the object returned by fetchIconsForOffline\r\n * (or parsed from saved JSON). Skips top-level keys starting with \"_\" and strips _notFound from each set.\r\n *\r\n * @param combined - The combined object (from fetchIconsForOffline or JSON.parse of saved output)\r\n * @param register - Callback to register each set, e.g. (prefix, data) => SkyIcon.registerLocalSet(prefix, data)\r\n */\r\nexport function registerOfflineIcons(\r\n combined: CombinedOfflineIcons & Record<string, unknown>,\r\n register: (prefix: string, data: SetDataForRegister) => void\r\n): void {\r\n for (const [key, set] of Object.entries(combined)) {\r\n if (key.startsWith('_')) continue;\r\n if (!set || typeof set !== 'object' || !('icons' in set)) continue;\r\n const { _notFound: _unused, ...data } = set as IconifySetData;\r\n register(key, data as SetDataForRegister);\r\n }\r\n}\r\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,2 +1,2 @@
1
- export{extractIconNames,fetchIconsForOffline,registerOfflineIcons}from"./iconify-offline.js";export{AnimationController}from"./controllers/animation-controller.js";export{CONTRAST_COLOR_SUPPORT_QUERY,ON_COLOR_FALLBACK_DARK,ON_COLOR_FALLBACK_LIGHT,contrastOnFill,contrastOverlay,contrastThemeInk,cssContrastColor,cssOnFill,cssOnFillEnhance,isLightFill,parseCssRgb,perceivedLuminance,relativeLuminance}from"./utils/contrast-color.js";export{ColorResolver,DEFAULT_COLOR_TOKENS,colorResolver,createGradient,darkShade,fadeMix,hoverShade,isDefaultColorToken,lightShade,parseGradientSpec,resolveAccentColorForToken,resolveColor,resolveGradientStop,resolveInkColorForToken,resolveSemanticSurfaceKey,semanticSoftSurfaceTokens,skyBorderPrimaryShorthand,skyBorderWidthSolid,tuneSurfaceVariantForDefaultColor}from"./utils/color-resolver.js";export{FormControlBase}from"./internals/form-control-base.js";export{PopoverController}from"./controllers/popover-controller.js";export{PresetController}from"./controllers/preset-controller.js";export{applyPresetLayer,camelToKebab,captureInitialUserAttributes,clearAppliedPresetLayer,configContext,findNearestPresetsConfig,getPresetLayer,isUserSetProp,lookupPresetSection,markUserSetProp,mergePresetsConfig,presetKeyAliases,refreshUserAttributes,resolveComponentProps,toPresetTagKey}from"./utils/component-presets.js";export{resolveRadius}from"./utils/size-resolver.js";
1
+ export{extractIconNames,fetchIconsForOffline,registerOfflineIcons}from"./iconify-offline.js";export{AnimationController}from"./controllers/animation-controller.js";export{CONTRAST_COLOR_SUPPORT_QUERY,ON_COLOR_FALLBACK_DARK,ON_COLOR_FALLBACK_LIGHT,contrastOnFill,contrastOverlay,contrastThemeInk,cssContrastColor,cssOnFill,cssOnFillEnhance,isLightFill,parseCssRgb,perceivedLuminance,relativeLuminance}from"./utils/contrast-color.js";export{ColorResolver,DEFAULT_COLOR_TOKENS,colorResolver,createGradient,darkShade,fadeMix,hoverShade,isDefaultColorToken,lightShade,parseGradientSpec,resolveAccentColorForToken,resolveColor,resolveGradientStop,resolveInkColorForToken,resolveSemanticSurfaceKey,semanticSoftSurfaceTokens,skyBorderPrimaryShorthand,skyBorderWidthSolid,tuneSurfaceVariantForDefaultColor}from"./utils/color-resolver.js";export{FormControlBase}from"./internals/form-control-base.js";export{PopoverController}from"./controllers/popover-controller.js";export{PresetController}from"./controllers/preset-controller.js";export{applyPresetLayer,camelToKebab,captureBuiltInPropSnapshot,captureInitialUserAttributes,clearAppliedPresetLayer,configContext,finalizeBuiltInPropSnapshot,findNearestPresetsConfig,getPresetLayer,isUserSetProp,lookupPresetSection,markUserSetProp,mergePresetsConfig,presetKeyAliases,refreshUserAttributes,resolveComponentProps,toPresetTagKey}from"./utils/component-presets.js";export{resolveRadius}from"./utils/size-resolver.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"color-resolver.js","sources":["../../../src/helper/utils/color-resolver.ts"],"sourcesContent":["/**\n * Color resolution utilities for Sky Design System\n * Provides consistent color token mapping across components\n */\n\nexport type ColorToken =\n | 'primary'\n | 'secondary'\n | 'success'\n | 'danger'\n | 'warning'\n | 'light'\n | 'dark'\n | 'default'\n | string; // Custom CSS color\n\nexport interface ColorResolverConfig {\n /** CSS variable prefix (default: 'sky') */\n prefix?: string;\n /** Whether to use color-mix for hover states */\n useColorMix?: boolean;\n}\n\n/**\n * Default color token mapping to CSS variables\n */\nexport const DEFAULT_COLOR_TOKENS: Record<string, string> = {\n primary: 'var(--sky-active-primary)',\n active: 'var(--sky-active-primary)',\n secondary: 'var(--sky-info-primary)',\n success: 'var(--sky-success-primary)',\n danger: 'var(--sky-danger-primary)',\n warning: 'var(--sky-warning-primary)',\n info: 'var(--sky-info-primary)',\n light: 'var(--sky-text-light)',\n dark: 'var(--sky-text-dark)',\n /** Muted line/text (e.g. dividers); maps to `--sky-text-tertiary`. */\n muted: 'var(--sky-text-tertiary)',\n /** Light border tone; maps to `--sky-border-light`. */\n 'border-light': 'var(--sky-border-light)',\n default: 'var(--sky-glass-solid)',\n} as const;\n\n/**\n * Resolves a color token to its CSS value\n * @param input - Color token (e.g., 'primary') or custom CSS color\n * @param customMap - Optional custom token mapping (overrides defaults)\n * @returns CSS color value\n */\nexport function resolveColor(\n input: string,\n customMap: Record<string, string> = {}\n): string {\n const map = { ...DEFAULT_COLOR_TOKENS, ...customMap };\n return map[input] || input || map.primary;\n}\n\n/** True when the token is the neutral glass surface (`default`), not a brand accent. */\nexport function isDefaultColorToken(input: string): boolean {\n return input === 'default';\n}\n\n/** Foreground ink (labels, borders) — never use glass-solid as text. */\nexport function resolveInkColorForToken(input: string): string {\n return isDefaultColorToken(input)\n ? 'var(--sky-text-primary)'\n : resolveColor(input);\n}\n\n/** Accent for hover/focus on neutral surfaces. */\nexport function resolveAccentColorForToken(input: string): string {\n return isDefaultColorToken(input)\n ? 'var(--sky-active-primary)'\n : resolveColor(input);\n}\n\n/** Maps component color tokens to theme semantic keys (`active`, `success`, …). */\nconst SEMANTIC_SURFACE_KEYS: Record<string, string> = {\n primary: \"active\",\n active: \"active\",\n success: \"success\",\n danger: \"danger\",\n warning: \"warning\",\n info: \"info\",\n secondary: \"info\",\n};\n\n/**\n * Resolves a color token to a theme semantic surface key, if one exists.\n */\nexport function resolveSemanticSurfaceKey(colorToken: string): string | null {\n return SEMANTIC_SURFACE_KEYS[colorToken] ?? null;\n}\n\n/** Soft semantic surface tokens for alerts, badges, and subtle status blocks. */\nexport function semanticSoftSurfaceTokens(\n semanticKey: string,\n options: { bordered?: boolean } = {},\n): { bg: string; color: string; border: string } {\n return {\n bg: `var(--sky-${semanticKey}-quinary)`,\n color: `var(--sky-${semanticKey}-primary-dark)`,\n border: options.bordered\n ? `1px solid var(--sky-${semanticKey}-g1)`\n : \"none\",\n };\n}\n\n/**\n * Theme glass border shorthand (`1px solid …` from `--sky-border-primary`).\n * Use as a full `border` value — do not wrap in `Npx solid var(--sky-border-primary)`.\n */\nexport function skyBorderPrimaryShorthand(): string {\n return 'var(--sky-border-primary)';\n}\n\n/**\n * Separator stroke for custom widths (`--sky-border-light` is a color, not shorthand).\n */\nexport function skyBorderWidthSolid(widthPx: number): string {\n return `${widthPx}px solid var(--sky-border-light)`;\n}\n\n/** Partial variant overrides when `color=\"default\"` (surface token, not ink). */\nexport type DefaultColorVariantPatch = Partial<{\n bg: string;\n color: string;\n border: string;\n shadow: string;\n hoverBg: string;\n hoverColor: string;\n hoverShadow: string;\n hoverBorder: string;\n arrow: string;\n}>;\n\nconst DEFAULT_COLOR_VARIANT_PATCHES: Record<string, DefaultColorVariantPatch> = {\n shadow: {\n bg: 'var(--sky-glass-solid)',\n hoverBg: 'var(--sky-glass-solid)',\n color: 'var(--sky-text-primary)',\n hoverColor: 'var(--sky-active-primary)',\n shadow: 'none',\n hoverShadow: 'var(--sky-box-shadow-hover)',\n arrow: 'var(--sky-glass-solid)',\n },\n solid: {\n color: 'var(--sky-text-primary)',\n hoverColor: 'var(--sky-text-primary)',\n hoverShadow: 'var(--sky-box-shadow-hover)',\n },\n faded: {\n color: 'var(--sky-text-primary)',\n hoverColor: 'var(--sky-text-primary)',\n arrow: 'var(--sky-hover-tertiary)',\n },\n bordered: {\n color: 'var(--sky-text-primary)',\n border: skyBorderWidthSolid(2),\n hoverColor: 'var(--sky-text-primary)',\n hoverBorder: '2px solid var(--sky-text-tertiary)',\n arrow: 'var(--sky-border-light)',\n },\n transparent: {\n color: 'var(--sky-text-primary)',\n hoverBg: 'var(--sky-hover-tertiary)',\n hoverColor: 'var(--sky-text-primary)',\n },\n flat: {\n bg: 'var(--sky-hover-tertiary)',\n color: 'var(--sky-text-primary)',\n hoverBg: 'var(--sky-hover-secondary)',\n hoverColor: 'var(--sky-active-primary)',\n arrow: 'var(--sky-hover-tertiary)',\n },\n ghost: {\n color: 'var(--sky-text-primary)',\n border: skyBorderWidthSolid(2),\n hoverBg: 'var(--sky-hover-secondary)',\n hoverColor: 'var(--sky-text-primary)',\n },\n link: {\n color: 'var(--sky-text-primary)',\n hoverColor: 'var(--sky-active-primary)',\n },\n light: {\n color: 'var(--sky-text-primary)',\n },\n};\n\n/**\n * Applies variant style overrides for `color=\"default\"` so glass tokens are not\n * used as text/border/hover ink on translucent surfaces.\n */\nexport function tuneSurfaceVariantForDefaultColor<T extends DefaultColorVariantPatch>(\n colorToken: string,\n variant: string,\n styles: T,\n): T {\n if (!isDefaultColorToken(colorToken)) return styles;\n const patch = DEFAULT_COLOR_VARIANT_PATCHES[variant];\n if (!patch) return styles;\n return { ...styles, ...patch };\n}\n\n/**\n * Creates a hover shade using color-mix\n * @param color - Base CSS color\n * @param amount - Percentage of black to mix (0-100)\n * @returns CSS color-mix() value\n */\nexport function hoverShade(color: string, amount: number = 20): string {\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-dark))`;\n}\n/**\n * Creates a hover shade using color-mix\n * @param color - Base CSS color\n * @param amount - Percentage of black to mix (0-100)\n * @returns CSS color-mix() value\n */\nexport function darkShade(color: string, amount: number = 20): string {\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-black))`;\n}\n/**\n * Creates a hover shade using color-mix\n * @param color - Base CSS color\n * @param amount - Percentage of black to mix (0-100)\n * @returns CSS color-mix() value\n */\nexport function lightShade(color: string, amount: number = 20): string {\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-white))`;\n}\n\n\n/**\n * Creates a faded/transparent version of a color\n * @param color - Base CSS color\n * @param amount - Percentage of transparency (0-100)\n * @returns CSS color-mix() value with transparency\n */\nexport function fadeMix(color: string, amount: number = 15): string {\n return `color-mix(in srgb, ${color} ${amount}%, transparent)`;\n}\n\n/**\n * Resolves gradient stop tokens to CSS colors\n * @param stop - Gradient stop token (e.g., 'primary 30%')\n * @returns CSS gradient stop value\n */\nexport function resolveGradientStop(stop: string): string {\n const match = stop.match(/^([^\\s]+)(\\s+.+)?$/);\n const head = match?.[1] ?? stop;\n const tail = match?.[2] ?? '';\n\n const resolved = DEFAULT_COLOR_TOKENS[head] || head;\n return `${resolved}${tail}`;\n}\n\n/**\n * Parses a gradient specification string\n */\nexport interface GradientSpec {\n kind: 'linear' | 'radial' | 'conic';\n args: string;\n stops: string[];\n}\n\n/**\n * Parses gradient specification string\n * Format: \"kind@args: stop1, stop2, stop3\"\n * Example: \"linear@90deg: primary, secondary 50%, danger\"\n * Example: \"radial@circle: primary, transparent\"\n */\nexport function parseGradientSpec(input: string): GradientSpec {\n const raw = (input || '').trim();\n const idx = raw.indexOf(':');\n let prefix = '';\n let stopsStr = raw;\n\n if (idx !== -1) {\n prefix = raw.slice(0, idx).trim();\n stopsStr = raw.slice(idx + 1).trim();\n }\n\n let kind: 'linear' | 'radial' | 'conic' = 'linear';\n let args = '90deg';\n\n if (prefix) {\n const [k, a] = prefix.split('@').map(s => s.trim());\n const kLower = (k || '').toLowerCase();\n if (kLower === 'linear' || kLower === 'radial' || kLower === 'conic') {\n kind = kLower;\n args = a || (kind === 'linear'\n ? '90deg'\n : kind === 'radial'\n ? 'circle'\n : 'from 0deg at 50% 50%');\n } else {\n stopsStr = raw;\n }\n }\n\n const stops = stopsStr\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n return { kind, args, stops };\n}\n\n/**\n * Creates a CSS gradient from a specification string\n * @param specStr - Gradient specification string\n * @param fallbackColor - Fallback color if no stops provided\n * @returns CSS gradient value\n */\nexport function createGradient(\n specStr: string,\n fallbackColor: string = 'var(--sky-active-primary)'\n): string {\n const spec = parseGradientSpec(specStr);\n const stops = (\n spec.stops.length\n ? spec.stops\n : [fallbackColor, fallbackColor]\n ).map(st => resolveGradientStop(st));\n\n const { kind, args } = spec;\n\n switch (kind) {\n case 'linear':\n return `linear-gradient(${args}, ${stops.join(', ')})`;\n case 'radial':\n return `radial-gradient(${args}, ${stops.join(', ')})`;\n case 'conic':\n return `conic-gradient(${args}, ${stops.join(', ')})`;\n default:\n return `linear-gradient(90deg, ${stops.join(', ')})`;\n }\n}\n\n/**\n * Color resolver with configuration support\n */\nexport class ColorResolver {\n private config: Required<ColorResolverConfig>;\n\n constructor(config: ColorResolverConfig = {}) {\n this.config = {\n prefix: config.prefix || 'sky',\n useColorMix: config.useColorMix ?? true,\n };\n }\n\n /**\n * Resolves a color with prefix-aware variable names\n */\n resolve(input: string): string {\n if (input.startsWith('#')) return input;\n if (input.startsWith('rgb') || input.startsWith('hsl')) return input;\n if (input.startsWith('var(')) return input;\n\n const customVar = `var(--${this.config.prefix}-${input})`;\n const defaultResolved = DEFAULT_COLOR_TOKENS[input];\n\n return defaultResolved || customVar || DEFAULT_COLOR_TOKENS.primary;\n }\n\n /**\n * Gets text color for a background color\n */\n getTextColor(backgroundColor: string): string {\n if (backgroundColor === \"default\") {\n return \"var(--sky-text-primary)\";\n }\n // Solid fills: no-@supports fallback; prefer contrast-color() in CSS.\n return \"var(--sky-on-color-fallback-light, var(--sky-text-white))\";\n }\n\n /**\n * Creates a hover state color\n */\n getHoverColor(baseColor: string, darkenAmount: number = 20): string {\n if (!this.config.useColorMix) {\n // Fallback for browsers without color-mix support\n return baseColor;\n }\n\n return hoverShade(baseColor, darkenAmount);\n }\n}\n\n/**\n * Singleton instance for convenience\n */\nexport const colorResolver = new ColorResolver();\n\n"],"names":["DEFAULT_COLOR_TOKENS","primary","active","secondary","success","danger","warning","info","light","dark","muted","default","resolveColor","input","customMap","map","isDefaultColorToken","resolveInkColorForToken","resolveAccentColorForToken","SEMANTIC_SURFACE_KEYS","resolveSemanticSurfaceKey","colorToken","semanticSoftSurfaceTokens","semanticKey","options","bg","color","border","bordered","skyBorderPrimaryShorthand","skyBorderWidthSolid","widthPx","DEFAULT_COLOR_VARIANT_PATCHES","shadow","hoverBg","hoverColor","hoverShadow","arrow","solid","faded","hoverBorder","transparent","flat","ghost","link","tuneSurfaceVariantForDefaultColor","variant","styles","patch","hoverShade","amount","darkShade","lightShade","fadeMix","resolveGradientStop","stop","match","head","parseGradientSpec","raw","trim","idx","indexOf","prefix","stopsStr","slice","kind","args","k","a","split","s","kLower","toLowerCase","stops","filter","Boolean","createGradient","specStr","fallbackColor","spec","length","st","join","ColorResolver","constructor","config","this","useColorMix","resolve","startsWith","customVar","getTextColor","backgroundColor","getHoverColor","baseColor","darkenAmount","colorResolver"],"mappings":"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"}
1
+ {"version":3,"file":"color-resolver.js","sources":["../../../src/helper/utils/color-resolver.ts"],"sourcesContent":["/**\r\n * Color resolution utilities for Sky Design System\r\n * Provides consistent color token mapping across components\r\n */\r\n\r\nexport type ColorToken =\r\n | 'primary'\r\n | 'secondary'\r\n | 'success'\r\n | 'danger'\r\n | 'warning'\r\n | 'light'\r\n | 'dark'\r\n | 'default'\r\n | string; // Custom CSS color\r\n\r\nexport interface ColorResolverConfig {\r\n /** CSS variable prefix (default: 'sky') */\r\n prefix?: string;\r\n /** Whether to use color-mix for hover states */\r\n useColorMix?: boolean;\r\n}\r\n\r\n/**\r\n * Default color token mapping to CSS variables\r\n */\r\nexport const DEFAULT_COLOR_TOKENS: Record<string, string> = {\r\n primary: 'var(--sky-active-primary)',\r\n active: 'var(--sky-active-primary)',\r\n secondary: 'var(--sky-info-primary)',\r\n success: 'var(--sky-success-primary)',\r\n danger: 'var(--sky-danger-primary)',\r\n warning: 'var(--sky-warning-primary)',\r\n info: 'var(--sky-info-primary)',\r\n light: 'var(--sky-text-light)',\r\n dark: 'var(--sky-text-dark)',\r\n /** Muted line/text (e.g. dividers); maps to `--sky-text-tertiary`. */\r\n muted: 'var(--sky-text-tertiary)',\r\n /** Light border tone; maps to `--sky-border-light`. */\r\n 'border-light': 'var(--sky-border-light)',\r\n default: 'var(--sky-glass-solid)',\r\n} as const;\r\n\r\n/**\r\n * Resolves a color token to its CSS value\r\n * @param input - Color token (e.g., 'primary') or custom CSS color\r\n * @param customMap - Optional custom token mapping (overrides defaults)\r\n * @returns CSS color value\r\n */\r\nexport function resolveColor(\r\n input: string,\r\n customMap: Record<string, string> = {}\r\n): string {\r\n const map = { ...DEFAULT_COLOR_TOKENS, ...customMap };\r\n return map[input] || input || map.primary;\r\n}\r\n\r\n/** True when the token is the neutral glass surface (`default`), not a brand accent. */\r\nexport function isDefaultColorToken(input: string): boolean {\r\n return input === 'default';\r\n}\r\n\r\n/** Foreground ink (labels, borders) — never use glass-solid as text. */\r\nexport function resolveInkColorForToken(input: string): string {\r\n return isDefaultColorToken(input)\r\n ? 'var(--sky-text-primary)'\r\n : resolveColor(input);\r\n}\r\n\r\n/** Accent for hover/focus on neutral surfaces. */\r\nexport function resolveAccentColorForToken(input: string): string {\r\n return isDefaultColorToken(input)\r\n ? 'var(--sky-active-primary)'\r\n : resolveColor(input);\r\n}\r\n\r\n/** Maps component color tokens to theme semantic keys (`active`, `success`, …). */\r\nconst SEMANTIC_SURFACE_KEYS: Record<string, string> = {\r\n primary: \"active\",\r\n active: \"active\",\r\n success: \"success\",\r\n danger: \"danger\",\r\n warning: \"warning\",\r\n info: \"info\",\r\n secondary: \"info\",\r\n};\r\n\r\n/**\r\n * Resolves a color token to a theme semantic surface key, if one exists.\r\n */\r\nexport function resolveSemanticSurfaceKey(colorToken: string): string | null {\r\n return SEMANTIC_SURFACE_KEYS[colorToken] ?? null;\r\n}\r\n\r\n/** Soft semantic surface tokens for alerts, badges, and subtle status blocks. */\r\nexport function semanticSoftSurfaceTokens(\r\n semanticKey: string,\r\n options: { bordered?: boolean } = {},\r\n): { bg: string; color: string; border: string } {\r\n return {\r\n bg: `var(--sky-${semanticKey}-quinary)`,\r\n color: `var(--sky-${semanticKey}-primary-dark)`,\r\n border: options.bordered\r\n ? `1px solid var(--sky-${semanticKey}-g1)`\r\n : \"none\",\r\n };\r\n}\r\n\r\n/**\r\n * Theme glass border shorthand (`1px solid …` from `--sky-border-primary`).\r\n * Use as a full `border` value — do not wrap in `Npx solid var(--sky-border-primary)`.\r\n */\r\nexport function skyBorderPrimaryShorthand(): string {\r\n return 'var(--sky-border-primary)';\r\n}\r\n\r\n/**\r\n * Separator stroke for custom widths (`--sky-border-light` is a color, not shorthand).\r\n */\r\nexport function skyBorderWidthSolid(widthPx: number): string {\r\n return `${widthPx}px solid var(--sky-border-light)`;\r\n}\r\n\r\n/** Partial variant overrides when `color=\"default\"` (surface token, not ink). */\r\nexport type DefaultColorVariantPatch = Partial<{\r\n bg: string;\r\n color: string;\r\n border: string;\r\n shadow: string;\r\n hoverBg: string;\r\n hoverColor: string;\r\n hoverShadow: string;\r\n hoverBorder: string;\r\n arrow: string;\r\n}>;\r\n\r\nconst DEFAULT_COLOR_VARIANT_PATCHES: Record<string, DefaultColorVariantPatch> = {\r\n shadow: {\r\n bg: 'var(--sky-glass-solid)',\r\n hoverBg: 'var(--sky-glass-solid)',\r\n color: 'var(--sky-text-primary)',\r\n hoverColor: 'var(--sky-active-primary)',\r\n shadow: 'none',\r\n hoverShadow: 'var(--sky-box-shadow-hover)',\r\n arrow: 'var(--sky-glass-solid)',\r\n },\r\n solid: {\r\n color: 'var(--sky-text-primary)',\r\n hoverColor: 'var(--sky-text-primary)',\r\n hoverShadow: 'var(--sky-box-shadow-hover)',\r\n },\r\n faded: {\r\n color: 'var(--sky-text-primary)',\r\n hoverColor: 'var(--sky-text-primary)',\r\n arrow: 'var(--sky-hover-tertiary)',\r\n },\r\n bordered: {\r\n color: 'var(--sky-text-primary)',\r\n border: skyBorderWidthSolid(2),\r\n hoverColor: 'var(--sky-text-primary)',\r\n hoverBorder: '2px solid var(--sky-text-tertiary)',\r\n arrow: 'var(--sky-border-light)',\r\n },\r\n transparent: {\r\n color: 'var(--sky-text-primary)',\r\n hoverBg: 'var(--sky-hover-tertiary)',\r\n hoverColor: 'var(--sky-text-primary)',\r\n },\r\n flat: {\r\n bg: 'var(--sky-hover-tertiary)',\r\n color: 'var(--sky-text-primary)',\r\n hoverBg: 'var(--sky-hover-secondary)',\r\n hoverColor: 'var(--sky-active-primary)',\r\n arrow: 'var(--sky-hover-tertiary)',\r\n },\r\n ghost: {\r\n color: 'var(--sky-text-primary)',\r\n border: skyBorderWidthSolid(2),\r\n hoverBg: 'var(--sky-hover-secondary)',\r\n hoverColor: 'var(--sky-text-primary)',\r\n },\r\n link: {\r\n color: 'var(--sky-text-primary)',\r\n hoverColor: 'var(--sky-active-primary)',\r\n },\r\n light: {\r\n color: 'var(--sky-text-primary)',\r\n },\r\n};\r\n\r\n/**\r\n * Applies variant style overrides for `color=\"default\"` so glass tokens are not\r\n * used as text/border/hover ink on translucent surfaces.\r\n */\r\nexport function tuneSurfaceVariantForDefaultColor<T extends DefaultColorVariantPatch>(\r\n colorToken: string,\r\n variant: string,\r\n styles: T,\r\n): T {\r\n if (!isDefaultColorToken(colorToken)) return styles;\r\n const patch = DEFAULT_COLOR_VARIANT_PATCHES[variant];\r\n if (!patch) return styles;\r\n return { ...styles, ...patch };\r\n}\r\n\r\n/**\r\n * Creates a hover shade using color-mix\r\n * @param color - Base CSS color\r\n * @param amount - Percentage of black to mix (0-100)\r\n * @returns CSS color-mix() value\r\n */\r\nexport function hoverShade(color: string, amount: number = 20): string {\r\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-dark))`;\r\n}\r\n/**\r\n * Creates a hover shade using color-mix\r\n * @param color - Base CSS color\r\n * @param amount - Percentage of black to mix (0-100)\r\n * @returns CSS color-mix() value\r\n */\r\nexport function darkShade(color: string, amount: number = 20): string {\r\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-black))`;\r\n}\r\n/**\r\n * Creates a hover shade using color-mix\r\n * @param color - Base CSS color\r\n * @param amount - Percentage of black to mix (0-100)\r\n * @returns CSS color-mix() value\r\n */\r\nexport function lightShade(color: string, amount: number = 20): string {\r\n return `color-mix(in srgb, ${color} ${100 - amount}%, var(--sky-text-white))`;\r\n}\r\n\r\n\r\n/**\r\n * Creates a faded/transparent version of a color\r\n * @param color - Base CSS color\r\n * @param amount - Percentage of transparency (0-100)\r\n * @returns CSS color-mix() value with transparency\r\n */\r\nexport function fadeMix(color: string, amount: number = 15): string {\r\n return `color-mix(in srgb, ${color} ${amount}%, transparent)`;\r\n}\r\n\r\n/**\r\n * Resolves gradient stop tokens to CSS colors\r\n * @param stop - Gradient stop token (e.g., 'primary 30%')\r\n * @returns CSS gradient stop value\r\n */\r\nexport function resolveGradientStop(stop: string): string {\r\n const match = stop.match(/^([^\\s]+)(\\s+.+)?$/);\r\n const head = match?.[1] ?? stop;\r\n const tail = match?.[2] ?? '';\r\n\r\n const resolved = DEFAULT_COLOR_TOKENS[head] || head;\r\n return `${resolved}${tail}`;\r\n}\r\n\r\n/**\r\n * Parses a gradient specification string\r\n */\r\nexport interface GradientSpec {\r\n kind: 'linear' | 'radial' | 'conic';\r\n args: string;\r\n stops: string[];\r\n}\r\n\r\n/**\r\n * Parses gradient specification string\r\n * Format: \"kind@args: stop1, stop2, stop3\"\r\n * Example: \"linear@90deg: primary, secondary 50%, danger\"\r\n * Example: \"radial@circle: primary, transparent\"\r\n */\r\nexport function parseGradientSpec(input: string): GradientSpec {\r\n const raw = (input || '').trim();\r\n const idx = raw.indexOf(':');\r\n let prefix = '';\r\n let stopsStr = raw;\r\n\r\n if (idx !== -1) {\r\n prefix = raw.slice(0, idx).trim();\r\n stopsStr = raw.slice(idx + 1).trim();\r\n }\r\n\r\n let kind: 'linear' | 'radial' | 'conic' = 'linear';\r\n let args = '90deg';\r\n\r\n if (prefix) {\r\n const [k, a] = prefix.split('@').map(s => s.trim());\r\n const kLower = (k || '').toLowerCase();\r\n if (kLower === 'linear' || kLower === 'radial' || kLower === 'conic') {\r\n kind = kLower;\r\n args = a || (kind === 'linear'\r\n ? '90deg'\r\n : kind === 'radial'\r\n ? 'circle'\r\n : 'from 0deg at 50% 50%');\r\n } else {\r\n stopsStr = raw;\r\n }\r\n }\r\n\r\n const stops = stopsStr\r\n .split(',')\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n\r\n return { kind, args, stops };\r\n}\r\n\r\n/**\r\n * Creates a CSS gradient from a specification string\r\n * @param specStr - Gradient specification string\r\n * @param fallbackColor - Fallback color if no stops provided\r\n * @returns CSS gradient value\r\n */\r\nexport function createGradient(\r\n specStr: string,\r\n fallbackColor: string = 'var(--sky-active-primary)'\r\n): string {\r\n const spec = parseGradientSpec(specStr);\r\n const stops = (\r\n spec.stops.length\r\n ? spec.stops\r\n : [fallbackColor, fallbackColor]\r\n ).map(st => resolveGradientStop(st));\r\n\r\n const { kind, args } = spec;\r\n\r\n switch (kind) {\r\n case 'linear':\r\n return `linear-gradient(${args}, ${stops.join(', ')})`;\r\n case 'radial':\r\n return `radial-gradient(${args}, ${stops.join(', ')})`;\r\n case 'conic':\r\n return `conic-gradient(${args}, ${stops.join(', ')})`;\r\n default:\r\n return `linear-gradient(90deg, ${stops.join(', ')})`;\r\n }\r\n}\r\n\r\n/**\r\n * Color resolver with configuration support\r\n */\r\nexport class ColorResolver {\r\n private config: Required<ColorResolverConfig>;\r\n\r\n constructor(config: ColorResolverConfig = {}) {\r\n this.config = {\r\n prefix: config.prefix || 'sky',\r\n useColorMix: config.useColorMix ?? true,\r\n };\r\n }\r\n\r\n /**\r\n * Resolves a color with prefix-aware variable names\r\n */\r\n resolve(input: string): string {\r\n if (input.startsWith('#')) return input;\r\n if (input.startsWith('rgb') || input.startsWith('hsl')) return input;\r\n if (input.startsWith('var(')) return input;\r\n\r\n const customVar = `var(--${this.config.prefix}-${input})`;\r\n const defaultResolved = DEFAULT_COLOR_TOKENS[input];\r\n\r\n return defaultResolved || customVar || DEFAULT_COLOR_TOKENS.primary;\r\n }\r\n\r\n /**\r\n * Gets text color for a background color\r\n */\r\n getTextColor(backgroundColor: string): string {\r\n if (backgroundColor === \"default\") {\r\n return \"var(--sky-text-primary)\";\r\n }\r\n // Solid fills: no-@supports fallback; prefer contrast-color() in CSS.\r\n return \"var(--sky-on-color-fallback-light, var(--sky-text-white))\";\r\n }\r\n\r\n /**\r\n * Creates a hover state color\r\n */\r\n getHoverColor(baseColor: string, darkenAmount: number = 20): string {\r\n if (!this.config.useColorMix) {\r\n // Fallback for browsers without color-mix support\r\n return baseColor;\r\n }\r\n\r\n return hoverShade(baseColor, darkenAmount);\r\n }\r\n}\r\n\r\n/**\r\n * Singleton instance for convenience\r\n */\r\nexport const colorResolver = new ColorResolver();\r\n\r\n"],"names":["DEFAULT_COLOR_TOKENS","primary","active","secondary","success","danger","warning","info","light","dark","muted","default","resolveColor","input","customMap","map","isDefaultColorToken","resolveInkColorForToken","resolveAccentColorForToken","SEMANTIC_SURFACE_KEYS","resolveSemanticSurfaceKey","colorToken","semanticSoftSurfaceTokens","semanticKey","options","bg","color","border","bordered","skyBorderPrimaryShorthand","skyBorderWidthSolid","widthPx","DEFAULT_COLOR_VARIANT_PATCHES","shadow","hoverBg","hoverColor","hoverShadow","arrow","solid","faded","hoverBorder","transparent","flat","ghost","link","tuneSurfaceVariantForDefaultColor","variant","styles","patch","hoverShade","amount","darkShade","lightShade","fadeMix","resolveGradientStop","stop","match","head","parseGradientSpec","raw","trim","idx","indexOf","prefix","stopsStr","slice","kind","args","k","a","split","s","kLower","toLowerCase","stops","filter","Boolean","createGradient","specStr","fallbackColor","spec","length","st","join","ColorResolver","constructor","config","this","useColorMix","resolve","startsWith","customVar","getTextColor","backgroundColor","getHoverColor","baseColor","darkenAmount","colorResolver"],"mappings":"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"}
@@ -2,8 +2,8 @@
2
2
  * Component prop presets — global defaults + named bundles for sky-* CEs.
3
3
  *
4
4
  * Precedence (highest wins):
5
- * explicit user prop (attribute or marked/diverged property) → named `preset`
6
- * → provider `defaults` → built-in class default
5
+ * explicit user prop (attribute, framework property, or marked/diverged) →
6
+ * named `preset` → provider `defaults` → built-in class default
7
7
  *
8
8
  * A named preset overlays `defaults` unless it sets `$inheritDefaults: false`,
9
9
  * in which case it is used standalone (keys it omits fall to the built-in).
@@ -12,6 +12,10 @@
12
12
  * - attributes present before Lit's first reflect (and any attrs added later)
13
13
  * - props marked via `markUserSetProp`
14
14
  * - props the consumer changed after presets applied (divergence)
15
+ * - props a framework set before first apply (current value ≠ constructor
16
+ * snapshot from `captureBuiltInPropSnapshot` — covers Vue/React CE property
17
+ * bindings that never create attributes). Overriding *back* to the exact
18
+ * built-in default still needs an attribute or `markUserSetProp`.
15
19
  *
16
20
  * @see sky-config-provider
17
21
  */
@@ -57,6 +61,23 @@ export declare function camelToKebab(name: string): string;
57
61
  * Declarative HTML / early `setAttribute` calls are treated as user-owned thereafter.
58
62
  */
59
63
  export declare function captureInitialUserAttributes(host: HTMLElement): void;
64
+ /**
65
+ * Snapshot Lit `@property` values once during host construction (before frameworks
66
+ * assign props). Later diffs against this map mark framework property bindings as
67
+ * user-owned so presets do not overwrite them.
68
+ *
69
+ * Declare `PresetController` after the host's presettable `@property` fields so
70
+ * those initials are already assigned when this runs. Keys still `undefined` are
71
+ * filled by `finalizeBuiltInPropSnapshot` on connect (constructor-body defaults).
72
+ */
73
+ export declare function captureBuiltInPropSnapshot(host: HTMLElement): void;
74
+ /**
75
+ * Fill snapshot entries that were still `undefined` when the controller
76
+ * constructed (e.g. defaults assigned in the host `constructor` body after the
77
+ * controller field). Does not overwrite defined snapshot values — those are the
78
+ * true built-ins Vue/React may have already overridden by connect time.
79
+ */
80
+ export declare function finalizeBuiltInPropSnapshot(host: HTMLElement): void;
60
81
  /**
61
82
  * Merge any attributes that appeared after connect (late framework bindings)
62
83
  * into the user-owned attribute snapshot.
@@ -69,7 +90,9 @@ export declare function refreshUserAttributes(host: HTMLElement): void;
69
90
  export declare function markUserSetProp(host: HTMLElement, prop: string): void;
70
91
  /**
71
92
  * True when the host had an attribute for this prop before/at apply time,
72
- * or the prop was marked user-owned (explicit mark or post-apply divergence).
93
+ * the prop was marked user-owned (explicit mark or post-apply divergence),
94
+ * or a framework assigned a property that differs from the constructor snapshot
95
+ * before presets first wrote that key.
73
96
  */
74
97
  export declare function isUserSetProp(host: HTMLElement, prop: string): boolean;
75
98
  /**
@@ -115,7 +138,8 @@ export type ApplyPresetLayerOptions = {
115
138
  *
116
139
  * Before writing, detects divergence: if the host value no longer matches the
117
140
  * last preset-applied value, that prop is marked user-owned (property-only
118
- * overrides after first apply).
141
+ * overrides after first apply). Also locks in framework property bindings that
142
+ * differ from the constructor snapshot before the first write for that key.
119
143
  *
120
144
  * @returns true if any host property changed
121
145
  */
@@ -1,2 +1,2 @@
1
- import{createContext as t}from"@lit/context";const e=new Set(["$inheritDefaults"]);function n(t){const e=String(t||"").trim();if(!e)return[];const n=new Set([e]);return e.startsWith("sky-")?n.add(e.slice(4)):n.add(`sky-${e}`),[...n]}function o(t,e){if(t)for(const o of n(e))if(Object.prototype.hasOwnProperty.call(t,o))return t[o]}function s(t){const e=String(t||"").trim();return e?e.startsWith("sky-")?e:`sky-${e}`:e}const r=t("sky/component-presets");function c(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/_/g,"-").toLowerCase()}const f=new WeakMap,i=new WeakMap,u=new WeakMap;function a(t){f.has(t)||f.set(t,new Set(t.getAttributeNames()))}function l(t){const e=f.get(t)??new Set;for(const n of t.getAttributeNames())e.add(n);f.set(t,e)}function p(t,e){const n=i.get(t)??new Set;n.add(e),i.set(t,n)}function g(t,e){if(i.get(t)?.has(e))return!0;const n=c(e),o=f.get(t);return o?o.has(n)||n!==e&&o.has(e):t.hasAttribute(n)||n!==e&&t.hasAttribute(e)}function d(t,e){const n=t??{},o=e??{},r={};for(const t of[n.defaults,o.defaults])for(const[e,n]of Object.entries(t??{})){const t=s(e);r[t]={...r[t]??{},...n??{}}}const c={};for(const t of[n.presets,o.presets])for(const[e,n]of Object.entries(t??{})){const t=s(e),o={...c[t]??{}};for(const[t,e]of Object.entries(n??{}))o[t]={...o[t]??{},...e??{}};c[t]=o}return{defaults:r,presets:c}}function h(t){const e=[];let n=t.parentElement;for(;n;){if("sky-config-provider"===n.localName){const t=n;if(t.mergedConfig)return t.mergedConfig;t.presets&&e.push(t.presets)}n=n.parentElement}return e.length?e.reduceRight((t,e)=>d(t,e),{}):null}function w(t,o,s){const r=function(t,e){if(!t)return{};let o={};for(const s of n(e)){const e=t[s];e&&(o={...o,...e})}return o}(t?.defaults,o),c=function(t,e){if(!t)return{};const o={};for(const s of n(e)){const e=t[s];if(e)for(const[t,n]of Object.entries(e))o[t]={...o[t]??{},...n??{}}}return o}(t?.presets,o),f=s&&c[s]?c[s]:null,i=f&&!1===f.$inheritDefaults?{...f}:{...r,...f??{}};for(const t of e)delete i[t];return i}const y=new WeakMap,k=new Set(["preset",...e]);function m(t,e,n){l(t),function(t){const e=u.get(t);if(!e?.size)return;const n=t;for(const[o,s]of e)g(t,o)||n[o]!==s&&p(t,o)}(t);const o=new Set([...k,...n?.skipKeys??[]]),s=y.get(t)??new Map,r=u.get(t)??new Map;let c=!1;const f=t;for(const n of[...s.keys()])if(!Object.prototype.hasOwnProperty.call(e,n)||void 0===e[n]){if(!g(t,n)&&s.has(n)){const t=s.get(n);f[n]!==t&&(f[n]=t,c=!0)}s.delete(n),r.delete(n)}for(const[n,i]of Object.entries(e))o.has(n)||void 0===i||(g(t,n)?r.delete(n):(s.has(n)||s.set(n,f[n]),f[n]!==i&&(f[n]=i,c=!0),r.set(n,i)));return y.set(t,s),u.set(t,r),c}function b(t){const e=y.get(t);if(!e?.size)return!1;const n=t;let o=!1;for(const[s,r]of e)g(t,s)||n[s]!==r&&(n[s]=r,o=!0);return e.clear(),u.get(t)?.clear(),o}function O(t){const{host:e,componentKey:n,presetName:o,config:s,values:r,keys:c}=t,f=w(s,n,o),i=c??Object.keys(f),u={...r};for(const t of i){const n=t;g(e,n)||Object.prototype.hasOwnProperty.call(f,n)&&void 0!==f[n]&&(u[n]=f[n])}return u}export{m as applyPresetLayer,c as camelToKebab,a as captureInitialUserAttributes,b as clearAppliedPresetLayer,r as configContext,h as findNearestPresetsConfig,w as getPresetLayer,g as isUserSetProp,o as lookupPresetSection,p as markUserSetProp,d as mergePresetsConfig,n as presetKeyAliases,l as refreshUserAttributes,O as resolveComponentProps,s as toPresetTagKey};
1
+ import{createContext as t}from"@lit/context";const e=new Set(["$inheritDefaults"]);function n(t){const e=String(t||"").trim();if(!e)return[];const n=new Set([e]);return e.startsWith("sky-")?n.add(e.slice(4)):n.add(`sky-${e}`),[...n]}function o(t,e){if(t)for(const o of n(e))if(Object.prototype.hasOwnProperty.call(t,o))return t[o]}function s(t){const e=String(t||"").trim();return e?e.startsWith("sky-")?e:`sky-${e}`:e}const r=t("sky/component-presets");function c(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/_/g,"-").toLowerCase()}const f=new WeakMap,i=new WeakMap,a=new WeakMap,u=new WeakMap,p=new Set(["preset",...e]);function l(t){f.has(t)||f.set(t,new Set(t.getAttributeNames()))}function g(t){if(u.has(t))return;const e=new Map,n=t,o=t.constructor.elementProperties;if(o)for(const t of o.keys())"string"!=typeof t||p.has(t)||e.set(t,n[t]);u.set(t,e)}function h(t){if(!u.has(t))return void g(t);const e=u.get(t),n=t;for(const[t,o]of e)void 0===o&&void 0!==n[t]&&e.set(t,n[t])}function d(t){const e=f.get(t)??new Set;for(const n of t.getAttributeNames())e.add(n);f.set(t,e)}function y(t,e){const n=i.get(t)??new Set;n.add(e),i.set(t,n)}function w(t,e){if(i.get(t)?.has(e))return!0;const n=c(e),o=f.get(t);if(o?.has(n)||n!==e&&o?.has(e))return!0;if(!o&&(t.hasAttribute(n)||n!==e&&t.hasAttribute(e)))return!0;if(!a.get(t)?.has(e)){const n=u.get(t);if(n?.has(e)){if(t[e]!==n.get(e))return!0}}return!1}function k(t,e){const n=t??{},o=e??{},r={};for(const t of[n.defaults,o.defaults])for(const[e,n]of Object.entries(t??{})){const t=s(e);r[t]={...r[t]??{},...n??{}}}const c={};for(const t of[n.presets,o.presets])for(const[e,n]of Object.entries(t??{})){const t=s(e),o={...c[t]??{}};for(const[t,e]of Object.entries(n??{}))o[t]={...o[t]??{},...e??{}};c[t]=o}return{defaults:r,presets:c}}function m(t){const e=[];let n=t.parentElement;for(;n;){if("sky-config-provider"===n.localName){const t=n;if(t.mergedConfig)return t.mergedConfig;t.presets&&e.push(t.presets)}n=n.parentElement}return e.length?e.reduceRight((t,e)=>k(t,e),{}):null}function b(t,o,s){const r=function(t,e){if(!t)return{};let o={};for(const s of n(e)){const e=t[s];e&&(o={...o,...e})}return o}(t?.defaults,o),c=function(t,e){if(!t)return{};const o={};for(const s of n(e)){const e=t[s];if(e)for(const[t,n]of Object.entries(e))o[t]={...o[t]??{},...n??{}}}return o}(t?.presets,o),f=s&&c[s]?c[s]:null,i=f&&!1===f.$inheritDefaults?{...f}:{...r,...f??{}};for(const t of e)delete i[t];return i}const O=new WeakMap;function j(t,e,n){d(t),function(t,e){const n=u.get(t);if(!n?.size)return;const o=a.get(t),s=t;for(const r of Object.keys(e))p.has(r)||o?.has(r)||i.get(t)?.has(r)||n.has(r)&&s[r]!==n.get(r)&&y(t,r)}(t,e),function(t){const e=a.get(t);if(!e?.size)return;const n=t;for(const[o,s]of e)w(t,o)||n[o]!==s&&y(t,o)}(t);const o=new Set([...p,...n?.skipKeys??[]]),s=O.get(t)??new Map,r=a.get(t)??new Map;let c=!1;const f=t;for(const n of[...s.keys()])if(!Object.prototype.hasOwnProperty.call(e,n)||void 0===e[n]){if(!w(t,n)&&s.has(n)){const t=s.get(n);f[n]!==t&&(f[n]=t,c=!0)}s.delete(n),r.delete(n)}for(const[n,i]of Object.entries(e))o.has(n)||void 0===i||(w(t,n)?r.delete(n):(s.has(n)||s.set(n,f[n]),f[n]!==i&&(f[n]=i,c=!0),r.set(n,i)));return O.set(t,s),a.set(t,r),c}function S(t){const e=O.get(t);if(!e?.size)return!1;const n=t;let o=!1;for(const[s,r]of e)w(t,s)||n[s]!==r&&(n[s]=r,o=!0);return e.clear(),a.get(t)?.clear(),o}function v(t){const{host:e,componentKey:n,presetName:o,config:s,values:r,keys:c}=t,f=b(s,n,o),i=c??Object.keys(f),a={...r};for(const t of i){const n=t;w(e,n)||Object.prototype.hasOwnProperty.call(f,n)&&void 0!==f[n]&&(a[n]=f[n])}return a}export{j as applyPresetLayer,c as camelToKebab,g as captureBuiltInPropSnapshot,l as captureInitialUserAttributes,S as clearAppliedPresetLayer,r as configContext,h as finalizeBuiltInPropSnapshot,m as findNearestPresetsConfig,b as getPresetLayer,w as isUserSetProp,o as lookupPresetSection,y as markUserSetProp,k as mergePresetsConfig,n as presetKeyAliases,d as refreshUserAttributes,v as resolveComponentProps,s as toPresetTagKey};
2
2
  //# sourceMappingURL=component-presets.js.map
@@ -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 or marked/diverged property) → named `preset`\n * → 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 *\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 * 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 * 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 * or the prop was marked user-owned (explicit mark or post-apply divergence).\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) {\n return initial.has(kebab) || (kebab !== prop && initial.has(prop));\n }\n return host.hasAttribute(kebab) || (kebab !== prop && host.hasAttribute(prop));\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 * 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\nconst PRESET_SKIP_KEYS = new Set([\"preset\", ...RESERVED_BAG_KEYS]);\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).\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 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","captureInitialUserAttributes","host","has","set","getAttributeNames","refreshUserAttributes","get","markUserSetProp","prop","isUserSetProp","kebab","initial","hasAttribute","mergePresetsConfig","parent","child","p","c","defaults","source","bag","entries","tag","presets","comp","named","mergedNamed","findNearestPresetsConfig","layers","el","parentElement","localName","provider","mergedConfig","push","length","reduceRight","acc","layer","getPresetLayer","config","componentKey","presetName","out","mergeAliasBags","namedMap","mergeAliasNamedMaps","merged","$inheritDefaults","presetRestoreValues","PRESET_SKIP_KEYS","applyPresetLayer","options","applied","size","record","appliedValue","detectDivergedUserProps","skip","skipKeys","restore","Map","changed","keys","prev","delete","value","clearAppliedPresetLayer","clear","resolveComponentProps","values","keysToApply"],"mappings":"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"}
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 * 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","findNearestPresetsConfig","layers","el","parentElement","localName","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,CAqBO,SAASI,EACdpC,GAEA,MAAMqC,EAAmC,GACzC,IAAIC,EAAyBtC,EAAKuC,cAClC,KAAOD,GAAI,CACT,GAAqB,wBAAjBA,EAAGE,UAAqC,CAC1C,MAAMC,EAAWH,EAKjB,GAAIG,EAASC,aAEX,OAAOD,EAASC,aAEdD,EAAST,SAASK,EAAOM,KAAKF,EAAST,QAC7C,CACAM,EAAKA,EAAGC,aACV,CACA,OAAKF,EAAOO,OAELP,EAAOQ,YACZ,CAACC,EAAKC,IAAUzB,EAAmBwB,EAAKC,GACxC,CAAA,GAJyB,IAM7B,CASO,SAASC,EACdC,EACAC,EACAC,GAEA,MAAMxB,EAvTR,SACE/C,EACAT,GAEA,IAAKS,EAAS,MAAO,CAAA,EACrB,IAAIwE,EAAwB,CAAA,EAC5B,IAAA,MAAWvE,KAASX,EAAiBC,GAAM,CACzC,MAAM0D,EAAMjD,EAAQC,GAChBgD,IAAKuB,EAAM,IAAKA,KAAQvB,GAC9B,CACA,OAAOuB,CACT,CA4SmBC,CAAeJ,GAAQtB,SAAUuB,GAC5CI,EA3SR,SACE1E,EAIAT,GAEA,IAAKS,EAAS,MAAO,CAAA,EACrB,MAAMwE,EAAwC,CAAA,EAC9C,IAAA,MAAWvE,KAASX,EAAiBC,GAAM,CACzC,MAAM+D,EAAQtD,EAAQC,GACtB,GAAKqD,EACL,IAAA,MAAY5C,EAAMuC,KAAQ/C,OAAOgD,QAAQI,GACvCkB,EAAI9D,GAAQ,IAAM8D,EAAI9D,IAAS,CAAA,KAASuC,GAAO,GAEnD,CACA,OAAOuB,CACT,CA0RmBG,CAAoBN,GAAQjB,QAASkB,GAChDhB,EACJiB,GAAcG,EAASH,GAAcG,EAASH,GAAe,KAEzDK,EACJtB,IAAoC,IAA3BA,EAAMuB,iBACX,IAAKvB,GACL,IAAKP,KAAcO,GAAS,CAAA,GAElC,IAAA,MAAW/D,KAAOH,SAA0BwF,EAAOrF,GACnD,OAAOqF,CACT,CAGA,MAAME,MAA0BhE,QAmBzB,SAASiE,EACd3D,EACA+C,EACAa,GAEA5C,EAAsBhB,GAxKxB,SACEA,EACA+C,GAEA,MAAM1C,EAAOR,EAAqBiB,IAAId,GACtC,IAAKK,GAAMwD,KAAM,OACjB,MAAMC,EAAUlE,EAAkBkB,IAAId,GAChCO,EAASP,EACf,IAAA,MAAW7B,KAAOW,OAAO8B,KAAKmC,GACxBjD,EAAiBG,IAAI9B,IAAQ2F,GAAS7D,IAAI9B,IAC1CwB,EAAemB,IAAId,IAAOC,IAAI9B,IAC7BkC,EAAKJ,IAAI9B,IACVoC,EAAOpC,KAASkC,EAAKS,IAAI3C,IAC3B8C,EAAgBjB,EAAM7B,EAG5B,CAyJE4F,CAA6B/D,EAAM+C,GAnJrC,SAAiC/C,GAC/B,MAAM8D,EAAUlE,EAAkBkB,IAAId,GACtC,IAAK8D,GAASD,KAAM,OACpB,MAAMtD,EAASP,EACf,IAAA,MAAY7B,EAAK6F,KAAiBF,EAC5B5C,EAAclB,EAAM7B,IACpBoC,EAAOpC,KAAS6F,GAClB/C,EAAgBjB,EAAM7B,EAG5B,CA0IE8F,CAAwBjE,GAExB,MAAMkE,MAAWjG,IAAI,IAChB6B,KACC8D,GAASO,UAAY,KAErBC,EAAUV,EAAoB5C,IAAId,QAAaM,IAC/CwD,EAAUlE,EAAkBkB,IAAId,QAAaM,IACnD,IAAI+D,GAAU,EACd,MAAM9D,EAASP,EAGf,IAAA,MAAW7B,IAAO,IAAIiG,EAAQxD,QAC5B,IACE9B,OAAOC,UAAUC,eAAeC,KAAK8D,EAAO5E,aAC5C4E,EAAM5E,GAFR,CAMA,IAAK+C,EAAclB,EAAM7B,IAAQiG,EAAQnE,IAAI9B,GAAM,CACjD,MAAMmG,EAAOF,EAAQtD,IAAI3C,GACrBoC,EAAOpC,KAASmG,IAClB/D,EAAOpC,GAAOmG,EACdD,GAAU,EAEd,CACAD,EAAQG,OAAOpG,GACf2F,EAAQS,OAAOpG,EATf,CAaF,IAAA,MAAYA,EAAK4C,KAAUjC,OAAOgD,QAAQiB,GACpCmB,EAAKjE,IAAI9B,SAAkB,IAAV4C,IACjBG,EAAclB,EAAM7B,GACtB2F,EAAQS,OAAOpG,IAGZiG,EAAQnE,IAAI9B,IACfiG,EAAQlE,IAAI/B,EAAKoC,EAAOpC,IAEtBoC,EAAOpC,KAAS4C,IAClBR,EAAOpC,GAAO4C,EACdsD,GAAU,GAEZP,EAAQ5D,IAAI/B,EAAK4C,KAKnB,OAFA2C,EAAoBxD,IAAIF,EAAMoE,GAC9BxE,EAAkBM,IAAIF,EAAM8D,GACrBO,CACT,CAKO,SAASG,EAAwBxE,GACtC,MAAMoE,EAAUV,EAAoB5C,IAAId,GACxC,IAAKoE,GAASP,KAAM,OAAO,EAC3B,MAAMtD,EAASP,EACf,IAAIqE,GAAU,EACd,IAAA,MAAYlG,EAAKmG,KAASF,EACpBlD,EAAclB,EAAM7B,IACpBoC,EAAOpC,KAASmG,IAClB/D,EAAOpC,GAAOmG,EACdD,GAAU,GAKd,OAFAD,EAAQK,QACR7E,EAAkBkB,IAAId,IAAOyE,QACtBJ,CACT,CAOO,SAASK,EACdd,GAEA,MAAM5D,KAAEA,EAAAkD,aAAMA,EAAAC,WAAcA,SAAYF,EAAA0B,OAAQA,EAAA/D,KAAQA,GAASgD,EAC3Db,EAAQC,EAAeC,EAAQC,EAAcC,GAC7CyB,EACJhE,GAAS9B,OAAO8B,KAAKmC,GAEjBK,EAAM,IAAKuB,GACjB,IAAA,MAAWxG,KAAOyG,EAAa,CAC7B,MAAMxG,EAAID,EACN+C,EAAclB,EAAM5B,IACpBU,OAAOC,UAAUC,eAAeC,KAAK8D,EAAO3E,aAAM2E,EAAM3E,KACzDgF,EAAgChF,GAAK2E,EAAM3E,GAEhD,CACA,OAAOgF,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"contrast-color.js","sources":["../../../src/helper/utils/contrast-color.ts"],"sourcesContent":["/**\n * CSS `contrast-color()` helpers for solid fills, plus JS luminance helpers\n * for canvas / SVG / preview surfaces where native CSS contrast cannot apply.\n *\n * Use only on opaque / solid surfaces. Soft tints, glass, and hover washes\n * should keep theme ink tokens (`--sky-text-primary`, soft semantic darks).\n *\n * Always declare a CSS fallback first; wrap the enhance rule in `@supports`.\n *\n * @see components-raw/CONTRAST_COLOR_AUDIT_PLAN.md\n */\n\nexport type RgbChannels = { r: number; g: number; b: number };\n\n/**\n * Parse `#rgb` / `#rrggbb` / `rgb()` / `rgba()` into 0–255 channels.\n * Returns `null` for CSS vars, named colors, oklch, etc.\n */\nexport function parseCssRgb(color: string): RgbChannels | null {\n const trimmed = color.trim();\n if (!trimmed) return null;\n\n if (trimmed.startsWith(\"#\")) {\n const hex = trimmed.slice(1);\n if (hex.length === 3 || hex.length === 4) {\n const r = parseInt(hex[0] + hex[0], 16);\n const g = parseInt(hex[1] + hex[1], 16);\n const b = parseInt(hex[2] + hex[2], 16);\n if ([r, g, b].some((n) => Number.isNaN(n))) return null;\n return { r, g, b };\n }\n if (hex.length === 6 || hex.length === 8) {\n const r = parseInt(hex.slice(0, 2), 16);\n const g = parseInt(hex.slice(2, 4), 16);\n const b = parseInt(hex.slice(4, 6), 16);\n if ([r, g, b].some((n) => Number.isNaN(n))) return null;\n return { r, g, b };\n }\n return null;\n }\n\n const m = trimmed.match(\n /^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)/i,\n );\n if (!m) return null;\n return {\n r: Math.min(255, Math.max(0, Number(m[1]))),\n g: Math.min(255, Math.max(0, Number(m[2]))),\n b: Math.min(255, Math.max(0, Number(m[3]))),\n };\n}\n\n/**\n * Rec.601 perceived luminance in 0–1 (matches historical chart/theme-gen usage).\n */\nexport function perceivedLuminance(rgb: RgbChannels): number {\n return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;\n}\n\nfunction srgbChannelToLinear(v: number): number {\n const c = v / 255;\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\n/** WCAG 2.1 relative luminance in 0–1. */\nexport function relativeLuminance(rgb: RgbChannels): number {\n return (\n 0.2126 * srgbChannelToLinear(rgb.r) +\n 0.7152 * srgbChannelToLinear(rgb.g) +\n 0.0722 * srgbChannelToLinear(rgb.b)\n );\n}\n\nexport type LuminanceMode = \"perceived\" | \"wcag\";\n\nfunction luminanceOf(\n rgb: RgbChannels,\n mode: LuminanceMode = \"perceived\",\n): number {\n return mode === \"wcag\" ? relativeLuminance(rgb) : perceivedLuminance(rgb);\n}\n\n/**\n * True when the fill is “light” enough that dark FG/overlays read better.\n * Unparseable colors return `false` (assume dark fill / light FG).\n */\nexport function isLightFill(\n color: string,\n threshold = 0.5,\n mode: LuminanceMode = \"perceived\",\n): boolean {\n const rgb = parseCssRgb(color);\n if (!rgb) return false;\n return luminanceOf(rgb, mode) > threshold;\n}\n\nexport type ContrastOnFillOptions = {\n /** Returned / used when the fill is light. */\n onLight?: string;\n /** Returned / used when the fill is dark. */\n onDark?: string;\n /** Luminance cutover. Default `0.5`. */\n threshold?: number;\n /** Default `perceived` (Rec.601). Use `wcag` for AA-aligned picks. */\n mode?: LuminanceMode;\n /** When parse fails. Defaults to `onDark`. */\n fallback?: string;\n};\n\n/**\n * Pick an opaque FG string for canvas / SVG paints against a solid fill.\n * Defaults: black on light fills, white on dark fills.\n */\nexport function contrastOnFill(\n color: string,\n options: ContrastOnFillOptions = {},\n): string {\n const {\n onLight = \"#000000\",\n onDark = \"#ffffff\",\n threshold = 0.5,\n mode = \"perceived\",\n fallback = onDark,\n } = options;\n const rgb = parseCssRgb(color);\n if (!rgb) return fallback;\n return luminanceOf(rgb, mode) > threshold ? onLight : onDark;\n}\n\n/**\n * Semi-transparent overlay for patterns / ripples on a solid fill.\n */\nexport function contrastOverlay(\n color: string,\n options: ContrastOnFillOptions = {},\n): string {\n return contrastOnFill(color, {\n onLight: \"rgba(0,0,0,0.55)\",\n onDark: \"rgba(255,255,255,0.6)\",\n threshold: 0.5,\n mode: \"perceived\",\n ...options,\n });\n}\n\n/**\n * Theme preview ink: primary on light swatches, inverse on dark.\n * Unparseable / non-hex CSS values fall back to `onLight` (theme text).\n */\nexport function contrastThemeInk(\n color: string,\n options: ContrastOnFillOptions = {},\n): string {\n return contrastOnFill(color, {\n onLight: \"var(--sky-text-primary)\",\n onDark: \"var(--sky-text-inverse)\",\n threshold: 0.6,\n mode: \"perceived\",\n fallback: \"var(--sky-text-primary)\",\n ...options,\n });\n}\n\n/** Feature query used by every `@supports` enhance block. */\nexport const CONTRAST_COLOR_SUPPORT_QUERY = \"(color: contrast-color(red))\" as const;\n\n/** Fallback FG when the fill is assumed dark (most brand solids / no-@supports). */\nexport const ON_COLOR_FALLBACK_LIGHT = \"var(--sky-on-color-fallback-light, var(--sky-text-white))\" as const;\n\n/** Fallback FG when a JS luminance check finds a light fill (canvas / SVG only). */\nexport const ON_COLOR_FALLBACK_DARK = \"var(--sky-on-color-fallback-dark, var(--sky-text-black))\" as const;\n\nfunction asCssVarRef(fillVar: string): string {\n const trimmed = fillVar.trim();\n if (trimmed.startsWith(\"var(\")) return trimmed;\n if (trimmed.startsWith(\"--\")) return `var(${trimmed})`;\n return trimmed;\n}\n\n/**\n * Returns `contrast-color(var(--fill))` (or `contrast-color(<color>)`).\n */\nexport function cssContrastColor(fillVar: string): string {\n return `contrast-color(${asCssVarRef(fillVar)})`;\n}\n\nexport type CssOnFillOptions = {\n /**\n * Selector whose `color` should follow the fill\n * (e.g. `.chip`, `button`, `:host([variant=\"solid\"]) button`).\n */\n selector: string;\n /**\n * Fill custom property or `var()` expression to contrast against\n * (e.g. `--chip-bg-color`, `var(--btn-bg-color)`).\n */\n fillVar: string;\n /**\n * Pre-`@supports` fallback. Defaults to on-color light (white) token.\n * Pass `false` to omit the fallback declaration (when the selector already sets `color`).\n */\n fallback?: string | false;\n /**\n * Appended to `selector` inside `@supports` only\n * (e.g. `:not([data-text-explicit])`).\n */\n enhanceSuffix?: string;\n /** CSS property to set. Default `color`. */\n property?: string;\n};\n\n/**\n * Fallback-first + `@supports` enhance block for a solid fill foreground.\n *\n * @example\n * ```ts\n * css`\n * ${unsafeCSS(cssOnFill({\n * selector: \".chip\",\n * fillVar: \"--chip-bg-color\",\n * fallback: \"var(--chip-text-color)\",\n * enhanceSuffix: \":not([data-text-explicit])\",\n * }))}\n * `\n * ```\n */\nexport function cssOnFill(options: CssOnFillOptions): string {\n const {\n selector,\n fillVar,\n fallback = ON_COLOR_FALLBACK_LIGHT,\n enhanceSuffix = \"\",\n property = \"color\",\n } = options;\n\n const fill = asCssVarRef(fillVar);\n const enhanceSelector = `${selector}${enhanceSuffix}`;\n const fallbackRule =\n fallback === false\n ? \"\"\n : `${selector} {\\n ${property}: ${fallback};\\n}\\n`;\n\n return `${fallbackRule}@supports ${CONTRAST_COLOR_SUPPORT_QUERY} {\n ${enhanceSelector} {\n ${property}: ${cssContrastColor(fill)};\n }\n}`;\n}\n\n/**\n * Multi-rule `@supports` enhance only (no fallback declarations).\n * Use when selectors already set fallback `color` via other rules / inline vars.\n *\n * @example\n * ```ts\n * css`\n * button { color: var(--btn-color); }\n * ${unsafeCSS(cssOnFillEnhance([\n * { selector: ':host([variant=\"solid\"]:not([data-text-explicit])) button', fillVar: '--btn-bg-color' },\n * ]))}\n * `\n * ```\n */\nexport function cssOnFillEnhance(\n rules: Array<{\n selector: string;\n fillVar: string;\n property?: string;\n }>,\n): string {\n if (!rules.length) return \"\";\n\n const body = rules\n .map(({ selector, fillVar, property = \"color\" }) => {\n return ` ${selector} {\\n ${property}: ${cssContrastColor(fillVar)};\\n }`;\n })\n .join(\"\\n\");\n\n return `@supports ${CONTRAST_COLOR_SUPPORT_QUERY} {\\n${body}\\n}`;\n}\n"],"names":["parseCssRgb","color","trimmed","trim","startsWith","hex","slice","length","r","parseInt","g","b","some","n","Number","isNaN","m","match","Math","min","max","perceivedLuminance","rgb","srgbChannelToLinear","v","c","pow","relativeLuminance","luminanceOf","mode","isLightFill","threshold","contrastOnFill","options","onLight","onDark","fallback","contrastOverlay","contrastThemeInk","CONTRAST_COLOR_SUPPORT_QUERY","ON_COLOR_FALLBACK_LIGHT","ON_COLOR_FALLBACK_DARK","asCssVarRef","fillVar","cssContrastColor","cssOnFill","selector","enhanceSuffix","property","fill","cssOnFillEnhance","rules","body","map","join"],"mappings":"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"}
1
+ {"version":3,"file":"contrast-color.js","sources":["../../../src/helper/utils/contrast-color.ts"],"sourcesContent":["/**\r\n * CSS `contrast-color()` helpers for solid fills, plus JS luminance helpers\r\n * for canvas / SVG / preview surfaces where native CSS contrast cannot apply.\r\n *\r\n * Use only on opaque / solid surfaces. Soft tints, glass, and hover washes\r\n * should keep theme ink tokens (`--sky-text-primary`, soft semantic darks).\r\n *\r\n * Always declare a CSS fallback first; wrap the enhance rule in `@supports`.\r\n *\r\n * @see components-raw/CONTRAST_COLOR_AUDIT_PLAN.md\r\n */\r\n\r\nexport type RgbChannels = { r: number; g: number; b: number };\r\n\r\n/**\r\n * Parse `#rgb` / `#rrggbb` / `rgb()` / `rgba()` into 0–255 channels.\r\n * Returns `null` for CSS vars, named colors, oklch, etc.\r\n */\r\nexport function parseCssRgb(color: string): RgbChannels | null {\r\n const trimmed = color.trim();\r\n if (!trimmed) return null;\r\n\r\n if (trimmed.startsWith(\"#\")) {\r\n const hex = trimmed.slice(1);\r\n if (hex.length === 3 || hex.length === 4) {\r\n const r = parseInt(hex[0] + hex[0], 16);\r\n const g = parseInt(hex[1] + hex[1], 16);\r\n const b = parseInt(hex[2] + hex[2], 16);\r\n if ([r, g, b].some((n) => Number.isNaN(n))) return null;\r\n return { r, g, b };\r\n }\r\n if (hex.length === 6 || hex.length === 8) {\r\n const r = parseInt(hex.slice(0, 2), 16);\r\n const g = parseInt(hex.slice(2, 4), 16);\r\n const b = parseInt(hex.slice(4, 6), 16);\r\n if ([r, g, b].some((n) => Number.isNaN(n))) return null;\r\n return { r, g, b };\r\n }\r\n return null;\r\n }\r\n\r\n const m = trimmed.match(\r\n /^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)/i,\r\n );\r\n if (!m) return null;\r\n return {\r\n r: Math.min(255, Math.max(0, Number(m[1]))),\r\n g: Math.min(255, Math.max(0, Number(m[2]))),\r\n b: Math.min(255, Math.max(0, Number(m[3]))),\r\n };\r\n}\r\n\r\n/**\r\n * Rec.601 perceived luminance in 0–1 (matches historical chart/theme-gen usage).\r\n */\r\nexport function perceivedLuminance(rgb: RgbChannels): number {\r\n return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;\r\n}\r\n\r\nfunction srgbChannelToLinear(v: number): number {\r\n const c = v / 255;\r\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\r\n}\r\n\r\n/** WCAG 2.1 relative luminance in 0–1. */\r\nexport function relativeLuminance(rgb: RgbChannels): number {\r\n return (\r\n 0.2126 * srgbChannelToLinear(rgb.r) +\r\n 0.7152 * srgbChannelToLinear(rgb.g) +\r\n 0.0722 * srgbChannelToLinear(rgb.b)\r\n );\r\n}\r\n\r\nexport type LuminanceMode = \"perceived\" | \"wcag\";\r\n\r\nfunction luminanceOf(\r\n rgb: RgbChannels,\r\n mode: LuminanceMode = \"perceived\",\r\n): number {\r\n return mode === \"wcag\" ? relativeLuminance(rgb) : perceivedLuminance(rgb);\r\n}\r\n\r\n/**\r\n * True when the fill is “light” enough that dark FG/overlays read better.\r\n * Unparseable colors return `false` (assume dark fill / light FG).\r\n */\r\nexport function isLightFill(\r\n color: string,\r\n threshold = 0.5,\r\n mode: LuminanceMode = \"perceived\",\r\n): boolean {\r\n const rgb = parseCssRgb(color);\r\n if (!rgb) return false;\r\n return luminanceOf(rgb, mode) > threshold;\r\n}\r\n\r\nexport type ContrastOnFillOptions = {\r\n /** Returned / used when the fill is light. */\r\n onLight?: string;\r\n /** Returned / used when the fill is dark. */\r\n onDark?: string;\r\n /** Luminance cutover. Default `0.5`. */\r\n threshold?: number;\r\n /** Default `perceived` (Rec.601). Use `wcag` for AA-aligned picks. */\r\n mode?: LuminanceMode;\r\n /** When parse fails. Defaults to `onDark`. */\r\n fallback?: string;\r\n};\r\n\r\n/**\r\n * Pick an opaque FG string for canvas / SVG paints against a solid fill.\r\n * Defaults: black on light fills, white on dark fills.\r\n */\r\nexport function contrastOnFill(\r\n color: string,\r\n options: ContrastOnFillOptions = {},\r\n): string {\r\n const {\r\n onLight = \"#000000\",\r\n onDark = \"#ffffff\",\r\n threshold = 0.5,\r\n mode = \"perceived\",\r\n fallback = onDark,\r\n } = options;\r\n const rgb = parseCssRgb(color);\r\n if (!rgb) return fallback;\r\n return luminanceOf(rgb, mode) > threshold ? onLight : onDark;\r\n}\r\n\r\n/**\r\n * Semi-transparent overlay for patterns / ripples on a solid fill.\r\n */\r\nexport function contrastOverlay(\r\n color: string,\r\n options: ContrastOnFillOptions = {},\r\n): string {\r\n return contrastOnFill(color, {\r\n onLight: \"rgba(0,0,0,0.55)\",\r\n onDark: \"rgba(255,255,255,0.6)\",\r\n threshold: 0.5,\r\n mode: \"perceived\",\r\n ...options,\r\n });\r\n}\r\n\r\n/**\r\n * Theme preview ink: primary on light swatches, inverse on dark.\r\n * Unparseable / non-hex CSS values fall back to `onLight` (theme text).\r\n */\r\nexport function contrastThemeInk(\r\n color: string,\r\n options: ContrastOnFillOptions = {},\r\n): string {\r\n return contrastOnFill(color, {\r\n onLight: \"var(--sky-text-primary)\",\r\n onDark: \"var(--sky-text-inverse)\",\r\n threshold: 0.6,\r\n mode: \"perceived\",\r\n fallback: \"var(--sky-text-primary)\",\r\n ...options,\r\n });\r\n}\r\n\r\n/** Feature query used by every `@supports` enhance block. */\r\nexport const CONTRAST_COLOR_SUPPORT_QUERY = \"(color: contrast-color(red))\" as const;\r\n\r\n/** Fallback FG when the fill is assumed dark (most brand solids / no-@supports). */\r\nexport const ON_COLOR_FALLBACK_LIGHT = \"var(--sky-on-color-fallback-light, var(--sky-text-white))\" as const;\r\n\r\n/** Fallback FG when a JS luminance check finds a light fill (canvas / SVG only). */\r\nexport const ON_COLOR_FALLBACK_DARK = \"var(--sky-on-color-fallback-dark, var(--sky-text-black))\" as const;\r\n\r\nfunction asCssVarRef(fillVar: string): string {\r\n const trimmed = fillVar.trim();\r\n if (trimmed.startsWith(\"var(\")) return trimmed;\r\n if (trimmed.startsWith(\"--\")) return `var(${trimmed})`;\r\n return trimmed;\r\n}\r\n\r\n/**\r\n * Returns `contrast-color(var(--fill))` (or `contrast-color(<color>)`).\r\n */\r\nexport function cssContrastColor(fillVar: string): string {\r\n return `contrast-color(${asCssVarRef(fillVar)})`;\r\n}\r\n\r\nexport type CssOnFillOptions = {\r\n /**\r\n * Selector whose `color` should follow the fill\r\n * (e.g. `.chip`, `button`, `:host([variant=\"solid\"]) button`).\r\n */\r\n selector: string;\r\n /**\r\n * Fill custom property or `var()` expression to contrast against\r\n * (e.g. `--chip-bg-color`, `var(--btn-bg-color)`).\r\n */\r\n fillVar: string;\r\n /**\r\n * Pre-`@supports` fallback. Defaults to on-color light (white) token.\r\n * Pass `false` to omit the fallback declaration (when the selector already sets `color`).\r\n */\r\n fallback?: string | false;\r\n /**\r\n * Appended to `selector` inside `@supports` only\r\n * (e.g. `:not([data-text-explicit])`).\r\n */\r\n enhanceSuffix?: string;\r\n /** CSS property to set. Default `color`. */\r\n property?: string;\r\n};\r\n\r\n/**\r\n * Fallback-first + `@supports` enhance block for a solid fill foreground.\r\n *\r\n * @example\r\n * ```ts\r\n * css`\r\n * ${unsafeCSS(cssOnFill({\r\n * selector: \".chip\",\r\n * fillVar: \"--chip-bg-color\",\r\n * fallback: \"var(--chip-text-color)\",\r\n * enhanceSuffix: \":not([data-text-explicit])\",\r\n * }))}\r\n * `\r\n * ```\r\n */\r\nexport function cssOnFill(options: CssOnFillOptions): string {\r\n const {\r\n selector,\r\n fillVar,\r\n fallback = ON_COLOR_FALLBACK_LIGHT,\r\n enhanceSuffix = \"\",\r\n property = \"color\",\r\n } = options;\r\n\r\n const fill = asCssVarRef(fillVar);\r\n const enhanceSelector = `${selector}${enhanceSuffix}`;\r\n const fallbackRule =\r\n fallback === false\r\n ? \"\"\r\n : `${selector} {\\n ${property}: ${fallback};\\n}\\n`;\r\n\r\n return `${fallbackRule}@supports ${CONTRAST_COLOR_SUPPORT_QUERY} {\r\n ${enhanceSelector} {\r\n ${property}: ${cssContrastColor(fill)};\r\n }\r\n}`;\r\n}\r\n\r\n/**\r\n * Multi-rule `@supports` enhance only (no fallback declarations).\r\n * Use when selectors already set fallback `color` via other rules / inline vars.\r\n *\r\n * @example\r\n * ```ts\r\n * css`\r\n * button { color: var(--btn-color); }\r\n * ${unsafeCSS(cssOnFillEnhance([\r\n * { selector: ':host([variant=\"solid\"]:not([data-text-explicit])) button', fillVar: '--btn-bg-color' },\r\n * ]))}\r\n * `\r\n * ```\r\n */\r\nexport function cssOnFillEnhance(\r\n rules: Array<{\r\n selector: string;\r\n fillVar: string;\r\n property?: string;\r\n }>,\r\n): string {\r\n if (!rules.length) return \"\";\r\n\r\n const body = rules\r\n .map(({ selector, fillVar, property = \"color\" }) => {\r\n return ` ${selector} {\\n ${property}: ${cssContrastColor(fillVar)};\\n }`;\r\n })\r\n .join(\"\\n\");\r\n\r\n return `@supports ${CONTRAST_COLOR_SUPPORT_QUERY} {\\n${body}\\n}`;\r\n}\r\n"],"names":["parseCssRgb","color","trimmed","trim","startsWith","hex","slice","length","r","parseInt","g","b","some","n","Number","isNaN","m","match","Math","min","max","perceivedLuminance","rgb","srgbChannelToLinear","v","c","pow","relativeLuminance","luminanceOf","mode","isLightFill","threshold","contrastOnFill","options","onLight","onDark","fallback","contrastOverlay","contrastThemeInk","CONTRAST_COLOR_SUPPORT_QUERY","ON_COLOR_FALLBACK_LIGHT","ON_COLOR_FALLBACK_DARK","asCssVarRef","fillVar","cssContrastColor","cssOnFill","selector","enhanceSuffix","property","fill","cssOnFillEnhance","rules","body","map","join"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export{ColorResolver,DEFAULT_COLOR_TOKENS,colorResolver,createGradient,darkShade,fadeMix,hoverShade,isDefaultColorToken,lightShade,parseGradientSpec,resolveAccentColorForToken,resolveColor,resolveGradientStop,resolveInkColorForToken,resolveSemanticSurfaceKey,semanticSoftSurfaceTokens,skyBorderPrimaryShorthand,skyBorderWidthSolid,tuneSurfaceVariantForDefaultColor}from"./color-resolver.js";export{applyPresetLayer,camelToKebab,captureInitialUserAttributes,clearAppliedPresetLayer,configContext,findNearestPresetsConfig,getPresetLayer,isUserSetProp,lookupPresetSection,markUserSetProp,mergePresetsConfig,presetKeyAliases,refreshUserAttributes,resolveComponentProps,toPresetTagKey}from"./component-presets.js";export{CONTRAST_COLOR_SUPPORT_QUERY,ON_COLOR_FALLBACK_DARK,ON_COLOR_FALLBACK_LIGHT,contrastOnFill,contrastOverlay,contrastThemeInk,cssContrastColor,cssOnFill,cssOnFillEnhance,isLightFill,parseCssRgb,perceivedLuminance,relativeLuminance}from"./contrast-color.js";export{resolveRadius}from"./size-resolver.js";
1
+ export{ColorResolver,DEFAULT_COLOR_TOKENS,colorResolver,createGradient,darkShade,fadeMix,hoverShade,isDefaultColorToken,lightShade,parseGradientSpec,resolveAccentColorForToken,resolveColor,resolveGradientStop,resolveInkColorForToken,resolveSemanticSurfaceKey,semanticSoftSurfaceTokens,skyBorderPrimaryShorthand,skyBorderWidthSolid,tuneSurfaceVariantForDefaultColor}from"./color-resolver.js";export{applyPresetLayer,camelToKebab,captureBuiltInPropSnapshot,captureInitialUserAttributes,clearAppliedPresetLayer,configContext,finalizeBuiltInPropSnapshot,findNearestPresetsConfig,getPresetLayer,isUserSetProp,lookupPresetSection,markUserSetProp,mergePresetsConfig,presetKeyAliases,refreshUserAttributes,resolveComponentProps,toPresetTagKey}from"./component-presets.js";export{CONTRAST_COLOR_SUPPORT_QUERY,ON_COLOR_FALLBACK_DARK,ON_COLOR_FALLBACK_LIGHT,contrastOnFill,contrastOverlay,contrastThemeInk,cssContrastColor,cssOnFill,cssOnFillEnhance,isLightFill,parseCssRgb,perceivedLuminance,relativeLuminance}from"./contrast-color.js";export{resolveRadius}from"./size-resolver.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"size-resolver.js","sources":["../../../src/helper/utils/size-resolver.ts"],"sourcesContent":["/**\r\n * Resolves a radius input into a valid CSS radius value.\r\n * @param input - The radius input (`sm`, `md`, `lg`, `full`, `none`, or CSS length).\r\n * @returns The resolved CSS radius value.\r\n */\r\nexport function resolveRadius(input: string) {\r\n const map: Record<string, string> = {\r\n none: \"0px\",\r\n sm: \"var(--sky-radius-primary)\",\r\n md: \"var(--sky-radius-secondary)\",\r\n lg: \"var(--sky-radius-tertiary)\",\r\n full: \"9999px\",\r\n };\r\n return Object.prototype.hasOwnProperty.call(map, input)\r\n ? map[input]\r\n : input;\r\n }"],"names":["resolveRadius","input","map","none","sm","md","lg","full","Object","prototype","hasOwnProperty","call"],"mappings":"AAKO,SAASA,EAAcC,GAC1B,MAAMC,EAA8B,CAClCC,KAAM,MACNC,GAAI,4BACJC,GAAI,8BACJC,GAAI,6BACJC,KAAM,UAER,OAAOC,OAAOC,UAAUC,eAAeC,KAAKT,EAAKD,GAC7CC,EAAID,GACJA,CACN"}
1
+ {"version":3,"file":"size-resolver.js","sources":["../../../src/helper/utils/size-resolver.ts"],"sourcesContent":["/**\r\n * Resolves a radius input into a valid CSS radius value.\r\n * @param input - The radius input (`sm`, `md`, `lg`, `full`, `none`, or CSS length).\r\n * @returns The resolved CSS radius value.\r\n */\r\nexport function resolveRadius(input: string) {\r\n const map: Record<string, string> = {\r\n none: '0px',\r\n sm: 'var(--sky-radius-primary)',\r\n md: 'var(--sky-radius-secondary)',\r\n lg: 'var(--sky-radius-tertiary)',\r\n full: '9999px',\r\n };\r\n return Object.prototype.hasOwnProperty.call(map, input) ? map[input] : input;\r\n}\r\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"}