@tenphi/tasty 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +1 -2
  2. package/dist/chunks/cacheKey.d.ts +1 -0
  3. package/dist/chunks/cacheKey.js +1 -2
  4. package/dist/chunks/cacheKey.js.map +1 -1
  5. package/dist/chunks/definitions.js +1 -2
  6. package/dist/chunks/definitions.js.map +1 -1
  7. package/dist/chunks/index.d.ts +1 -0
  8. package/dist/chunks/renderChunk.d.ts +1 -0
  9. package/dist/chunks/renderChunk.js +1 -2
  10. package/dist/chunks/renderChunk.js.map +1 -1
  11. package/dist/compute-styles.d.ts +31 -0
  12. package/dist/compute-styles.js +356 -0
  13. package/dist/compute-styles.js.map +1 -0
  14. package/dist/config.d.ts +7 -1
  15. package/dist/config.js +25 -22
  16. package/dist/config.js.map +1 -1
  17. package/dist/core/index.d.ts +4 -4
  18. package/dist/core/index.js +6 -6
  19. package/dist/counter-style/index.js +1 -1
  20. package/dist/counter-style/index.js.map +1 -1
  21. package/dist/debug.js +1 -2
  22. package/dist/debug.js.map +1 -1
  23. package/dist/font-face/index.js +1 -1
  24. package/dist/font-face/index.js.map +1 -1
  25. package/dist/hooks/index.d.ts +7 -0
  26. package/dist/hooks/resolve-ssr-collector.js +1 -2
  27. package/dist/hooks/resolve-ssr-collector.js.map +1 -1
  28. package/dist/hooks/useCounterStyle.js +2 -3
  29. package/dist/hooks/useCounterStyle.js.map +1 -1
  30. package/dist/hooks/useFontFace.js +2 -3
  31. package/dist/hooks/useFontFace.js.map +1 -1
  32. package/dist/hooks/useGlobalStyles.js +4 -5
  33. package/dist/hooks/useGlobalStyles.js.map +1 -1
  34. package/dist/hooks/useKeyframes.js +3 -4
  35. package/dist/hooks/useKeyframes.js.map +1 -1
  36. package/dist/hooks/useProperty.js +2 -3
  37. package/dist/hooks/useProperty.js.map +1 -1
  38. package/dist/hooks/useRawCSS.js +2 -3
  39. package/dist/hooks/useRawCSS.js.map +1 -1
  40. package/dist/hooks/useStyles.d.ts +3 -8
  41. package/dist/hooks/useStyles.js +7 -214
  42. package/dist/hooks/useStyles.js.map +1 -1
  43. package/dist/index.d.ts +5 -5
  44. package/dist/index.js +7 -7
  45. package/dist/injector/index.d.ts +1 -18
  46. package/dist/injector/index.js +5 -19
  47. package/dist/injector/index.js.map +1 -1
  48. package/dist/injector/injector.js +1 -2
  49. package/dist/injector/injector.js.map +1 -1
  50. package/dist/injector/sheet-manager.js +1 -2
  51. package/dist/injector/sheet-manager.js.map +1 -1
  52. package/dist/keyframes/index.js +1 -1
  53. package/dist/parser/classify.js +1 -2
  54. package/dist/parser/classify.js.map +1 -1
  55. package/dist/parser/const.js +14 -3
  56. package/dist/parser/const.js.map +1 -1
  57. package/dist/parser/lru.js +1 -1
  58. package/dist/parser/lru.js.map +1 -1
  59. package/dist/parser/parser.js +1 -2
  60. package/dist/parser/parser.js.map +1 -1
  61. package/dist/parser/tokenizer.js +1 -1
  62. package/dist/parser/tokenizer.js.map +1 -1
  63. package/dist/parser/types.js +1 -1
  64. package/dist/parser/types.js.map +1 -1
  65. package/dist/pipeline/conditions.js +1 -1
  66. package/dist/pipeline/conditions.js.map +1 -1
  67. package/dist/pipeline/exclusive.js +1 -2
  68. package/dist/pipeline/exclusive.js.map +1 -1
  69. package/dist/pipeline/index.js +2 -3
  70. package/dist/pipeline/index.js.map +1 -1
  71. package/dist/pipeline/materialize.js +1 -2
  72. package/dist/pipeline/materialize.js.map +1 -1
  73. package/dist/pipeline/parseStateKey.js +1 -2
  74. package/dist/pipeline/parseStateKey.js.map +1 -1
  75. package/dist/pipeline/simplify.js +1 -2
  76. package/dist/pipeline/simplify.js.map +1 -1
  77. package/dist/pipeline/warnings.js +1 -1
  78. package/dist/plugins/index.d.ts +2 -0
  79. package/dist/plugins/okhsl-plugin.js +1 -2
  80. package/dist/plugins/okhsl-plugin.js.map +1 -1
  81. package/dist/properties/index.js +2 -3
  82. package/dist/properties/index.js.map +1 -1
  83. package/dist/properties/property-type-resolver.js +1 -2
  84. package/dist/properties/property-type-resolver.js.map +1 -1
  85. package/dist/ssr/astro.js +1 -2
  86. package/dist/ssr/astro.js.map +1 -1
  87. package/dist/ssr/async-storage.js +1 -2
  88. package/dist/ssr/async-storage.js.map +1 -1
  89. package/dist/ssr/collect-auto-properties.js +1 -2
  90. package/dist/ssr/collect-auto-properties.js.map +1 -1
  91. package/dist/ssr/collector.js +4 -12
  92. package/dist/ssr/collector.js.map +1 -1
  93. package/dist/ssr/context.d.ts +2 -2
  94. package/dist/ssr/context.js +1 -2
  95. package/dist/ssr/context.js.map +1 -1
  96. package/dist/ssr/format-global-rules.js +1 -1
  97. package/dist/ssr/format-keyframes.js +1 -2
  98. package/dist/ssr/format-keyframes.js.map +1 -1
  99. package/dist/ssr/format-property.js +1 -2
  100. package/dist/ssr/format-property.js.map +1 -1
  101. package/dist/ssr/format-rules.js +1 -1
  102. package/dist/ssr/hydrate.js +1 -2
  103. package/dist/ssr/hydrate.js.map +1 -1
  104. package/dist/ssr/index.js +1 -2
  105. package/dist/ssr/index.js.map +1 -1
  106. package/dist/ssr/next.d.ts +2 -2
  107. package/dist/ssr/next.js +9 -5
  108. package/dist/ssr/next.js.map +1 -1
  109. package/dist/ssr/ssr-collector-ref.js +1 -1
  110. package/dist/states/index.js +1 -2
  111. package/dist/states/index.js.map +1 -1
  112. package/dist/static/index.js +1 -2
  113. package/dist/static/inject.d.ts +5 -0
  114. package/dist/static/inject.js +17 -0
  115. package/dist/static/inject.js.map +1 -0
  116. package/dist/static/tastyStatic.js +1 -2
  117. package/dist/static/tastyStatic.js.map +1 -1
  118. package/dist/static/types.js +1 -1
  119. package/dist/styles/border.d.ts +1 -1
  120. package/dist/styles/border.js +28 -22
  121. package/dist/styles/border.js.map +1 -1
  122. package/dist/styles/color.d.ts +1 -1
  123. package/dist/styles/color.js +2 -3
  124. package/dist/styles/color.js.map +1 -1
  125. package/dist/styles/const.js +17 -0
  126. package/dist/styles/const.js.map +1 -0
  127. package/dist/styles/createStyle.js +4 -5
  128. package/dist/styles/createStyle.js.map +1 -1
  129. package/dist/styles/dimension.js +15 -3
  130. package/dist/styles/dimension.js.map +1 -1
  131. package/dist/styles/directional.js +133 -0
  132. package/dist/styles/directional.js.map +1 -0
  133. package/dist/styles/display.d.ts +3 -10
  134. package/dist/styles/display.js +45 -39
  135. package/dist/styles/display.js.map +1 -1
  136. package/dist/styles/fade.d.ts +1 -1
  137. package/dist/styles/fade.js +9 -5
  138. package/dist/styles/fade.js.map +1 -1
  139. package/dist/styles/fill.d.ts +2 -2
  140. package/dist/styles/fill.js +3 -4
  141. package/dist/styles/fill.js.map +1 -1
  142. package/dist/styles/flow.js +1 -1
  143. package/dist/styles/gap.d.ts +1 -1
  144. package/dist/styles/gap.js +4 -3
  145. package/dist/styles/gap.js.map +1 -1
  146. package/dist/styles/height.d.ts +1 -1
  147. package/dist/styles/height.js +1 -2
  148. package/dist/styles/height.js.map +1 -1
  149. package/dist/styles/index.d.ts +0 -1
  150. package/dist/styles/index.js +3 -4
  151. package/dist/styles/index.js.map +1 -1
  152. package/dist/styles/inset.d.ts +1 -29
  153. package/dist/styles/inset.js +19 -135
  154. package/dist/styles/inset.js.map +1 -1
  155. package/dist/styles/list.d.ts +5 -5
  156. package/dist/styles/list.js +4 -2
  157. package/dist/styles/list.js.map +1 -1
  158. package/dist/styles/margin.d.ts +1 -1
  159. package/dist/styles/margin.js +17 -89
  160. package/dist/styles/margin.js.map +1 -1
  161. package/dist/styles/outline.d.ts +1 -1
  162. package/dist/styles/outline.js +6 -16
  163. package/dist/styles/outline.js.map +1 -1
  164. package/dist/styles/padding.d.ts +1 -1
  165. package/dist/styles/padding.js +17 -89
  166. package/dist/styles/padding.js.map +1 -1
  167. package/dist/styles/placement.d.ts +37 -0
  168. package/dist/styles/placement.js +74 -0
  169. package/dist/styles/placement.js.map +1 -0
  170. package/dist/styles/predefined.d.ts +4 -4
  171. package/dist/styles/predefined.js +8 -9
  172. package/dist/styles/predefined.js.map +1 -1
  173. package/dist/styles/preset.d.ts +1 -1
  174. package/dist/styles/preset.js +7 -7
  175. package/dist/styles/preset.js.map +1 -1
  176. package/dist/styles/radius.d.ts +1 -3
  177. package/dist/styles/radius.js +38 -6
  178. package/dist/styles/radius.js.map +1 -1
  179. package/dist/styles/scrollMargin.d.ts +24 -0
  180. package/dist/styles/scrollMargin.js +32 -0
  181. package/dist/styles/scrollMargin.js.map +1 -0
  182. package/dist/styles/scrollbar.d.ts +1 -1
  183. package/dist/styles/scrollbar.js +8 -5
  184. package/dist/styles/scrollbar.js.map +1 -1
  185. package/dist/styles/shadow.d.ts +1 -1
  186. package/dist/styles/shadow.js +4 -3
  187. package/dist/styles/shadow.js.map +1 -1
  188. package/dist/styles/shared.js +17 -0
  189. package/dist/styles/shared.js.map +1 -0
  190. package/dist/styles/transition.d.ts +1 -1
  191. package/dist/styles/transition.js +5 -4
  192. package/dist/styles/transition.js.map +1 -1
  193. package/dist/styles/types.d.ts +24 -7
  194. package/dist/styles/width.d.ts +1 -1
  195. package/dist/styles/width.js +1 -2
  196. package/dist/styles/width.js.map +1 -1
  197. package/dist/tasty.d.ts +29 -14
  198. package/dist/tasty.js +70 -62
  199. package/dist/tasty.js.map +1 -1
  200. package/dist/utils/cache-wrapper.js +1 -2
  201. package/dist/utils/cache-wrapper.js.map +1 -1
  202. package/dist/utils/case-converter.js +1 -1
  203. package/dist/utils/color-math.js +1 -1
  204. package/dist/utils/color-math.js.map +1 -1
  205. package/dist/utils/color-space.js +1 -2
  206. package/dist/utils/color-space.js.map +1 -1
  207. package/dist/utils/colors.js +1 -2
  208. package/dist/utils/colors.js.map +1 -1
  209. package/dist/utils/dotize.js +1 -1
  210. package/dist/utils/dotize.js.map +1 -1
  211. package/dist/utils/filter-base-props.js +1 -1
  212. package/dist/utils/get-display-name.js +1 -1
  213. package/dist/utils/has-keys.js +1 -1
  214. package/dist/utils/is-dev-env.js +1 -1
  215. package/dist/utils/is-dev-env.js.map +1 -1
  216. package/dist/utils/is-valid-element-type.js +1 -1
  217. package/dist/utils/merge-styles.js +1 -2
  218. package/dist/utils/merge-styles.js.map +1 -1
  219. package/dist/utils/mod-attrs.d.ts +0 -2
  220. package/dist/utils/mod-attrs.js +1 -2
  221. package/dist/utils/mod-attrs.js.map +1 -1
  222. package/dist/utils/process-tokens.d.ts +1 -5
  223. package/dist/utils/process-tokens.js +3 -11
  224. package/dist/utils/process-tokens.js.map +1 -1
  225. package/dist/utils/resolve-recipes.js +1 -2
  226. package/dist/utils/resolve-recipes.js.map +1 -1
  227. package/dist/utils/selector-transform.js +1 -1
  228. package/dist/utils/string.js +1 -1
  229. package/dist/utils/styles.d.ts +1 -1
  230. package/dist/utils/styles.js +2 -3
  231. package/dist/utils/styles.js.map +1 -1
  232. package/dist/utils/typography.js +1 -1
  233. package/dist/utils/typography.js.map +1 -1
  234. package/dist/utils/warnings.js +1 -1
  235. package/dist/zero/babel.d.ts +17 -2
  236. package/dist/zero/babel.js +105 -41
  237. package/dist/zero/babel.js.map +1 -1
  238. package/dist/zero/css-writer.js +1 -2
  239. package/dist/zero/css-writer.js.map +1 -1
  240. package/dist/zero/extractor.js +1 -2
  241. package/dist/zero/extractor.js.map +1 -1
  242. package/dist/zero/index.js +1 -2
  243. package/dist/zero/next.d.ts +12 -0
  244. package/dist/zero/next.js +5 -4
  245. package/dist/zero/next.js.map +1 -1
  246. package/docs/methodology.md +50 -1
  247. package/docs/runtime.md +90 -3
  248. package/docs/ssr.md +10 -10
  249. package/docs/styles.md +17 -0
  250. package/docs/tasty-static.md +87 -0
  251. package/package.json +12 -7
  252. package/dist/styles/align.d.ts +0 -15
  253. package/dist/styles/align.js +0 -14
  254. package/dist/styles/align.js.map +0 -1
  255. package/dist/styles/justify.d.ts +0 -15
  256. package/dist/styles/justify.js +0 -14
  257. package/dist/styles/justify.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/states/index.ts"],"sourcesContent":["/**\n * Advanced State Mapping - Predefined States Management\n *\n * This module handles global and local predefined states for the Tasty styling system.\n * See ADVANCED_STATE_MAPPING.md for full specification.\n */\n\nimport { hasStylesGenerated } from '../config';\nimport type { Styles } from '../styles/types';\nimport { isDevEnv } from '../utils/is-dev-env';\n\n/**\n * Parsed advanced state information\n */\nexport interface ParsedAdvancedState {\n type:\n | 'media'\n | 'container'\n | 'root'\n | 'parent'\n | 'own'\n | 'starting'\n | 'predefined'\n | 'modifier';\n condition: string; // e.g., 'width <= 920px' or 'hovered'\n containerName?: string; // for container queries\n raw: string; // original state key\n mediaType?: string; // for @media:screen, @media:print, etc.\n}\n\n/**\n * Context for state parsing operations\n */\nexport interface StateParserContext {\n localPredefinedStates: Record<string, string>;\n globalPredefinedStates: Record<string, string>;\n isSubElement?: boolean; // true when processing sub-element styles (for @own() validation)\n}\n\n/**\n * At-rule context for CSS generation\n */\nexport interface AtRuleContext {\n media?: string[]; // @media conditions to wrap rule in (merged with 'and')\n container?: { name?: string; condition: string }[]; // @container conditions (nested)\n startingStyle?: boolean;\n rootStates?: string[]; // :root state selectors (e.g., '[data-theme=\"dark\"]')\n negatedRootStates?: string[]; // Negated :root state selectors for non-overlapping rules (e.g., ':not([data-theme=\"dark\"])')\n}\n\n// Built-in state names that cannot be overridden\nconst BUILTIN_STATES = new Set([\n '@starting',\n '@keyframes',\n '@properties',\n '@fontFace',\n '@counterStyle',\n '@supports',\n // @inherit is a value (not a key), but reserved here to prevent\n // users from accidentally defining a state named '@inherit'.\n '@inherit',\n]);\n\n// Reserved prefixes that are built-in\nconst RESERVED_PREFIXES = [\n '@media',\n '@root',\n '@parent',\n '@own',\n '@(',\n '@starting',\n '@keyframes',\n '@properties',\n '@supports',\n '@inherit',\n];\n\n// Global predefined states storage\nlet globalPredefinedStates: Record<string, string> = {};\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n/**\n * Configure global predefined states\n */\nexport function setGlobalPredefinedStates(\n states: Record<string, string>,\n): void {\n if (hasStylesGenerated()) {\n const newStateNames = Object.keys(states).join(', ');\n warnOnce(\n `dynamic-states:${newStateNames}`,\n `[Tasty] Cannot update predefined states after styles have been generated.\\n` +\n `The new definition(s) for ${newStateNames} will be ignored.`,\n );\n return;\n }\n\n // Validate state names\n for (const [name, value] of Object.entries(states)) {\n // Check for valid name format\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(name)) {\n warnOnce(\n `invalid-state-name:${name}`,\n `[Tasty] Invalid predefined state name '${name}'. Must start with '@' followed by a letter.`,\n );\n continue;\n }\n\n // Check for reserved names\n if (BUILTIN_STATES.has(name)) {\n warnOnce(\n `reserved-state:${name}`,\n `[Tasty] Cannot define predefined state '${name}'. This name is reserved for built-in functionality.`,\n );\n continue;\n }\n\n // Check for reserved prefixes (but only exact matches, not user-defined states like @mobile)\n // Reserved prefixes are: @media, @root, @parent, @own, @(\n // A user state like @mobile should NOT be blocked\n const isReservedPrefix =\n name === '@media' ||\n name === '@root' ||\n name === '@parent' ||\n name === '@own' ||\n name.startsWith('@(');\n\n if (isReservedPrefix) {\n warnOnce(\n `reserved-prefix:${name}`,\n `[Tasty] Cannot define predefined state '${name}'. This prefix is reserved for built-in functionality.`,\n );\n continue;\n }\n\n // Check for cross-references\n const crossRefs = extractPredefinedStateRefs(value);\n if (crossRefs.length > 0) {\n warnOnce(\n `cross-ref:${name}`,\n `[Tasty] Predefined state '${name}' references another predefined state '${crossRefs[0]}'.\\n` +\n `Predefined states cannot reference each other. Use the full definition instead.`,\n );\n continue;\n }\n\n // Check for duplicates\n if (\n globalPredefinedStates[name] &&\n globalPredefinedStates[name] !== value\n ) {\n warnOnce(\n `duplicate-state:${name}`,\n `[Tasty] Duplicate predefined state '${name}' in configure(). The last definition will be used.`,\n );\n }\n\n globalPredefinedStates[name] = value;\n }\n}\n\n/**\n * Get global predefined states\n */\nexport function getGlobalPredefinedStates(): Record<string, string> {\n return globalPredefinedStates;\n}\n\n/**\n * Clear global predefined states (for testing only)\n */\nexport function clearGlobalPredefinedStates(): void {\n globalPredefinedStates = {};\n emittedWarnings.clear();\n}\n\n/**\n * Regex to match predefined state references in a string\n * Matches @name that is NOT followed by ( or : and is a complete word\n * Uses word boundary and negative lookahead\n */\nconst PREDEFINED_STATE_PATTERN = /@([A-Za-z][A-Za-z0-9-]*)(?![A-Za-z0-9-:(])/g;\n\n/**\n * Extract predefined state references from a string\n */\nexport function extractPredefinedStateRefs(value: string): string[] {\n const matches = value.matchAll(PREDEFINED_STATE_PATTERN);\n const refs: string[] = [];\n\n for (const match of matches) {\n const stateName = '@' + match[1];\n // Skip built-in states (@starting) and duplicates\n // Note: @media, @root, @own are always followed by '(' so the regex\n // negative lookahead (?![A-Za-z0-9-:(]) already excludes them\n if (!BUILTIN_STATES.has(stateName) && !refs.includes(stateName)) {\n refs.push(stateName);\n }\n }\n\n return refs;\n}\n\n/**\n * Check if a state key is a predefined state reference\n */\nexport function isPredefinedStateRef(stateKey: string): boolean {\n if (!stateKey.startsWith('@')) return false;\n if (BUILTIN_STATES.has(stateKey)) return false;\n\n // Check if it's NOT a built-in prefix\n for (const prefix of RESERVED_PREFIXES) {\n if (stateKey === prefix || stateKey.startsWith(prefix)) {\n // Check if it's exactly @media, @root, @parent, @own, or starts with @( or @media(\n if (\n stateKey === '@media' ||\n stateKey.startsWith('@media(') ||\n stateKey.startsWith('@media:')\n ) {\n return false;\n }\n if (stateKey === '@root' || stateKey.startsWith('@root(')) return false;\n if (stateKey === '@parent' || stateKey.startsWith('@parent('))\n return false;\n if (stateKey === '@own' || stateKey.startsWith('@own(')) return false;\n if (stateKey.startsWith('@(')) return false;\n }\n }\n\n // Must match the predefined state pattern\n return /^@[A-Za-z][A-Za-z0-9-]*$/.test(stateKey);\n}\n\nconst _localStatesCache = new WeakMap<object, Record<string, string>>();\n\n/**\n * Extract local predefined states from a styles object\n * Local predefined states are top-level keys starting with @ that have string values\n * and are valid predefined state names (not built-in like @media, @root, etc.)\n *\n * Results are cached by object identity via WeakMap since the same styles object\n * is passed to this function multiple times per pipeline run (once per chunk\n * in renderStylesForChunk and generateChunkCacheKey).\n */\nexport function extractLocalPredefinedStates(\n styles: Styles,\n): Record<string, string> {\n if (!styles || typeof styles !== 'object') {\n return {};\n }\n\n const cached = _localStatesCache.get(styles as object);\n if (cached !== undefined) return cached;\n\n const localStates: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(styles)) {\n // Check if it's a predefined state definition (starts with @, has string value)\n if (key.startsWith('@') && typeof value === 'string') {\n // Validate name format - must be @[letter][letters/numbers/dashes]*\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(key)) {\n continue; // Skip invalid names silently (might be something else)\n }\n\n // Skip built-in states\n if (BUILTIN_STATES.has(key)) {\n continue;\n }\n\n // Skip reserved prefixes\n if (\n key === '@media' ||\n key === '@root' ||\n key === '@parent' ||\n key === '@own' ||\n key.startsWith('@(')\n ) {\n continue;\n }\n\n // Check for cross-references (predefined states cannot reference each other)\n const crossRefs = extractPredefinedStateRefs(value);\n if (crossRefs.length > 0) {\n warnOnce(\n `local-cross-ref:${key}`,\n `[Tasty] Predefined state '${key}' references another predefined state '${crossRefs[0]}'.\\n` +\n `Predefined states cannot reference each other. Use the full definition instead.`,\n );\n continue;\n }\n\n localStates[key] = value;\n }\n }\n\n _localStatesCache.set(styles as object, localStates);\n\n return localStates;\n}\n\n/**\n * Create a state parser context from styles\n */\nexport function createStateParserContext(\n styles?: Styles,\n isSubElement?: boolean,\n): StateParserContext {\n const localStates = styles ? extractLocalPredefinedStates(styles) : {};\n\n return {\n localPredefinedStates: localStates,\n globalPredefinedStates: getGlobalPredefinedStates(),\n isSubElement,\n };\n}\n\n/**\n * Resolve a predefined state reference to its value\n * Returns the resolved value or null if not found\n */\nexport function resolvePredefinedState(\n stateKey: string,\n ctx: StateParserContext,\n): string | null {\n // Check local first (higher priority)\n if (ctx.localPredefinedStates[stateKey]) {\n return ctx.localPredefinedStates[stateKey];\n }\n\n // Then check global\n if (ctx.globalPredefinedStates[stateKey]) {\n return ctx.globalPredefinedStates[stateKey];\n }\n\n // Not found - emit warning\n warnOnce(\n `undefined-state:${stateKey}`,\n `[Tasty] Undefined predefined state '${stateKey}'.\\n` +\n `Define it in configure({ states: { '${stateKey}': '...' } }) or in the component's styles.`,\n );\n\n return null;\n}\n\n/**\n * Normalize state key by trimming whitespace and removing trailing/leading operators\n */\nexport function normalizeStateKey(stateKey: string): {\n key: string;\n warnings: string[];\n} {\n const warnings: string[] = [];\n let key = stateKey;\n\n // Check for whitespace-only\n if (key.trim() === '') {\n if (key !== '') {\n warnings.push(\n `[Tasty] Whitespace-only state key normalized to default state ''.`,\n );\n }\n return { key: '', warnings };\n }\n\n // Trim whitespace\n key = key.trim();\n\n // Remove trailing operators\n const trailingOpMatch = key.match(/\\s*[&|^]\\s*$/);\n if (trailingOpMatch) {\n const originalKey = key;\n key = key.slice(0, -trailingOpMatch[0].length).trim();\n warnings.push(\n `[Tasty] State key '${originalKey}' has trailing operator. Normalized to '${key}'.`,\n );\n }\n\n // Remove leading operators (except !)\n const leadingOpMatch = key.match(/^\\s*[&|^]\\s*/);\n if (leadingOpMatch) {\n const originalKey = key;\n key = key.slice(leadingOpMatch[0].length).trim();\n warnings.push(\n `[Tasty] State key '${originalKey}' has leading operator. Normalized to '${key}'.`,\n );\n }\n\n return { key, warnings };\n}\n\n/**\n * Expand dimension shorthands in a condition string\n * w -> width, h -> height, is -> inline-size, bs -> block-size\n */\nexport function expandDimensionShorthands(condition: string): string {\n // Replace dimension shorthands (only when they appear as standalone words)\n let result = condition;\n\n // w -> width (but not part of other words)\n result = result.replace(/\\bw\\b/g, 'width');\n\n // h -> height\n result = result.replace(/\\bh\\b/g, 'height');\n\n // is -> inline-size\n result = result.replace(/\\bis\\b/g, 'inline-size');\n\n // bs -> block-size\n result = result.replace(/\\bbs\\b/g, 'block-size');\n\n return result;\n}\n\n/**\n * Convert tasty units in a string (e.g., 40x -> calc(var(--gap) * 40))\n */\nexport function expandTastyUnits(value: string): string {\n // Match number followed by 'x' unit (tasty gap unit)\n return value.replace(/(\\d+(?:\\.\\d+)?)\\s*x\\b/g, (_, num) => {\n return `calc(var(--gap) * ${num})`;\n });\n}\n\n/**\n * Parse an advanced state key and return its type and components\n */\nexport function parseAdvancedState(\n stateKey: string,\n ctx: StateParserContext,\n): ParsedAdvancedState {\n const raw = stateKey;\n\n // Check for @starting (exact match)\n if (stateKey === '@starting') {\n return {\n type: 'starting',\n condition: '',\n raw,\n };\n }\n\n // Check for @media:type (e.g., @media:print)\n if (stateKey.startsWith('@media:')) {\n const mediaType = stateKey.slice(7); // Remove '@media:'\n if (!['all', 'screen', 'print', 'speech'].includes(mediaType)) {\n warnOnce(\n `unknown-media-type:${mediaType}`,\n `[Tasty] Unknown media type '${mediaType}'. Valid types: all, screen, print, speech.`,\n );\n }\n return {\n type: 'media',\n condition: '',\n mediaType,\n raw,\n };\n }\n\n // Check for @media(...) - media query with condition\n if (stateKey.startsWith('@media(')) {\n const endParen = findMatchingParen(stateKey, 6);\n if (endParen === -1) {\n warnOnce(\n `unclosed-media:${stateKey}`,\n `[Tasty] Unclosed media query '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n let condition = stateKey.slice(7, endParen);\n\n // Check for empty condition\n if (!condition.trim()) {\n warnOnce(\n `empty-media:${stateKey}`,\n `[Tasty] Empty media query condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Expand shorthands and units\n condition = expandDimensionShorthands(condition);\n condition = expandTastyUnits(condition);\n\n return {\n type: 'media',\n condition,\n raw,\n };\n }\n\n // Check for @root(...) - root state\n if (stateKey.startsWith('@root(')) {\n const endParen = findMatchingParen(stateKey, 5);\n if (endParen === -1) {\n warnOnce(\n `unclosed-root:${stateKey}`,\n `[Tasty] Unclosed root state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(6, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-root:${stateKey}`,\n `[Tasty] Empty root state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n return {\n type: 'root',\n condition,\n raw,\n };\n }\n\n // Check for @parent(...) - parent element state\n if (stateKey.startsWith('@parent(')) {\n const endParen = findMatchingParen(stateKey, 7);\n if (endParen === -1) {\n warnOnce(\n `unclosed-parent:${stateKey}`,\n `[Tasty] Unclosed parent state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(8, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-parent:${stateKey}`,\n `[Tasty] Empty parent state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n return {\n type: 'parent',\n condition,\n raw,\n };\n }\n\n // Check for @own(...) - sub-element own state\n if (stateKey.startsWith('@own(')) {\n const endParen = findMatchingParen(stateKey, 4);\n if (endParen === -1) {\n warnOnce(\n `unclosed-own:${stateKey}`,\n `[Tasty] Unclosed own state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(5, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-own:${stateKey}`,\n `[Tasty] Empty own state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Check if used outside sub-element context\n if (!ctx.isSubElement) {\n warnOnce(\n `own-outside-subelement:${stateKey}`,\n `[Tasty] @own(${condition}) used outside sub-element context.\\n` +\n `@own() is equivalent to '${condition}' at the root level. ` +\n `Did you mean to use it inside a sub-element?`,\n );\n // Treat as regular modifier\n return {\n type: 'modifier',\n condition,\n raw,\n };\n }\n\n return {\n type: 'own',\n condition,\n raw,\n };\n }\n\n // Check for @(...) - container query (unnamed or named)\n if (stateKey.startsWith('@(')) {\n const endParen = findMatchingParen(stateKey, 1);\n if (endParen === -1) {\n warnOnce(\n `unclosed-container:${stateKey}`,\n `[Tasty] Unclosed container query '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const content = stateKey.slice(2, endParen);\n\n if (!content.trim()) {\n warnOnce(\n `empty-container:${stateKey}`,\n `[Tasty] Empty container query '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Check if named container (first token is name, followed by comma)\n // Use parentheses-aware comma search so inner commas (e.g., scroll-state(a, b)) are skipped\n const commaIndex = findTopLevelComma(content);\n let containerName: string | undefined;\n let condition: string;\n\n if (commaIndex !== -1) {\n // Named container: @(layout, w < 600px)\n containerName = content.slice(0, commaIndex).trim();\n condition = content.slice(commaIndex + 1).trim();\n } else {\n // Unnamed container: @(w < 600px)\n condition = content.trim();\n }\n\n // Check for style query shorthand (starts with $)\n if (condition.startsWith('$')) {\n // Style query: @(layout, $compact) or @(layout, $variant=compact)\n const styleCondition = parseStyleQuery(condition);\n if (!styleCondition) {\n warnOnce(\n `invalid-style-query:${stateKey}`,\n `[Tasty] Invalid style query '${condition}' in container query.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n condition = styleCondition;\n } else if (/^[a-zA-Z][\\w-]*\\s*\\(/.test(condition)) {\n // Function-like syntax: scroll-state(...), style(...), etc.\n // Pass through verbatim — no dimension expansion needed\n } else {\n // Dimension query - expand shorthands and units\n condition = expandDimensionShorthands(condition);\n condition = expandTastyUnits(condition);\n }\n\n return {\n type: 'container',\n condition,\n containerName,\n raw,\n };\n }\n\n // Check for predefined state reference\n if (isPredefinedStateRef(stateKey)) {\n const resolved = resolvePredefinedState(stateKey, ctx);\n if (resolved) {\n // Recursively parse the resolved value to extract the actual state type\n // (e.g., @mobile -> @media(w < 768px) should become a media state)\n const parsedResolved = parseAdvancedState(resolved, ctx);\n return {\n ...parsedResolved,\n raw, // Keep original raw for traceability\n };\n }\n // If not resolved, treat as modifier (will likely produce invalid CSS)\n return {\n type: 'modifier',\n condition: stateKey,\n raw,\n };\n }\n\n // Regular modifier (boolean mod, pseudo-class, class, attribute)\n return {\n type: 'modifier',\n condition: stateKey,\n raw,\n };\n}\n\n/**\n * Parse a style query condition (e.g., $compact, $variant=compact, $variant=\"very compact\")\n */\nfunction parseStyleQuery(condition: string): string | null {\n // Remove $ prefix\n const query = condition.slice(1);\n\n // Check for comparison operators (not supported)\n if (/[<>]/.test(query)) {\n warnOnce(\n `style-query-comparison:${condition}`,\n `[Tasty] Style queries only support equality. '${condition}' is invalid. Use '${condition.split(/[<>]/)[0]}=...' instead.`,\n );\n return null;\n }\n\n // Check for equality\n const eqIndex = query.indexOf('=');\n if (eqIndex === -1) {\n // Just existence check: style(--compact)\n return `style(--${query})`;\n }\n\n const propName = query.slice(0, eqIndex).trim();\n let value = query.slice(eqIndex + 1).trim();\n\n // Handle quoted values\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n // Keep quotes for CSS\n } else {\n // Add quotes if needed\n value = `\"${value}\"`;\n }\n\n // Expand tasty units in value\n value = expandTastyUnits(value);\n\n return `style(--${propName}: ${value})`;\n}\n\n/**\n * Find the index of the first comma at parentheses depth 0.\n * Returns -1 if no top-level comma is found.\n * This prevents splitting on commas inside function calls like scroll-state(a, b).\n */\nexport function findTopLevelComma(s: string): number {\n let depth = 0;\n\n for (let i = 0; i < s.length; i++) {\n if (s[i] === '(') depth++;\n else if (s[i] === ')') depth--;\n else if (s[i] === ',' && depth === 0) return i;\n }\n\n return -1;\n}\n\nfunction findMatchingParen(str: string, startIndex: number): number {\n let depth = 1;\n let i = startIndex + 1;\n\n while (i < str.length && depth > 0) {\n if (str[i] === '(') depth++;\n else if (str[i] === ')') depth--;\n i++;\n }\n\n return depth === 0 ? i - 1 : -1;\n}\n\n/**\n * Check if a state key is an advanced state (starts with @)\n */\nexport function isAdvancedState(stateKey: string): boolean {\n return stateKey.startsWith('@') || stateKey.startsWith('!@');\n}\n\n/**\n * Detect the type of advanced state from a raw state key\n */\nexport function detectAdvancedStateType(\n stateKey: string,\n): ParsedAdvancedState['type'] {\n // Handle negation prefix\n const key = stateKey.startsWith('!') ? stateKey.slice(1) : stateKey;\n\n if (key === '@starting') return 'starting';\n if (key.startsWith('@media')) return 'media';\n if (key.startsWith('@root(')) return 'root';\n if (key.startsWith('@parent(')) return 'parent';\n if (key.startsWith('@own(')) return 'own';\n if (key.startsWith('@(')) return 'container';\n if (isPredefinedStateRef(key)) return 'predefined';\n return 'modifier';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/states/index.ts"],"sourcesContent":["/**\n * Advanced State Mapping - Predefined States Management\n *\n * This module handles global and local predefined states for the Tasty styling system.\n * See ADVANCED_STATE_MAPPING.md for full specification.\n */\n\nimport { hasStylesGenerated } from '../config';\nimport type { Styles } from '../styles/types';\nimport { isDevEnv } from '../utils/is-dev-env';\n\n/**\n * Parsed advanced state information\n */\nexport interface ParsedAdvancedState {\n type:\n | 'media'\n | 'container'\n | 'root'\n | 'parent'\n | 'own'\n | 'starting'\n | 'predefined'\n | 'modifier';\n condition: string; // e.g., 'width <= 920px' or 'hovered'\n containerName?: string; // for container queries\n raw: string; // original state key\n mediaType?: string; // for @media:screen, @media:print, etc.\n}\n\n/**\n * Context for state parsing operations\n */\nexport interface StateParserContext {\n localPredefinedStates: Record<string, string>;\n globalPredefinedStates: Record<string, string>;\n isSubElement?: boolean; // true when processing sub-element styles (for @own() validation)\n}\n\n/**\n * At-rule context for CSS generation\n */\nexport interface AtRuleContext {\n media?: string[]; // @media conditions to wrap rule in (merged with 'and')\n container?: { name?: string; condition: string }[]; // @container conditions (nested)\n startingStyle?: boolean;\n rootStates?: string[]; // :root state selectors (e.g., '[data-theme=\"dark\"]')\n negatedRootStates?: string[]; // Negated :root state selectors for non-overlapping rules (e.g., ':not([data-theme=\"dark\"])')\n}\n\n// Built-in state names that cannot be overridden\nconst BUILTIN_STATES = new Set([\n '@starting',\n '@keyframes',\n '@properties',\n '@fontFace',\n '@counterStyle',\n '@supports',\n // @inherit is a value (not a key), but reserved here to prevent\n // users from accidentally defining a state named '@inherit'.\n '@inherit',\n]);\n\n// Reserved prefixes that are built-in\nconst RESERVED_PREFIXES = [\n '@media',\n '@root',\n '@parent',\n '@own',\n '@(',\n '@starting',\n '@keyframes',\n '@properties',\n '@supports',\n '@inherit',\n];\n\n// Global predefined states storage\nlet globalPredefinedStates: Record<string, string> = {};\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n/**\n * Configure global predefined states\n */\nexport function setGlobalPredefinedStates(\n states: Record<string, string>,\n): void {\n if (hasStylesGenerated()) {\n const newStateNames = Object.keys(states).join(', ');\n warnOnce(\n `dynamic-states:${newStateNames}`,\n `[Tasty] Cannot update predefined states after styles have been generated.\\n` +\n `The new definition(s) for ${newStateNames} will be ignored.`,\n );\n return;\n }\n\n // Validate state names\n for (const [name, value] of Object.entries(states)) {\n // Check for valid name format\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(name)) {\n warnOnce(\n `invalid-state-name:${name}`,\n `[Tasty] Invalid predefined state name '${name}'. Must start with '@' followed by a letter.`,\n );\n continue;\n }\n\n // Check for reserved names\n if (BUILTIN_STATES.has(name)) {\n warnOnce(\n `reserved-state:${name}`,\n `[Tasty] Cannot define predefined state '${name}'. This name is reserved for built-in functionality.`,\n );\n continue;\n }\n\n // Check for reserved prefixes (but only exact matches, not user-defined states like @mobile)\n // Reserved prefixes are: @media, @root, @parent, @own, @(\n // A user state like @mobile should NOT be blocked\n const isReservedPrefix =\n name === '@media' ||\n name === '@root' ||\n name === '@parent' ||\n name === '@own' ||\n name.startsWith('@(');\n\n if (isReservedPrefix) {\n warnOnce(\n `reserved-prefix:${name}`,\n `[Tasty] Cannot define predefined state '${name}'. This prefix is reserved for built-in functionality.`,\n );\n continue;\n }\n\n // Check for cross-references\n const crossRefs = extractPredefinedStateRefs(value);\n if (crossRefs.length > 0) {\n warnOnce(\n `cross-ref:${name}`,\n `[Tasty] Predefined state '${name}' references another predefined state '${crossRefs[0]}'.\\n` +\n `Predefined states cannot reference each other. Use the full definition instead.`,\n );\n continue;\n }\n\n // Check for duplicates\n if (\n globalPredefinedStates[name] &&\n globalPredefinedStates[name] !== value\n ) {\n warnOnce(\n `duplicate-state:${name}`,\n `[Tasty] Duplicate predefined state '${name}' in configure(). The last definition will be used.`,\n );\n }\n\n globalPredefinedStates[name] = value;\n }\n}\n\n/**\n * Get global predefined states\n */\nexport function getGlobalPredefinedStates(): Record<string, string> {\n return globalPredefinedStates;\n}\n\n/**\n * Clear global predefined states (for testing only)\n */\nexport function clearGlobalPredefinedStates(): void {\n globalPredefinedStates = {};\n emittedWarnings.clear();\n}\n\n/**\n * Regex to match predefined state references in a string\n * Matches @name that is NOT followed by ( or : and is a complete word\n * Uses word boundary and negative lookahead\n */\nconst PREDEFINED_STATE_PATTERN = /@([A-Za-z][A-Za-z0-9-]*)(?![A-Za-z0-9-:(])/g;\n\n/**\n * Extract predefined state references from a string\n */\nexport function extractPredefinedStateRefs(value: string): string[] {\n const matches = value.matchAll(PREDEFINED_STATE_PATTERN);\n const refs: string[] = [];\n\n for (const match of matches) {\n const stateName = '@' + match[1];\n // Skip built-in states (@starting) and duplicates\n // Note: @media, @root, @own are always followed by '(' so the regex\n // negative lookahead (?![A-Za-z0-9-:(]) already excludes them\n if (!BUILTIN_STATES.has(stateName) && !refs.includes(stateName)) {\n refs.push(stateName);\n }\n }\n\n return refs;\n}\n\n/**\n * Check if a state key is a predefined state reference\n */\nexport function isPredefinedStateRef(stateKey: string): boolean {\n if (!stateKey.startsWith('@')) return false;\n if (BUILTIN_STATES.has(stateKey)) return false;\n\n // Check if it's NOT a built-in prefix\n for (const prefix of RESERVED_PREFIXES) {\n if (stateKey === prefix || stateKey.startsWith(prefix)) {\n // Check if it's exactly @media, @root, @parent, @own, or starts with @( or @media(\n if (\n stateKey === '@media' ||\n stateKey.startsWith('@media(') ||\n stateKey.startsWith('@media:')\n ) {\n return false;\n }\n if (stateKey === '@root' || stateKey.startsWith('@root(')) return false;\n if (stateKey === '@parent' || stateKey.startsWith('@parent('))\n return false;\n if (stateKey === '@own' || stateKey.startsWith('@own(')) return false;\n if (stateKey.startsWith('@(')) return false;\n }\n }\n\n // Must match the predefined state pattern\n return /^@[A-Za-z][A-Za-z0-9-]*$/.test(stateKey);\n}\n\nconst _localStatesCache = new WeakMap<object, Record<string, string>>();\n\n/**\n * Extract local predefined states from a styles object\n * Local predefined states are top-level keys starting with @ that have string values\n * and are valid predefined state names (not built-in like @media, @root, etc.)\n *\n * Results are cached by object identity via WeakMap since the same styles object\n * is passed to this function multiple times per pipeline run (once per chunk\n * in renderStylesForChunk and generateChunkCacheKey).\n */\nexport function extractLocalPredefinedStates(\n styles: Styles,\n): Record<string, string> {\n if (!styles || typeof styles !== 'object') {\n return {};\n }\n\n const cached = _localStatesCache.get(styles as object);\n if (cached !== undefined) return cached;\n\n const localStates: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(styles)) {\n // Check if it's a predefined state definition (starts with @, has string value)\n if (key.startsWith('@') && typeof value === 'string') {\n // Validate name format - must be @[letter][letters/numbers/dashes]*\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(key)) {\n continue; // Skip invalid names silently (might be something else)\n }\n\n // Skip built-in states\n if (BUILTIN_STATES.has(key)) {\n continue;\n }\n\n // Skip reserved prefixes\n if (\n key === '@media' ||\n key === '@root' ||\n key === '@parent' ||\n key === '@own' ||\n key.startsWith('@(')\n ) {\n continue;\n }\n\n // Check for cross-references (predefined states cannot reference each other)\n const crossRefs = extractPredefinedStateRefs(value);\n if (crossRefs.length > 0) {\n warnOnce(\n `local-cross-ref:${key}`,\n `[Tasty] Predefined state '${key}' references another predefined state '${crossRefs[0]}'.\\n` +\n `Predefined states cannot reference each other. Use the full definition instead.`,\n );\n continue;\n }\n\n localStates[key] = value;\n }\n }\n\n _localStatesCache.set(styles as object, localStates);\n\n return localStates;\n}\n\n/**\n * Create a state parser context from styles\n */\nexport function createStateParserContext(\n styles?: Styles,\n isSubElement?: boolean,\n): StateParserContext {\n const localStates = styles ? extractLocalPredefinedStates(styles) : {};\n\n return {\n localPredefinedStates: localStates,\n globalPredefinedStates: getGlobalPredefinedStates(),\n isSubElement,\n };\n}\n\n/**\n * Resolve a predefined state reference to its value\n * Returns the resolved value or null if not found\n */\nexport function resolvePredefinedState(\n stateKey: string,\n ctx: StateParserContext,\n): string | null {\n // Check local first (higher priority)\n if (ctx.localPredefinedStates[stateKey]) {\n return ctx.localPredefinedStates[stateKey];\n }\n\n // Then check global\n if (ctx.globalPredefinedStates[stateKey]) {\n return ctx.globalPredefinedStates[stateKey];\n }\n\n // Not found - emit warning\n warnOnce(\n `undefined-state:${stateKey}`,\n `[Tasty] Undefined predefined state '${stateKey}'.\\n` +\n `Define it in configure({ states: { '${stateKey}': '...' } }) or in the component's styles.`,\n );\n\n return null;\n}\n\n/**\n * Normalize state key by trimming whitespace and removing trailing/leading operators\n */\nexport function normalizeStateKey(stateKey: string): {\n key: string;\n warnings: string[];\n} {\n const warnings: string[] = [];\n let key = stateKey;\n\n // Check for whitespace-only\n if (key.trim() === '') {\n if (key !== '') {\n warnings.push(\n `[Tasty] Whitespace-only state key normalized to default state ''.`,\n );\n }\n return { key: '', warnings };\n }\n\n // Trim whitespace\n key = key.trim();\n\n // Remove trailing operators\n const trailingOpMatch = key.match(/\\s*[&|^]\\s*$/);\n if (trailingOpMatch) {\n const originalKey = key;\n key = key.slice(0, -trailingOpMatch[0].length).trim();\n warnings.push(\n `[Tasty] State key '${originalKey}' has trailing operator. Normalized to '${key}'.`,\n );\n }\n\n // Remove leading operators (except !)\n const leadingOpMatch = key.match(/^\\s*[&|^]\\s*/);\n if (leadingOpMatch) {\n const originalKey = key;\n key = key.slice(leadingOpMatch[0].length).trim();\n warnings.push(\n `[Tasty] State key '${originalKey}' has leading operator. Normalized to '${key}'.`,\n );\n }\n\n return { key, warnings };\n}\n\n/**\n * Expand dimension shorthands in a condition string\n * w -> width, h -> height, is -> inline-size, bs -> block-size\n */\nexport function expandDimensionShorthands(condition: string): string {\n // Replace dimension shorthands (only when they appear as standalone words)\n let result = condition;\n\n // w -> width (but not part of other words)\n result = result.replace(/\\bw\\b/g, 'width');\n\n // h -> height\n result = result.replace(/\\bh\\b/g, 'height');\n\n // is -> inline-size\n result = result.replace(/\\bis\\b/g, 'inline-size');\n\n // bs -> block-size\n result = result.replace(/\\bbs\\b/g, 'block-size');\n\n return result;\n}\n\n/**\n * Convert tasty units in a string (e.g., 40x -> calc(var(--gap) * 40))\n */\nexport function expandTastyUnits(value: string): string {\n // Match number followed by 'x' unit (tasty gap unit)\n return value.replace(/(\\d+(?:\\.\\d+)?)\\s*x\\b/g, (_, num) => {\n return `calc(var(--gap) * ${num})`;\n });\n}\n\n/**\n * Parse an advanced state key and return its type and components\n */\nexport function parseAdvancedState(\n stateKey: string,\n ctx: StateParserContext,\n): ParsedAdvancedState {\n const raw = stateKey;\n\n // Check for @starting (exact match)\n if (stateKey === '@starting') {\n return {\n type: 'starting',\n condition: '',\n raw,\n };\n }\n\n // Check for @media:type (e.g., @media:print)\n if (stateKey.startsWith('@media:')) {\n const mediaType = stateKey.slice(7); // Remove '@media:'\n if (!['all', 'screen', 'print', 'speech'].includes(mediaType)) {\n warnOnce(\n `unknown-media-type:${mediaType}`,\n `[Tasty] Unknown media type '${mediaType}'. Valid types: all, screen, print, speech.`,\n );\n }\n return {\n type: 'media',\n condition: '',\n mediaType,\n raw,\n };\n }\n\n // Check for @media(...) - media query with condition\n if (stateKey.startsWith('@media(')) {\n const endParen = findMatchingParen(stateKey, 6);\n if (endParen === -1) {\n warnOnce(\n `unclosed-media:${stateKey}`,\n `[Tasty] Unclosed media query '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n let condition = stateKey.slice(7, endParen);\n\n // Check for empty condition\n if (!condition.trim()) {\n warnOnce(\n `empty-media:${stateKey}`,\n `[Tasty] Empty media query condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Expand shorthands and units\n condition = expandDimensionShorthands(condition);\n condition = expandTastyUnits(condition);\n\n return {\n type: 'media',\n condition,\n raw,\n };\n }\n\n // Check for @root(...) - root state\n if (stateKey.startsWith('@root(')) {\n const endParen = findMatchingParen(stateKey, 5);\n if (endParen === -1) {\n warnOnce(\n `unclosed-root:${stateKey}`,\n `[Tasty] Unclosed root state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(6, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-root:${stateKey}`,\n `[Tasty] Empty root state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n return {\n type: 'root',\n condition,\n raw,\n };\n }\n\n // Check for @parent(...) - parent element state\n if (stateKey.startsWith('@parent(')) {\n const endParen = findMatchingParen(stateKey, 7);\n if (endParen === -1) {\n warnOnce(\n `unclosed-parent:${stateKey}`,\n `[Tasty] Unclosed parent state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(8, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-parent:${stateKey}`,\n `[Tasty] Empty parent state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n return {\n type: 'parent',\n condition,\n raw,\n };\n }\n\n // Check for @own(...) - sub-element own state\n if (stateKey.startsWith('@own(')) {\n const endParen = findMatchingParen(stateKey, 4);\n if (endParen === -1) {\n warnOnce(\n `unclosed-own:${stateKey}`,\n `[Tasty] Unclosed own state '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const condition = stateKey.slice(5, endParen);\n\n if (!condition.trim()) {\n warnOnce(\n `empty-own:${stateKey}`,\n `[Tasty] Empty own state condition '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Check if used outside sub-element context\n if (!ctx.isSubElement) {\n warnOnce(\n `own-outside-subelement:${stateKey}`,\n `[Tasty] @own(${condition}) used outside sub-element context.\\n` +\n `@own() is equivalent to '${condition}' at the root level. ` +\n `Did you mean to use it inside a sub-element?`,\n );\n // Treat as regular modifier\n return {\n type: 'modifier',\n condition,\n raw,\n };\n }\n\n return {\n type: 'own',\n condition,\n raw,\n };\n }\n\n // Check for @(...) - container query (unnamed or named)\n if (stateKey.startsWith('@(')) {\n const endParen = findMatchingParen(stateKey, 1);\n if (endParen === -1) {\n warnOnce(\n `unclosed-container:${stateKey}`,\n `[Tasty] Unclosed container query '${stateKey}'. Missing closing parenthesis.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n const content = stateKey.slice(2, endParen);\n\n if (!content.trim()) {\n warnOnce(\n `empty-container:${stateKey}`,\n `[Tasty] Empty container query '${stateKey}'.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n\n // Check if named container (first token is name, followed by comma)\n // Use parentheses-aware comma search so inner commas (e.g., scroll-state(a, b)) are skipped\n const commaIndex = findTopLevelComma(content);\n let containerName: string | undefined;\n let condition: string;\n\n if (commaIndex !== -1) {\n // Named container: @(layout, w < 600px)\n containerName = content.slice(0, commaIndex).trim();\n condition = content.slice(commaIndex + 1).trim();\n } else {\n // Unnamed container: @(w < 600px)\n condition = content.trim();\n }\n\n // Check for style query shorthand (starts with $)\n if (condition.startsWith('$')) {\n // Style query: @(layout, $compact) or @(layout, $variant=compact)\n const styleCondition = parseStyleQuery(condition);\n if (!styleCondition) {\n warnOnce(\n `invalid-style-query:${stateKey}`,\n `[Tasty] Invalid style query '${condition}' in container query.`,\n );\n return { type: 'modifier', condition: stateKey, raw };\n }\n condition = styleCondition;\n } else if (/^[a-zA-Z][\\w-]*\\s*\\(/.test(condition)) {\n // Function-like syntax: scroll-state(...), style(...), etc.\n // Pass through verbatim — no dimension expansion needed\n } else {\n // Dimension query - expand shorthands and units\n condition = expandDimensionShorthands(condition);\n condition = expandTastyUnits(condition);\n }\n\n return {\n type: 'container',\n condition,\n containerName,\n raw,\n };\n }\n\n // Check for predefined state reference\n if (isPredefinedStateRef(stateKey)) {\n const resolved = resolvePredefinedState(stateKey, ctx);\n if (resolved) {\n // Recursively parse the resolved value to extract the actual state type\n // (e.g., @mobile -> @media(w < 768px) should become a media state)\n const parsedResolved = parseAdvancedState(resolved, ctx);\n return {\n ...parsedResolved,\n raw, // Keep original raw for traceability\n };\n }\n // If not resolved, treat as modifier (will likely produce invalid CSS)\n return {\n type: 'modifier',\n condition: stateKey,\n raw,\n };\n }\n\n // Regular modifier (boolean mod, pseudo-class, class, attribute)\n return {\n type: 'modifier',\n condition: stateKey,\n raw,\n };\n}\n\n/**\n * Parse a style query condition (e.g., $compact, $variant=compact, $variant=\"very compact\")\n */\nfunction parseStyleQuery(condition: string): string | null {\n // Remove $ prefix\n const query = condition.slice(1);\n\n // Check for comparison operators (not supported)\n if (/[<>]/.test(query)) {\n warnOnce(\n `style-query-comparison:${condition}`,\n `[Tasty] Style queries only support equality. '${condition}' is invalid. Use '${condition.split(/[<>]/)[0]}=...' instead.`,\n );\n return null;\n }\n\n // Check for equality\n const eqIndex = query.indexOf('=');\n if (eqIndex === -1) {\n // Just existence check: style(--compact)\n return `style(--${query})`;\n }\n\n const propName = query.slice(0, eqIndex).trim();\n let value = query.slice(eqIndex + 1).trim();\n\n // Handle quoted values\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n // Keep quotes for CSS\n } else {\n // Add quotes if needed\n value = `\"${value}\"`;\n }\n\n // Expand tasty units in value\n value = expandTastyUnits(value);\n\n return `style(--${propName}: ${value})`;\n}\n\n/**\n * Find the index of the first comma at parentheses depth 0.\n * Returns -1 if no top-level comma is found.\n * This prevents splitting on commas inside function calls like scroll-state(a, b).\n */\nexport function findTopLevelComma(s: string): number {\n let depth = 0;\n\n for (let i = 0; i < s.length; i++) {\n if (s[i] === '(') depth++;\n else if (s[i] === ')') depth--;\n else if (s[i] === ',' && depth === 0) return i;\n }\n\n return -1;\n}\n\nfunction findMatchingParen(str: string, startIndex: number): number {\n let depth = 1;\n let i = startIndex + 1;\n\n while (i < str.length && depth > 0) {\n if (str[i] === '(') depth++;\n else if (str[i] === ')') depth--;\n i++;\n }\n\n return depth === 0 ? i - 1 : -1;\n}\n\n/**\n * Check if a state key is an advanced state (starts with @)\n */\nexport function isAdvancedState(stateKey: string): boolean {\n return stateKey.startsWith('@') || stateKey.startsWith('!@');\n}\n\n/**\n * Detect the type of advanced state from a raw state key\n */\nexport function detectAdvancedStateType(\n stateKey: string,\n): ParsedAdvancedState['type'] {\n // Handle negation prefix\n const key = stateKey.startsWith('!') ? stateKey.slice(1) : stateKey;\n\n if (key === '@starting') return 'starting';\n if (key.startsWith('@media')) return 'media';\n if (key.startsWith('@root(')) return 'root';\n if (key.startsWith('@parent(')) return 'parent';\n if (key.startsWith('@own(')) return 'own';\n if (key.startsWith('@(')) return 'container';\n if (isPredefinedStateRef(key)) return 'predefined';\n return 'modifier';\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { mergeStyles } from "../utils/merge-styles.js";
2
2
  import { createStaticStyle, isStaticStyle } from "./types.js";
3
3
  import { tastyStatic } from "./tastyStatic.js";
4
-
5
- export { createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
4
+ export { createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
@@ -0,0 +1,5 @@
1
+ //#region src/static/inject.d.ts
2
+ declare function injectCSS(id: string, css: string): void;
3
+ //#endregion
4
+ export { injectCSS };
5
+ //# sourceMappingURL=inject.d.ts.map
@@ -0,0 +1,17 @@
1
+ //#region src/static/inject.ts
2
+ const _ids = /* @__PURE__ */ new Set();
3
+ function injectCSS(id, css) {
4
+ if (_ids.has(id) || typeof document === "undefined") return;
5
+ _ids.add(id);
6
+ let el = document.head.querySelector("style[data-tasty-static]");
7
+ if (!el) {
8
+ el = document.createElement("style");
9
+ el.setAttribute("data-tasty-static", "");
10
+ document.head.appendChild(el);
11
+ }
12
+ el.appendChild(document.createTextNode(css + "\n"));
13
+ }
14
+ //#endregion
15
+ export { injectCSS };
16
+
17
+ //# sourceMappingURL=inject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject.js","names":[],"sources":["../../src/static/inject.ts"],"sourcesContent":["const _ids = new Set<string>();\n\nexport function injectCSS(id: string, css: string): void {\n if (_ids.has(id) || typeof document === 'undefined') return;\n _ids.add(id);\n let el = document.head.querySelector(\n 'style[data-tasty-static]',\n ) as HTMLStyleElement | null;\n if (!el) {\n el = document.createElement('style');\n el.setAttribute('data-tasty-static', '');\n document.head.appendChild(el);\n }\n el.appendChild(document.createTextNode(css + '\\n'));\n}\n"],"mappings":";AAAA,MAAM,uBAAO,IAAI,KAAa;AAE9B,SAAgB,UAAU,IAAY,KAAmB;AACvD,KAAI,KAAK,IAAI,GAAG,IAAI,OAAO,aAAa,YAAa;AACrD,MAAK,IAAI,GAAG;CACZ,IAAI,KAAK,SAAS,KAAK,cACrB,2BACD;AACD,KAAI,CAAC,IAAI;AACP,OAAK,SAAS,cAAc,QAAQ;AACpC,KAAG,aAAa,qBAAqB,GAAG;AACxC,WAAS,KAAK,YAAY,GAAG;;AAE/B,IAAG,YAAY,SAAS,eAAe,MAAM,KAAK,CAAC"}
@@ -1,6 +1,5 @@
1
1
  import { mergeStyles } from "../utils/merge-styles.js";
2
2
  import { createStaticStyle, isStaticStyle } from "./types.js";
3
-
4
3
  //#region src/static/tastyStatic.ts
5
4
  /**
6
5
  * Build-time only function for zero-runtime static site generation.
@@ -25,7 +24,7 @@ function tastyStatic(stylesOrBaseOrSelector, styles) {
25
24
  console.warn("[tasty] tastyStatic(styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.");
26
25
  return createStaticStyle("__TASTY_STATIC_NOT_TRANSFORMED__", stylesOrBaseOrSelector);
27
26
  }
28
-
29
27
  //#endregion
30
28
  export { tastyStatic };
29
+
31
30
  //# sourceMappingURL=tastyStatic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tastyStatic.js","names":[],"sources":["../../src/static/tastyStatic.ts"],"sourcesContent":["import type { Styles } from '../styles/types';\nimport { mergeStyles } from '../utils/merge-styles';\n\nimport type { StaticStyle } from './types';\nimport { createStaticStyle, isStaticStyle } from './types';\n\n/**\n * Generate styles and return a StaticStyle object.\n * The object has `className`, `styles`, and `toString()`.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({\n * fill: '#blue',\n * padding: '2x',\n * });\n * // After build: { className: 'ts3f2a1b', styles: {...}, toString() }\n *\n * <div className={button} /> // Works via toString()\n * ```\n */\nexport function tastyStatic(styles: Styles): StaticStyle;\n\n/**\n * Extend an existing StaticStyle with additional styles.\n * Uses mergeStyles() internally for proper nested selector handling.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({ fill: '#blue' });\n * const primary = tastyStatic(button, { fill: '#purple' });\n * // After build: { className: 'ts8c4d2e', styles: {...merged...}, toString() }\n * ```\n */\nexport function tastyStatic(base: StaticStyle, styles: Styles): StaticStyle;\n\n/**\n * Generate styles for a specific CSS selector.\n * The call is completely removed after build transformation.\n *\n * @example\n * ```typescript\n * tastyStatic('.heading', { preset: 'h1', color: '#primary' });\n * // After build: (removed)\n * ```\n */\nexport function tastyStatic(selector: string, styles: Styles): void;\n\n/**\n * Build-time only function for zero-runtime static site generation.\n *\n * This function is transformed by the Babel plugin:\n * - `tastyStatic(styles)` → StaticStyle object with className\n * - `tastyStatic(base, styles)` → StaticStyle object with merged styles\n * - `tastyStatic(selector, styles)` → removed entirely\n *\n * At runtime (during development/build), this function returns a placeholder.\n * In production, all calls are replaced/removed by the build plugin.\n */\nexport function tastyStatic(\n stylesOrBaseOrSelector: Styles | StaticStyle | string,\n styles?: Styles,\n): StaticStyle | void {\n // This code only executes if the Babel plugin hasn't processed the file yet.\n // In a properly configured build, this function is never called at runtime.\n\n if (typeof stylesOrBaseOrSelector === 'string') {\n // Selector mode: tastyStatic(selector, styles)\n // The plugin will remove this call entirely\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[tasty] tastyStatic('${stylesOrBaseOrSelector}', styles) was called at runtime. ` +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n return; // void\n }\n\n if (isStaticStyle(stylesOrBaseOrSelector)) {\n // Extension mode: tastyStatic(base, styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[tasty] tastyStatic(base, styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n // Merge styles for dev mode preview (won't have real classNames)\n const mergedStyles = mergeStyles(\n stylesOrBaseOrSelector.styles,\n styles || {},\n );\n return createStaticStyle('__TASTY_STATIC_NOT_TRANSFORMED__', mergedStyles);\n }\n\n // Styles mode: tastyStatic(styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[tasty] tastyStatic(styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n\n // Return placeholder - styles won't be applied without the plugin\n return createStaticStyle(\n '__TASTY_STATIC_NOT_TRANSFORMED__',\n stylesOrBaseOrSelector,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2DA,SAAgB,YACd,wBACA,QACoB;AAIpB,KAAI,OAAO,2BAA2B,UAAU;AAI5C,UAAQ,KACN,wBAAwB,uBAAuB,2IAGhD;AAEH;;AAGF,KAAI,cAAc,uBAAuB,EAAE;AAGvC,UAAQ,KACN,mKAGD;AAOH,SAAO,kBAAkB,oCAJJ,YACnB,uBAAuB,QACvB,UAAU,EAAE,CACb,CACyE;;AAK1E,SAAQ,KACN,6JAGD;AAIH,QAAO,kBACL,oCACA,uBACD"}
1
+ {"version":3,"file":"tastyStatic.js","names":[],"sources":["../../src/static/tastyStatic.ts"],"sourcesContent":["import type { Styles } from '../styles/types';\nimport { mergeStyles } from '../utils/merge-styles';\n\nimport type { StaticStyle } from './types';\nimport { createStaticStyle, isStaticStyle } from './types';\n\n/**\n * Generate styles and return a StaticStyle object.\n * The object has `className`, `styles`, and `toString()`.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({\n * fill: '#blue',\n * padding: '2x',\n * });\n * // After build: { className: 'ts3f2a1b', styles: {...}, toString() }\n *\n * <div className={button} /> // Works via toString()\n * ```\n */\nexport function tastyStatic(styles: Styles): StaticStyle;\n\n/**\n * Extend an existing StaticStyle with additional styles.\n * Uses mergeStyles() internally for proper nested selector handling.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({ fill: '#blue' });\n * const primary = tastyStatic(button, { fill: '#purple' });\n * // After build: { className: 'ts8c4d2e', styles: {...merged...}, toString() }\n * ```\n */\nexport function tastyStatic(base: StaticStyle, styles: Styles): StaticStyle;\n\n/**\n * Generate styles for a specific CSS selector.\n * The call is completely removed after build transformation.\n *\n * @example\n * ```typescript\n * tastyStatic('.heading', { preset: 'h1', color: '#primary' });\n * // After build: (removed)\n * ```\n */\nexport function tastyStatic(selector: string, styles: Styles): void;\n\n/**\n * Build-time only function for zero-runtime static site generation.\n *\n * This function is transformed by the Babel plugin:\n * - `tastyStatic(styles)` → StaticStyle object with className\n * - `tastyStatic(base, styles)` → StaticStyle object with merged styles\n * - `tastyStatic(selector, styles)` → removed entirely\n *\n * At runtime (during development/build), this function returns a placeholder.\n * In production, all calls are replaced/removed by the build plugin.\n */\nexport function tastyStatic(\n stylesOrBaseOrSelector: Styles | StaticStyle | string,\n styles?: Styles,\n): StaticStyle | void {\n // This code only executes if the Babel plugin hasn't processed the file yet.\n // In a properly configured build, this function is never called at runtime.\n\n if (typeof stylesOrBaseOrSelector === 'string') {\n // Selector mode: tastyStatic(selector, styles)\n // The plugin will remove this call entirely\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[tasty] tastyStatic('${stylesOrBaseOrSelector}', styles) was called at runtime. ` +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n return; // void\n }\n\n if (isStaticStyle(stylesOrBaseOrSelector)) {\n // Extension mode: tastyStatic(base, styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[tasty] tastyStatic(base, styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n // Merge styles for dev mode preview (won't have real classNames)\n const mergedStyles = mergeStyles(\n stylesOrBaseOrSelector.styles,\n styles || {},\n );\n return createStaticStyle('__TASTY_STATIC_NOT_TRANSFORMED__', mergedStyles);\n }\n\n // Styles mode: tastyStatic(styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[tasty] tastyStatic(styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n\n // Return placeholder - styles won't be applied without the plugin\n return createStaticStyle(\n '__TASTY_STATIC_NOT_TRANSFORMED__',\n stylesOrBaseOrSelector,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AA2DA,SAAgB,YACd,wBACA,QACoB;AAIpB,KAAI,OAAO,2BAA2B,UAAU;AAI5C,UAAQ,KACN,wBAAwB,uBAAuB,2IAGhD;AAEH;;AAGF,KAAI,cAAc,uBAAuB,EAAE;AAGvC,UAAQ,KACN,mKAGD;AAOH,SAAO,kBAAkB,oCAJJ,YACnB,uBAAuB,QACvB,UAAU,EAAE,CACb,CACyE;;AAK1E,SAAQ,KACN,6JAGD;AAIH,QAAO,kBACL,oCACA,uBACD"}
@@ -18,7 +18,7 @@ function createStaticStyle(className, styles) {
18
18
  function isStaticStyle(value) {
19
19
  return typeof value === "object" && value !== null && "className" in value && "styles" in value && "toString" in value && typeof value.className === "string" && typeof value.styles === "object";
20
20
  }
21
-
22
21
  //#endregion
23
22
  export { createStaticStyle, isStaticStyle };
23
+
24
24
  //# sourceMappingURL=types.js.map
@@ -16,7 +16,7 @@ declare function borderStyle({
16
16
  border
17
17
  }: {
18
18
  border?: string | number | boolean;
19
- }): Record<string, string> | undefined;
19
+ }): Record<string, string> | null;
20
20
  declare namespace borderStyle {
21
21
  var __lookupStyles: string[];
22
22
  }
@@ -1,18 +1,8 @@
1
+ import { CSS_WIDE_KEYWORDS } from "../parser/const.js";
1
2
  import { DIRECTIONS, filterMods, parseStyle } from "../utils/styles.js";
2
-
3
+ import { BORDER_STYLES } from "./const.js";
4
+ import { extractCSSWideKeyword } from "./shared.js";
3
5
  //#region src/styles/border.ts
4
- const BORDER_STYLES = [
5
- "none",
6
- "hidden",
7
- "dotted",
8
- "dashed",
9
- "solid",
10
- "double",
11
- "groove",
12
- "ridge",
13
- "inset",
14
- "outset"
15
- ];
16
6
  /**
17
7
  * Process a single group and return border values for its directions.
18
8
  * @returns Object with directions as keys and border values, or null for "all directions"
@@ -50,18 +40,30 @@ function formatBorderValue(value) {
50
40
  * Later groups override earlier groups for conflicting directions.
51
41
  */
52
42
  function borderStyle({ border }) {
53
- if (!border && border !== 0) return;
43
+ if (!border && border !== 0) return null;
54
44
  if (border === true) border = "1bw";
55
- const groups = parseStyle(String(border)).groups ?? [];
56
- if (!groups.length) return;
45
+ const strBorder = String(border);
46
+ if (CSS_WIDE_KEYWORDS.has(strBorder)) return { border: strBorder };
47
+ const groups = parseStyle(strBorder).groups ?? [];
48
+ if (!groups.length) return null;
49
+ const useLonghand = groups.some((g) => (g.mods ?? []).includes("longhand"));
57
50
  if (groups.length === 1) {
51
+ const group = groups[0];
52
+ const keyword = extractCSSWideKeyword(group);
53
+ if (keyword) {
54
+ if (useLonghand) return Object.fromEntries(DIRECTIONS.map((dir) => [`border-${dir}`, keyword]));
55
+ return { border: keyword };
56
+ }
58
57
  const { directions, borderValue } = processGroup({
59
- values: groups[0].values ?? [],
60
- mods: groups[0].mods ?? [],
61
- colors: groups[0].colors ?? []
58
+ values: group.values ?? [],
59
+ mods: group.mods ?? [],
60
+ colors: group.colors ?? []
62
61
  });
63
62
  const styleValue = formatBorderValue(borderValue);
64
- if (!directions.length) return { border: styleValue };
63
+ if (!directions.length) {
64
+ if (useLonghand) return Object.fromEntries(DIRECTIONS.map((dir) => [`border-${dir}`, styleValue]));
65
+ return { border: styleValue };
66
+ }
65
67
  const zeroValue = `0 ${borderValue.style} ${borderValue.color}`;
66
68
  return DIRECTIONS.reduce((styles, dir) => {
67
69
  if (directions.includes(dir)) styles[`border-${dir}`] = styleValue;
@@ -91,7 +93,11 @@ function borderStyle({ border }) {
91
93
  for (const dir of directions) directionMap[dir] = borderValue;
92
94
  }
93
95
  }
94
- if (!hasAnyDirections && allDirectionsValue) return { border: formatBorderValue(allDirectionsValue) };
96
+ if (!hasAnyDirections && allDirectionsValue) {
97
+ const formatted = formatBorderValue(allDirectionsValue);
98
+ if (useLonghand) return Object.fromEntries(DIRECTIONS.map((dir) => [`border-${dir}`, formatted]));
99
+ return { border: formatted };
100
+ }
95
101
  const result = {};
96
102
  for (const dir of DIRECTIONS) {
97
103
  const value = directionMap[dir];
@@ -108,7 +114,7 @@ function borderStyle({ border }) {
108
114
  return result;
109
115
  }
110
116
  borderStyle.__lookupStyles = ["border"];
111
-
112
117
  //#endregion
113
118
  export { borderStyle };
119
+
114
120
  //# sourceMappingURL=border.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"border.js","names":[],"sources":["../../src/styles/border.ts"],"sourcesContent":["import { DIRECTIONS, filterMods, parseStyle } from '../utils/styles';\n\nconst BORDER_STYLES = [\n 'none',\n 'hidden',\n 'dotted',\n 'dashed',\n 'solid',\n 'double',\n 'groove',\n 'ridge',\n 'inset',\n 'outset',\n] as const;\n\ntype Direction = (typeof DIRECTIONS)[number];\n\ninterface GroupData {\n values: string[];\n mods: string[];\n colors: string[];\n}\n\ninterface BorderValue {\n width: string;\n style: string;\n color: string;\n}\n\n/**\n * Process a single group and return border values for its directions.\n * @returns Object with directions as keys and border values, or null for \"all directions\"\n */\nfunction processGroup(group: GroupData): {\n directions: Direction[];\n borderValue: BorderValue;\n} {\n const { values, mods, colors } = group;\n\n const directions = filterMods(mods, DIRECTIONS) as Direction[];\n const typeMods = filterMods(mods, BORDER_STYLES as unknown as string[]);\n\n const width = values[0] || 'var(--border-width)';\n const style = typeMods[0] || 'solid';\n const color = colors?.[0] || 'var(--border-color, currentColor)';\n\n return {\n directions,\n borderValue: { width, style, color },\n };\n}\n\n/**\n * Format a border value to CSS string.\n */\nfunction formatBorderValue(value: BorderValue): string {\n return `${value.width} ${value.style} ${value.color}`;\n}\n\n/**\n * Border style handler with multi-group support.\n *\n * Single group (backward compatible):\n * - `border=\"1bw solid #red\"` - all sides\n * - `border=\"1bw solid #red top left\"` - only top and left\n *\n * Multi-group (new):\n * - `border=\"1bw #red, 2bw #blue top\"` - all sides red 1bw, then top overridden to blue 2bw\n * - `border=\"1bw, dashed top bottom, #purple left right\"` - base 1bw, dashed on top/bottom, purple on left/right\n *\n * Later groups override earlier groups for conflicting directions.\n */\nexport function borderStyle({\n border,\n}: {\n border?: string | number | boolean;\n}) {\n if (!border && border !== 0) return;\n\n if (border === true) border = '1bw';\n\n const processed = parseStyle(String(border));\n const groups: GroupData[] = processed.groups ?? [];\n\n if (!groups.length) return;\n\n // Single group - use original logic for backward compatibility\n if (groups.length === 1) {\n const { directions, borderValue } = processGroup({\n values: groups[0].values ?? [],\n mods: groups[0].mods ?? [],\n colors: groups[0].colors ?? [],\n });\n\n const styleValue = formatBorderValue(borderValue);\n\n if (!directions.length) {\n return { border: styleValue };\n }\n\n const zeroValue = `0 ${borderValue.style} ${borderValue.color}`;\n\n return DIRECTIONS.reduce(\n (styles, dir) => {\n if (directions.includes(dir)) {\n styles[`border-${dir}`] = styleValue;\n } else {\n styles[`border-${dir}`] = zeroValue;\n }\n return styles;\n },\n {} as Record<string, string>,\n );\n }\n\n // Multi-group - process groups in order, later groups override earlier\n // Track whether any group specifies directions\n let hasAnyDirections = false;\n\n // Build a map of direction -> border value\n // Start with undefined (no border set)\n const directionMap: Record<Direction, BorderValue | null> = {\n top: null,\n right: null,\n bottom: null,\n left: null,\n };\n\n // Track the last \"all directions\" value for fallback\n let allDirectionsValue: BorderValue | null = null;\n\n // Process groups in order (first to last)\n for (const group of groups) {\n const { directions, borderValue } = processGroup({\n values: group.values ?? [],\n mods: group.mods ?? [],\n colors: group.colors ?? [],\n });\n\n if (directions.length === 0) {\n // No specific directions - applies to all\n allDirectionsValue = borderValue;\n // Set all directions\n for (const dir of DIRECTIONS) {\n directionMap[dir] = borderValue;\n }\n } else {\n // Specific directions - override only those\n hasAnyDirections = true;\n for (const dir of directions) {\n directionMap[dir] = borderValue;\n }\n }\n }\n\n // If no group specified any directions and we have an all-directions value,\n // return the simple `border` shorthand\n if (!hasAnyDirections && allDirectionsValue) {\n return { border: formatBorderValue(allDirectionsValue) };\n }\n\n // Otherwise, output individual border-* properties\n const result: Record<string, string> = {};\n\n for (const dir of DIRECTIONS) {\n const value = directionMap[dir];\n if (value) {\n result[`border-${dir}`] = formatBorderValue(value);\n } else {\n // No border for this direction - set to 0\n // Use the last all-directions value for style/color consistency, or defaults\n const fallback = allDirectionsValue || {\n width: '0',\n style: 'solid',\n color: 'var(--border-color, currentColor)',\n };\n result[`border-${dir}`] = `0 ${fallback.style} ${fallback.color}`;\n }\n }\n\n return result;\n}\n\nborderStyle.__lookupStyles = ['border'];\n"],"mappings":";;;AAEA,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;AAoBD,SAAS,aAAa,OAGpB;CACA,MAAM,EAAE,QAAQ,MAAM,WAAW;CAEjC,MAAM,aAAa,WAAW,MAAM,WAAW;CAC/C,MAAM,WAAW,WAAW,MAAM,cAAqC;AAMvE,QAAO;EACL;EACA,aAAa;GAAE,OANH,OAAO,MAAM;GAMH,OALV,SAAS,MAAM;GAKE,OAJjB,SAAS,MAAM;GAIS;EACrC;;;;;AAMH,SAAS,kBAAkB,OAA4B;AACrD,QAAO,GAAG,MAAM,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM;;;;;;;;;;;;;;;AAgBhD,SAAgB,YAAY,EAC1B,UAGC;AACD,KAAI,CAAC,UAAU,WAAW,EAAG;AAE7B,KAAI,WAAW,KAAM,UAAS;CAG9B,MAAM,SADY,WAAW,OAAO,OAAO,CAAC,CACN,UAAU,EAAE;AAElD,KAAI,CAAC,OAAO,OAAQ;AAGpB,KAAI,OAAO,WAAW,GAAG;EACvB,MAAM,EAAE,YAAY,gBAAgB,aAAa;GAC/C,QAAQ,OAAO,GAAG,UAAU,EAAE;GAC9B,MAAM,OAAO,GAAG,QAAQ,EAAE;GAC1B,QAAQ,OAAO,GAAG,UAAU,EAAE;GAC/B,CAAC;EAEF,MAAM,aAAa,kBAAkB,YAAY;AAEjD,MAAI,CAAC,WAAW,OACd,QAAO,EAAE,QAAQ,YAAY;EAG/B,MAAM,YAAY,KAAK,YAAY,MAAM,GAAG,YAAY;AAExD,SAAO,WAAW,QACf,QAAQ,QAAQ;AACf,OAAI,WAAW,SAAS,IAAI,CAC1B,QAAO,UAAU,SAAS;OAE1B,QAAO,UAAU,SAAS;AAE5B,UAAO;KAET,EAAE,CACH;;CAKH,IAAI,mBAAmB;CAIvB,MAAM,eAAsD;EAC1D,KAAK;EACL,OAAO;EACP,QAAQ;EACR,MAAM;EACP;CAGD,IAAI,qBAAyC;AAG7C,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,EAAE,YAAY,gBAAgB,aAAa;GAC/C,QAAQ,MAAM,UAAU,EAAE;GAC1B,MAAM,MAAM,QAAQ,EAAE;GACtB,QAAQ,MAAM,UAAU,EAAE;GAC3B,CAAC;AAEF,MAAI,WAAW,WAAW,GAAG;AAE3B,wBAAqB;AAErB,QAAK,MAAM,OAAO,WAChB,cAAa,OAAO;SAEjB;AAEL,sBAAmB;AACnB,QAAK,MAAM,OAAO,WAChB,cAAa,OAAO;;;AAO1B,KAAI,CAAC,oBAAoB,mBACvB,QAAO,EAAE,QAAQ,kBAAkB,mBAAmB,EAAE;CAI1D,MAAM,SAAiC,EAAE;AAEzC,MAAK,MAAM,OAAO,YAAY;EAC5B,MAAM,QAAQ,aAAa;AAC3B,MAAI,MACF,QAAO,UAAU,SAAS,kBAAkB,MAAM;OAC7C;GAGL,MAAM,WAAW,sBAAsB;IACrC,OAAO;IACP,OAAO;IACP,OAAO;IACR;AACD,UAAO,UAAU,SAAS,KAAK,SAAS,MAAM,GAAG,SAAS;;;AAI9D,QAAO;;AAGT,YAAY,iBAAiB,CAAC,SAAS"}
1
+ {"version":3,"file":"border.js","names":[],"sources":["../../src/styles/border.ts"],"sourcesContent":["import { CSS_WIDE_KEYWORDS } from '../parser/const';\nimport { DIRECTIONS, filterMods, parseStyle } from '../utils/styles';\nimport { BORDER_STYLES } from './const';\nimport { extractCSSWideKeyword } from './shared';\n\ntype Direction = (typeof DIRECTIONS)[number];\n\ninterface GroupData {\n values: string[];\n mods: string[];\n colors: string[];\n}\n\ninterface BorderValue {\n width: string;\n style: string;\n color: string;\n}\n\n/**\n * Process a single group and return border values for its directions.\n * @returns Object with directions as keys and border values, or null for \"all directions\"\n */\nfunction processGroup(group: GroupData): {\n directions: Direction[];\n borderValue: BorderValue;\n} {\n const { values, mods, colors } = group;\n\n const directions = filterMods(mods, DIRECTIONS) as Direction[];\n const typeMods = filterMods(mods, BORDER_STYLES as unknown as string[]);\n\n const width = values[0] || 'var(--border-width)';\n const style = typeMods[0] || 'solid';\n const color = colors?.[0] || 'var(--border-color, currentColor)';\n\n return {\n directions,\n borderValue: { width, style, color },\n };\n}\n\n/**\n * Format a border value to CSS string.\n */\nfunction formatBorderValue(value: BorderValue): string {\n return `${value.width} ${value.style} ${value.color}`;\n}\n\n/**\n * Border style handler with multi-group support.\n *\n * Single group (backward compatible):\n * - `border=\"1bw solid #red\"` - all sides\n * - `border=\"1bw solid #red top left\"` - only top and left\n *\n * Multi-group (new):\n * - `border=\"1bw #red, 2bw #blue top\"` - all sides red 1bw, then top overridden to blue 2bw\n * - `border=\"1bw, dashed top bottom, #purple left right\"` - base 1bw, dashed on top/bottom, purple on left/right\n *\n * Later groups override earlier groups for conflicting directions.\n */\nexport function borderStyle({\n border,\n}: {\n border?: string | number | boolean;\n}) {\n if (!border && border !== 0) return null;\n\n if (border === true) border = '1bw';\n\n const strBorder = String(border);\n\n if (CSS_WIDE_KEYWORDS.has(strBorder)) {\n return { border: strBorder };\n }\n\n const processed = parseStyle(strBorder);\n const groups = processed.groups ?? [];\n\n if (!groups.length) return null;\n\n const useLonghand = groups.some((g) => (g.mods ?? []).includes('longhand'));\n\n // Single group - use original logic for backward compatibility\n if (groups.length === 1) {\n const group = groups[0];\n const keyword = extractCSSWideKeyword(group);\n\n if (keyword) {\n if (useLonghand) {\n return Object.fromEntries(\n DIRECTIONS.map((dir) => [`border-${dir}`, keyword]),\n );\n }\n\n return { border: keyword };\n }\n\n const { directions, borderValue } = processGroup({\n values: group.values ?? [],\n mods: group.mods ?? [],\n colors: group.colors ?? [],\n });\n\n const styleValue = formatBorderValue(borderValue);\n\n if (!directions.length) {\n if (useLonghand) {\n return Object.fromEntries(\n DIRECTIONS.map((dir) => [`border-${dir}`, styleValue]),\n );\n }\n\n return { border: styleValue };\n }\n\n const zeroValue = `0 ${borderValue.style} ${borderValue.color}`;\n\n return DIRECTIONS.reduce(\n (styles, dir) => {\n if (directions.includes(dir)) {\n styles[`border-${dir}`] = styleValue;\n } else {\n styles[`border-${dir}`] = zeroValue;\n }\n return styles;\n },\n {} as Record<string, string>,\n );\n }\n\n // Multi-group - process groups in order, later groups override earlier\n // Track whether any group specifies directions\n let hasAnyDirections = false;\n\n // Build a map of direction -> border value\n // Start with undefined (no border set)\n const directionMap: Record<Direction, BorderValue | null> = {\n top: null,\n right: null,\n bottom: null,\n left: null,\n };\n\n // Track the last \"all directions\" value for fallback\n let allDirectionsValue: BorderValue | null = null;\n\n // Process groups in order (first to last)\n for (const group of groups) {\n const { directions, borderValue } = processGroup({\n values: group.values ?? [],\n mods: group.mods ?? [],\n colors: group.colors ?? [],\n });\n\n if (directions.length === 0) {\n // No specific directions - applies to all\n allDirectionsValue = borderValue;\n // Set all directions\n for (const dir of DIRECTIONS) {\n directionMap[dir] = borderValue;\n }\n } else {\n // Specific directions - override only those\n hasAnyDirections = true;\n for (const dir of directions) {\n directionMap[dir] = borderValue;\n }\n }\n }\n\n // If no group specified any directions and we have an all-directions value,\n // return the simple `border` shorthand (or longhands if requested)\n if (!hasAnyDirections && allDirectionsValue) {\n const formatted = formatBorderValue(allDirectionsValue);\n\n if (useLonghand) {\n return Object.fromEntries(\n DIRECTIONS.map((dir) => [`border-${dir}`, formatted]),\n );\n }\n\n return { border: formatted };\n }\n\n // Otherwise, output individual border-* properties\n const result: Record<string, string> = {};\n\n for (const dir of DIRECTIONS) {\n const value = directionMap[dir];\n if (value) {\n result[`border-${dir}`] = formatBorderValue(value);\n } else {\n // No border for this direction - set to 0\n // Use the last all-directions value for style/color consistency, or defaults\n const fallback = allDirectionsValue || {\n width: '0',\n style: 'solid',\n color: 'var(--border-color, currentColor)',\n };\n result[`border-${dir}`] = `0 ${fallback.style} ${fallback.color}`;\n }\n }\n\n return result;\n}\n\nborderStyle.__lookupStyles = ['border'];\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ declare function colorStyle({
5
5
  color?: string | boolean;
6
6
  }): {
7
7
  color: string;
8
- } | undefined;
8
+ } | null;
9
9
  declare namespace colorStyle {
10
10
  var __lookupStyles: string[];
11
11
  }
@@ -1,10 +1,9 @@
1
1
  import { getColorSpaceSuffix } from "../utils/color-space.js";
2
2
  import { parseColor } from "../utils/styles.js";
3
3
  import { convertColorChainToComponentChain } from "./createStyle.js";
4
-
5
4
  //#region src/styles/color.ts
6
5
  function colorStyle({ color }) {
7
- if (!color) return;
6
+ if (!color) return null;
8
7
  if (color === true) color = "currentColor";
9
8
  if (typeof color === "string" && (color.startsWith("#") || color.startsWith("(#"))) color = parseColor(color).color || color;
10
9
  const match = color.match(/var\(--(.+?)-color/);
@@ -21,7 +20,7 @@ function colorStyle({ color }) {
21
20
  return styles;
22
21
  }
23
22
  colorStyle.__lookupStyles = ["color"];
24
-
25
23
  //#endregion
26
24
  export { colorStyle };
25
+
27
26
  //# sourceMappingURL=color.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"color.js","names":[],"sources":["../../src/styles/color.ts"],"sourcesContent":["import { getColorSpaceSuffix } from '../utils/color-space';\nimport { parseColor } from '../utils/styles';\n\nimport { convertColorChainToComponentChain } from './createStyle';\n\nexport function colorStyle({ color }: { color?: string | boolean }) {\n if (!color) return;\n\n if (color === true) color = 'currentColor';\n\n if (\n typeof color === 'string' &&\n (color.startsWith('#') || color.startsWith('(#'))\n ) {\n color = parseColor(color).color || color;\n }\n\n const match = color.match(/var\\(--(.+?)-color/);\n let name = '';\n\n if (match) {\n name = match[1];\n }\n\n const styles = {\n color: color,\n };\n\n if (name && name !== 'current') {\n const suffix = getColorSpaceSuffix();\n Object.assign(styles, {\n '--current-color': color,\n [`--current-color-${suffix}`]: convertColorChainToComponentChain(color),\n });\n }\n\n return styles;\n}\n\ncolorStyle.__lookupStyles = ['color'];\n"],"mappings":";;;;;AAKA,SAAgB,WAAW,EAAE,SAAuC;AAClE,KAAI,CAAC,MAAO;AAEZ,KAAI,UAAU,KAAM,SAAQ;AAE5B,KACE,OAAO,UAAU,aAChB,MAAM,WAAW,IAAI,IAAI,MAAM,WAAW,KAAK,EAEhD,SAAQ,WAAW,MAAM,CAAC,SAAS;CAGrC,MAAM,QAAQ,MAAM,MAAM,qBAAqB;CAC/C,IAAI,OAAO;AAEX,KAAI,MACF,QAAO,MAAM;CAGf,MAAM,SAAS,EACN,OACR;AAED,KAAI,QAAQ,SAAS,WAAW;EAC9B,MAAM,SAAS,qBAAqB;AACpC,SAAO,OAAO,QAAQ;GACpB,mBAAmB;IAClB,mBAAmB,WAAW,kCAAkC,MAAM;GACxE,CAAC;;AAGJ,QAAO;;AAGT,WAAW,iBAAiB,CAAC,QAAQ"}
1
+ {"version":3,"file":"color.js","names":[],"sources":["../../src/styles/color.ts"],"sourcesContent":["import { getColorSpaceSuffix } from '../utils/color-space';\nimport { parseColor } from '../utils/styles';\n\nimport { convertColorChainToComponentChain } from './createStyle';\n\nexport function colorStyle({ color }: { color?: string | boolean }) {\n if (!color) return null;\n\n if (color === true) color = 'currentColor';\n\n if (\n typeof color === 'string' &&\n (color.startsWith('#') || color.startsWith('(#'))\n ) {\n color = parseColor(color).color || color;\n }\n\n const match = color.match(/var\\(--(.+?)-color/);\n let name = '';\n\n if (match) {\n name = match[1];\n }\n\n const styles = {\n color: color,\n };\n\n if (name && name !== 'current') {\n const suffix = getColorSpaceSuffix();\n Object.assign(styles, {\n '--current-color': color,\n [`--current-color-${suffix}`]: convertColorChainToComponentChain(color),\n });\n }\n\n return styles;\n}\n\ncolorStyle.__lookupStyles = ['color'];\n"],"mappings":";;;;AAKA,SAAgB,WAAW,EAAE,SAAuC;AAClE,KAAI,CAAC,MAAO,QAAO;AAEnB,KAAI,UAAU,KAAM,SAAQ;AAE5B,KACE,OAAO,UAAU,aAChB,MAAM,WAAW,IAAI,IAAI,MAAM,WAAW,KAAK,EAEhD,SAAQ,WAAW,MAAM,CAAC,SAAS;CAGrC,MAAM,QAAQ,MAAM,MAAM,qBAAqB;CAC/C,IAAI,OAAO;AAEX,KAAI,MACF,QAAO,MAAM;CAGf,MAAM,SAAS,EACN,OACR;AAED,KAAI,QAAQ,SAAS,WAAW;EAC9B,MAAM,SAAS,qBAAqB;AACpC,SAAO,OAAO,QAAQ;GACpB,mBAAmB;IAClB,mBAAmB,WAAW,kCAAkC,MAAM;GACxE,CAAC;;AAGJ,QAAO;;AAGT,WAAW,iBAAiB,CAAC,QAAQ"}
@@ -0,0 +1,17 @@
1
+ //#region src/styles/const.ts
2
+ const BORDER_STYLES = [
3
+ "none",
4
+ "hidden",
5
+ "dotted",
6
+ "dashed",
7
+ "solid",
8
+ "double",
9
+ "groove",
10
+ "ridge",
11
+ "inset",
12
+ "outset"
13
+ ];
14
+ //#endregion
15
+ export { BORDER_STYLES };
16
+
17
+ //# sourceMappingURL=const.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"const.js","names":[],"sources":["../../src/styles/const.ts"],"sourcesContent":["export const BORDER_STYLES = [\n 'none',\n 'hidden',\n 'dotted',\n 'dashed',\n 'solid',\n 'double',\n 'groove',\n 'ridge',\n 'inset',\n 'outset',\n] as const;\n"],"mappings":";AAAA,MAAa,gBAAgB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
@@ -1,7 +1,6 @@
1
1
  import { getColorSpaceComponents, getColorSpaceSuffix, strToColorSpace } from "../utils/color-space.js";
2
2
  import { normalizeColorTokenValue, parseColor, parseStyle } from "../utils/styles.js";
3
3
  import { toSnakeCase } from "../utils/string.js";
4
-
5
4
  //#region src/styles/createStyle.ts
6
5
  const CACHE = {};
7
6
  /**
@@ -28,19 +27,19 @@ function createStyle(styleName, cssStyle, converter) {
28
27
  if (!CACHE[key]) {
29
28
  const styleHandler = (styleMap) => {
30
29
  let styleValue = styleMap[styleName];
31
- if (styleValue == null || styleValue === false) return;
30
+ if (styleValue == null || styleValue === false) return null;
32
31
  let finalCssStyle;
33
32
  const isColorToken = !cssStyle && typeof styleName === "string" && styleName.startsWith("#");
34
33
  if (isColorToken) finalCssStyle = `--${toSnakeCase(styleName.slice(1)).replace(/^-+/, "")}-color`;
35
34
  else finalCssStyle = cssStyle || toSnakeCase(styleName).replace(/^\$/, "--");
36
35
  if (isColorToken) {
37
36
  const normalized = normalizeColorTokenValue(styleValue);
38
- if (normalized === null) return;
37
+ if (normalized === null) return null;
39
38
  styleValue = normalized;
40
39
  }
41
40
  if (converter && typeof styleValue !== "string") {
42
41
  styleValue = converter(styleValue);
43
- if (!styleValue) return;
42
+ if (!styleValue) return null;
44
43
  }
45
44
  if (typeof styleValue === "string" && finalCssStyle.startsWith("--") && finalCssStyle.endsWith("-color")) {
46
45
  styleValue = styleValue.trim();
@@ -74,7 +73,7 @@ function createStyle(styleName, cssStyle, converter) {
74
73
  }
75
74
  return CACHE[key];
76
75
  }
77
-
78
76
  //#endregion
79
77
  export { convertColorChainToComponentChain, createStyle };
78
+
80
79
  //# sourceMappingURL=createStyle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createStyle.js","names":[],"sources":["../../src/styles/createStyle.ts"],"sourcesContent":["import {\n getColorSpaceComponents,\n getColorSpaceSuffix,\n strToColorSpace,\n} from '../utils/color-space';\nimport { toSnakeCase } from '../utils/string';\nimport {\n normalizeColorTokenValue,\n parseColor,\n parseStyle,\n} from '../utils/styles';\nimport type {\n CSSMap,\n StyleHandler,\n StyleValue,\n StyleValueStateMap,\n} from '../utils/styles';\n\nconst CACHE: Record<string, StyleHandler> = {};\n\n/**\n * Convert color fallback chain to component fallback chain.\n * Example: var(--primary-color, var(--secondary-color))\n * → var(--primary-color-oklch, var(--secondary-color-oklch))\n */\nexport function convertColorChainToComponentChain(colorValue: string): string {\n const suffix = getColorSpaceSuffix();\n\n // Handle func(var(--name-color-{suffix}) / alpha) pattern.\n // When #name.opacity is parsed, the classifier produces e.g.\n // oklch(var(--name-color-oklch) / .opacity).\n // The component chain should be just the inner var() reference.\n const componentVarMatch = colorValue.match(\n /^(?:rgb|hsl|oklch)a?\\(\\s*(var\\(--[a-z0-9-]+-color-(?:rgb|hsl|oklch)\\))\\s*\\//,\n );\n if (componentVarMatch) {\n return componentVarMatch[1];\n }\n\n // Match var(--name-color, ...) pattern\n const varPattern = /var\\(--([a-z0-9-]+)-color\\s*(?:,\\s*(.+))?\\)/;\n const match = colorValue.match(varPattern);\n\n if (!match) {\n // Not a color variable — try to convert to components\n const components = getColorSpaceComponents(colorValue);\n if (components !== colorValue) return components;\n return colorValue;\n }\n\n const [, name, fallback] = match;\n\n if (!fallback) {\n return `var(--${name}-color-${suffix})`;\n }\n\n const processedFallback = convertColorChainToComponentChain(fallback.trim());\n return `var(--${name}-color-${suffix}, ${processedFallback})`;\n}\n\nexport function createStyle(\n styleName: string,\n cssStyle?: string,\n converter?: (styleValue: string | number | true) => string | undefined,\n) {\n const key = `${styleName}.${cssStyle ?? ''}`;\n\n if (!CACHE[key]) {\n const styleHandler = (styleMap: StyleValueStateMap): CSSMap | void => {\n let styleValue = styleMap[styleName];\n\n if (styleValue == null || styleValue === false) return;\n\n let finalCssStyle: string;\n const isColorToken =\n !cssStyle && typeof styleName === 'string' && styleName.startsWith('#');\n\n if (isColorToken) {\n const raw = styleName.slice(1);\n const name = toSnakeCase(raw).replace(/^-+/, '');\n finalCssStyle = `--${name}-color`;\n } else {\n finalCssStyle = cssStyle || toSnakeCase(styleName).replace(/^\\$/, '--');\n }\n\n if (isColorToken) {\n const normalized = normalizeColorTokenValue(styleValue);\n if (normalized === null) return;\n styleValue = normalized;\n }\n\n if (converter && typeof styleValue !== 'string') {\n styleValue = converter(styleValue as string | number | true);\n\n if (!styleValue) return;\n }\n\n if (\n typeof styleValue === 'string' &&\n finalCssStyle.startsWith('--') &&\n finalCssStyle.endsWith('-color')\n ) {\n styleValue = styleValue.trim();\n const suffix = getColorSpaceSuffix();\n\n const colorSpaceStr = strToColorSpace(styleValue as string);\n\n const { color, name } = parseColor(styleValue as string);\n\n if (name && colorSpaceStr) {\n return {\n [finalCssStyle]: `var(--${name}-color, ${colorSpaceStr})`,\n [`${finalCssStyle}-${suffix}`]: `var(--${name}-color-${suffix}, ${getColorSpaceComponents(\n colorSpaceStr,\n )})`,\n };\n } else if (name) {\n if (color) {\n return {\n [finalCssStyle]: color,\n [`${finalCssStyle}-${suffix}`]:\n convertColorChainToComponentChain(color),\n };\n }\n\n return {\n [finalCssStyle]: `var(--${name}-color)`,\n [`${finalCssStyle}-${suffix}`]: `var(--${name}-color-${suffix})`,\n };\n } else if (colorSpaceStr) {\n return {\n [finalCssStyle]: colorSpaceStr,\n [`${finalCssStyle}-${suffix}`]:\n getColorSpaceComponents(colorSpaceStr),\n };\n }\n\n return {\n [finalCssStyle]: color ?? '',\n };\n }\n\n const processed = parseStyle(styleValue as StyleValue);\n return { [finalCssStyle]: processed.output };\n };\n\n styleHandler.__lookupStyles = [styleName];\n\n CACHE[key] = styleHandler;\n }\n\n return CACHE[key];\n}\n"],"mappings":";;;;;AAkBA,MAAM,QAAsC,EAAE;;;;;;AAO9C,SAAgB,kCAAkC,YAA4B;CAC5E,MAAM,SAAS,qBAAqB;CAMpC,MAAM,oBAAoB,WAAW,MACnC,8EACD;AACD,KAAI,kBACF,QAAO,kBAAkB;CAK3B,MAAM,QAAQ,WAAW,MADN,8CACuB;AAE1C,KAAI,CAAC,OAAO;EAEV,MAAM,aAAa,wBAAwB,WAAW;AACtD,MAAI,eAAe,WAAY,QAAO;AACtC,SAAO;;CAGT,MAAM,GAAG,MAAM,YAAY;AAE3B,KAAI,CAAC,SACH,QAAO,SAAS,KAAK,SAAS,OAAO;AAIvC,QAAO,SAAS,KAAK,SAAS,OAAO,IADX,kCAAkC,SAAS,MAAM,CAAC,CACjB;;AAG7D,SAAgB,YACd,WACA,UACA,WACA;CACA,MAAM,MAAM,GAAG,UAAU,GAAG,YAAY;AAExC,KAAI,CAAC,MAAM,MAAM;EACf,MAAM,gBAAgB,aAAgD;GACpE,IAAI,aAAa,SAAS;AAE1B,OAAI,cAAc,QAAQ,eAAe,MAAO;GAEhD,IAAI;GACJ,MAAM,eACJ,CAAC,YAAY,OAAO,cAAc,YAAY,UAAU,WAAW,IAAI;AAEzE,OAAI,aAGF,iBAAgB,KADH,YADD,UAAU,MAAM,EAAE,CACD,CAAC,QAAQ,OAAO,GAAG,CACtB;OAE1B,iBAAgB,YAAY,YAAY,UAAU,CAAC,QAAQ,OAAO,KAAK;AAGzE,OAAI,cAAc;IAChB,MAAM,aAAa,yBAAyB,WAAW;AACvD,QAAI,eAAe,KAAM;AACzB,iBAAa;;AAGf,OAAI,aAAa,OAAO,eAAe,UAAU;AAC/C,iBAAa,UAAU,WAAqC;AAE5D,QAAI,CAAC,WAAY;;AAGnB,OACE,OAAO,eAAe,YACtB,cAAc,WAAW,KAAK,IAC9B,cAAc,SAAS,SAAS,EAChC;AACA,iBAAa,WAAW,MAAM;IAC9B,MAAM,SAAS,qBAAqB;IAEpC,MAAM,gBAAgB,gBAAgB,WAAqB;IAE3D,MAAM,EAAE,OAAO,SAAS,WAAW,WAAqB;AAExD,QAAI,QAAQ,cACV,QAAO;MACJ,gBAAgB,SAAS,KAAK,UAAU,cAAc;MACtD,GAAG,cAAc,GAAG,WAAW,SAAS,KAAK,SAAS,OAAO,IAAI,wBAChE,cACD,CAAC;KACH;aACQ,MAAM;AACf,SAAI,MACF,QAAO;OACJ,gBAAgB;OAChB,GAAG,cAAc,GAAG,WACnB,kCAAkC,MAAM;MAC3C;AAGH,YAAO;OACJ,gBAAgB,SAAS,KAAK;OAC9B,GAAG,cAAc,GAAG,WAAW,SAAS,KAAK,SAAS,OAAO;MAC/D;eACQ,cACT,QAAO;MACJ,gBAAgB;MAChB,GAAG,cAAc,GAAG,WACnB,wBAAwB,cAAc;KACzC;AAGH,WAAO,GACJ,gBAAgB,SAAS,IAC3B;;GAGH,MAAM,YAAY,WAAW,WAAyB;AACtD,UAAO,GAAG,gBAAgB,UAAU,QAAQ;;AAG9C,eAAa,iBAAiB,CAAC,UAAU;AAEzC,QAAM,OAAO;;AAGf,QAAO,MAAM"}
1
+ {"version":3,"file":"createStyle.js","names":[],"sources":["../../src/styles/createStyle.ts"],"sourcesContent":["import {\n getColorSpaceComponents,\n getColorSpaceSuffix,\n strToColorSpace,\n} from '../utils/color-space';\nimport { toSnakeCase } from '../utils/string';\nimport {\n normalizeColorTokenValue,\n parseColor,\n parseStyle,\n} from '../utils/styles';\nimport type {\n CSSMap,\n StyleHandler,\n StyleValue,\n StyleValueStateMap,\n} from '../utils/styles';\n\nconst CACHE: Record<string, StyleHandler> = {};\n\n/**\n * Convert color fallback chain to component fallback chain.\n * Example: var(--primary-color, var(--secondary-color))\n * → var(--primary-color-oklch, var(--secondary-color-oklch))\n */\nexport function convertColorChainToComponentChain(colorValue: string): string {\n const suffix = getColorSpaceSuffix();\n\n // Handle func(var(--name-color-{suffix}) / alpha) pattern.\n // When #name.opacity is parsed, the classifier produces e.g.\n // oklch(var(--name-color-oklch) / .opacity).\n // The component chain should be just the inner var() reference.\n const componentVarMatch = colorValue.match(\n /^(?:rgb|hsl|oklch)a?\\(\\s*(var\\(--[a-z0-9-]+-color-(?:rgb|hsl|oklch)\\))\\s*\\//,\n );\n if (componentVarMatch) {\n return componentVarMatch[1];\n }\n\n // Match var(--name-color, ...) pattern\n const varPattern = /var\\(--([a-z0-9-]+)-color\\s*(?:,\\s*(.+))?\\)/;\n const match = colorValue.match(varPattern);\n\n if (!match) {\n // Not a color variable — try to convert to components\n const components = getColorSpaceComponents(colorValue);\n if (components !== colorValue) return components;\n return colorValue;\n }\n\n const [, name, fallback] = match;\n\n if (!fallback) {\n return `var(--${name}-color-${suffix})`;\n }\n\n const processedFallback = convertColorChainToComponentChain(fallback.trim());\n return `var(--${name}-color-${suffix}, ${processedFallback})`;\n}\n\nexport function createStyle(\n styleName: string,\n cssStyle?: string,\n converter?: (styleValue: string | number | true) => string | undefined,\n) {\n const key = `${styleName}.${cssStyle ?? ''}`;\n\n if (!CACHE[key]) {\n const styleHandler = (styleMap: StyleValueStateMap): CSSMap | null => {\n let styleValue = styleMap[styleName];\n\n if (styleValue == null || styleValue === false) return null;\n\n let finalCssStyle: string;\n const isColorToken =\n !cssStyle && typeof styleName === 'string' && styleName.startsWith('#');\n\n if (isColorToken) {\n const raw = styleName.slice(1);\n const name = toSnakeCase(raw).replace(/^-+/, '');\n finalCssStyle = `--${name}-color`;\n } else {\n finalCssStyle = cssStyle || toSnakeCase(styleName).replace(/^\\$/, '--');\n }\n\n if (isColorToken) {\n const normalized = normalizeColorTokenValue(styleValue);\n if (normalized === null) return null;\n styleValue = normalized;\n }\n\n if (converter && typeof styleValue !== 'string') {\n styleValue = converter(styleValue as string | number | true);\n\n if (!styleValue) return null;\n }\n\n if (\n typeof styleValue === 'string' &&\n finalCssStyle.startsWith('--') &&\n finalCssStyle.endsWith('-color')\n ) {\n styleValue = styleValue.trim();\n const suffix = getColorSpaceSuffix();\n\n const colorSpaceStr = strToColorSpace(styleValue as string);\n\n const { color, name } = parseColor(styleValue as string);\n\n if (name && colorSpaceStr) {\n return {\n [finalCssStyle]: `var(--${name}-color, ${colorSpaceStr})`,\n [`${finalCssStyle}-${suffix}`]: `var(--${name}-color-${suffix}, ${getColorSpaceComponents(\n colorSpaceStr,\n )})`,\n };\n } else if (name) {\n if (color) {\n return {\n [finalCssStyle]: color,\n [`${finalCssStyle}-${suffix}`]:\n convertColorChainToComponentChain(color),\n };\n }\n\n return {\n [finalCssStyle]: `var(--${name}-color)`,\n [`${finalCssStyle}-${suffix}`]: `var(--${name}-color-${suffix})`,\n };\n } else if (colorSpaceStr) {\n return {\n [finalCssStyle]: colorSpaceStr,\n [`${finalCssStyle}-${suffix}`]:\n getColorSpaceComponents(colorSpaceStr),\n };\n }\n\n return {\n [finalCssStyle]: color ?? '',\n };\n }\n\n const processed = parseStyle(styleValue as StyleValue);\n return { [finalCssStyle]: processed.output };\n };\n\n styleHandler.__lookupStyles = [styleName];\n\n CACHE[key] = styleHandler;\n }\n\n return CACHE[key];\n}\n"],"mappings":";;;;AAkBA,MAAM,QAAsC,EAAE;;;;;;AAO9C,SAAgB,kCAAkC,YAA4B;CAC5E,MAAM,SAAS,qBAAqB;CAMpC,MAAM,oBAAoB,WAAW,MACnC,8EACD;AACD,KAAI,kBACF,QAAO,kBAAkB;CAK3B,MAAM,QAAQ,WAAW,MADN,8CACuB;AAE1C,KAAI,CAAC,OAAO;EAEV,MAAM,aAAa,wBAAwB,WAAW;AACtD,MAAI,eAAe,WAAY,QAAO;AACtC,SAAO;;CAGT,MAAM,GAAG,MAAM,YAAY;AAE3B,KAAI,CAAC,SACH,QAAO,SAAS,KAAK,SAAS,OAAO;AAIvC,QAAO,SAAS,KAAK,SAAS,OAAO,IADX,kCAAkC,SAAS,MAAM,CAAC,CACjB;;AAG7D,SAAgB,YACd,WACA,UACA,WACA;CACA,MAAM,MAAM,GAAG,UAAU,GAAG,YAAY;AAExC,KAAI,CAAC,MAAM,MAAM;EACf,MAAM,gBAAgB,aAAgD;GACpE,IAAI,aAAa,SAAS;AAE1B,OAAI,cAAc,QAAQ,eAAe,MAAO,QAAO;GAEvD,IAAI;GACJ,MAAM,eACJ,CAAC,YAAY,OAAO,cAAc,YAAY,UAAU,WAAW,IAAI;AAEzE,OAAI,aAGF,iBAAgB,KADH,YADD,UAAU,MAAM,EAAE,CACD,CAAC,QAAQ,OAAO,GAAG,CACtB;OAE1B,iBAAgB,YAAY,YAAY,UAAU,CAAC,QAAQ,OAAO,KAAK;AAGzE,OAAI,cAAc;IAChB,MAAM,aAAa,yBAAyB,WAAW;AACvD,QAAI,eAAe,KAAM,QAAO;AAChC,iBAAa;;AAGf,OAAI,aAAa,OAAO,eAAe,UAAU;AAC/C,iBAAa,UAAU,WAAqC;AAE5D,QAAI,CAAC,WAAY,QAAO;;AAG1B,OACE,OAAO,eAAe,YACtB,cAAc,WAAW,KAAK,IAC9B,cAAc,SAAS,SAAS,EAChC;AACA,iBAAa,WAAW,MAAM;IAC9B,MAAM,SAAS,qBAAqB;IAEpC,MAAM,gBAAgB,gBAAgB,WAAqB;IAE3D,MAAM,EAAE,OAAO,SAAS,WAAW,WAAqB;AAExD,QAAI,QAAQ,cACV,QAAO;MACJ,gBAAgB,SAAS,KAAK,UAAU,cAAc;MACtD,GAAG,cAAc,GAAG,WAAW,SAAS,KAAK,SAAS,OAAO,IAAI,wBAChE,cACD,CAAC;KACH;aACQ,MAAM;AACf,SAAI,MACF,QAAO;OACJ,gBAAgB;OAChB,GAAG,cAAc,GAAG,WACnB,kCAAkC,MAAM;MAC3C;AAGH,YAAO;OACJ,gBAAgB,SAAS,KAAK;OAC9B,GAAG,cAAc,GAAG,WAAW,SAAS,KAAK,SAAS,OAAO;MAC/D;eACQ,cACT,QAAO;MACJ,gBAAgB;MAChB,GAAG,cAAc,GAAG,WACnB,wBAAwB,cAAc;KACzC;AAGH,WAAO,GACJ,gBAAgB,SAAS,IAC3B;;GAGH,MAAM,YAAY,WAAW,WAAyB;AACtD,UAAO,GAAG,gBAAgB,UAAU,QAAQ;;AAG9C,eAAa,iBAAiB,CAAC,UAAU;AAEzC,QAAM,OAAO;;AAGf,QAAO,MAAM"}
@@ -1,6 +1,6 @@
1
+ import { CSS_WIDE_KEYWORDS } from "../parser/const.js";
1
2
  import { makeEmptyDetails } from "../parser/types.js";
2
3
  import { parseStyle } from "../utils/styles.js";
3
-
4
4
  //#region src/styles/dimension.ts
5
5
  const DEFAULT_MIN_SIZE = "var(--gap)";
6
6
  const DEFAULT_MAX_SIZE = "100%";
@@ -26,7 +26,19 @@ function dimensionStyle(name) {
26
26
  const minStyle = `min-${name}`;
27
27
  const maxStyle = `max-${name}`;
28
28
  return ({ value, min, max }) => {
29
- if (value == null && min == null && max == null) return;
29
+ if (value == null && min == null && max == null) return null;
30
+ if (value != null && typeof value === "string" && CSS_WIDE_KEYWORDS.has(value)) {
31
+ const styles = {
32
+ [name]: value,
33
+ [minStyle]: value,
34
+ [maxStyle]: value
35
+ };
36
+ const minVal = parseDimensionValue(min);
37
+ const maxVal = parseDimensionValue(max);
38
+ if (minVal) styles[minStyle] = minVal;
39
+ if (maxVal) styles[maxStyle] = maxVal;
40
+ return styles;
41
+ }
30
42
  if (value === true) {
31
43
  const styles = {
32
44
  [name]: "auto",
@@ -91,7 +103,7 @@ function dimensionStyle(name) {
91
103
  return styles;
92
104
  };
93
105
  }
94
-
95
106
  //#endregion
96
107
  export { dimensionStyle };
108
+
97
109
  //# sourceMappingURL=dimension.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dimension.js","names":[],"sources":["../../src/styles/dimension.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\nconst DEFAULT_MIN_SIZE = 'var(--gap)';\nconst DEFAULT_MAX_SIZE = '100%';\n\n/**\n * Parse a dimension value (string, number, or boolean) into a CSS value\n */\nfunction parseDimensionValue(\n val: string | number | boolean | undefined,\n): string | null {\n if (val == null) return null;\n if (typeof val === 'number') return `${val}px`;\n if (val === true) return 'initial';\n\n const processed = parseStyle(String(val));\n return processed.groups[0]?.values[0] || null;\n}\n\ninterface DimensionProps {\n value?: string | number | boolean;\n min?: string | number | boolean;\n max?: string | number | boolean;\n}\n\n/**\n * Creates a dimension style handler for width or height.\n *\n * Supports:\n * - Main dimension prop (width/height) with syntax for min/max\n * - Separate min/max props (minWidth/maxWidth or minHeight/maxHeight)\n *\n * Priority: Individual min/max props override values from main prop syntax\n */\nexport function dimensionStyle(name: 'width' | 'height') {\n const minStyle = `min-${name}`;\n const maxStyle = `max-${name}`;\n\n return ({ value, min, max }: DimensionProps) => {\n // If nothing is defined, return undefined\n if (value == null && min == null && max == null) return;\n\n // Handle boolean true on main value - reset all to initial\n if (value === true) {\n const styles: Record<string, string | string[]> = {\n [name]: 'auto',\n [minStyle]: 'initial',\n [maxStyle]: 'initial',\n };\n\n // Apply individual min/max overrides\n const minVal = parseDimensionValue(min);\n const maxVal = parseDimensionValue(max);\n if (minVal) styles[minStyle] = minVal;\n if (maxVal) styles[maxStyle] = maxVal;\n\n return styles;\n }\n\n const styles: Record<string, string | string[]> = {\n [name]: 'auto',\n [minStyle]: 'initial',\n [maxStyle]: 'initial',\n };\n\n // Process main dimension value\n if (value != null) {\n let val = value;\n if (typeof val === 'number') {\n val = `${val}px`;\n }\n\n val = String(val);\n\n const processed = parseStyle(val);\n const { mods, values } = processed.groups[0] ?? makeEmptyDetails();\n\n let flag = false;\n\n for (const mod of mods) {\n switch (mod) {\n case 'min':\n styles[minStyle] = values[0] || DEFAULT_MIN_SIZE;\n flag = true;\n break;\n case 'max':\n styles[maxStyle] = values[0] || DEFAULT_MAX_SIZE;\n flag = true;\n break;\n case 'fixed': {\n // Fixed modifier: set all three dimensions to the same value\n const fixedValue = values[0] || 'max-content';\n styles[minStyle] = fixedValue;\n styles[name] = fixedValue;\n styles[maxStyle] = fixedValue;\n flag = true;\n break;\n }\n default:\n break;\n }\n }\n\n if (!flag || !mods.length) {\n if (values.length === 2) {\n styles[minStyle] = values[0];\n styles[maxStyle] = values[1];\n } else if (values.length === 3) {\n styles[minStyle] = values[0];\n styles[name] = values[1];\n styles[maxStyle] = values[2];\n } else {\n styles[name] = values[0] || 'auto';\n }\n }\n\n if (styles[name] === 'stretch') {\n if (name === 'width') {\n styles[name] = [\n 'stretch',\n '-webkit-fill-available',\n '-moz-available',\n ];\n } else {\n styles[name] = 'auto';\n }\n }\n }\n\n // Apply individual min/max props (higher priority, override main prop syntax)\n const minVal = parseDimensionValue(min);\n const maxVal = parseDimensionValue(max);\n if (minVal) styles[minStyle] = minVal;\n if (maxVal) styles[maxStyle] = maxVal;\n\n return styles;\n };\n}\n"],"mappings":";;;;AAGA,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;;;;AAKzB,SAAS,oBACP,KACe;AACf,KAAI,OAAO,KAAM,QAAO;AACxB,KAAI,OAAO,QAAQ,SAAU,QAAO,GAAG,IAAI;AAC3C,KAAI,QAAQ,KAAM,QAAO;AAGzB,QADkB,WAAW,OAAO,IAAI,CAAC,CACxB,OAAO,IAAI,OAAO,MAAM;;;;;;;;;;;AAkB3C,SAAgB,eAAe,MAA0B;CACvD,MAAM,WAAW,OAAO;CACxB,MAAM,WAAW,OAAO;AAExB,SAAQ,EAAE,OAAO,KAAK,UAA0B;AAE9C,MAAI,SAAS,QAAQ,OAAO,QAAQ,OAAO,KAAM;AAGjD,MAAI,UAAU,MAAM;GAClB,MAAM,SAA4C;KAC/C,OAAO;KACP,WAAW;KACX,WAAW;IACb;GAGD,MAAM,SAAS,oBAAoB,IAAI;GACvC,MAAM,SAAS,oBAAoB,IAAI;AACvC,OAAI,OAAQ,QAAO,YAAY;AAC/B,OAAI,OAAQ,QAAO,YAAY;AAE/B,UAAO;;EAGT,MAAM,SAA4C;IAC/C,OAAO;IACP,WAAW;IACX,WAAW;GACb;AAGD,MAAI,SAAS,MAAM;GACjB,IAAI,MAAM;AACV,OAAI,OAAO,QAAQ,SACjB,OAAM,GAAG,IAAI;AAGf,SAAM,OAAO,IAAI;GAGjB,MAAM,EAAE,MAAM,WADI,WAAW,IAAI,CACE,OAAO,MAAM,kBAAkB;GAElE,IAAI,OAAO;AAEX,QAAK,MAAM,OAAO,KAChB,SAAQ,KAAR;IACE,KAAK;AACH,YAAO,YAAY,OAAO,MAAM;AAChC,YAAO;AACP;IACF,KAAK;AACH,YAAO,YAAY,OAAO,MAAM;AAChC,YAAO;AACP;IACF,KAAK,SAAS;KAEZ,MAAM,aAAa,OAAO,MAAM;AAChC,YAAO,YAAY;AACnB,YAAO,QAAQ;AACf,YAAO,YAAY;AACnB,YAAO;AACP;;IAEF,QACE;;AAIN,OAAI,CAAC,QAAQ,CAAC,KAAK,OACjB,KAAI,OAAO,WAAW,GAAG;AACvB,WAAO,YAAY,OAAO;AAC1B,WAAO,YAAY,OAAO;cACjB,OAAO,WAAW,GAAG;AAC9B,WAAO,YAAY,OAAO;AAC1B,WAAO,QAAQ,OAAO;AACtB,WAAO,YAAY,OAAO;SAE1B,QAAO,QAAQ,OAAO,MAAM;AAIhC,OAAI,OAAO,UAAU,UACnB,KAAI,SAAS,QACX,QAAO,QAAQ;IACb;IACA;IACA;IACD;OAED,QAAO,QAAQ;;EAMrB,MAAM,SAAS,oBAAoB,IAAI;EACvC,MAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,OAAQ,QAAO,YAAY;AAC/B,MAAI,OAAQ,QAAO,YAAY;AAE/B,SAAO"}
1
+ {"version":3,"file":"dimension.js","names":[],"sources":["../../src/styles/dimension.ts"],"sourcesContent":["import { CSS_WIDE_KEYWORDS } from '../parser/const';\nimport { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\nconst DEFAULT_MIN_SIZE = 'var(--gap)';\nconst DEFAULT_MAX_SIZE = '100%';\n\n/**\n * Parse a dimension value (string, number, or boolean) into a CSS value\n */\nfunction parseDimensionValue(\n val: string | number | boolean | undefined,\n): string | null {\n if (val == null) return null;\n if (typeof val === 'number') return `${val}px`;\n if (val === true) return 'initial';\n\n const processed = parseStyle(String(val));\n return processed.groups[0]?.values[0] || null;\n}\n\ninterface DimensionProps {\n value?: string | number | boolean;\n min?: string | number | boolean;\n max?: string | number | boolean;\n}\n\n/**\n * Creates a dimension style handler for width or height.\n *\n * Supports:\n * - Main dimension prop (width/height) with syntax for min/max\n * - Separate min/max props (minWidth/maxWidth or minHeight/maxHeight)\n *\n * Priority: Individual min/max props override values from main prop syntax\n */\nexport function dimensionStyle(name: 'width' | 'height') {\n const minStyle = `min-${name}`;\n const maxStyle = `max-${name}`;\n\n return ({ value, min, max }: DimensionProps) => {\n if (value == null && min == null && max == null) return null;\n\n if (\n value != null &&\n typeof value === 'string' &&\n CSS_WIDE_KEYWORDS.has(value)\n ) {\n const styles: Record<string, string> = {\n [name]: value,\n [minStyle]: value,\n [maxStyle]: value,\n };\n\n const minVal = parseDimensionValue(min);\n const maxVal = parseDimensionValue(max);\n if (minVal) styles[minStyle] = minVal;\n if (maxVal) styles[maxStyle] = maxVal;\n\n return styles;\n }\n\n if (value === true) {\n const styles: Record<string, string | string[]> = {\n [name]: 'auto',\n [minStyle]: 'initial',\n [maxStyle]: 'initial',\n };\n\n // Apply individual min/max overrides\n const minVal = parseDimensionValue(min);\n const maxVal = parseDimensionValue(max);\n if (minVal) styles[minStyle] = minVal;\n if (maxVal) styles[maxStyle] = maxVal;\n\n return styles;\n }\n\n const styles: Record<string, string | string[]> = {\n [name]: 'auto',\n [minStyle]: 'initial',\n [maxStyle]: 'initial',\n };\n\n // Process main dimension value\n if (value != null) {\n let val = value;\n if (typeof val === 'number') {\n val = `${val}px`;\n }\n\n val = String(val);\n\n const processed = parseStyle(val);\n const { mods, values } = processed.groups[0] ?? makeEmptyDetails();\n\n let flag = false;\n\n for (const mod of mods) {\n switch (mod) {\n case 'min':\n styles[minStyle] = values[0] || DEFAULT_MIN_SIZE;\n flag = true;\n break;\n case 'max':\n styles[maxStyle] = values[0] || DEFAULT_MAX_SIZE;\n flag = true;\n break;\n case 'fixed': {\n // Fixed modifier: set all three dimensions to the same value\n const fixedValue = values[0] || 'max-content';\n styles[minStyle] = fixedValue;\n styles[name] = fixedValue;\n styles[maxStyle] = fixedValue;\n flag = true;\n break;\n }\n default:\n break;\n }\n }\n\n if (!flag || !mods.length) {\n if (values.length === 2) {\n styles[minStyle] = values[0];\n styles[maxStyle] = values[1];\n } else if (values.length === 3) {\n styles[minStyle] = values[0];\n styles[name] = values[1];\n styles[maxStyle] = values[2];\n } else {\n styles[name] = values[0] || 'auto';\n }\n }\n\n if (styles[name] === 'stretch') {\n if (name === 'width') {\n styles[name] = [\n 'stretch',\n '-webkit-fill-available',\n '-moz-available',\n ];\n } else {\n styles[name] = 'auto';\n }\n }\n }\n\n // Apply individual min/max props (higher priority, override main prop syntax)\n const minVal = parseDimensionValue(min);\n const maxVal = parseDimensionValue(max);\n if (minVal) styles[minStyle] = minVal;\n if (maxVal) styles[maxStyle] = maxVal;\n\n return styles;\n };\n}\n"],"mappings":";;;;AAIA,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;;;;AAKzB,SAAS,oBACP,KACe;AACf,KAAI,OAAO,KAAM,QAAO;AACxB,KAAI,OAAO,QAAQ,SAAU,QAAO,GAAG,IAAI;AAC3C,KAAI,QAAQ,KAAM,QAAO;AAGzB,QADkB,WAAW,OAAO,IAAI,CAAC,CACxB,OAAO,IAAI,OAAO,MAAM;;;;;;;;;;;AAkB3C,SAAgB,eAAe,MAA0B;CACvD,MAAM,WAAW,OAAO;CACxB,MAAM,WAAW,OAAO;AAExB,SAAQ,EAAE,OAAO,KAAK,UAA0B;AAC9C,MAAI,SAAS,QAAQ,OAAO,QAAQ,OAAO,KAAM,QAAO;AAExD,MACE,SAAS,QACT,OAAO,UAAU,YACjB,kBAAkB,IAAI,MAAM,EAC5B;GACA,MAAM,SAAiC;KACpC,OAAO;KACP,WAAW;KACX,WAAW;IACb;GAED,MAAM,SAAS,oBAAoB,IAAI;GACvC,MAAM,SAAS,oBAAoB,IAAI;AACvC,OAAI,OAAQ,QAAO,YAAY;AAC/B,OAAI,OAAQ,QAAO,YAAY;AAE/B,UAAO;;AAGT,MAAI,UAAU,MAAM;GAClB,MAAM,SAA4C;KAC/C,OAAO;KACP,WAAW;KACX,WAAW;IACb;GAGD,MAAM,SAAS,oBAAoB,IAAI;GACvC,MAAM,SAAS,oBAAoB,IAAI;AACvC,OAAI,OAAQ,QAAO,YAAY;AAC/B,OAAI,OAAQ,QAAO,YAAY;AAE/B,UAAO;;EAGT,MAAM,SAA4C;IAC/C,OAAO;IACP,WAAW;IACX,WAAW;GACb;AAGD,MAAI,SAAS,MAAM;GACjB,IAAI,MAAM;AACV,OAAI,OAAO,QAAQ,SACjB,OAAM,GAAG,IAAI;AAGf,SAAM,OAAO,IAAI;GAGjB,MAAM,EAAE,MAAM,WADI,WAAW,IAAI,CACE,OAAO,MAAM,kBAAkB;GAElE,IAAI,OAAO;AAEX,QAAK,MAAM,OAAO,KAChB,SAAQ,KAAR;IACE,KAAK;AACH,YAAO,YAAY,OAAO,MAAM;AAChC,YAAO;AACP;IACF,KAAK;AACH,YAAO,YAAY,OAAO,MAAM;AAChC,YAAO;AACP;IACF,KAAK,SAAS;KAEZ,MAAM,aAAa,OAAO,MAAM;AAChC,YAAO,YAAY;AACnB,YAAO,QAAQ;AACf,YAAO,YAAY;AACnB,YAAO;AACP;;IAEF,QACE;;AAIN,OAAI,CAAC,QAAQ,CAAC,KAAK,OACjB,KAAI,OAAO,WAAW,GAAG;AACvB,WAAO,YAAY,OAAO;AAC1B,WAAO,YAAY,OAAO;cACjB,OAAO,WAAW,GAAG;AAC9B,WAAO,YAAY,OAAO;AAC1B,WAAO,QAAQ,OAAO;AACtB,WAAO,YAAY,OAAO;SAE1B,QAAO,QAAQ,OAAO,MAAM;AAIhC,OAAI,OAAO,UAAU,UACnB,KAAI,SAAS,QACX,QAAO,QAAQ;IACb;IACA;IACA;IACD;OAED,QAAO,QAAQ;;EAMrB,MAAM,SAAS,oBAAoB,IAAI;EACvC,MAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,OAAQ,QAAO,YAAY;AAC/B,MAAI,OAAQ,QAAO,YAAY;AAE/B,SAAO"}