* feat: write variable-bound properties as tokens in exported code HTML and Tailwind JSX export reference the design tokens a layer is bound to, var(--color-primary) or bg-primary, instead of baking in their values. A binding becomes a reference only where the stylesheet resolves it as the canvas draws it: the layer still draws the value, the token's unit fits the property, and an attribute such as data-theme="dark" can put the element in the layer's mode. Standalone HTML includes the stylesheet for the tokens it uses, compiled into Tailwind's @theme for Tailwind pages. * refactor: share the collection variable list and tighten token helpers Read every variable in collection order through one collectionVariables helper built on es-toolkit's compact, used by the stylesheet and the token references, and map padding sides and color channels instead of repeating them.
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
import type { GridTrack, SceneNode } from '@open-pencil/scene-graph'
|
|
|
|
import type { DesignStyleDeclaration } from '../types'
|
|
|
|
function trackToCSS(track: GridTrack): string {
|
|
if (track.sizing === 'FR') return `${track.value}fr`
|
|
if (track.sizing === 'FIXED') return `${track.value}px`
|
|
return 'auto'
|
|
}
|
|
|
|
/** Equal `1fr` tracks use Tailwind's `grid-cols-N` form, which keeps them from overflowing. */
|
|
function tracksToCSS(tracks: GridTrack[]): string {
|
|
if (tracks.every((track) => track.sizing === 'FR' && track.value === 1))
|
|
return `repeat(${tracks.length}, minmax(0, 1fr))`
|
|
return tracks.map(trackToCSS).join(' ')
|
|
}
|
|
|
|
export function addGridContainer(
|
|
style: DesignStyleDeclaration,
|
|
node: SceneNode,
|
|
css: (field: string, literal: string) => string
|
|
): void {
|
|
style.display = 'grid'
|
|
if (node.gridTemplateColumns.length > 0)
|
|
style['grid-template-columns'] = tracksToCSS(node.gridTemplateColumns)
|
|
if (node.gridTemplateRows.length > 0)
|
|
style['grid-template-rows'] = tracksToCSS(node.gridTemplateRows)
|
|
if (node.gridColumnGap > 0) style['column-gap'] = css('gridColumnGap', `${node.gridColumnGap}px`)
|
|
if (node.gridRowGap > 0) style['row-gap'] = css('gridRowGap', `${node.gridRowGap}px`)
|
|
}
|
|
|
|
/** A span sets the whole placement, so the start follows it. */
|
|
export function addGridPlacement(style: DesignStyleDeclaration, node: SceneNode): void {
|
|
const position = node.gridPosition
|
|
if (!position) return
|
|
if (position.columnSpan > 1)
|
|
style['grid-column'] = `span ${position.columnSpan} / span ${position.columnSpan}`
|
|
if (position.column > 0) style['grid-column-start'] = String(position.column)
|
|
if (position.rowSpan > 1)
|
|
style['grid-row'] = `span ${position.rowSpan} / span ${position.rowSpan}`
|
|
if (position.row > 0) style['grid-row-start'] = String(position.row)
|
|
}
|