{"version":3,"file":"email-node-B-_g4X-S.mjs","names":[],"sources":["../src/utils/styles.ts","../src/core/serializer/email-node.ts"],"sourcesContent":["import type { CssJs } from '../plugins/email-theming/types';\n\nconst WHITE_SPACE_REGEX = /\\s+/;\n\nconst BORDER_WIDTH_TO_STYLE: Record<string, string> = {\n  borderWidth: 'borderStyle',\n  borderTopWidth: 'borderTopStyle',\n  borderRightWidth: 'borderRightStyle',\n  borderBottomWidth: 'borderBottomStyle',\n  borderLeftWidth: 'borderLeftStyle',\n};\n\nexport const jsToInlineCss = (styleObject: { [key: string]: any }) => {\n  const parts: string[] = [];\n\n  for (const key in styleObject) {\n    const value = styleObject[key];\n    if (value !== 0 && value !== undefined && value !== null && value !== '') {\n      const KEBAB_CASE_REGEX = /[A-Z]/g;\n      const formattedKey = key.replace(\n        KEBAB_CASE_REGEX,\n        (match) => `-${match.toLowerCase()}`,\n      );\n      parts.push(`${formattedKey}:${value}`);\n    }\n  }\n\n  return parts.join(';') + (parts.length ? ';' : '');\n};\n\nconst splitDeclarations = (input: string): string[] => {\n  const results: string[] = [];\n  let current = '';\n  let depth = 0;\n\n  for (const char of input) {\n    if (char === '(') depth++;\n    if (char === ')') depth--;\n\n    if (char === ';' && depth === 0) {\n      results.push(current);\n      current = '';\n    } else {\n      current += char;\n    }\n  }\n\n  if (current) results.push(current);\n  return results;\n};\n\nexport const inlineCssToJs = (\n  inlineStyle: string,\n  options: { removeUnit?: boolean } = {},\n) => {\n  const styleObject: { [key: string]: string } = {};\n\n  if (!inlineStyle || inlineStyle === '' || typeof inlineStyle === 'object') {\n    return styleObject;\n  }\n\n  splitDeclarations(inlineStyle).forEach((style: string) => {\n    const trimmed = style.trim();\n    if (trimmed) {\n      const separatorIndex = trimmed.indexOf(':');\n      if (separatorIndex === -1) return;\n\n      const key = trimmed.slice(0, separatorIndex);\n      const value = trimmed.slice(separatorIndex + 1);\n      const valueTrimmed = value.trim();\n\n      if (!valueTrimmed) {\n        return;\n      }\n\n      const formattedKey = key\n        .trim()\n        .replace(/-\\w/g, (match) => match[1].toUpperCase());\n\n      const UNIT_REGEX = /px|%/g;\n      const sanitizedValue = options?.removeUnit\n        ? valueTrimmed.replace(UNIT_REGEX, '')\n        : valueTrimmed;\n\n      styleObject[formattedKey] = sanitizedValue;\n    }\n  });\n\n  return styleObject;\n};\n\n/**\n * Expands CSS shorthand properties (margin, padding) into their longhand equivalents.\n * This prevents shorthand properties from overriding specific longhand properties in email clients.\n *\n * @param styles - Style object that may contain shorthand properties\n * @returns New style object with shorthand properties expanded to longhand\n *\n * @example\n * expandShorthandProperties({ margin: '0', paddingTop: '10px' })\n * // Returns: { marginTop: '0', marginRight: '0', marginBottom: '0', marginLeft: '0', paddingTop: '10px' }\n */\nexport function expandShorthandProperties(\n  styles: Record<string, string>,\n): Record<string, string> {\n  if (!styles || typeof styles !== 'object') {\n    return {};\n  }\n\n  const expanded: Record<string, any> = {};\n\n  for (const key in styles) {\n    const value = styles[key];\n    if (value === undefined || value === null || value === '') {\n      continue;\n    }\n\n    switch (key) {\n      case 'margin': {\n        const values = parseShorthandValue(value);\n        expanded.marginTop = values.top;\n        expanded.marginRight = values.right;\n        expanded.marginBottom = values.bottom;\n        expanded.marginLeft = values.left;\n        break;\n      }\n      case 'padding': {\n        const values = parseShorthandValue(value);\n        expanded.paddingTop = values.top;\n        expanded.paddingRight = values.right;\n        expanded.paddingBottom = values.bottom;\n        expanded.paddingLeft = values.left;\n        break;\n      }\n      case 'border': {\n        const values = convertBorderValue(value);\n        expanded.borderStyle = values.style;\n        expanded.borderWidth = values.width;\n        expanded.borderColor = values.color;\n        break;\n      }\n      case 'borderTopLeftRadius':\n      case 'borderTopRightRadius':\n      case 'borderBottomLeftRadius':\n      case 'borderBottomRightRadius': {\n        // Always preserve the longhand property\n        expanded[key] = value;\n\n        // When all four corners are present and identical, also add the shorthand\n        if (\n          styles.borderTopLeftRadius &&\n          styles.borderTopRightRadius &&\n          styles.borderBottomLeftRadius &&\n          styles.borderBottomRightRadius\n        ) {\n          const values = [\n            styles.borderTopLeftRadius,\n            styles.borderTopRightRadius,\n            styles.borderBottomLeftRadius,\n            styles.borderBottomRightRadius,\n          ];\n\n          if (new Set(values).size === 1) {\n            expanded.borderRadius = values[0];\n          }\n        }\n\n        break;\n      }\n\n      default: {\n        // Keep all other properties as-is\n        expanded[key] = value;\n      }\n    }\n  }\n\n  return expanded;\n}\n\n/**\n * Parses CSS shorthand value (1-4 values) into individual side values.\n * Follows CSS specification for shorthand property value parsing.\n *\n * @param value - Shorthand value string (e.g., '0', '10px 20px', '5px 10px 15px 20px')\n * @returns Object with top, right, bottom, left values\n */\nfunction parseShorthandValue(value: string | number): {\n  top: string;\n  right: string;\n  bottom: string;\n  left: string;\n} {\n  const stringValue = String(value).trim();\n  const parts = stringValue.split(WHITE_SPACE_REGEX);\n  const len = parts.length;\n\n  if (len === 1) {\n    return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] };\n  }\n  if (len === 2) {\n    return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] };\n  }\n  if (len === 3) {\n    return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[1] };\n  }\n  if (len === 4) {\n    return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };\n  }\n\n  return {\n    top: stringValue,\n    right: stringValue,\n    bottom: stringValue,\n    left: stringValue,\n  };\n}\n\nfunction convertBorderValue(value: string | number): {\n  style: string;\n  width: string;\n  color: string;\n} {\n  const stringValue = String(value).trim();\n  const parts = stringValue.split(WHITE_SPACE_REGEX);\n\n  switch (parts.length) {\n    case 1:\n      // border: 1px → all sides\n      return {\n        style: 'solid',\n        width: parts[0],\n        color: 'black',\n      };\n    case 2:\n      // border: 1px solid → top/bottom, left/right\n      return {\n        style: parts[1],\n        width: parts[0],\n        color: 'black',\n      };\n    case 3:\n      // border: 1px solid #000 → top, left/right, bottom\n      return {\n        style: parts[1],\n        width: parts[0],\n        color: parts[2],\n      };\n    case 4:\n      // border: 1px solid #000 #fff → top, right, bottom, left\n      return {\n        style: parts[1],\n        width: parts[0],\n        color: parts[2],\n      };\n    default:\n      // Invalid format, return the original value for all sides\n      return {\n        style: 'solid',\n        width: stringValue,\n        color: 'black',\n      };\n  }\n}\n\n/**\n * When a border-width is present but border-style is missing, browsers default\n * to `none` and the border is invisible. This adds `solid` as a sensible\n * fallback so borders show up immediately after setting a width.\n */\nexport function ensureBorderStyleFallback(\n  styles: Record<string, string | number>,\n): Record<string, string | number> {\n  for (const [widthKey, styleKey] of Object.entries(BORDER_WIDTH_TO_STYLE)) {\n    const widthValue = styles[widthKey];\n    if (!widthValue || widthValue === '0' || widthValue === '0px') {\n      continue;\n    }\n    if (!styles[styleKey]) {\n      // Keep shorthand borderStyle authoritative for each side when present.\n      if (styleKey !== 'borderStyle' && styles.borderStyle) {\n        continue;\n      }\n      styles[styleKey] = 'solid';\n    }\n  }\n  return styles;\n}\n\n/**\n * Resolves conflicts between reset styles and inline styles by expanding\n * shorthand properties (margin, padding) to longhand before merging.\n * This prevents shorthand properties from overriding specific longhand properties.\n *\n * @param resetStyles - Base reset styles that may contain shorthand properties\n * @param inlineStyles - Inline styles that should override reset styles\n * @returns Merged styles with inline styles taking precedence\n */\nexport function resolveConflictingStyles(\n  resetStyles: CssJs['reset'],\n  inlineStyles: Record<string, string>,\n) {\n  const expandedResetStyles = expandShorthandProperties(\n    resetStyles as Record<string, string>,\n  );\n  const expandedInlineStyles = expandShorthandProperties(inlineStyles);\n\n  return {\n    ...expandedResetStyles,\n    ...expandedInlineStyles,\n  };\n}\n","import {\n  type Editor,\n  type JSONContent,\n  Node,\n  type NodeConfig,\n  type NodeType,\n} from '@tiptap/core';\n\nexport type NodeRendererComponent = (props: {\n  node: JSONContent;\n  style: React.CSSProperties;\n  children?: React.ReactNode;\n\n  extension: EmailNode<any, any>;\n}) => React.ReactNode;\n\nexport interface EmailNodeConfig<Options, Storage>\n  extends NodeConfig<Options, Storage> {\n  renderToReactEmail: NodeRendererComponent;\n}\n\ntype ConfigParameter<Options, Storage> = Partial<\n  Omit<EmailNodeConfig<Options, Storage>, 'renderToReactEmail'>\n> &\n  Pick<EmailNodeConfig<Options, Storage>, 'renderToReactEmail'> &\n  ThisType<{\n    name: string;\n    options: Options;\n    storage: Storage;\n    editor: Editor;\n    type: NodeType;\n    parent: (...args: any[]) => any;\n  }>;\n\nexport class EmailNode<\n  Options = Record<string, never>,\n  Storage = Record<string, never>,\n> extends Node<Options, Storage> {\n  declare config: EmailNodeConfig<Options, Storage>;\n\n  // biome-ignore lint/complexity/noUselessConstructor: This is only meant to change the types for config, hence why we keep it\n  constructor(config: ConfigParameter<Options, Storage>) {\n    super(config);\n  }\n\n  /**\n   * Create a new Node instance\n   * @param config - Node configuration object or a function that returns a configuration object\n   */\n  static create<O = Record<string, never>, S = Record<string, never>>(\n    config: ConfigParameter<O, S> | (() => ConfigParameter<O, S>),\n  ) {\n    // If the config is a function, execute it to get the configuration object\n    const resolvedConfig = typeof config === 'function' ? config() : config;\n    return new EmailNode<O, S>(resolvedConfig);\n  }\n\n  static from<O, S>(\n    node: Node<O, S>,\n    renderToReactEmail: NodeRendererComponent,\n  ): EmailNode<O, S> {\n    const customNode = EmailNode.create({} as ConfigParameter<O, S>);\n    // This only makes a shallow copy, so if there's nested objects here mutating things will be dangerous\n    Object.assign(customNode, { ...node });\n    customNode.config = { ...node.config, renderToReactEmail };\n    return customNode;\n  }\n\n  // Subclass return types for configure/extend; safe at runtime. TipTap's Node typings cause TS2416 when returning EmailNode.\n  // @ts-expect-error - EmailNode is a valid Node subclass; base typings don't support subclass return types\n  configure(options?: Partial<Options>) {\n    return super.configure(options) as EmailNode<Options, Storage>;\n  }\n\n  // @ts-expect-error - same as configure: extend returns EmailNode for chaining; base typings are incompatible\n  extend<\n    ExtendedOptions = Options,\n    ExtendedStorage = Storage,\n    ExtendedConfig extends NodeConfig<\n      ExtendedOptions,\n      ExtendedStorage\n    > = EmailNodeConfig<ExtendedOptions, ExtendedStorage>,\n  >(\n    extendedConfig?:\n      | (() => Partial<ExtendedConfig>)\n      | (Partial<ExtendedConfig> &\n          ThisType<{\n            name: string;\n            options: ExtendedOptions;\n            storage: ExtendedStorage;\n            editor: Editor;\n            type: NodeType;\n          }>),\n  ): EmailNode<ExtendedOptions, ExtendedStorage> {\n    // If the extended config is a function, execute it to get the configuration object\n    const resolvedConfig =\n      typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig;\n    return super.extend(resolvedConfig) as EmailNode<\n      ExtendedOptions,\n      ExtendedStorage\n    >;\n  }\n}\n"],"mappings":"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"}