refactor: layer alignment

This commit is contained in:
hypercross 2026-03-27 15:17:25 +08:00
parent 273f949839
commit ea57cf8d2b
4 changed files with 47 additions and 8 deletions

View File

@ -26,16 +26,23 @@ export function CardLayer(props: CardLayerProps) {
const iconPath = resolvePath(props.store.state.cards.sourcePath, props.cardData.iconPath ?? "./assets"); const iconPath = resolvePath(props.store.state.cards.sourcePath, props.cardData.iconPath ?? "./assets");
return parseMarkdown(processVariables(content, props.cardData, props.store.state.cards), iconPath) as string; return parseMarkdown(processVariables(content, props.cardData, props.store.state.cards), iconPath) as string;
} }
const getAlignStyle = (align?: 'l' | 'c' | 'r') => {
if (align === 'l') return 'left';
if (align === 'r') return 'right';
return 'center';
};
return ( return (
<For each={layers()}> <For each={layers()}>
{(layer) => { {(layer) => {
return ( return (
<> <>
<article <article
class="absolute flex flex-col items-center justify-center text-center prose prose-sm" class="absolute flex flex-col items-stretch justify-center prose prose-sm"
style={{ style={{
...getLayerStyle(layer, dimensions()), ...getLayerStyle(layer, dimensions()),
'font-size': `${layer.fontSize || 3}mm` 'font-size': `${layer.fontSize || 3}mm`,
'text-align': getAlignStyle(layer.align)
}} }}
innerHTML={renderLayerContent(props.cardData[layer.prop])} innerHTML={renderLayerContent(props.cardData[layer.prop])}
/> />

View File

@ -12,6 +12,13 @@ const ORIENTATION_OPTIONS = [
{ value: 'w', label: '← 西' } { value: 'w', label: '← 西' }
] as const; ] as const;
const ALIGN_OPTIONS = [
{ value: '', label: '对齐' },
{ value: 'l', label: '← 左' },
{ value: 'c', label: '≡ 中' },
{ value: 'r', label: '→ 右' }
] as const;
/** /**
* *
*/ */
@ -38,6 +45,13 @@ export function LayerEditorPanel(props: LayerEditorPanelProps) {
updateFn(layerProp, { fontSize }); updateFn(layerProp, { fontSize });
}; };
const updateLayerAlign = (layerProp: string, align?: 'l' | 'c' | 'r') => {
const updateFn = store.state.activeSide === 'front'
? store.actions.updateFrontLayerConfig
: store.actions.updateBackLayerConfig;
updateFn(layerProp, { align });
};
const toggleLayerVisible = (layerProp: string) => { const toggleLayerVisible = (layerProp: string) => {
const toggleFn = store.state.activeSide === 'front' const toggleFn = store.state.activeSide === 'front'
? store.actions.toggleFrontLayerVisible ? store.actions.toggleFrontLayerVisible
@ -94,6 +108,17 @@ export function LayerEditorPanel(props: LayerEditorPanelProps) {
)} )}
</For> </For>
</select> </select>
<select
value={layer.align || ''}
onChange={(e) => updateLayerAlign(layer.prop, e.target.value as 'l' | 'c' | 'r' | undefined || undefined)}
class="text-xs px-2 py-1 rounded border border-gray-300 bg-white cursor-pointer"
>
<For each={ALIGN_OPTIONS}>
{(opt) => (
<option value={opt.value}>{opt.label}</option>
)}
</For>
</select>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<label class="text-xs text-gray-600">/mm</label> <label class="text-xs text-gray-600">/mm</label>

View File

@ -3,15 +3,15 @@ import {CSV} from "../../utils/csv-loader";
/** /**
* layers * layers
* body:1,7-5,8 title:1,1-4,1f6.6s * body:1,7-5,8 title:1,1-4,1f6.6sl
* f[fontSize] * f[fontSize] l/c/r
*/ */
export function parseLayers(layersStr: string): Layer[] { export function parseLayers(layersStr: string): Layer[] {
if (!layersStr) return []; if (!layersStr) return [];
const layers: Layer[] = []; const layers: Layer[] = [];
// 匹配prop:x1,y1-x2,y2[ffontSize][direction] // 匹配prop:x1,y1-x2,y2[ffontSize][direction][align]
const regex = /(\w+):(\d+),(\d+)-(\d+),(\d+)(?:f([\d.]+))?([nsew])?/g; const regex = /(\w+):(\d+),(\d+)-(\d+),(\d+)(?:f([\d.]+))?([nsew])?([lcr])?/g;
let match; let match;
while ((match = regex.exec(layersStr)) !== null) { while ((match = regex.exec(layersStr)) !== null) {
@ -22,7 +22,8 @@ export function parseLayers(layersStr: string): Layer[] {
x2: parseInt(match[4]), x2: parseInt(match[4]),
y2: parseInt(match[5]), y2: parseInt(match[5]),
fontSize: match[6] ? parseFloat(match[6]) : undefined, fontSize: match[6] ? parseFloat(match[6]) : undefined,
orientation: match[7] as 'n' | 's' | 'e' | 'w' | undefined orientation: match[7] as 'n' | 's' | 'e' | 'w' | undefined,
align: match[8] as 'l' | 'c' | 'r' | undefined
}); });
} }
@ -43,6 +44,9 @@ export function formatLayers(layers: LayerConfig[]): string {
if (l.orientation && l.orientation !== 'n') { if (l.orientation && l.orientation !== 'n') {
str += l.orientation; str += l.orientation;
} }
if (l.align) {
str += l.align;
}
return str; return str;
}) })
.join(' '); .join(' ');
@ -68,7 +72,8 @@ export function initLayerConfigsForSide(
x2: existing?.x2 || 2, x2: existing?.x2 || 2,
y2: existing?.y2 || 2, y2: existing?.y2 || 2,
orientation: existing?.orientation, orientation: existing?.orientation,
fontSize: existing?.fontSize fontSize: existing?.fontSize,
align: existing?.align
}; };
}); });
} }

View File

@ -14,6 +14,7 @@ export interface Layer {
y2: number; y2: number;
orientation?: 'n' | 's' | 'e' | 'w'; orientation?: 'n' | 's' | 'e' | 'w';
fontSize?: number; fontSize?: number;
align?: 'l' | 'c' | 'r';
} }
export interface LayerConfig { export interface LayerConfig {
@ -25,6 +26,7 @@ export interface LayerConfig {
y2: number; y2: number;
orientation?: 'n' | 's' | 'e' | 'w'; orientation?: 'n' | 's' | 'e' | 'w';
fontSize?: number; fontSize?: number;
align?: 'l' | 'c' | 'r';
} }
export interface Dimensions { export interface Dimensions {