AiAssistant.tsx
2 days ago
App.tsx
2 days ago
BuilderSync.ts
2 days ago
ControlRenderer.tsx
2 days ago
HoverTip.tsx
2 days ago
Popover.tsx
2 days ago
PreviewBridge.ts
2 days ago
PreviewPane.tsx
2 days ago
constants.ts
2 days ago
cssVars.ts
2 days ago
globals.d.ts
2 days ago
index.tsx
2 days ago
panes.tsx
2 days ago
store.ts
2 days ago
style.scss
2 days ago
types.ts
2 days ago
cssVars.ts
106 lines
| 1 | /** |
| 2 | * Style Customizer v2 — token → CSS custom properties. Client mirror of Compiler.php; keep |
| 3 | * this in lockstep with Compiler::declarations()/format(). |
| 4 | */ |
| 5 | import { BoxValue, DeviceBag, Device, FontStyleValue, ScalarValue, Token } from './types'; |
| 6 | |
| 7 | const SIDES: Array<keyof BoxValue> = [ 'top', 'right', 'bottom', 'left' ]; |
| 8 | |
| 9 | /** Resolves a device bag to a concrete value for a device. Mirrors Compiler::resolve(). */ |
| 10 | export function resolveValue( bag: DeviceBag | undefined, token: Token, device: Device ): ScalarValue { |
| 11 | if ( bag && bag[ device ] !== undefined && bag[ device ] !== '' ) { |
| 12 | return bag[ device ] as ScalarValue; |
| 13 | } |
| 14 | if ( bag && bag.desktop !== undefined && bag.desktop !== '' ) { |
| 15 | return bag.desktop; |
| 16 | } |
| 17 | return token.default; |
| 18 | } |
| 19 | |
| 20 | /** Is this device explicitly overriding the desktop base for a responsive token? */ |
| 21 | export function isOverride( bag: DeviceBag | undefined, token: Token, device: Device ): boolean { |
| 22 | return !! ( token.responsive && device !== 'desktop' && bag && bag[ device ] !== undefined ); |
| 23 | } |
| 24 | |
| 25 | /** Belt-and-suspenders: strip anything that could break out of a CSS declaration. */ |
| 26 | function cssSafe( value: string ): string { |
| 27 | return value.replace( /[<>{};\\]|\/\*|\*\//g, '' ); |
| 28 | } |
| 29 | |
| 30 | /** Format a single value into its CSS string. Mirrors Compiler::format(). */ |
| 31 | export function formatValue( token: Token, value: ScalarValue ): string { |
| 32 | switch ( token.type ) { |
| 33 | case 'slider': { |
| 34 | if ( value === '' || value === null || value === undefined || Number.isNaN( Number( value ) ) ) { |
| 35 | return ''; |
| 36 | } |
| 37 | const unit = token.unit !== undefined ? token.unit : 'px'; |
| 38 | return `${ value }${ unit }`; |
| 39 | } |
| 40 | case 'box4': |
| 41 | return formatBox( token, value as BoxValue ); |
| 42 | case 'media': |
| 43 | return value ? `url("${ String( value ).replace( /"/g, '' ) }")` : 'none'; |
| 44 | case 'color': |
| 45 | case 'select': |
| 46 | default: |
| 47 | return value === '' || value === null || value === undefined ? '' : cssSafe( String( value ) ); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | function formatBox( token: Token, value: BoxValue ): string { |
| 52 | if ( ! value || typeof value !== 'object' ) { |
| 53 | return ''; |
| 54 | } |
| 55 | let unit = 'px'; |
| 56 | if ( token.units && token.units.length ) { |
| 57 | unit = value.unit && token.units.indexOf( value.unit ) !== -1 ? value.unit : token.units[ 0 ]; |
| 58 | } |
| 59 | const out: string[] = []; |
| 60 | for ( const side of SIDES ) { |
| 61 | if ( value[ side ] === undefined || value[ side ] === null ) { |
| 62 | return ''; |
| 63 | } |
| 64 | out.push( `${ parseInt( String( value[ side ] ), 10 ) || 0 }${ unit }` ); |
| 65 | } |
| 66 | return out.join( ' ' ); |
| 67 | } |
| 68 | |
| 69 | /** The `[cssVar, value]` declarations a token contributes for one resolved value. Mirrors Compiler::declarations(). */ |
| 70 | export function tokenDeclarations( |
| 71 | token: Token, |
| 72 | value: ScalarValue, |
| 73 | themeFont: boolean |
| 74 | ): Array<[ string, string ]> { |
| 75 | if ( value === null || value === undefined ) { |
| 76 | return []; |
| 77 | } |
| 78 | |
| 79 | if ( token.type === 'fontstyle' && token.vars ) { |
| 80 | const v = ( value || {} ) as Partial<FontStyleValue>; |
| 81 | // An explicit weight (Font weight dropdown) wins; absent/empty (every pre-existing value) |
| 82 | // falls back to the original bold-derived value — mirrors Compiler::declarations(). |
| 83 | const weight = v.weight ? v.weight : ( v.bold ? '700' : String( token.neutral_weight || '400' ) ); |
| 84 | return [ |
| 85 | [ token.vars.weight, weight ], |
| 86 | [ token.vars.style, v.italic ? 'italic' : 'normal' ], |
| 87 | [ token.vars.decoration, v.underline ? 'underline' : 'none' ], |
| 88 | [ token.vars.transform, v.uppercase ? 'uppercase' : 'none' ], |
| 89 | ]; |
| 90 | } |
| 91 | |
| 92 | if ( ! token.var ) { |
| 93 | return []; |
| 94 | } |
| 95 | |
| 96 | if ( token.key === 'fonts.family' && themeFont ) { |
| 97 | return [ [ token.var, 'inherit' ] ]; |
| 98 | } |
| 99 | |
| 100 | const formatted = formatValue( token, value ); |
| 101 | if ( formatted === '' ) { |
| 102 | return []; |
| 103 | } |
| 104 | return [ [ token.var, formatted ] ]; |
| 105 | } |
| 106 |