@tenphi/tasty 0.1.2 → 0.2.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.
@@ -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 | '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 '@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 '@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, @own, @(\n // A user state like @mobile should NOT be blocked\n const isReservedPrefix =\n name === '@media' ||\n name === '@root' ||\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, @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 === '@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\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 */\nexport function extractLocalPredefinedStates(\n styles: Styles,\n): Record<string, string> {\n const localStates: Record<string, string> = {};\n\n if (!styles || typeof styles !== 'object') {\n return localStates;\n }\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 === '@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 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 @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('@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 '@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\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 */\nexport function extractLocalPredefinedStates(\n styles: Styles,\n): Record<string, string> {\n const localStates: Record<string, string> = {};\n\n if (!styles || typeof styles !== 'object') {\n return localStates;\n }\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 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 +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 } =\n 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,CAErB,OAAO,MAAM,kBAAkB;GAE3C,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 { 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 +1 @@
1
- {"version":3,"file":"predefined.js","names":[],"sources":["../../src/styles/predefined.ts"],"sourcesContent":["import { isDevEnv } from '../utils/is-dev-env';\nimport type {\n RawStyleHandler,\n StyleHandler,\n StyleHandlerDefinition,\n} from '../utils/styles';\n\nimport { alignStyle } from './align';\nimport { borderStyle } from './border';\nimport { colorStyle } from './color';\nimport { createStyle } from './createStyle';\nimport { displayStyle } from './display';\nimport { fadeStyle } from './fade';\nimport { fillStyle, svgFillStyle } from './fill';\nimport { flowStyle } from './flow';\n// Note: fontStyle (font.ts) and fontStyleStyle (fontStyle.ts) removed\n// Both font and fontStyle props are now handled by presetStyle\nimport { gapStyle } from './gap';\nimport { heightStyle } from './height';\nimport { insetStyle } from './inset';\nimport { justifyStyle } from './justify';\nimport { marginStyle } from './margin';\nimport { outlineStyle } from './outline';\nimport { paddingStyle } from './padding';\nimport { presetStyle } from './preset';\nimport { radiusStyle } from './radius';\nimport { scrollbarStyle } from './scrollbar';\nimport { shadowStyle } from './shadow';\nimport { styledScrollbarStyle } from './styledScrollbar';\nimport { transitionStyle } from './transition';\nimport { widthStyle } from './width';\n\nconst devMode = isDevEnv();\n\n \nconst _numberConverter = (val) => {\n if (typeof val === 'number') {\n return `${val}px`;\n }\n\n return val;\n};\nconst columnsConverter = (val) => {\n if (typeof val === 'number') {\n return 'minmax(1px, 1fr) '.repeat(val).trim();\n }\n\n return;\n};\nconst rowsConverter = (val) => {\n if (typeof val === 'number') {\n return 'auto '.repeat(val).trim();\n }\n\n return;\n};\n\ntype StyleHandlerMap = Record<string, StyleHandler[]>;\n\nexport const STYLE_HANDLER_MAP: StyleHandlerMap = {};\n\n// Store initial handler state for reset functionality\nlet initialHandlerMapSnapshot: StyleHandlerMap | null = null;\n\n/**\n * Capture a snapshot of the current STYLE_HANDLER_MAP.\n * Called after predefine() to preserve built-in handler state.\n */\nfunction captureInitialHandlerState(): void {\n initialHandlerMapSnapshot = {};\n for (const key of Object.keys(STYLE_HANDLER_MAP)) {\n // Shallow copy the array - handlers themselves are immutable\n initialHandlerMapSnapshot[key] = [...STYLE_HANDLER_MAP[key]];\n }\n}\n\n/**\n * Reset STYLE_HANDLER_MAP to the initial built-in state.\n * Called by resetConfig() to restore handlers after tests.\n */\nexport function resetHandlers(): void {\n if (!initialHandlerMapSnapshot) {\n // predefine() hasn't been called yet, nothing to reset\n return;\n }\n\n // Clear current map\n for (const key of Object.keys(STYLE_HANDLER_MAP)) {\n delete STYLE_HANDLER_MAP[key];\n }\n\n // Restore initial state\n for (const key of Object.keys(initialHandlerMapSnapshot)) {\n STYLE_HANDLER_MAP[key] = [...initialHandlerMapSnapshot[key]];\n }\n}\n\nexport function defineCustomStyle(\n names: string[] | StyleHandler,\n handler?: RawStyleHandler,\n) {\n let handlerWithLookup: StyleHandler;\n\n if (typeof names === 'function') {\n handlerWithLookup = names;\n names = handlerWithLookup.__lookupStyles;\n } else if (handler) {\n handlerWithLookup = Object.assign(handler, { __lookupStyles: names });\n } else {\n console.warn('CubeUIKit: incorrect custom style definition: ', names);\n return;\n }\n\n if (Array.isArray(names)) {\n // just to pass type checking\n names.forEach((name) => {\n if (!STYLE_HANDLER_MAP[name]) {\n STYLE_HANDLER_MAP[name] = [];\n }\n\n STYLE_HANDLER_MAP[name].push(handlerWithLookup);\n });\n }\n}\n\ntype ConverterHandler = (\n s: string | boolean | number | undefined,\n) => string | undefined;\n\nexport function defineStyleAlias(\n styleName: string,\n cssStyleName?: string,\n converter?: ConverterHandler,\n) {\n const styleHandler = createStyle(styleName, cssStyleName, converter);\n\n if (!STYLE_HANDLER_MAP[styleName]) {\n STYLE_HANDLER_MAP[styleName] = [];\n }\n\n STYLE_HANDLER_MAP[styleName].push(styleHandler);\n}\n\nexport function predefine() {\n // Style aliases\n defineStyleAlias('gridAreas', 'grid-template-areas');\n defineStyleAlias('gridColumns', 'grid-template-columns', columnsConverter);\n defineStyleAlias('gridRows', 'grid-template-rows', rowsConverter);\n defineStyleAlias('gridTemplate', 'grid-template', (val) => {\n if (typeof val !== 'string') return;\n\n return val\n .split('/')\n .map((s, i) => (i ? columnsConverter : rowsConverter)(s))\n .join('/');\n });\n // Note: outlineOffset is now handled by outlineStyle\n\n [\n displayStyle,\n transitionStyle,\n fillStyle,\n svgFillStyle,\n widthStyle,\n marginStyle,\n gapStyle,\n flowStyle,\n colorStyle,\n heightStyle,\n radiusStyle,\n borderStyle,\n shadowStyle,\n paddingStyle,\n alignStyle,\n justifyStyle,\n presetStyle,\n outlineStyle,\n // DEPRECATED: `styledScrollbar` is deprecated, use `scrollbar` instead\n styledScrollbarStyle,\n scrollbarStyle,\n fadeStyle,\n insetStyle,\n ]\n // @ts-expect-error handler type varies across built-in style handlers\n .forEach((handler) => defineCustomStyle(handler));\n\n // Capture initial state after all built-in handlers are registered\n captureInitialHandlerState();\n\n return { STYLE_HANDLER_MAP, defineCustomStyle, defineStyleAlias };\n}\n\n// ============================================================================\n// Handler Registration API (for configure())\n// ============================================================================\n\n/**\n * Normalize a handler definition to a StyleHandler with __lookupStyles.\n * - Function only: lookup styles inferred from key name\n * - [string, fn]: single lookup style\n * - [string[], fn]: multiple lookup styles\n */\nexport function normalizeHandlerDefinition(\n keyName: string,\n definition: StyleHandlerDefinition,\n): StyleHandler {\n let handler: RawStyleHandler;\n let lookupStyles: string[];\n\n if (typeof definition === 'function') {\n // Function only - lookup styles inferred from key name\n handler = definition;\n lookupStyles = [keyName];\n } else if (Array.isArray(definition)) {\n const [first, fn] = definition;\n\n if (typeof fn !== 'function') {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'Tuple must have a function as the second element: [string, function] or [string[], function].',\n );\n }\n\n handler = fn;\n\n if (typeof first === 'string') {\n // [string, fn] - single lookup style\n lookupStyles = [first];\n } else if (Array.isArray(first)) {\n // [string[], fn] - multiple lookup styles\n lookupStyles = first;\n } else {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'First element must be a string or string array.',\n );\n }\n } else {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'Expected function, [string, function], or [string[], function].',\n );\n }\n\n // Validate handler in dev mode\n validateHandler(keyName, handler, lookupStyles);\n\n // Wrap the handler to avoid mutation issues when the same function\n // is reused for multiple handler definitions. Each registration\n // gets its own function identity with its own __lookupStyles.\n const wrappedHandler = ((props) => handler(props)) as StyleHandler;\n wrappedHandler.__lookupStyles = lookupStyles;\n\n return wrappedHandler;\n}\n\n/**\n * Validate a handler definition in development mode.\n */\nfunction validateHandler(\n name: string,\n handler: RawStyleHandler,\n lookupStyles: string[],\n): void {\n if (!devMode) return;\n\n if (typeof handler !== 'function') {\n console.warn(\n `[Tasty] Handler \"${name}\" is not a function. ` +\n 'Handlers must be functions that return CSSMap, CSSMap[], or void.',\n );\n }\n\n if (\n !lookupStyles ||\n !Array.isArray(lookupStyles) ||\n lookupStyles.length === 0\n ) {\n console.warn(\n `[Tasty] Handler \"${name}\" has invalid lookupStyles. ` +\n 'Expected non-empty array of style names.',\n );\n }\n}\n\n/**\n * Validate a handler result in development mode.\n * Call this after invoking a handler to check the return value.\n */\nexport function validateHandlerResult(name: string, result: unknown): void {\n if (!devMode) return;\n if (result === undefined || result === null) return; // void is valid\n\n // Check for empty string (migration from old pattern)\n if (result === '') {\n console.warn(\n `[Tasty] Handler \"${name}\" returned empty string. ` +\n 'Return void/undefined instead for no output.',\n );\n return;\n }\n\n // Check result is object (CSSMap or CSSMap[])\n if (typeof result !== 'object') {\n console.warn(\n `[Tasty] Handler \"${name}\" returned invalid type: ${typeof result}. ` +\n 'Expected CSSMap, CSSMap[], or void.',\n );\n }\n}\n\n/**\n * Register a custom handler, replacing any existing handlers for the same lookup styles.\n * This is called by configure() to process user-defined handlers.\n *\n * When registering a handler for style X, any existing handler that processes X\n * is removed from ALL its lookup styles to prevent double-processing.\n * For example, if gapStyle handles ['display', 'flow', 'gap'] and a new handler\n * is registered for just ['gap'], gapStyle is removed from display and flow too.\n */\nexport function registerHandler(handler: StyleHandler): void {\n const lookupStyles = handler.__lookupStyles;\n\n if (!lookupStyles || lookupStyles.length === 0) {\n if (devMode) {\n console.warn(\n '[Tasty] Cannot register handler without __lookupStyles property.',\n );\n }\n return;\n }\n\n // Find and remove existing handlers that would conflict\n // A handler conflicts if it handles any of the same styles as the new handler\n const handlersToRemove = new Set<StyleHandler>();\n\n for (const styleName of lookupStyles) {\n const existing = STYLE_HANDLER_MAP[styleName];\n if (existing) {\n for (const existingHandler of existing) {\n handlersToRemove.add(existingHandler);\n }\n }\n }\n\n // Remove conflicting handlers from ALL their lookup styles\n for (const oldHandler of handlersToRemove) {\n const oldLookupStyles = oldHandler.__lookupStyles;\n if (oldLookupStyles) {\n for (const oldStyleName of oldLookupStyles) {\n const handlers = STYLE_HANDLER_MAP[oldStyleName];\n if (handlers) {\n const filtered = handlers.filter((h) => h !== oldHandler);\n if (filtered.length === 0) {\n delete STYLE_HANDLER_MAP[oldStyleName];\n } else {\n STYLE_HANDLER_MAP[oldStyleName] = filtered;\n }\n }\n }\n }\n }\n\n // Register the new handler under its lookup styles\n for (const styleName of lookupStyles) {\n const existing = STYLE_HANDLER_MAP[styleName];\n if (existing) {\n existing.push(handler);\n } else {\n STYLE_HANDLER_MAP[styleName] = [handler];\n }\n }\n}\n\n// ============================================================================\n// Wrapped Style Handlers Export\n// ============================================================================\n\n/**\n * Create a wrapped handler that can be safely called by users.\n * The wrapper preserves __lookupStyles for proper registration.\n */\nfunction wrapHandler<T extends { __lookupStyles: string[] }>(handler: T): T {\n const fn = handler as unknown as (props: unknown) => unknown;\n const wrapped = ((props: unknown) => fn(props)) as unknown as T;\n wrapped.__lookupStyles = handler.__lookupStyles;\n return wrapped;\n}\n\n/**\n * Exported object containing wrapped predefined style handlers.\n * Users can import and call these to extend or delegate to built-in behavior.\n *\n * Internal handlers use *Style suffix for searchability.\n * External API uses short names for convenience.\n *\n * @example\n * ```ts\n * import { styleHandlers, configure } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * },\n * });\n * ```\n */\nexport const styleHandlers = {\n align: wrapHandler(alignStyle),\n border: wrapHandler(borderStyle),\n color: wrapHandler(colorStyle),\n display: wrapHandler(displayStyle),\n fade: wrapHandler(fadeStyle),\n fill: wrapHandler(fillStyle),\n svgFill: wrapHandler(svgFillStyle),\n flow: wrapHandler(flowStyle),\n gap: wrapHandler(gapStyle),\n height: wrapHandler(heightStyle),\n inset: wrapHandler(insetStyle),\n justify: wrapHandler(justifyStyle),\n margin: wrapHandler(marginStyle),\n outline: wrapHandler(outlineStyle),\n padding: wrapHandler(paddingStyle),\n preset: wrapHandler(presetStyle),\n radius: wrapHandler(radiusStyle),\n scrollbar: wrapHandler(scrollbarStyle),\n shadow: wrapHandler(shadowStyle),\n styledScrollbar: wrapHandler(styledScrollbarStyle),\n transition: wrapHandler(transitionStyle),\n width: wrapHandler(widthStyle),\n} as const;\n"],"mappings":"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"}
1
+ {"version":3,"file":"predefined.js","names":[],"sources":["../../src/styles/predefined.ts"],"sourcesContent":["import { isDevEnv } from '../utils/is-dev-env';\nimport type {\n RawStyleHandler,\n StyleHandler,\n StyleHandlerDefinition,\n} from '../utils/styles';\n\nimport { alignStyle } from './align';\nimport { borderStyle } from './border';\nimport { colorStyle } from './color';\nimport { createStyle } from './createStyle';\nimport { displayStyle } from './display';\nimport { fadeStyle } from './fade';\nimport { fillStyle, svgFillStyle } from './fill';\nimport { flowStyle } from './flow';\n// Note: fontStyle (font.ts) and fontStyleStyle (fontStyle.ts) removed\n// Both font and fontStyle props are now handled by presetStyle\nimport { gapStyle } from './gap';\nimport { heightStyle } from './height';\nimport { insetStyle } from './inset';\nimport { justifyStyle } from './justify';\nimport { marginStyle } from './margin';\nimport { outlineStyle } from './outline';\nimport { paddingStyle } from './padding';\nimport { presetStyle } from './preset';\nimport { radiusStyle } from './radius';\nimport { scrollbarStyle } from './scrollbar';\nimport { shadowStyle } from './shadow';\nimport { styledScrollbarStyle } from './styledScrollbar';\nimport { transitionStyle } from './transition';\nimport { widthStyle } from './width';\n\nconst devMode = isDevEnv();\n\nconst _numberConverter = (val) => {\n if (typeof val === 'number') {\n return `${val}px`;\n }\n\n return val;\n};\nconst columnsConverter = (val) => {\n if (typeof val === 'number') {\n return 'minmax(1px, 1fr) '.repeat(val).trim();\n }\n\n return;\n};\nconst rowsConverter = (val) => {\n if (typeof val === 'number') {\n return 'auto '.repeat(val).trim();\n }\n\n return;\n};\n\ntype StyleHandlerMap = Record<string, StyleHandler[]>;\n\nexport const STYLE_HANDLER_MAP: StyleHandlerMap = {};\n\n// Store initial handler state for reset functionality\nlet initialHandlerMapSnapshot: StyleHandlerMap | null = null;\n\n/**\n * Capture a snapshot of the current STYLE_HANDLER_MAP.\n * Called after predefine() to preserve built-in handler state.\n */\nfunction captureInitialHandlerState(): void {\n initialHandlerMapSnapshot = {};\n for (const key of Object.keys(STYLE_HANDLER_MAP)) {\n // Shallow copy the array - handlers themselves are immutable\n initialHandlerMapSnapshot[key] = [...STYLE_HANDLER_MAP[key]];\n }\n}\n\n/**\n * Reset STYLE_HANDLER_MAP to the initial built-in state.\n * Called by resetConfig() to restore handlers after tests.\n */\nexport function resetHandlers(): void {\n if (!initialHandlerMapSnapshot) {\n // predefine() hasn't been called yet, nothing to reset\n return;\n }\n\n // Clear current map\n for (const key of Object.keys(STYLE_HANDLER_MAP)) {\n delete STYLE_HANDLER_MAP[key];\n }\n\n // Restore initial state\n for (const key of Object.keys(initialHandlerMapSnapshot)) {\n STYLE_HANDLER_MAP[key] = [...initialHandlerMapSnapshot[key]];\n }\n}\n\nexport function defineCustomStyle(\n names: string[] | StyleHandler,\n handler?: RawStyleHandler,\n) {\n let handlerWithLookup: StyleHandler;\n\n if (typeof names === 'function') {\n handlerWithLookup = names;\n names = handlerWithLookup.__lookupStyles;\n } else if (handler) {\n handlerWithLookup = Object.assign(handler, { __lookupStyles: names });\n } else {\n console.warn('CubeUIKit: incorrect custom style definition: ', names);\n return;\n }\n\n if (Array.isArray(names)) {\n // just to pass type checking\n names.forEach((name) => {\n if (!STYLE_HANDLER_MAP[name]) {\n STYLE_HANDLER_MAP[name] = [];\n }\n\n STYLE_HANDLER_MAP[name].push(handlerWithLookup);\n });\n }\n}\n\ntype ConverterHandler = (\n s: string | boolean | number | undefined,\n) => string | undefined;\n\nexport function defineStyleAlias(\n styleName: string,\n cssStyleName?: string,\n converter?: ConverterHandler,\n) {\n const styleHandler = createStyle(styleName, cssStyleName, converter);\n\n if (!STYLE_HANDLER_MAP[styleName]) {\n STYLE_HANDLER_MAP[styleName] = [];\n }\n\n STYLE_HANDLER_MAP[styleName].push(styleHandler);\n}\n\nexport function predefine() {\n // Style aliases\n defineStyleAlias('gridAreas', 'grid-template-areas');\n defineStyleAlias('gridColumns', 'grid-template-columns', columnsConverter);\n defineStyleAlias('gridRows', 'grid-template-rows', rowsConverter);\n defineStyleAlias('gridTemplate', 'grid-template', (val) => {\n if (typeof val !== 'string') return;\n\n return val\n .split('/')\n .map((s, i) => (i ? columnsConverter : rowsConverter)(s))\n .join('/');\n });\n // Note: outlineOffset is now handled by outlineStyle\n\n [\n displayStyle,\n transitionStyle,\n fillStyle,\n svgFillStyle,\n widthStyle,\n marginStyle,\n gapStyle,\n flowStyle,\n colorStyle,\n heightStyle,\n radiusStyle,\n borderStyle,\n shadowStyle,\n paddingStyle,\n alignStyle,\n justifyStyle,\n presetStyle,\n outlineStyle,\n // DEPRECATED: `styledScrollbar` is deprecated, use `scrollbar` instead\n styledScrollbarStyle,\n scrollbarStyle,\n fadeStyle,\n insetStyle,\n ]\n // @ts-expect-error handler type varies across built-in style handlers\n .forEach((handler) => defineCustomStyle(handler));\n\n // Capture initial state after all built-in handlers are registered\n captureInitialHandlerState();\n\n return { STYLE_HANDLER_MAP, defineCustomStyle, defineStyleAlias };\n}\n\n// ============================================================================\n// Handler Registration API (for configure())\n// ============================================================================\n\n/**\n * Normalize a handler definition to a StyleHandler with __lookupStyles.\n * - Function only: lookup styles inferred from key name\n * - [string, fn]: single lookup style\n * - [string[], fn]: multiple lookup styles\n */\nexport function normalizeHandlerDefinition(\n keyName: string,\n definition: StyleHandlerDefinition,\n): StyleHandler {\n let handler: RawStyleHandler;\n let lookupStyles: string[];\n\n if (typeof definition === 'function') {\n // Function only - lookup styles inferred from key name\n handler = definition;\n lookupStyles = [keyName];\n } else if (Array.isArray(definition)) {\n const [first, fn] = definition;\n\n if (typeof fn !== 'function') {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'Tuple must have a function as the second element: [string, function] or [string[], function].',\n );\n }\n\n handler = fn;\n\n if (typeof first === 'string') {\n // [string, fn] - single lookup style\n lookupStyles = [first];\n } else if (Array.isArray(first)) {\n // [string[], fn] - multiple lookup styles\n lookupStyles = first;\n } else {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'First element must be a string or string array.',\n );\n }\n } else {\n throw new Error(\n `[Tasty] Invalid handler definition for \"${keyName}\". ` +\n 'Expected function, [string, function], or [string[], function].',\n );\n }\n\n // Validate handler in dev mode\n validateHandler(keyName, handler, lookupStyles);\n\n // Wrap the handler to avoid mutation issues when the same function\n // is reused for multiple handler definitions. Each registration\n // gets its own function identity with its own __lookupStyles.\n const wrappedHandler = ((props) => handler(props)) as StyleHandler;\n wrappedHandler.__lookupStyles = lookupStyles;\n\n return wrappedHandler;\n}\n\n/**\n * Validate a handler definition in development mode.\n */\nfunction validateHandler(\n name: string,\n handler: RawStyleHandler,\n lookupStyles: string[],\n): void {\n if (!devMode) return;\n\n if (typeof handler !== 'function') {\n console.warn(\n `[Tasty] Handler \"${name}\" is not a function. ` +\n 'Handlers must be functions that return CSSMap, CSSMap[], or void.',\n );\n }\n\n if (\n !lookupStyles ||\n !Array.isArray(lookupStyles) ||\n lookupStyles.length === 0\n ) {\n console.warn(\n `[Tasty] Handler \"${name}\" has invalid lookupStyles. ` +\n 'Expected non-empty array of style names.',\n );\n }\n}\n\n/**\n * Validate a handler result in development mode.\n * Call this after invoking a handler to check the return value.\n */\nexport function validateHandlerResult(name: string, result: unknown): void {\n if (!devMode) return;\n if (result === undefined || result === null) return; // void is valid\n\n // Check for empty string (migration from old pattern)\n if (result === '') {\n console.warn(\n `[Tasty] Handler \"${name}\" returned empty string. ` +\n 'Return void/undefined instead for no output.',\n );\n return;\n }\n\n // Check result is object (CSSMap or CSSMap[])\n if (typeof result !== 'object') {\n console.warn(\n `[Tasty] Handler \"${name}\" returned invalid type: ${typeof result}. ` +\n 'Expected CSSMap, CSSMap[], or void.',\n );\n }\n}\n\n/**\n * Register a custom handler, replacing any existing handlers for the same lookup styles.\n * This is called by configure() to process user-defined handlers.\n *\n * When registering a handler for style X, any existing handler that processes X\n * is removed from ALL its lookup styles to prevent double-processing.\n * For example, if gapStyle handles ['display', 'flow', 'gap'] and a new handler\n * is registered for just ['gap'], gapStyle is removed from display and flow too.\n */\nexport function registerHandler(handler: StyleHandler): void {\n const lookupStyles = handler.__lookupStyles;\n\n if (!lookupStyles || lookupStyles.length === 0) {\n if (devMode) {\n console.warn(\n '[Tasty] Cannot register handler without __lookupStyles property.',\n );\n }\n return;\n }\n\n // Find and remove existing handlers that would conflict\n // A handler conflicts if it handles any of the same styles as the new handler\n const handlersToRemove = new Set<StyleHandler>();\n\n for (const styleName of lookupStyles) {\n const existing = STYLE_HANDLER_MAP[styleName];\n if (existing) {\n for (const existingHandler of existing) {\n handlersToRemove.add(existingHandler);\n }\n }\n }\n\n // Remove conflicting handlers from ALL their lookup styles\n for (const oldHandler of handlersToRemove) {\n const oldLookupStyles = oldHandler.__lookupStyles;\n if (oldLookupStyles) {\n for (const oldStyleName of oldLookupStyles) {\n const handlers = STYLE_HANDLER_MAP[oldStyleName];\n if (handlers) {\n const filtered = handlers.filter((h) => h !== oldHandler);\n if (filtered.length === 0) {\n delete STYLE_HANDLER_MAP[oldStyleName];\n } else {\n STYLE_HANDLER_MAP[oldStyleName] = filtered;\n }\n }\n }\n }\n }\n\n // Register the new handler under its lookup styles\n for (const styleName of lookupStyles) {\n const existing = STYLE_HANDLER_MAP[styleName];\n if (existing) {\n existing.push(handler);\n } else {\n STYLE_HANDLER_MAP[styleName] = [handler];\n }\n }\n}\n\n// ============================================================================\n// Wrapped Style Handlers Export\n// ============================================================================\n\n/**\n * Create a wrapped handler that can be safely called by users.\n * The wrapper preserves __lookupStyles for proper registration.\n */\nfunction wrapHandler<T extends { __lookupStyles: string[] }>(handler: T): T {\n const fn = handler as unknown as (props: unknown) => unknown;\n const wrapped = ((props: unknown) => fn(props)) as unknown as T;\n wrapped.__lookupStyles = handler.__lookupStyles;\n return wrapped;\n}\n\n/**\n * Exported object containing wrapped predefined style handlers.\n * Users can import and call these to extend or delegate to built-in behavior.\n *\n * Internal handlers use *Style suffix for searchability.\n * External API uses short names for convenience.\n *\n * @example\n * ```ts\n * import { styleHandlers, configure } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * },\n * });\n * ```\n */\nexport const styleHandlers = {\n align: wrapHandler(alignStyle),\n border: wrapHandler(borderStyle),\n color: wrapHandler(colorStyle),\n display: wrapHandler(displayStyle),\n fade: wrapHandler(fadeStyle),\n fill: wrapHandler(fillStyle),\n svgFill: wrapHandler(svgFillStyle),\n flow: wrapHandler(flowStyle),\n gap: wrapHandler(gapStyle),\n height: wrapHandler(heightStyle),\n inset: wrapHandler(insetStyle),\n justify: wrapHandler(justifyStyle),\n margin: wrapHandler(marginStyle),\n outline: wrapHandler(outlineStyle),\n padding: wrapHandler(paddingStyle),\n preset: wrapHandler(presetStyle),\n radius: wrapHandler(radiusStyle),\n scrollbar: wrapHandler(scrollbarStyle),\n shadow: wrapHandler(shadowStyle),\n styledScrollbar: wrapHandler(styledScrollbarStyle),\n transition: wrapHandler(transitionStyle),\n width: wrapHandler(widthStyle),\n} as const;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"radius.js","names":[],"sources":["../../src/styles/radius.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { DIRECTIONS, parseStyle } from '../utils/styles';\n\nconst PROP = 'var(--radius)';\nconst SHARP = 'var(--sharp-radius)';\n\nexport function radiusStyle({ radius }) {\n if (typeof radius === 'number') {\n radius = `${radius}px`;\n }\n\n if (!radius) return;\n\n if (radius === true) radius = '1r';\n\n const processed = parseStyle(radius);\n const { mods } =\n processed.groups[0] ?? makeEmptyDetails();\n let { values } =\n processed.groups[0] ?? makeEmptyDetails();\n\n if (mods.includes('round')) {\n values = ['9999rem'];\n } else if (mods.includes('ellipse')) {\n values = ['50%'];\n } else if (!values.length) {\n values = [PROP];\n }\n\n if (mods.includes('leaf')) {\n values = [\n values[1] || SHARP,\n values[0] || PROP,\n values[1] || SHARP,\n values[0] || PROP,\n ];\n } else if (mods.includes('backleaf')) {\n values = [\n values[0] || PROP,\n values[1] || SHARP,\n values[0] || PROP,\n values[1] || SHARP,\n ];\n } else if (mods.length) {\n const arr = ['0', '0', '0', '0'];\n\n let flag = false;\n\n DIRECTIONS.forEach((dir, i) => {\n if (!mods.includes(dir)) return;\n\n flag = true;\n\n arr[i] = values[0] || PROP;\n arr[(i + 1) % 4] = values[0] || PROP;\n });\n\n if (flag) {\n values = arr;\n }\n }\n\n return {\n 'border-radius': values.join(' '),\n };\n}\n\nradiusStyle.__lookupStyles = ['radius'];\n"],"mappings":";;;;AAGA,MAAM,OAAO;AACb,MAAM,QAAQ;AAEd,SAAgB,YAAY,EAAE,UAAU;AACtC,KAAI,OAAO,WAAW,SACpB,UAAS,GAAG,OAAO;AAGrB,KAAI,CAAC,OAAQ;AAEb,KAAI,WAAW,KAAM,UAAS;CAE9B,MAAM,YAAY,WAAW,OAAO;CACpC,MAAM,EAAE,SACN,UAAU,OAAO,MAAM,kBAAkB;CAC3C,IAAI,EAAE,WACJ,UAAU,OAAO,MAAM,kBAAkB;AAE3C,KAAI,KAAK,SAAS,QAAQ,CACxB,UAAS,CAAC,UAAU;UACX,KAAK,SAAS,UAAU,CACjC,UAAS,CAAC,MAAM;UACP,CAAC,OAAO,OACjB,UAAS,CAAC,KAAK;AAGjB,KAAI,KAAK,SAAS,OAAO,CACvB,UAAS;EACP,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACd;UACQ,KAAK,SAAS,WAAW,CAClC,UAAS;EACP,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACd;UACQ,KAAK,QAAQ;EACtB,MAAM,MAAM;GAAC;GAAK;GAAK;GAAK;GAAI;EAEhC,IAAI,OAAO;AAEX,aAAW,SAAS,KAAK,MAAM;AAC7B,OAAI,CAAC,KAAK,SAAS,IAAI,CAAE;AAEzB,UAAO;AAEP,OAAI,KAAK,OAAO,MAAM;AACtB,QAAK,IAAI,KAAK,KAAK,OAAO,MAAM;IAChC;AAEF,MAAI,KACF,UAAS;;AAIb,QAAO,EACL,iBAAiB,OAAO,KAAK,IAAI,EAClC;;AAGH,YAAY,iBAAiB,CAAC,SAAS"}
1
+ {"version":3,"file":"radius.js","names":[],"sources":["../../src/styles/radius.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { DIRECTIONS, parseStyle } from '../utils/styles';\n\nconst PROP = 'var(--radius)';\nconst SHARP = 'var(--sharp-radius)';\n\nexport function radiusStyle({ radius }) {\n if (typeof radius === 'number') {\n radius = `${radius}px`;\n }\n\n if (!radius) return;\n\n if (radius === true) radius = '1r';\n\n const processed = parseStyle(radius);\n const { mods } = processed.groups[0] ?? makeEmptyDetails();\n let { values } = processed.groups[0] ?? makeEmptyDetails();\n\n if (mods.includes('round')) {\n values = ['9999rem'];\n } else if (mods.includes('ellipse')) {\n values = ['50%'];\n } else if (!values.length) {\n values = [PROP];\n }\n\n if (mods.includes('leaf')) {\n values = [\n values[1] || SHARP,\n values[0] || PROP,\n values[1] || SHARP,\n values[0] || PROP,\n ];\n } else if (mods.includes('backleaf')) {\n values = [\n values[0] || PROP,\n values[1] || SHARP,\n values[0] || PROP,\n values[1] || SHARP,\n ];\n } else if (mods.length) {\n const arr = ['0', '0', '0', '0'];\n\n let flag = false;\n\n DIRECTIONS.forEach((dir, i) => {\n if (!mods.includes(dir)) return;\n\n flag = true;\n\n arr[i] = values[0] || PROP;\n arr[(i + 1) % 4] = values[0] || PROP;\n });\n\n if (flag) {\n values = arr;\n }\n }\n\n return {\n 'border-radius': values.join(' '),\n };\n}\n\nradiusStyle.__lookupStyles = ['radius'];\n"],"mappings":";;;;AAGA,MAAM,OAAO;AACb,MAAM,QAAQ;AAEd,SAAgB,YAAY,EAAE,UAAU;AACtC,KAAI,OAAO,WAAW,SACpB,UAAS,GAAG,OAAO;AAGrB,KAAI,CAAC,OAAQ;AAEb,KAAI,WAAW,KAAM,UAAS;CAE9B,MAAM,YAAY,WAAW,OAAO;CACpC,MAAM,EAAE,SAAS,UAAU,OAAO,MAAM,kBAAkB;CAC1D,IAAI,EAAE,WAAW,UAAU,OAAO,MAAM,kBAAkB;AAE1D,KAAI,KAAK,SAAS,QAAQ,CACxB,UAAS,CAAC,UAAU;UACX,KAAK,SAAS,UAAU,CACjC,UAAS,CAAC,MAAM;UACP,CAAC,OAAO,OACjB,UAAS,CAAC,KAAK;AAGjB,KAAI,KAAK,SAAS,OAAO,CACvB,UAAS;EACP,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACd;UACQ,KAAK,SAAS,WAAW,CAClC,UAAS;EACP,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACb,OAAO,MAAM;EACd;UACQ,KAAK,QAAQ;EACtB,MAAM,MAAM;GAAC;GAAK;GAAK;GAAK;GAAI;EAEhC,IAAI,OAAO;AAEX,aAAW,SAAS,KAAK,MAAM;AAC7B,OAAI,CAAC,KAAK,SAAS,IAAI,CAAE;AAEzB,UAAO;AAEP,OAAI,KAAK,OAAO,MAAM;AACtB,QAAK,IAAI,KAAK,KAAK,OAAO,MAAM;IAChC;AAEF,MAAI,KACF,UAAS;;AAIb,QAAO,EACL,iBAAiB,OAAO,KAAK,IAAI,EAClC;;AAGH,YAAY,iBAAiB,CAAC,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"scrollbar.js","names":[],"sources":["../../src/styles/scrollbar.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\ninterface ScrollbarStyleProps {\n scrollbar?: string | boolean | number;\n overflow?: string;\n}\n\n/**\n * Creates cross-browser compatible scrollbar styles\n *\n * Supports both Firefox (scrollbar-width, scrollbar-color) and\n * WebKit/Chromium browsers (::-webkit-scrollbar)\n */\nexport function scrollbarStyle({ scrollbar, overflow }: ScrollbarStyleProps) {\n // Check if scrollbar is defined\n if (!scrollbar && scrollbar !== 0) return;\n\n // Support true as alias for thin\n const value = scrollbar === true || scrollbar === '' ? 'thin' : scrollbar;\n const processed = parseStyle(String(value));\n const { mods, colors, values } =\n processed.groups[0] ?? makeEmptyDetails();\n const style = {};\n\n // Default colors for scrollbar\n const defaultThumbColor = 'var(--scrollbar-thumb-color)';\n const defaultTrackColor = 'var(--scrollbar-track-color, transparent)';\n\n // Setup default Firefox scrollbar style\n style['scrollbar-color'] = `${defaultThumbColor} transparent`;\n\n // Default scrollbar size\n const defaultSize = '8px';\n const sizeValue = values[0] || defaultSize;\n\n // Process modifiers\n if (mods.includes('thin')) {\n style['scrollbar-width'] = 'thin';\n } else if (mods.includes('none')) {\n style['scrollbar-width'] = 'none';\n // Remove scrollbar-color as it's not needed when scrollbar is hidden\n delete style['scrollbar-color'];\n // Also hide WebKit scrollbars\n style['&::-webkit-scrollbar'] = {\n width: '0px',\n height: '0px',\n display: 'none',\n };\n\n return style;\n } else if (mods.includes('auto')) {\n style['scrollbar-width'] = 'auto';\n }\n\n // Handle scrollbar gutter behavior\n if (mods.includes('stable') || mods.includes('both-edges')) {\n // scrollbar-gutter is supported in newer browsers only\n style['scrollbar-gutter'] = mods.includes('both-edges')\n ? 'stable both-edges'\n : 'stable';\n }\n\n // Custom size setup for WebKit\n if (sizeValue) {\n style['&::-webkit-scrollbar'] = {\n ...(style['&::-webkit-scrollbar'] || {}),\n width: sizeValue,\n height: sizeValue,\n };\n }\n\n // Extract colors (support up to 3: thumb, track, corner)\n // These will be used in various places throughout the function\n const thumbColor = colors && colors[0] ? colors[0] : defaultThumbColor;\n const trackColor = colors && colors[1] ? colors[1] : defaultTrackColor;\n const cornerColor = colors && colors[2] ? colors[2] : trackColor;\n\n // Apply colors if they are specified\n if (colors && colors.length) {\n // Firefox\n style['scrollbar-color'] = `${thumbColor} ${trackColor}`;\n\n // WebKit - always set these for consistency\n if (!style['&::-webkit-scrollbar']) {\n style['&::-webkit-scrollbar'] = {};\n }\n style['&::-webkit-scrollbar']['background'] = trackColor;\n\n style['&::-webkit-scrollbar-track'] = {\n ...(style['&::-webkit-scrollbar-track'] || {}),\n background: trackColor,\n };\n\n style['&::-webkit-scrollbar-thumb'] = {\n ...(style['&::-webkit-scrollbar-thumb'] || {}),\n background: thumbColor,\n };\n\n style['&::-webkit-scrollbar-corner'] = {\n ...(style['&::-webkit-scrollbar-corner'] || {}),\n background: cornerColor,\n };\n }\n\n // Handle 'always' mode: force scrollbars to show\n if (mods.includes('always')) {\n style['overflow'] = overflow || 'scroll';\n\n // Use auto for WebKit browsers since they don't support 'always'\n // This is closer to the expected behavior\n if (!style['scrollbar-gutter']) {\n style['scrollbar-gutter'] = 'stable';\n }\n\n // Ensure scrollbars appear in WebKit even with little content\n if (!style['&::-webkit-scrollbar']) {\n style['&::-webkit-scrollbar'] = {};\n }\n style['&::-webkit-scrollbar']['display'] = 'block';\n }\n\n // Enhanced 'styled' mode with better transitions and appearance\n if (mods.includes('styled')) {\n const baseTransition = [\n 'background var(--transition)',\n 'border-radius var(--transition)',\n 'box-shadow var(--transition)',\n 'width var(--transition)',\n 'height var(--transition)',\n 'border var(--transition)',\n ].join(', ');\n\n // Firefox\n style['scrollbar-width'] = style['scrollbar-width'] || 'thin';\n style['scrollbar-color'] =\n style['scrollbar-color'] || `${defaultThumbColor} ${defaultTrackColor}`;\n\n // WebKit\n style['&::-webkit-scrollbar'] = {\n width: sizeValue,\n height: sizeValue,\n transition: baseTransition,\n background: defaultTrackColor,\n ...(style['&::-webkit-scrollbar'] || {}),\n };\n\n style['&::-webkit-scrollbar-thumb'] = {\n 'border-radius': '8px',\n 'min-height': '24px',\n transition: baseTransition,\n background: defaultThumbColor,\n ...(style['&::-webkit-scrollbar-thumb'] || {}),\n };\n\n style['&::-webkit-scrollbar-track'] = {\n background: defaultTrackColor,\n transition: baseTransition,\n ...(style['&::-webkit-scrollbar-track'] || {}),\n };\n\n style['&::-webkit-scrollbar-corner'] = {\n background: defaultTrackColor,\n transition: baseTransition,\n ...(style['&::-webkit-scrollbar-corner'] || {}),\n };\n }\n\n return style;\n}\n\nscrollbarStyle.__lookupStyles = ['scrollbar', 'overflow'];\n"],"mappings":";;;;;;;;;;AAcA,SAAgB,eAAe,EAAE,WAAW,YAAiC;AAE3E,KAAI,CAAC,aAAa,cAAc,EAAG;CAGnC,MAAM,QAAQ,cAAc,QAAQ,cAAc,KAAK,SAAS;CAEhE,MAAM,EAAE,MAAM,QAAQ,WADJ,WAAW,OAAO,MAAM,CAAC,CAE/B,OAAO,MAAM,kBAAkB;CAC3C,MAAM,QAAQ,EAAE;CAGhB,MAAM,oBAAoB;CAC1B,MAAM,oBAAoB;AAG1B,OAAM,qBAAqB,GAAG,kBAAkB;CAIhD,MAAM,YAAY,OAAO,MADL;AAIpB,KAAI,KAAK,SAAS,OAAO,CACvB,OAAM,qBAAqB;UAClB,KAAK,SAAS,OAAO,EAAE;AAChC,QAAM,qBAAqB;AAE3B,SAAO,MAAM;AAEb,QAAM,0BAA0B;GAC9B,OAAO;GACP,QAAQ;GACR,SAAS;GACV;AAED,SAAO;YACE,KAAK,SAAS,OAAO,CAC9B,OAAM,qBAAqB;AAI7B,KAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,aAAa,CAExD,OAAM,sBAAsB,KAAK,SAAS,aAAa,GACnD,sBACA;AAIN,KAAI,UACF,OAAM,0BAA0B;EAC9B,GAAI,MAAM,2BAA2B,EAAE;EACvC,OAAO;EACP,QAAQ;EACT;CAKH,MAAM,aAAa,UAAU,OAAO,KAAK,OAAO,KAAK;CACrD,MAAM,aAAa,UAAU,OAAO,KAAK,OAAO,KAAK;CACrD,MAAM,cAAc,UAAU,OAAO,KAAK,OAAO,KAAK;AAGtD,KAAI,UAAU,OAAO,QAAQ;AAE3B,QAAM,qBAAqB,GAAG,WAAW,GAAG;AAG5C,MAAI,CAAC,MAAM,wBACT,OAAM,0BAA0B,EAAE;AAEpC,QAAM,wBAAwB,gBAAgB;AAE9C,QAAM,gCAAgC;GACpC,GAAI,MAAM,iCAAiC,EAAE;GAC7C,YAAY;GACb;AAED,QAAM,gCAAgC;GACpC,GAAI,MAAM,iCAAiC,EAAE;GAC7C,YAAY;GACb;AAED,QAAM,iCAAiC;GACrC,GAAI,MAAM,kCAAkC,EAAE;GAC9C,YAAY;GACb;;AAIH,KAAI,KAAK,SAAS,SAAS,EAAE;AAC3B,QAAM,cAAc,YAAY;AAIhC,MAAI,CAAC,MAAM,oBACT,OAAM,sBAAsB;AAI9B,MAAI,CAAC,MAAM,wBACT,OAAM,0BAA0B,EAAE;AAEpC,QAAM,wBAAwB,aAAa;;AAI7C,KAAI,KAAK,SAAS,SAAS,EAAE;EAC3B,MAAM,iBAAiB;GACrB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;AAGZ,QAAM,qBAAqB,MAAM,sBAAsB;AACvD,QAAM,qBACJ,MAAM,sBAAsB,GAAG,kBAAkB,GAAG;AAGtD,QAAM,0BAA0B;GAC9B,OAAO;GACP,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,2BAA2B,EAAE;GACxC;AAED,QAAM,gCAAgC;GACpC,iBAAiB;GACjB,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,iCAAiC,EAAE;GAC9C;AAED,QAAM,gCAAgC;GACpC,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,iCAAiC,EAAE;GAC9C;AAED,QAAM,iCAAiC;GACrC,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,kCAAkC,EAAE;GAC/C;;AAGH,QAAO;;AAGT,eAAe,iBAAiB,CAAC,aAAa,WAAW"}
1
+ {"version":3,"file":"scrollbar.js","names":[],"sources":["../../src/styles/scrollbar.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\ninterface ScrollbarStyleProps {\n scrollbar?: string | boolean | number;\n overflow?: string;\n}\n\n/**\n * Creates cross-browser compatible scrollbar styles\n *\n * Supports both Firefox (scrollbar-width, scrollbar-color) and\n * WebKit/Chromium browsers (::-webkit-scrollbar)\n */\nexport function scrollbarStyle({ scrollbar, overflow }: ScrollbarStyleProps) {\n // Check if scrollbar is defined\n if (!scrollbar && scrollbar !== 0) return;\n\n // Support true as alias for thin\n const value = scrollbar === true || scrollbar === '' ? 'thin' : scrollbar;\n const processed = parseStyle(String(value));\n const { mods, colors, values } = processed.groups[0] ?? makeEmptyDetails();\n const style = {};\n\n // Default colors for scrollbar\n const defaultThumbColor = 'var(--scrollbar-thumb-color)';\n const defaultTrackColor = 'var(--scrollbar-track-color, transparent)';\n\n // Setup default Firefox scrollbar style\n style['scrollbar-color'] = `${defaultThumbColor} transparent`;\n\n // Default scrollbar size\n const defaultSize = '8px';\n const sizeValue = values[0] || defaultSize;\n\n // Process modifiers\n if (mods.includes('thin')) {\n style['scrollbar-width'] = 'thin';\n } else if (mods.includes('none')) {\n style['scrollbar-width'] = 'none';\n // Remove scrollbar-color as it's not needed when scrollbar is hidden\n delete style['scrollbar-color'];\n // Also hide WebKit scrollbars\n style['&::-webkit-scrollbar'] = {\n width: '0px',\n height: '0px',\n display: 'none',\n };\n\n return style;\n } else if (mods.includes('auto')) {\n style['scrollbar-width'] = 'auto';\n }\n\n // Handle scrollbar gutter behavior\n if (mods.includes('stable') || mods.includes('both-edges')) {\n // scrollbar-gutter is supported in newer browsers only\n style['scrollbar-gutter'] = mods.includes('both-edges')\n ? 'stable both-edges'\n : 'stable';\n }\n\n // Custom size setup for WebKit\n if (sizeValue) {\n style['&::-webkit-scrollbar'] = {\n ...(style['&::-webkit-scrollbar'] || {}),\n width: sizeValue,\n height: sizeValue,\n };\n }\n\n // Extract colors (support up to 3: thumb, track, corner)\n // These will be used in various places throughout the function\n const thumbColor = colors && colors[0] ? colors[0] : defaultThumbColor;\n const trackColor = colors && colors[1] ? colors[1] : defaultTrackColor;\n const cornerColor = colors && colors[2] ? colors[2] : trackColor;\n\n // Apply colors if they are specified\n if (colors && colors.length) {\n // Firefox\n style['scrollbar-color'] = `${thumbColor} ${trackColor}`;\n\n // WebKit - always set these for consistency\n if (!style['&::-webkit-scrollbar']) {\n style['&::-webkit-scrollbar'] = {};\n }\n style['&::-webkit-scrollbar']['background'] = trackColor;\n\n style['&::-webkit-scrollbar-track'] = {\n ...(style['&::-webkit-scrollbar-track'] || {}),\n background: trackColor,\n };\n\n style['&::-webkit-scrollbar-thumb'] = {\n ...(style['&::-webkit-scrollbar-thumb'] || {}),\n background: thumbColor,\n };\n\n style['&::-webkit-scrollbar-corner'] = {\n ...(style['&::-webkit-scrollbar-corner'] || {}),\n background: cornerColor,\n };\n }\n\n // Handle 'always' mode: force scrollbars to show\n if (mods.includes('always')) {\n style['overflow'] = overflow || 'scroll';\n\n // Use auto for WebKit browsers since they don't support 'always'\n // This is closer to the expected behavior\n if (!style['scrollbar-gutter']) {\n style['scrollbar-gutter'] = 'stable';\n }\n\n // Ensure scrollbars appear in WebKit even with little content\n if (!style['&::-webkit-scrollbar']) {\n style['&::-webkit-scrollbar'] = {};\n }\n style['&::-webkit-scrollbar']['display'] = 'block';\n }\n\n // Enhanced 'styled' mode with better transitions and appearance\n if (mods.includes('styled')) {\n const baseTransition = [\n 'background var(--transition)',\n 'border-radius var(--transition)',\n 'box-shadow var(--transition)',\n 'width var(--transition)',\n 'height var(--transition)',\n 'border var(--transition)',\n ].join(', ');\n\n // Firefox\n style['scrollbar-width'] = style['scrollbar-width'] || 'thin';\n style['scrollbar-color'] =\n style['scrollbar-color'] || `${defaultThumbColor} ${defaultTrackColor}`;\n\n // WebKit\n style['&::-webkit-scrollbar'] = {\n width: sizeValue,\n height: sizeValue,\n transition: baseTransition,\n background: defaultTrackColor,\n ...(style['&::-webkit-scrollbar'] || {}),\n };\n\n style['&::-webkit-scrollbar-thumb'] = {\n 'border-radius': '8px',\n 'min-height': '24px',\n transition: baseTransition,\n background: defaultThumbColor,\n ...(style['&::-webkit-scrollbar-thumb'] || {}),\n };\n\n style['&::-webkit-scrollbar-track'] = {\n background: defaultTrackColor,\n transition: baseTransition,\n ...(style['&::-webkit-scrollbar-track'] || {}),\n };\n\n style['&::-webkit-scrollbar-corner'] = {\n background: defaultTrackColor,\n transition: baseTransition,\n ...(style['&::-webkit-scrollbar-corner'] || {}),\n };\n }\n\n return style;\n}\n\nscrollbarStyle.__lookupStyles = ['scrollbar', 'overflow'];\n"],"mappings":";;;;;;;;;;AAcA,SAAgB,eAAe,EAAE,WAAW,YAAiC;AAE3E,KAAI,CAAC,aAAa,cAAc,EAAG;CAGnC,MAAM,QAAQ,cAAc,QAAQ,cAAc,KAAK,SAAS;CAEhE,MAAM,EAAE,MAAM,QAAQ,WADJ,WAAW,OAAO,MAAM,CAAC,CACA,OAAO,MAAM,kBAAkB;CAC1E,MAAM,QAAQ,EAAE;CAGhB,MAAM,oBAAoB;CAC1B,MAAM,oBAAoB;AAG1B,OAAM,qBAAqB,GAAG,kBAAkB;CAIhD,MAAM,YAAY,OAAO,MADL;AAIpB,KAAI,KAAK,SAAS,OAAO,CACvB,OAAM,qBAAqB;UAClB,KAAK,SAAS,OAAO,EAAE;AAChC,QAAM,qBAAqB;AAE3B,SAAO,MAAM;AAEb,QAAM,0BAA0B;GAC9B,OAAO;GACP,QAAQ;GACR,SAAS;GACV;AAED,SAAO;YACE,KAAK,SAAS,OAAO,CAC9B,OAAM,qBAAqB;AAI7B,KAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,aAAa,CAExD,OAAM,sBAAsB,KAAK,SAAS,aAAa,GACnD,sBACA;AAIN,KAAI,UACF,OAAM,0BAA0B;EAC9B,GAAI,MAAM,2BAA2B,EAAE;EACvC,OAAO;EACP,QAAQ;EACT;CAKH,MAAM,aAAa,UAAU,OAAO,KAAK,OAAO,KAAK;CACrD,MAAM,aAAa,UAAU,OAAO,KAAK,OAAO,KAAK;CACrD,MAAM,cAAc,UAAU,OAAO,KAAK,OAAO,KAAK;AAGtD,KAAI,UAAU,OAAO,QAAQ;AAE3B,QAAM,qBAAqB,GAAG,WAAW,GAAG;AAG5C,MAAI,CAAC,MAAM,wBACT,OAAM,0BAA0B,EAAE;AAEpC,QAAM,wBAAwB,gBAAgB;AAE9C,QAAM,gCAAgC;GACpC,GAAI,MAAM,iCAAiC,EAAE;GAC7C,YAAY;GACb;AAED,QAAM,gCAAgC;GACpC,GAAI,MAAM,iCAAiC,EAAE;GAC7C,YAAY;GACb;AAED,QAAM,iCAAiC;GACrC,GAAI,MAAM,kCAAkC,EAAE;GAC9C,YAAY;GACb;;AAIH,KAAI,KAAK,SAAS,SAAS,EAAE;AAC3B,QAAM,cAAc,YAAY;AAIhC,MAAI,CAAC,MAAM,oBACT,OAAM,sBAAsB;AAI9B,MAAI,CAAC,MAAM,wBACT,OAAM,0BAA0B,EAAE;AAEpC,QAAM,wBAAwB,aAAa;;AAI7C,KAAI,KAAK,SAAS,SAAS,EAAE;EAC3B,MAAM,iBAAiB;GACrB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;AAGZ,QAAM,qBAAqB,MAAM,sBAAsB;AACvD,QAAM,qBACJ,MAAM,sBAAsB,GAAG,kBAAkB,GAAG;AAGtD,QAAM,0BAA0B;GAC9B,OAAO;GACP,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,2BAA2B,EAAE;GACxC;AAED,QAAM,gCAAgC;GACpC,iBAAiB;GACjB,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,iCAAiC,EAAE;GAC9C;AAED,QAAM,gCAAgC;GACpC,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,iCAAiC,EAAE;GAC9C;AAED,QAAM,iCAAiC;GACrC,YAAY;GACZ,YAAY;GACZ,GAAI,MAAM,kCAAkC,EAAE;GAC/C;;AAGH,QAAO;;AAGT,eAAe,iBAAiB,CAAC,aAAa,WAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"shadow.js","names":[],"sources":["../../src/styles/shadow.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\nfunction toBoxShadow(shadow) {\n const processed = parseStyle(shadow);\n const { values, mods, colors } =\n processed.groups[0] ?? makeEmptyDetails();\n const mod = mods[0] || '';\n const shadowColor = (colors && colors[0]) ?? '';\n\n return [mod, ...values, shadowColor].join(' ');\n}\n\nexport function shadowStyle({ shadow }) {\n if (!shadow) return;\n\n if (shadow === true) shadow = 'var(--shadow)';\n\n return {\n 'box-shadow': shadow.split(',').map(toBoxShadow).join(','),\n };\n}\n\nshadowStyle.__lookupStyles = ['shadow'];\n"],"mappings":";;;;AAGA,SAAS,YAAY,QAAQ;CAE3B,MAAM,EAAE,QAAQ,MAAM,WADJ,WAAW,OAAO,CAExB,OAAO,MAAM,kBAAkB;CAC3C,MAAM,MAAM,KAAK,MAAM;CACvB,MAAM,eAAe,UAAU,OAAO,OAAO;AAE7C,QAAO;EAAC;EAAK,GAAG;EAAQ;EAAY,CAAC,KAAK,IAAI;;AAGhD,SAAgB,YAAY,EAAE,UAAU;AACtC,KAAI,CAAC,OAAQ;AAEb,KAAI,WAAW,KAAM,UAAS;AAE9B,QAAO,EACL,cAAc,OAAO,MAAM,IAAI,CAAC,IAAI,YAAY,CAAC,KAAK,IAAI,EAC3D;;AAGH,YAAY,iBAAiB,CAAC,SAAS"}
1
+ {"version":3,"file":"shadow.js","names":[],"sources":["../../src/styles/shadow.ts"],"sourcesContent":["import { makeEmptyDetails } from '../parser/types';\nimport { parseStyle } from '../utils/styles';\n\nfunction toBoxShadow(shadow) {\n const processed = parseStyle(shadow);\n const { values, mods, colors } = processed.groups[0] ?? makeEmptyDetails();\n const mod = mods[0] || '';\n const shadowColor = (colors && colors[0]) ?? '';\n\n return [mod, ...values, shadowColor].join(' ');\n}\n\nexport function shadowStyle({ shadow }) {\n if (!shadow) return;\n\n if (shadow === true) shadow = 'var(--shadow)';\n\n return {\n 'box-shadow': shadow.split(',').map(toBoxShadow).join(','),\n };\n}\n\nshadowStyle.__lookupStyles = ['shadow'];\n"],"mappings":";;;;AAGA,SAAS,YAAY,QAAQ;CAE3B,MAAM,EAAE,QAAQ,MAAM,WADJ,WAAW,OAAO,CACO,OAAO,MAAM,kBAAkB;CAC1E,MAAM,MAAM,KAAK,MAAM;CACvB,MAAM,eAAe,UAAU,OAAO,OAAO;AAE7C,QAAO;EAAC;EAAK,GAAG;EAAQ;EAAY,CAAC,KAAK,IAAI;;AAGhD,SAAgB,YAAY,EAAE,UAAU;AACtC,KAAI,CAAC,OAAQ;AAEb,KAAI,WAAW,KAAM,UAAS;AAE9B,QAAO,EACL,cAAc,OAAO,MAAM,IAAI,CAAC,IAAI,YAAY,CAAC,KAAK,IAAI,EAC3D;;AAGH,YAAY,iBAAiB,CAAC,SAAS"}
package/dist/tasty.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tasty.js","names":["modAttrs"],"sources":["../src/tasty.tsx"],"sourcesContent":["import type {\n AllHTMLAttributes,\n ComponentType,\n ForwardRefExoticComponent,\n JSX,\n PropsWithoutRef,\n RefAttributes} from 'react';\nimport {\n createElement,\n forwardRef,\n useMemo,\n} from 'react';\nimport { isValidElementType } from 'react-is';\n\nimport { useStyles } from './hooks/useStyles';\nimport { BASE_STYLES } from './styles/list';\nimport type { Styles, StylesInterface } from './styles/types';\nimport type {\n AllBaseProps,\n BaseProps,\n BaseStyleProps,\n Mods,\n Props,\n Tokens,\n} from './types';\nimport { getDisplayName } from './utils/get-display-name';\nimport { mergeStyles } from './utils/merge-styles';\nimport { modAttrs } from './utils/mod-attrs';\nimport { processTokens, stringifyTokens } from './utils/process-tokens';\n\nimport type { StyleValue, StyleValueStateMap } from './utils/styles';\n\n/**\n * Mapping of is* properties to their corresponding HTML attributes\n */\nconst IS_PROPERTIES_MAP = {\n isDisabled: 'disabled',\n isHidden: 'hidden',\n isChecked: 'checked',\n} as const;\n\n/**\n * Precalculated entries for performance optimization\n */\nconst IS_PROPERTIES_ENTRIES = Object.entries(IS_PROPERTIES_MAP);\n\n/**\n * Helper function to handle is* properties consistently\n * Transforms is* props to HTML attributes and adds corresponding data-* attributes\n */\nfunction handleIsProperties(props: Record<string, unknown>) {\n for (const [isProperty, targetAttribute] of IS_PROPERTIES_ENTRIES) {\n if (isProperty in props) {\n props[targetAttribute] = props[isProperty];\n delete props[isProperty];\n }\n\n // Add data-* attribute if target attribute is truthy and doesn't already exist\n const dataAttribute = `data-${targetAttribute}`;\n if (!(dataAttribute in props) && props[targetAttribute]) {\n props[dataAttribute] = '';\n }\n }\n}\n\n/**\n * Creates a sub-element component for compound component patterns.\n * Sub-elements are lightweight components with data-element attribute for CSS targeting.\n */\nfunction createSubElement<Tag extends keyof JSX.IntrinsicElements>(\n elementName: string,\n definition: SubElementDefinition<Tag>,\n): ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<Tag>> & RefAttributes<unknown>\n> {\n // Normalize definition to object form\n const config =\n typeof definition === 'string'\n ? { as: definition as Tag }\n : (definition as { as?: Tag; qa?: string; qaVal?: string | number });\n\n const tag = config.as ?? ('div' as Tag);\n const defaultQa = config.qa;\n const defaultQaVal = config.qaVal;\n\n const SubElement = forwardRef<unknown, SubElementProps<Tag>>((props, ref) => {\n const {\n qa,\n qaVal,\n mods,\n tokens,\n isDisabled,\n isHidden,\n isChecked,\n className,\n style,\n ...htmlProps\n } = props as SubElementProps<Tag> & {\n className?: string;\n style?: Record<string, unknown>;\n };\n\n // Build mod attributes\n let modProps: Record<string, unknown> | undefined;\n if (mods) {\n modProps = modAttrs(mods as Mods) as Record<string, unknown>;\n }\n\n // Process tokens into inline style properties\n const tokenStyle = tokens\n ? (processTokens(tokens) as Record<string, unknown>)\n : undefined;\n\n // Merge token styles with explicit style prop (style has priority)\n let mergedStyle: Record<string, unknown> | undefined;\n if (tokenStyle || style) {\n mergedStyle =\n tokenStyle && style\n ? { ...tokenStyle, ...style }\n : ((tokenStyle ?? style) as Record<string, unknown>);\n }\n\n const elementProps = {\n 'data-element': elementName,\n 'data-qa': qa ?? defaultQa,\n 'data-qaval': qaVal ?? defaultQaVal,\n ...(modProps || {}),\n ...htmlProps,\n className,\n style: mergedStyle,\n isDisabled,\n isHidden,\n isChecked,\n ref,\n } as Record<string, unknown>;\n\n // Handle is* properties (isDisabled -> disabled + data-disabled, etc.)\n handleIsProperties(elementProps);\n\n // Clean up undefined data attributes\n if (elementProps['data-qa'] === undefined) delete elementProps['data-qa'];\n if (elementProps['data-qaval'] === undefined)\n delete elementProps['data-qaval'];\n\n return createElement(tag, elementProps);\n });\n\n SubElement.displayName = `SubElement(${elementName})`;\n\n return SubElement as ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<Tag>> & RefAttributes<unknown>\n >;\n}\n\ntype StyleList = readonly (keyof {\n [key in keyof StylesInterface]: StylesInterface[key];\n})[];\n\nexport type PropsWithStyles = {\n styles?: Styles;\n} & Omit<Props, 'styles'>;\n\nexport type VariantMap = Record<string, Styles>;\n\nexport interface WithVariant<V extends VariantMap> {\n variant?: keyof V;\n}\n\n// ============================================================================\n// Sub-element types for compound components\n// ============================================================================\n\n/**\n * Definition for a sub-element. Can be either:\n * - A tag name string (e.g., 'div', 'span')\n * - An object with configuration options\n */\nexport type SubElementDefinition<\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> =\n | Tag\n | {\n as?: Tag;\n qa?: string;\n qaVal?: string | number;\n };\n\n/**\n * Map of sub-element definitions.\n * Keys become the sub-component names (e.g., { Icon: 'span' } -> Component.Icon)\n */\nexport type ElementsDefinition = Record<\n string,\n SubElementDefinition<keyof JSX.IntrinsicElements>\n>;\n\n/**\n * Resolves the tag from a SubElementDefinition\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ResolveElementTag<T extends SubElementDefinition<any>> = T extends string\n ? T\n : T extends { as?: infer Tag }\n ? Tag extends keyof JSX.IntrinsicElements\n ? Tag\n : 'div'\n : 'div';\n\n/**\n * Props for sub-element components.\n * Combines HTML attributes with tasty-specific props (qa, qaVal, mods, tokens, isDisabled, etc.)\n */\nexport type SubElementProps<Tag extends keyof JSX.IntrinsicElements = 'div'> =\n Omit<\n JSX.IntrinsicElements[Tag],\n 'ref' | 'color' | 'content' | 'translate'\n > & {\n qa?: string;\n qaVal?: string | number;\n mods?: Mods;\n tokens?: Tokens;\n isDisabled?: boolean;\n isHidden?: boolean;\n isChecked?: boolean;\n };\n\n/**\n * Generates the sub-element component types from an ElementsDefinition\n */\ntype SubElementComponents<E extends ElementsDefinition> = {\n [K in keyof E]: ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<ResolveElementTag<E[K]>>> &\n RefAttributes<\n ResolveElementTag<E[K]> extends keyof HTMLElementTagNameMap\n ? HTMLElementTagNameMap[ResolveElementTag<E[K]>]\n : Element\n >\n >;\n};\n\n/**\n * Base type containing common properties shared between TastyProps and TastyElementOptions.\n * Separated to avoid code duplication while allowing different type constraints.\n */\ntype TastyBaseProps<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n> = {\n /** Default styles of the element. */\n styles?: Styles;\n /** The list of styles that can be provided by props */\n styleProps?: K;\n element?: BaseProps['element'];\n variants?: V;\n /** Default tokens for inline CSS custom properties */\n tokens?: Tokens;\n /** Sub-element definitions for compound components */\n elements?: E;\n} & Pick<BaseProps, 'qa' | 'qaVal'> &\n WithVariant<V>;\n\nexport type TastyProps<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n DefaultProps = Props,\n> = TastyBaseProps<K, V, E> & {\n /** The tag name of the element or a React component. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n as?: string | ComponentType<any>;\n} & Partial<Omit<DefaultProps, 'as' | 'styles' | 'styleProps' | 'tokens'>>;\n\n/**\n * TastyElementOptions is used for the element-creation overload of tasty().\n * It includes a Tag generic that allows TypeScript to infer the correct\n * HTML element type from the `as` prop.\n *\n * Note: Uses a separate index signature with `unknown` instead of inheriting\n * from Props (which has `any`) to ensure strict type checking for styles.\n */\nexport type TastyElementOptions<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> = TastyBaseProps<K, V, E> & {\n /** The tag name of the element or a React component. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n as?: Tag | ComponentType<any>;\n} & Record<string, unknown>;\n\nexport type AllBasePropsWithMods<K extends StyleList> = AllBaseProps & {\n [key in K[number]]?:\n | StyleValue<StylesInterface[key]>\n | StyleValueStateMap<StylesInterface[key]>;\n} & BaseStyleProps;\n\n/**\n * Keys from BasePropsWithoutChildren that should be omitted from HTML attributes.\n * This excludes event handlers so they can be properly typed from JSX.IntrinsicElements.\n */\ntype TastySpecificKeys =\n | 'as'\n | 'qa'\n | 'qaVal'\n | 'element'\n | 'styles'\n | 'breakpoints'\n | 'block'\n | 'inline'\n | 'mods'\n | 'isHidden'\n | 'isDisabled'\n | 'css'\n | 'style'\n | 'theme'\n | 'tokens'\n | 'ref'\n | 'color';\n\n/**\n * Props type for tasty elements that combines:\n * - AllBasePropsWithMods for style props with strict tokens type\n * - HTML attributes for flexibility (properly typed based on tag)\n * - Variant support\n *\n * Uses AllHTMLAttributes as base for common attributes (like disabled),\n * but overrides event handlers with tag-specific types from JSX.IntrinsicElements.\n */\nexport type TastyElementProps<\n K extends StyleList,\n V extends VariantMap,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> = AllBasePropsWithMods<K> &\n WithVariant<V> &\n Omit<\n Omit<AllHTMLAttributes<HTMLElement>, keyof JSX.IntrinsicElements[Tag]> &\n JSX.IntrinsicElements[Tag],\n TastySpecificKeys | K[number]\n >;\n\ntype TastyComponentPropsWithDefaults<\n Props extends PropsWithStyles,\n DefaultProps extends Partial<Props>,\n> = keyof DefaultProps extends never\n ? Props\n : {\n [key in Extract<keyof Props, keyof DefaultProps>]?: Props[key];\n } & {\n [key in keyof Omit<Props, keyof DefaultProps>]: Props[key];\n };\n\nexport function tasty<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n>(\n options: TastyElementOptions<K, V, E, Tag>,\n secondArg?: never,\n): ForwardRefExoticComponent<\n PropsWithoutRef<TastyElementProps<K, V, Tag>> & RefAttributes<unknown>\n> &\n SubElementComponents<E>;\nexport function tasty<\n Props extends PropsWithStyles,\n DefaultProps extends Partial<Props> = Partial<Props>,\n>(\n Component: ComponentType<Props>,\n options?: TastyProps<never, never, Record<string, never>, Props>,\n): ComponentType<TastyComponentPropsWithDefaults<Props, DefaultProps>>;\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// Implementation\nexport function tasty<\n K extends StyleList,\n V extends VariantMap,\n \n _C = Record<string, unknown>,\n>(Component: any, options?: any) {\n if (isValidElementType(Component)) {\n return tastyWrap(Component as ComponentType<any>, options);\n }\n\n return tastyElement(Component as TastyProps<K, V>);\n}\n\nfunction tastyWrap<\n P extends PropsWithStyles,\n DefaultProps extends Partial<P> = Partial<P>,\n>(\n Component: ComponentType<P>,\n options?: TastyProps<never, never, P>,\n): ComponentType<TastyComponentPropsWithDefaults<P, DefaultProps>> {\n const {\n as: extendTag,\n element: extendElement,\n ...defaultProps\n } = (options ?? {}) as TastyProps<never, never, P>;\n\n const propsWithStyles = ['styles'].concat(\n Object.keys(defaultProps).filter((prop) => prop.endsWith('Styles')),\n );\n\n const _WrappedComponent = forwardRef<any, any>((props, ref) => {\n const { as, element, ...restProps } = props as Record<string, unknown>;\n const propsWithStylesValues = propsWithStyles.map(\n (prop) => (props as any)[prop],\n );\n\n const mergedStylesMap: Styles | undefined = useMemo(() => {\n return propsWithStyles.reduce((map, prop) => {\n const restValue = (restProps as any)[prop];\n const defaultValue = (defaultProps as any)[prop];\n\n if (restValue != null && defaultValue != null) {\n (map as any)[prop] = mergeStyles(defaultValue, restValue);\n } else {\n (map as any)[prop] = restValue ?? defaultValue;\n }\n\n return map;\n }, {} as Styles);\n }, [propsWithStylesValues]);\n\n const elementProps = {\n ...(defaultProps as unknown as Record<string, unknown>),\n ...(restProps as unknown as Record<string, unknown>),\n ...(mergedStylesMap as unknown as Record<string, unknown>),\n as: (as as string | undefined) ?? extendTag,\n element: (element as string | undefined) || extendElement,\n ref,\n } as unknown as P;\n\n return createElement(Component as ComponentType<P>, elementProps);\n });\n\n _WrappedComponent.displayName = `TastyWrappedComponent(${getDisplayName(\n Component,\n (defaultProps as any).qa ?? (extendTag as any) ?? 'Anonymous',\n )})`;\n\n return _WrappedComponent as unknown as ComponentType<\n TastyComponentPropsWithDefaults<P, DefaultProps>\n >;\n}\n\nfunction tastyElement<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition,\n>(tastyOptions: TastyProps<K, V, E>) {\n const {\n as: originalAs = 'div',\n element: defaultElement,\n styles: defaultStyles,\n styleProps,\n variants,\n tokens: defaultTokens,\n elements,\n ...defaultProps\n } = tastyOptions;\n\n // Pre-compute merged styles for each variant (if variants are defined)\n // This avoids creating separate component instances per variant\n let variantStylesMap: Record<string, Styles | undefined> | undefined;\n if (variants) {\n const variantEntries = Object.entries(variants) as [string, Styles][];\n variantStylesMap = variantEntries.reduce(\n (map, [variant, variantStyles]) => {\n map[variant] = mergeStyles(defaultStyles, variantStyles);\n return map;\n },\n {} as Record<string, Styles | undefined>,\n );\n // Ensure 'default' variant always exists\n if (!variantStylesMap['default']) {\n variantStylesMap['default'] = defaultStyles;\n }\n }\n\n const {\n qa: defaultQa,\n qaVal: defaultQaVal,\n ...otherDefaultProps\n } = defaultProps ?? {};\n\n const _TastyComponent = forwardRef<\n unknown,\n AllBasePropsWithMods<K> & WithVariant<V>\n >((allProps, ref) => {\n const {\n as,\n styles: rawStyles,\n variant,\n mods,\n element,\n qa,\n qaVal,\n className: userClassName,\n tokens,\n style,\n ...otherProps\n } = allProps as Record<string, unknown> as AllBasePropsWithMods<K> &\n WithVariant<V> & {\n className?: string;\n tokens?: Tokens;\n style?: Record<string, unknown>;\n };\n\n let styles = rawStyles;\n\n // Optimize propStyles extraction - avoid creating empty objects\n let propStyles: Styles | null = null;\n const propsToCheck = styleProps\n ? (styleProps as StyleList).concat(BASE_STYLES)\n : BASE_STYLES;\n\n for (const prop of propsToCheck) {\n const key = prop as unknown as string;\n\n if (!propStyles) propStyles = {};\n\n if (key in otherProps) {\n (propStyles as any)[key] = (otherProps as any)[key];\n delete (otherProps as any)[key];\n }\n }\n\n if (\n !styles ||\n (styles && Object.keys(styles as Record<string, unknown>).length === 0)\n ) {\n styles = undefined as unknown as Styles;\n }\n\n // Determine base styles: use variant styles if available, otherwise default styles\n const baseStyles = variantStylesMap\n ? variantStylesMap[(variant as string) || 'default'] ??\n variantStylesMap['default']\n : defaultStyles;\n\n // Merge base styles with instance styles and prop styles\n const allStyles = useMemo(() => {\n const hasStyles =\n styles && Object.keys(styles as Record<string, unknown>).length > 0;\n const hasPropStyles = propStyles && Object.keys(propStyles).length > 0;\n\n if (!hasStyles && !hasPropStyles) {\n return baseStyles;\n }\n\n return mergeStyles(baseStyles, styles as Styles, propStyles as Styles);\n }, [baseStyles, styles, propStyles]);\n\n // Use the useStyles hook for style generation and injection\n const { className: stylesClassName } = useStyles(allStyles);\n\n // Merge default tokens with instance tokens (instance overrides defaults)\n const tokensKey = stringifyTokens(tokens as Tokens | undefined);\n const mergedTokens = useMemo(() => {\n if (!defaultTokens && !tokens) return undefined;\n if (!defaultTokens) return tokens as Tokens;\n if (!tokens) return defaultTokens;\n return { ...defaultTokens, ...tokens } as Tokens;\n }, [tokensKey]);\n\n // Process merged tokens into inline style properties\n const processedTokenStyle = useMemo(() => {\n return processTokens(mergedTokens);\n }, [mergedTokens]);\n\n // Merge processed tokens with explicit style prop (style has priority)\n const mergedStyle = useMemo(() => {\n if (!processedTokenStyle && !style) return undefined;\n if (!processedTokenStyle) return style;\n if (!style) return processedTokenStyle;\n return { ...processedTokenStyle, ...style };\n }, [processedTokenStyle, style]);\n\n let modProps: Record<string, unknown> | undefined;\n if (mods) {\n const modsObject = mods as unknown as Record<string, unknown>;\n modProps = modAttrs(modsObject as any) as Record<string, unknown>;\n }\n\n // Merge user className with generated className\n const finalClassName = [(userClassName as string) || '', stylesClassName]\n .filter(Boolean)\n .join(' ');\n\n const elementProps = {\n 'data-element': (element as string | undefined) || defaultElement,\n 'data-qa': (qa as string | undefined) || defaultQa,\n 'data-qaval': (qaVal as string | undefined) || defaultQaVal,\n ...(otherDefaultProps as unknown as Record<string, unknown>),\n ...(modProps || {}),\n ...(otherProps as unknown as Record<string, unknown>),\n className: finalClassName,\n style: mergedStyle,\n ref,\n } as Record<string, unknown>;\n\n // Apply the helper to handle is* properties\n handleIsProperties(elementProps);\n\n const renderedElement = createElement(\n (as as string | 'div') ?? originalAs,\n elementProps,\n );\n\n return renderedElement;\n });\n\n _TastyComponent.displayName = `TastyComponent(${\n (defaultProps as any).qa || originalAs\n })`;\n\n // Attach sub-element components if elements are defined\n if (elements) {\n const subElements = Object.entries(elements).reduce(\n (acc, [name, definition]) => {\n acc[name] = createSubElement(\n name,\n definition as SubElementDefinition<keyof JSX.IntrinsicElements>,\n );\n return acc;\n },\n {} as Record<string, ForwardRefExoticComponent<any>>,\n );\n\n return Object.assign(_TastyComponent, subElements);\n }\n\n return _TastyComponent;\n}\n\nexport const Element = tasty({});\n"],"mappings":"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"}
1
+ {"version":3,"file":"tasty.js","names":["modAttrs"],"sources":["../src/tasty.tsx"],"sourcesContent":["import type {\n AllHTMLAttributes,\n ComponentType,\n ForwardRefExoticComponent,\n JSX,\n PropsWithoutRef,\n RefAttributes,\n} from 'react';\nimport { createElement, forwardRef, useMemo } from 'react';\nimport { isValidElementType } from 'react-is';\n\nimport { useStyles } from './hooks/useStyles';\nimport { BASE_STYLES } from './styles/list';\nimport type { Styles, StylesInterface } from './styles/types';\nimport type {\n AllBaseProps,\n BaseProps,\n BaseStyleProps,\n Mods,\n Props,\n Tokens,\n} from './types';\nimport { getDisplayName } from './utils/get-display-name';\nimport { mergeStyles } from './utils/merge-styles';\nimport { modAttrs } from './utils/mod-attrs';\nimport { processTokens, stringifyTokens } from './utils/process-tokens';\n\nimport type { StyleValue, StyleValueStateMap } from './utils/styles';\n\n/**\n * Mapping of is* properties to their corresponding HTML attributes\n */\nconst IS_PROPERTIES_MAP = {\n isDisabled: 'disabled',\n isHidden: 'hidden',\n isChecked: 'checked',\n} as const;\n\n/**\n * Precalculated entries for performance optimization\n */\nconst IS_PROPERTIES_ENTRIES = Object.entries(IS_PROPERTIES_MAP);\n\n/**\n * Helper function to handle is* properties consistently\n * Transforms is* props to HTML attributes and adds corresponding data-* attributes\n */\nfunction handleIsProperties(props: Record<string, unknown>) {\n for (const [isProperty, targetAttribute] of IS_PROPERTIES_ENTRIES) {\n if (isProperty in props) {\n props[targetAttribute] = props[isProperty];\n delete props[isProperty];\n }\n\n // Add data-* attribute if target attribute is truthy and doesn't already exist\n const dataAttribute = `data-${targetAttribute}`;\n if (!(dataAttribute in props) && props[targetAttribute]) {\n props[dataAttribute] = '';\n }\n }\n}\n\n/**\n * Creates a sub-element component for compound component patterns.\n * Sub-elements are lightweight components with data-element attribute for CSS targeting.\n */\nfunction createSubElement<Tag extends keyof JSX.IntrinsicElements>(\n elementName: string,\n definition: SubElementDefinition<Tag>,\n): ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<Tag>> & RefAttributes<unknown>\n> {\n // Normalize definition to object form\n const config =\n typeof definition === 'string'\n ? { as: definition as Tag }\n : (definition as { as?: Tag; qa?: string; qaVal?: string | number });\n\n const tag = config.as ?? ('div' as Tag);\n const defaultQa = config.qa;\n const defaultQaVal = config.qaVal;\n\n const SubElement = forwardRef<unknown, SubElementProps<Tag>>((props, ref) => {\n const {\n qa,\n qaVal,\n mods,\n tokens,\n isDisabled,\n isHidden,\n isChecked,\n className,\n style,\n ...htmlProps\n } = props as SubElementProps<Tag> & {\n className?: string;\n style?: Record<string, unknown>;\n };\n\n // Build mod attributes\n let modProps: Record<string, unknown> | undefined;\n if (mods) {\n modProps = modAttrs(mods as Mods) as Record<string, unknown>;\n }\n\n // Process tokens into inline style properties\n const tokenStyle = tokens\n ? (processTokens(tokens) as Record<string, unknown>)\n : undefined;\n\n // Merge token styles with explicit style prop (style has priority)\n let mergedStyle: Record<string, unknown> | undefined;\n if (tokenStyle || style) {\n mergedStyle =\n tokenStyle && style\n ? { ...tokenStyle, ...style }\n : ((tokenStyle ?? style) as Record<string, unknown>);\n }\n\n const elementProps = {\n 'data-element': elementName,\n 'data-qa': qa ?? defaultQa,\n 'data-qaval': qaVal ?? defaultQaVal,\n ...(modProps || {}),\n ...htmlProps,\n className,\n style: mergedStyle,\n isDisabled,\n isHidden,\n isChecked,\n ref,\n } as Record<string, unknown>;\n\n // Handle is* properties (isDisabled -> disabled + data-disabled, etc.)\n handleIsProperties(elementProps);\n\n // Clean up undefined data attributes\n if (elementProps['data-qa'] === undefined) delete elementProps['data-qa'];\n if (elementProps['data-qaval'] === undefined)\n delete elementProps['data-qaval'];\n\n return createElement(tag, elementProps);\n });\n\n SubElement.displayName = `SubElement(${elementName})`;\n\n return SubElement as ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<Tag>> & RefAttributes<unknown>\n >;\n}\n\ntype StyleList = readonly (keyof {\n [key in keyof StylesInterface]: StylesInterface[key];\n})[];\n\nexport type PropsWithStyles = {\n styles?: Styles;\n} & Omit<Props, 'styles'>;\n\nexport type VariantMap = Record<string, Styles>;\n\nexport interface WithVariant<V extends VariantMap> {\n variant?: keyof V;\n}\n\n// ============================================================================\n// Sub-element types for compound components\n// ============================================================================\n\n/**\n * Definition for a sub-element. Can be either:\n * - A tag name string (e.g., 'div', 'span')\n * - An object with configuration options\n */\nexport type SubElementDefinition<\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> =\n | Tag\n | {\n as?: Tag;\n qa?: string;\n qaVal?: string | number;\n };\n\n/**\n * Map of sub-element definitions.\n * Keys become the sub-component names (e.g., { Icon: 'span' } -> Component.Icon)\n */\nexport type ElementsDefinition = Record<\n string,\n SubElementDefinition<keyof JSX.IntrinsicElements>\n>;\n\n/**\n * Resolves the tag from a SubElementDefinition\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ResolveElementTag<T extends SubElementDefinition<any>> = T extends string\n ? T\n : T extends { as?: infer Tag }\n ? Tag extends keyof JSX.IntrinsicElements\n ? Tag\n : 'div'\n : 'div';\n\n/**\n * Props for sub-element components.\n * Combines HTML attributes with tasty-specific props (qa, qaVal, mods, tokens, isDisabled, etc.)\n */\nexport type SubElementProps<Tag extends keyof JSX.IntrinsicElements = 'div'> =\n Omit<\n JSX.IntrinsicElements[Tag],\n 'ref' | 'color' | 'content' | 'translate'\n > & {\n qa?: string;\n qaVal?: string | number;\n mods?: Mods;\n tokens?: Tokens;\n isDisabled?: boolean;\n isHidden?: boolean;\n isChecked?: boolean;\n };\n\n/**\n * Generates the sub-element component types from an ElementsDefinition\n */\ntype SubElementComponents<E extends ElementsDefinition> = {\n [K in keyof E]: ForwardRefExoticComponent<\n PropsWithoutRef<SubElementProps<ResolveElementTag<E[K]>>> &\n RefAttributes<\n ResolveElementTag<E[K]> extends keyof HTMLElementTagNameMap\n ? HTMLElementTagNameMap[ResolveElementTag<E[K]>]\n : Element\n >\n >;\n};\n\n/**\n * Base type containing common properties shared between TastyProps and TastyElementOptions.\n * Separated to avoid code duplication while allowing different type constraints.\n */\ntype TastyBaseProps<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n> = {\n /** Default styles of the element. */\n styles?: Styles;\n /** The list of styles that can be provided by props */\n styleProps?: K;\n element?: BaseProps['element'];\n variants?: V;\n /** Default tokens for inline CSS custom properties */\n tokens?: Tokens;\n /** Sub-element definitions for compound components */\n elements?: E;\n} & Pick<BaseProps, 'qa' | 'qaVal'> &\n WithVariant<V>;\n\nexport type TastyProps<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n DefaultProps = Props,\n> = TastyBaseProps<K, V, E> & {\n /** The tag name of the element or a React component. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n as?: string | ComponentType<any>;\n} & Partial<Omit<DefaultProps, 'as' | 'styles' | 'styleProps' | 'tokens'>>;\n\n/**\n * TastyElementOptions is used for the element-creation overload of tasty().\n * It includes a Tag generic that allows TypeScript to infer the correct\n * HTML element type from the `as` prop.\n *\n * Note: Uses a separate index signature with `unknown` instead of inheriting\n * from Props (which has `any`) to ensure strict type checking for styles.\n */\nexport type TastyElementOptions<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> = TastyBaseProps<K, V, E> & {\n /** The tag name of the element or a React component. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n as?: Tag | ComponentType<any>;\n} & Record<string, unknown>;\n\nexport type AllBasePropsWithMods<K extends StyleList> = AllBaseProps & {\n [key in K[number]]?:\n | StyleValue<StylesInterface[key]>\n | StyleValueStateMap<StylesInterface[key]>;\n} & BaseStyleProps;\n\n/**\n * Keys from BasePropsWithoutChildren that should be omitted from HTML attributes.\n * This excludes event handlers so they can be properly typed from JSX.IntrinsicElements.\n */\ntype TastySpecificKeys =\n | 'as'\n | 'qa'\n | 'qaVal'\n | 'element'\n | 'styles'\n | 'breakpoints'\n | 'block'\n | 'inline'\n | 'mods'\n | 'isHidden'\n | 'isDisabled'\n | 'css'\n | 'style'\n | 'theme'\n | 'tokens'\n | 'ref'\n | 'color';\n\n/**\n * Props type for tasty elements that combines:\n * - AllBasePropsWithMods for style props with strict tokens type\n * - HTML attributes for flexibility (properly typed based on tag)\n * - Variant support\n *\n * Uses AllHTMLAttributes as base for common attributes (like disabled),\n * but overrides event handlers with tag-specific types from JSX.IntrinsicElements.\n */\nexport type TastyElementProps<\n K extends StyleList,\n V extends VariantMap,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n> = AllBasePropsWithMods<K> &\n WithVariant<V> &\n Omit<\n Omit<AllHTMLAttributes<HTMLElement>, keyof JSX.IntrinsicElements[Tag]> &\n JSX.IntrinsicElements[Tag],\n TastySpecificKeys | K[number]\n >;\n\ntype TastyComponentPropsWithDefaults<\n Props extends PropsWithStyles,\n DefaultProps extends Partial<Props>,\n> = keyof DefaultProps extends never\n ? Props\n : {\n [key in Extract<keyof Props, keyof DefaultProps>]?: Props[key];\n } & {\n [key in keyof Omit<Props, keyof DefaultProps>]: Props[key];\n };\n\nexport function tasty<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition = Record<string, never>,\n Tag extends keyof JSX.IntrinsicElements = 'div',\n>(\n options: TastyElementOptions<K, V, E, Tag>,\n secondArg?: never,\n): ForwardRefExoticComponent<\n PropsWithoutRef<TastyElementProps<K, V, Tag>> & RefAttributes<unknown>\n> &\n SubElementComponents<E>;\nexport function tasty<\n Props extends PropsWithStyles,\n DefaultProps extends Partial<Props> = Partial<Props>,\n>(\n Component: ComponentType<Props>,\n options?: TastyProps<never, never, Record<string, never>, Props>,\n): ComponentType<TastyComponentPropsWithDefaults<Props, DefaultProps>>;\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// Implementation\nexport function tasty<\n K extends StyleList,\n V extends VariantMap,\n _C = Record<string, unknown>,\n>(Component: any, options?: any) {\n if (isValidElementType(Component)) {\n return tastyWrap(Component as ComponentType<any>, options);\n }\n\n return tastyElement(Component as TastyProps<K, V>);\n}\n\nfunction tastyWrap<\n P extends PropsWithStyles,\n DefaultProps extends Partial<P> = Partial<P>,\n>(\n Component: ComponentType<P>,\n options?: TastyProps<never, never, P>,\n): ComponentType<TastyComponentPropsWithDefaults<P, DefaultProps>> {\n const {\n as: extendTag,\n element: extendElement,\n ...defaultProps\n } = (options ?? {}) as TastyProps<never, never, P>;\n\n const propsWithStyles = ['styles'].concat(\n Object.keys(defaultProps).filter((prop) => prop.endsWith('Styles')),\n );\n\n const _WrappedComponent = forwardRef<any, any>((props, ref) => {\n const { as, element, ...restProps } = props as Record<string, unknown>;\n const propsWithStylesValues = propsWithStyles.map(\n (prop) => (props as any)[prop],\n );\n\n const mergedStylesMap: Styles | undefined = useMemo(() => {\n return propsWithStyles.reduce((map, prop) => {\n const restValue = (restProps as any)[prop];\n const defaultValue = (defaultProps as any)[prop];\n\n if (restValue != null && defaultValue != null) {\n (map as any)[prop] = mergeStyles(defaultValue, restValue);\n } else {\n (map as any)[prop] = restValue ?? defaultValue;\n }\n\n return map;\n }, {} as Styles);\n }, [propsWithStylesValues]);\n\n const elementProps = {\n ...(defaultProps as unknown as Record<string, unknown>),\n ...(restProps as unknown as Record<string, unknown>),\n ...(mergedStylesMap as unknown as Record<string, unknown>),\n as: (as as string | undefined) ?? extendTag,\n element: (element as string | undefined) || extendElement,\n ref,\n } as unknown as P;\n\n return createElement(Component as ComponentType<P>, elementProps);\n });\n\n _WrappedComponent.displayName = `TastyWrappedComponent(${getDisplayName(\n Component,\n (defaultProps as any).qa ?? (extendTag as any) ?? 'Anonymous',\n )})`;\n\n return _WrappedComponent as unknown as ComponentType<\n TastyComponentPropsWithDefaults<P, DefaultProps>\n >;\n}\n\nfunction tastyElement<\n K extends StyleList,\n V extends VariantMap,\n E extends ElementsDefinition,\n>(tastyOptions: TastyProps<K, V, E>) {\n const {\n as: originalAs = 'div',\n element: defaultElement,\n styles: defaultStyles,\n styleProps,\n variants,\n tokens: defaultTokens,\n elements,\n ...defaultProps\n } = tastyOptions;\n\n // Pre-compute merged styles for each variant (if variants are defined)\n // This avoids creating separate component instances per variant\n let variantStylesMap: Record<string, Styles | undefined> | undefined;\n if (variants) {\n const variantEntries = Object.entries(variants) as [string, Styles][];\n variantStylesMap = variantEntries.reduce(\n (map, [variant, variantStyles]) => {\n map[variant] = mergeStyles(defaultStyles, variantStyles);\n return map;\n },\n {} as Record<string, Styles | undefined>,\n );\n // Ensure 'default' variant always exists\n if (!variantStylesMap['default']) {\n variantStylesMap['default'] = defaultStyles;\n }\n }\n\n const {\n qa: defaultQa,\n qaVal: defaultQaVal,\n ...otherDefaultProps\n } = defaultProps ?? {};\n\n const _TastyComponent = forwardRef<\n unknown,\n AllBasePropsWithMods<K> & WithVariant<V>\n >((allProps, ref) => {\n const {\n as,\n styles: rawStyles,\n variant,\n mods,\n element,\n qa,\n qaVal,\n className: userClassName,\n tokens,\n style,\n ...otherProps\n } = allProps as Record<string, unknown> as AllBasePropsWithMods<K> &\n WithVariant<V> & {\n className?: string;\n tokens?: Tokens;\n style?: Record<string, unknown>;\n };\n\n let styles = rawStyles;\n\n // Optimize propStyles extraction - avoid creating empty objects\n let propStyles: Styles | null = null;\n const propsToCheck = styleProps\n ? (styleProps as StyleList).concat(BASE_STYLES)\n : BASE_STYLES;\n\n for (const prop of propsToCheck) {\n const key = prop as unknown as string;\n\n if (!propStyles) propStyles = {};\n\n if (key in otherProps) {\n (propStyles as any)[key] = (otherProps as any)[key];\n delete (otherProps as any)[key];\n }\n }\n\n if (\n !styles ||\n (styles && Object.keys(styles as Record<string, unknown>).length === 0)\n ) {\n styles = undefined as unknown as Styles;\n }\n\n // Determine base styles: use variant styles if available, otherwise default styles\n const baseStyles = variantStylesMap\n ? (variantStylesMap[(variant as string) || 'default'] ??\n variantStylesMap['default'])\n : defaultStyles;\n\n // Merge base styles with instance styles and prop styles\n const allStyles = useMemo(() => {\n const hasStyles =\n styles && Object.keys(styles as Record<string, unknown>).length > 0;\n const hasPropStyles = propStyles && Object.keys(propStyles).length > 0;\n\n if (!hasStyles && !hasPropStyles) {\n return baseStyles;\n }\n\n return mergeStyles(baseStyles, styles as Styles, propStyles as Styles);\n }, [baseStyles, styles, propStyles]);\n\n // Use the useStyles hook for style generation and injection\n const { className: stylesClassName } = useStyles(allStyles);\n\n // Merge default tokens with instance tokens (instance overrides defaults)\n const tokensKey = stringifyTokens(tokens as Tokens | undefined);\n const mergedTokens = useMemo(() => {\n if (!defaultTokens && !tokens) return undefined;\n if (!defaultTokens) return tokens as Tokens;\n if (!tokens) return defaultTokens;\n return { ...defaultTokens, ...tokens } as Tokens;\n }, [tokensKey]);\n\n // Process merged tokens into inline style properties\n const processedTokenStyle = useMemo(() => {\n return processTokens(mergedTokens);\n }, [mergedTokens]);\n\n // Merge processed tokens with explicit style prop (style has priority)\n const mergedStyle = useMemo(() => {\n if (!processedTokenStyle && !style) return undefined;\n if (!processedTokenStyle) return style;\n if (!style) return processedTokenStyle;\n return { ...processedTokenStyle, ...style };\n }, [processedTokenStyle, style]);\n\n let modProps: Record<string, unknown> | undefined;\n if (mods) {\n const modsObject = mods as unknown as Record<string, unknown>;\n modProps = modAttrs(modsObject as any) as Record<string, unknown>;\n }\n\n // Merge user className with generated className\n const finalClassName = [(userClassName as string) || '', stylesClassName]\n .filter(Boolean)\n .join(' ');\n\n const elementProps = {\n 'data-element': (element as string | undefined) || defaultElement,\n 'data-qa': (qa as string | undefined) || defaultQa,\n 'data-qaval': (qaVal as string | undefined) || defaultQaVal,\n ...(otherDefaultProps as unknown as Record<string, unknown>),\n ...(modProps || {}),\n ...(otherProps as unknown as Record<string, unknown>),\n className: finalClassName,\n style: mergedStyle,\n ref,\n } as Record<string, unknown>;\n\n // Apply the helper to handle is* properties\n handleIsProperties(elementProps);\n\n const renderedElement = createElement(\n (as as string | 'div') ?? originalAs,\n elementProps,\n );\n\n return renderedElement;\n });\n\n _TastyComponent.displayName = `TastyComponent(${\n (defaultProps as any).qa || originalAs\n })`;\n\n // Attach sub-element components if elements are defined\n if (elements) {\n const subElements = Object.entries(elements).reduce(\n (acc, [name, definition]) => {\n acc[name] = createSubElement(\n name,\n definition as SubElementDefinition<keyof JSX.IntrinsicElements>,\n );\n return acc;\n },\n {} as Record<string, ForwardRefExoticComponent<any>>,\n );\n\n return Object.assign(_TastyComponent, subElements);\n }\n\n return _TastyComponent;\n}\n\nexport const Element = tasty({});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `|` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 | post1 post2'`\n * - Names are space-separated within each group\n * - `|` separates base (before component) from post (after component) groups\n * - `|` is optional; if absent, all names are base\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const pipeIndex = trimmed.indexOf('|');\n\n if (pipeIndex === -1) {\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, pipeIndex);\n const postPart = trimmed.slice(pipeIndex + 1);\n\n return {\n base: splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n \n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;AAiB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,YAAY,QAAQ,QAAQ,IAAI;AAEtC,KAAI,cAAc,GAEhB,QAAO;EAAE,MADK,WAAW,QAAQ;EACX,MAAM;EAAM;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,YAAY,EAAE;AAE7C,QAAO;EACL,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}
1
+ {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `|` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 | post1 post2'`\n * - Names are space-separated within each group\n * - `|` separates base (before component) from post (after component) groups\n * - `|` is optional; if absent, all names are base\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const pipeIndex = trimmed.indexOf('|');\n\n if (pipeIndex === -1) {\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, pipeIndex);\n const postPart = trimmed.slice(pipeIndex + 1);\n\n return {\n base: splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;AAiB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,YAAY,QAAQ,QAAQ,IAAI;AAEtC,KAAI,cAAc,GAEhB,QAAO;EAAE,MADK,WAAW,QAAQ;EACX,MAAM;EAAM;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,YAAY,EAAE;AAE7C,QAAO;EACL,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}