|
| 1 | +/** |
| 2 | + * Outline utilities (outline width, style, offset) |
| 3 | + */ |
| 4 | + |
| 5 | +import type { StyleObject } from "../types"; |
| 6 | +import { BORDER_WIDTH_SCALE } from "./borders"; |
| 7 | + |
| 8 | +/** |
| 9 | + * Parse arbitrary outline width/offset value: [8px], [4] |
| 10 | + * Returns number for px values, null for unsupported formats |
| 11 | + */ |
| 12 | +function parseArbitraryOutlineValue(value: string): number | null { |
| 13 | + // Match: [8px] or [8] (pixels only) |
| 14 | + const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/); |
| 15 | + if (pxMatch) { |
| 16 | + return parseInt(pxMatch[1], 10); |
| 17 | + } |
| 18 | + |
| 19 | + // Warn about unsupported formats |
| 20 | + if (value.startsWith("[") && value.endsWith("]")) { |
| 21 | + /* v8 ignore next 5 */ |
| 22 | + if (process.env.NODE_ENV !== "production") { |
| 23 | + console.warn( |
| 24 | + `[react-native-tailwind] Unsupported arbitrary outline value: ${value}. Only px values are supported (e.g., [8px] or [8]).`, |
| 25 | + ); |
| 26 | + } |
| 27 | + return null; |
| 28 | + } |
| 29 | + |
| 30 | + return null; |
| 31 | +} |
| 32 | + |
| 33 | +/** |
| 34 | + * Parse outline classes |
| 35 | + * @param cls - The class name to parse |
| 36 | + */ |
| 37 | +export function parseOutline(cls: string): StyleObject | null { |
| 38 | + // Shorthand: outline (width: 1, style: solid) |
| 39 | + if (cls === "outline") { |
| 40 | + return { outlineWidth: 1, outlineStyle: "solid" }; |
| 41 | + } |
| 42 | + |
| 43 | + // Outline none |
| 44 | + if (cls === "outline-none") { |
| 45 | + return { outlineWidth: 0 }; |
| 46 | + } |
| 47 | + |
| 48 | + // Outline style |
| 49 | + if (cls === "outline-solid") return { outlineStyle: "solid" }; |
| 50 | + if (cls === "outline-dotted") return { outlineStyle: "dotted" }; |
| 51 | + if (cls === "outline-dashed") return { outlineStyle: "dashed" }; |
| 52 | + |
| 53 | + // Outline offset: outline-offset-2, outline-offset-[3px] |
| 54 | + if (cls.startsWith("outline-offset-")) { |
| 55 | + const valueStr = cls.substring(15); // "outline-offset-".length = 15 |
| 56 | + |
| 57 | + // Try arbitrary value first |
| 58 | + if (valueStr.startsWith("[")) { |
| 59 | + const arbitraryValue = parseArbitraryOutlineValue(valueStr); |
| 60 | + if (arbitraryValue !== null) { |
| 61 | + return { outlineOffset: arbitraryValue }; |
| 62 | + } |
| 63 | + return null; |
| 64 | + } |
| 65 | + |
| 66 | + // Try preset scale (reuse border width scale for consistency with default Tailwind) |
| 67 | + const scaleValue = BORDER_WIDTH_SCALE[valueStr]; |
| 68 | + if (scaleValue !== undefined) { |
| 69 | + return { outlineOffset: scaleValue }; |
| 70 | + } |
| 71 | + |
| 72 | + return null; |
| 73 | + } |
| 74 | + |
| 75 | + // Outline width: outline-0, outline-2, outline-[5px] |
| 76 | + // Must handle potential collision with outline-red-500 (colors) |
| 77 | + // Logic: if it matches width pattern, return width. If it looks like color, return null (let parseColor handle it) |
| 78 | + |
| 79 | + const widthMatch = cls.match(/^outline-(\d+)$/); |
| 80 | + if (widthMatch) { |
| 81 | + const value = BORDER_WIDTH_SCALE[widthMatch[1]]; |
| 82 | + if (value !== undefined) { |
| 83 | + return { outlineWidth: value }; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + const arbMatch = cls.match(/^outline-(\[.+\])$/); |
| 88 | + if (arbMatch) { |
| 89 | + // Check if it's a color first? No, colors usually look like [#...] or [rgb(...)] |
| 90 | + // parseArbitraryOutlineValue only accepts [123] or [123px] |
| 91 | + // If it fails, it might be a color, so we return null |
| 92 | + const arbitraryValue = parseArbitraryOutlineValue(arbMatch[1]); |
| 93 | + if (arbitraryValue !== null) { |
| 94 | + return { outlineWidth: arbitraryValue }; |
| 95 | + } |
| 96 | + return null; |
| 97 | + } |
| 98 | + |
| 99 | + // If it's outline-{color}, return null so parseColor (called later in index.ts) handles it |
| 100 | + return null; |
| 101 | +} |
0 commit comments