{"version":3,"file":"styleToTheme.mjs","sources":["../../../../../../../../web/src/ui/compat/styleToTheme.ts"],"sourcesContent":["import { logger, RUN_IN_DEV } from '@stytch/core';\nimport type { StyleConfig } from '@stytch/core/public';\n\nimport type { PresentationOptions } from '../components/PresentationConfig';\nimport type { Theme } from '../components/themes/ThemeConfig';\n\n/* eslint-disable lingui/no-unlocalized-strings */\n\n/**\n * Maps a legacy StyleConfig to the new Theme format.\n *\n * This is a best-effort mapping - some StyleConfig properties don't have\n * direct equivalents in Theme, and some Theme properties don't have\n * StyleConfig equivalents.\n *\n * StyleConfig properties that don't have good mappings to Theme:\n *  - inputs.placeholderColor - No equivalent (could potentially map to muted-foreground)\n *  - inputs.textColor - Mapped to foreground as approximation, but is input-specific\n *  - buttons.primary.borderColor - No specific mapping (could use border but loses specificity)\n *  - buttons.primary.borderRadius - Mapped to button-radius but loses primary-specific control\n *  - buttons.secondary.borderColor - No specific mapping (could use border but loses specificity)\n *  - buttons.secondary.borderRadius - Mapped to button-radius but loses secondary-specific control\n *  - buttons.disabled.* - No equivalents (disabled state styling not in Theme)\n *\n * Theme properties that don't have StyleConfig equivalents:\n *  - 'transition-duration' - defaulted to 0 instead of 0.15s in our new default theme\n *  - 'font-family-mono' - defaults to theme default which is system UI monospace\n *  - 'text-base' - defaults to 1rem which is close to the old font sizes\n *  - spacing - defaults to new theme default which is close but not equivalent to old spacing\n *  - shadow, 'shadow-button', 'shadow-input' - no default in new theme\n *  - foreground - Approximated from inputs.textColor but not exact\n *  - muted, 'muted-foreground'\n *  - 'accent-foreground'\n *  - ring - previously defaults to OS default focus outline color\n */\nexport function styleToTheme(\n  styleConfig: StyleConfig,\n  { silent }: { silent?: boolean } = {},\n): {\n  theme: Partial<Theme>;\n  options: Partial<PresentationOptions>;\n} {\n  const warn = silent\n    ? () => {\n        // noop\n      }\n    : logger.warn;\n  const error = silent\n    ? () => {\n        // noop\n      }\n    : logger.error;\n\n  const theme: Partial<Theme> = {};\n  const options: Partial<PresentationOptions> = {};\n\n  const { colors, container, inputs, hideHeaderText, buttons, logo, fontFamily, ...outerEmpty } = styleConfig;\n\n  // presentation.theme mapping\n  // ------------------------------------------------\n  RUN_IN_DEV(() => {\n    warn(\n      \"styleToTheme: We recommend setting theme['color-scheme'] to either 'light' or 'dark' explicitly \" +\n        'depending on whether this is a light or dark theme. This enables icons to automatically automatically ' +\n        'apply contrasting colors.',\n    );\n\n    if (Object.keys(outerEmpty).length > 0) warn('styleToTheme: Unrecognized style properties', outerEmpty);\n  });\n\n  theme['font-family'] = fontFamily;\n\n  if (colors) {\n    const { primary, secondary, success, warning, accent, error, ...expectEmpty } = colors;\n    RUN_IN_DEV(() => {\n      if (Object.keys(expectEmpty).length > 0) warn('styleToTheme: Unrecognized style.colors properties', expectEmpty);\n    });\n\n    Object.assign(theme, {\n      primary,\n      secondary,\n      success,\n      warning,\n      accent,\n      destructive: error,\n    } satisfies Partial<Theme>);\n  }\n\n  if (container) {\n    const { backgroundColor, borderColor, width, borderRadius, ...expectEmpty } = container;\n\n    RUN_IN_DEV(() => {\n      if (Object.keys(expectEmpty).length > 0)\n        warn('styleToTheme: Unrecognized style.container properties', expectEmpty);\n    });\n\n    Object.assign(theme, {\n      background: backgroundColor,\n      border: borderColor,\n      'container-width': width,\n      // 1/4 because the new base is quite small and most components use a 2x or 4x multiplier\n      'rounded-base': updateLength(borderRadius, 0.25),\n    } satisfies Partial<Theme>);\n  }\n\n  if (buttons) {\n    const { primary, secondary, disabled, ...expectEmpty } = buttons;\n\n    RUN_IN_DEV(() => {\n      if (Object.keys(expectEmpty).length > 0) warn('styleToTheme: Unrecognized style.buttons properties', expectEmpty);\n\n      if (disabled && Object.keys(disabled).length > 0)\n        warn('styleToTheme: buttons.disabled is no longer supported', { disabled });\n\n      if (primary?.borderColor && primary?.backgroundColor && primary.borderColor !== primary.backgroundColor)\n        error(\n          \"styleToTheme: primary button's border color is now always equal to background color; \" +\n            'having distinct colors is no longer supported',\n          {\n            borderColor: primary.borderColor,\n            backgroundColor: primary.backgroundColor,\n          },\n        );\n\n      if (secondary?.borderColor && secondary.textColor && secondary.borderColor !== secondary.textColor)\n        error(\"styleToTheme: secondary button's border color is now always equal to text color\", {\n          borderColor: secondary.borderColor,\n          textColor: secondary.textColor,\n        });\n\n      if (primary?.borderRadius !== secondary?.borderRadius)\n        error('styleToTheme: All buttons must use the same border-radius', {\n          primaryRadius: primary?.borderRadius,\n          secondaryRadius: secondary?.borderRadius,\n        });\n    });\n\n    // Set background and text colors\n    Object.assign(theme, {\n      primary: primary?.backgroundColor,\n      'primary-foreground': primary?.textColor,\n\n      secondary: secondary?.backgroundColor,\n      'secondary-foreground': secondary?.textColor,\n    } satisfies Partial<Theme>);\n\n    // Set border radius\n    if (primary?.borderRadius || secondary?.borderRadius) {\n      // Conflicts between primary and secondary border radius is handled above\n      const baseRadius = updateLength(primary?.borderRadius, 0.5) ?? updateLength(secondary?.borderRadius, 0.5);\n\n      if (theme['rounded-base']) {\n        if (theme['rounded-base'] !== baseRadius) {\n          theme['button-radius'] = primary?.borderRadius;\n        }\n      } else {\n        theme['rounded-base'] = baseRadius;\n      }\n    }\n  }\n\n  if (inputs) {\n    const { backgroundColor, textColor, placeholderColor, borderColor, borderRadius, ...expectEmpty } = inputs;\n    RUN_IN_DEV(() => {\n      if (Object.keys(expectEmpty).length > 0) warn('styleToTheme: Unrecognized style.buttons properties', expectEmpty);\n\n      if (backgroundColor && theme.background && theme.background !== backgroundColor)\n        error('styleToTheme: Input now always use the container background', {\n          inputBackground: backgroundColor,\n          containerBackground: theme.background,\n        });\n    });\n\n    Object.assign(theme, {\n      foreground: textColor,\n      input: borderColor,\n      'muted-foreground': placeholderColor,\n    } satisfies Partial<Theme>);\n\n    // Set border radius\n    if (theme['rounded-base']) {\n      if (theme['rounded-base'] !== borderRadius) {\n        theme['input-radius'] = borderRadius;\n      }\n    } else {\n      theme['rounded-base'] = borderRadius;\n    }\n  }\n\n  // presentation.options mapping\n  // ------------------------------------------------\n\n  options.hideHeaderText = hideHeaderText;\n  if (logo?.logoImageUrl) {\n    options.logo = {\n      url: logo.logoImageUrl,\n      alt: '',\n    };\n\n    RUN_IN_DEV(() => {\n      logger.warn('styleToTheme: Please set options.logo.alt for the logo alt text for accessibility');\n    });\n  }\n\n  return { theme: removeEmptyValues(theme), options: removeEmptyValues(options) };\n}\n\nfunction removeEmptyValues<T extends Record<string, unknown>>(obj: T): T {\n  return Object.fromEntries(Object.entries(obj).filter(([, value]) => value != null && value !== '')) as T;\n}\n\nfunction updateLength(length: string | undefined, factor: number): string | undefined {\n  if (!length) return length;\n\n  // Loose check that this is a numeric and not keyword value\n  const value = parseFloat(length);\n  if (Number.isNaN(value)) return length;\n\n  return `calc(${length} * ${factor})`;\n}\n"],"names":["styleToTheme","styleConfig","silent","theme","options","colors","container","inputs","hideHeaderText","buttons","logo","fontFamily","outerEmpty","primary","secondary","success","warning","accent","error","expectEmpty","Object","assign","destructive","backgroundColor","borderColor","width","borderRadius","background","border","updateLength","disabled","textColor","baseRadius","placeholderColor","foreground","input","logoImageUrl","url","alt","removeEmptyValues","obj","fromEntries","entries","filter","value","length","factor","parseFloat","Number","isNaN"],"mappings":"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"}