refactor: font size and split fields
This commit is contained in:
parent
c6580b7c69
commit
ce01044c41
|
|
@ -99,7 +99,10 @@ export function CardPreview(props: CardPreviewProps) {
|
||||||
class={`absolute flex items-center justify-center text-center prose prose-sm ${
|
class={`absolute flex items-center justify-center text-center prose prose-sm ${
|
||||||
store.state.isEditing ? 'bg-blue-500/20 ring-2 ring-blue-500' : ''
|
store.state.isEditing ? 'bg-blue-500/20 ring-2 ring-blue-500' : ''
|
||||||
}`}
|
}`}
|
||||||
style={style}
|
style={{
|
||||||
|
...style,
|
||||||
|
'font-size': `${store.state.dimensions?.fontSize}mm`
|
||||||
|
}}
|
||||||
innerHTML={renderLayerContent(layer, currentCard())}
|
innerHTML={renderLayerContent(layer, currentCard())}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,51 @@ export function PropertiesEditorPanel(props: PropertiesEditorPanelProps) {
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700">尺寸 (mm)</label>
|
<label class="block text-sm font-medium text-gray-700">尺寸 (mm)</label>
|
||||||
<input
|
<div class="flex gap-2">
|
||||||
type="text"
|
<input
|
||||||
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
type="number"
|
||||||
value={store.state.size}
|
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
||||||
onInput={(e) => store.actions.setSize(e.target.value)}
|
value={store.state.sizeW}
|
||||||
/>
|
onChange={(e) => store.actions.setSizeW(Number(e.target.value))}
|
||||||
|
placeholder="宽"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
||||||
|
value={store.state.sizeH}
|
||||||
|
onChange={(e) => store.actions.setSizeH(Number(e.target.value))}
|
||||||
|
placeholder="高"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700">网格</label>
|
<label class="block text-sm font-medium text-gray-700">网格</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
||||||
|
value={store.state.gridW}
|
||||||
|
onChange={(e) => store.actions.setGridW(Number(e.target.value))}
|
||||||
|
placeholder="宽"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
||||||
|
value={store.state.gridH}
|
||||||
|
onChange={(e) => store.actions.setGridH(Number(e.target.value))}
|
||||||
|
placeholder="高"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700">牌面字体 (mm)</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="number"
|
||||||
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
|
||||||
value={store.state.grid}
|
value={store.state.fontSize}
|
||||||
onInput={(e) => store.actions.setGrid(e.target.value)}
|
onChange={(e) => store.actions.setFontSize(Number(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -70,7 +100,7 @@ export function PropertiesEditorPanel(props: PropertiesEditorPanelProps) {
|
||||||
/>
|
/>
|
||||||
<span class="text-sm flex-1">{layer.prop}</span>
|
<span class="text-sm flex-1">{layer.prop}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => store.actions.setEditingLayer(layer.prop)}
|
onClick={() => store.actions.setEditingLayer(store.state.editingLayer === layer.prop ? null : layer.prop)}
|
||||||
class={`text-xs px-2 py-0.5 rounded cursor-pointer ${
|
class={`text-xs px-2 py-0.5 rounded cursor-pointer ${
|
||||||
store.state.editingLayer === layer.prop
|
store.state.editingLayer === layer.prop
|
||||||
? 'bg-blue-500 text-white'
|
? 'bg-blue-500 text-white'
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,24 @@ import type { CardData, LayerConfig, Dimensions } from '../types';
|
||||||
* 默认配置常量
|
* 默认配置常量
|
||||||
*/
|
*/
|
||||||
export const DECK_DEFAULTS = {
|
export const DECK_DEFAULTS = {
|
||||||
SIZE: '54x86',
|
SIZE_W: 54,
|
||||||
GRID: '5x8',
|
SIZE_H: 86,
|
||||||
|
GRID_W: 5,
|
||||||
|
GRID_H: 8,
|
||||||
BLEED: '1',
|
BLEED: '1',
|
||||||
PADDING: '2'
|
PADDING: '2',
|
||||||
|
FONT_SIZE: 3
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export interface DeckState {
|
export interface DeckState {
|
||||||
// 基本属性
|
// 基本属性
|
||||||
size: string;
|
sizeW: number;
|
||||||
grid: string;
|
sizeH: number;
|
||||||
|
gridW: number;
|
||||||
|
gridH: number;
|
||||||
bleed: string;
|
bleed: string;
|
||||||
padding: string;
|
padding: string;
|
||||||
|
fontSize: number;
|
||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
src: string;
|
src: string;
|
||||||
|
|
||||||
|
|
@ -51,10 +57,13 @@ export interface DeckState {
|
||||||
|
|
||||||
export interface DeckActions {
|
export interface DeckActions {
|
||||||
// 基本属性设置
|
// 基本属性设置
|
||||||
setSize: (size: string) => void;
|
setSizeW: (size: number) => void;
|
||||||
setGrid: (grid: string) => void;
|
setSizeH: (size: number) => void;
|
||||||
|
setGridW: (grid: number) => void;
|
||||||
|
setGridH: (grid: number) => void;
|
||||||
setBleed: (bleed: string) => void;
|
setBleed: (bleed: string) => void;
|
||||||
setPadding: (padding: string) => void;
|
setPadding: (padding: string) => void;
|
||||||
|
setFontSize: (size: number) => void;
|
||||||
|
|
||||||
// 数据设置
|
// 数据设置
|
||||||
setCards: (cards: CardData[]) => void;
|
setCards: (cards: CardData[]) => void;
|
||||||
|
|
@ -100,10 +109,13 @@ export function createDeckStore(
|
||||||
initialLayers: string = ''
|
initialLayers: string = ''
|
||||||
): DeckStore {
|
): DeckStore {
|
||||||
const [state, setState] = createStore<DeckState>({
|
const [state, setState] = createStore<DeckState>({
|
||||||
size: DECK_DEFAULTS.SIZE,
|
sizeW: DECK_DEFAULTS.SIZE_W,
|
||||||
grid: DECK_DEFAULTS.GRID,
|
sizeH: DECK_DEFAULTS.SIZE_H,
|
||||||
|
gridW: DECK_DEFAULTS.GRID_W,
|
||||||
|
gridH: DECK_DEFAULTS.GRID_H,
|
||||||
bleed: DECK_DEFAULTS.BLEED,
|
bleed: DECK_DEFAULTS.BLEED,
|
||||||
padding: DECK_DEFAULTS.PADDING,
|
padding: DECK_DEFAULTS.PADDING,
|
||||||
|
fontSize: DECK_DEFAULTS.FONT_SIZE,
|
||||||
fixed: false,
|
fixed: false,
|
||||||
src: initialSrc,
|
src: initialSrc,
|
||||||
dimensions: null,
|
dimensions: null,
|
||||||
|
|
@ -122,20 +134,31 @@ export function createDeckStore(
|
||||||
// 更新尺寸并重新计算 dimensions
|
// 更新尺寸并重新计算 dimensions
|
||||||
const updateDimensions = () => {
|
const updateDimensions = () => {
|
||||||
const dims = calculateDimensions({
|
const dims = calculateDimensions({
|
||||||
size: state.size,
|
sizeW: state.sizeW,
|
||||||
grid: state.grid,
|
sizeH: state.sizeH,
|
||||||
|
gridW: state.gridW,
|
||||||
|
gridH: state.gridH,
|
||||||
bleed: state.bleed,
|
bleed: state.bleed,
|
||||||
padding: state.padding
|
padding: state.padding,
|
||||||
|
fontSize: state.fontSize
|
||||||
});
|
});
|
||||||
setState({ dimensions: dims });
|
setState({ dimensions: dims });
|
||||||
};
|
};
|
||||||
|
|
||||||
const setSize = (size: string) => {
|
const setSizeW = (size: number) => {
|
||||||
setState({ size });
|
setState({ sizeW: size });
|
||||||
updateDimensions();
|
updateDimensions();
|
||||||
};
|
};
|
||||||
const setGrid = (grid: string) => {
|
const setSizeH = (size: number) => {
|
||||||
setState({ grid });
|
setState({ sizeH: size });
|
||||||
|
updateDimensions();
|
||||||
|
};
|
||||||
|
const setGridW = (grid: number) => {
|
||||||
|
setState({ gridW: grid });
|
||||||
|
updateDimensions();
|
||||||
|
};
|
||||||
|
const setGridH = (grid: number) => {
|
||||||
|
setState({ gridH: grid });
|
||||||
updateDimensions();
|
updateDimensions();
|
||||||
};
|
};
|
||||||
const setBleed = (bleed: string) => {
|
const setBleed = (bleed: string) => {
|
||||||
|
|
@ -146,6 +169,10 @@ export function createDeckStore(
|
||||||
setState({ padding });
|
setState({ padding });
|
||||||
updateDimensions();
|
updateDimensions();
|
||||||
};
|
};
|
||||||
|
const setFontSize = (size: number) => {
|
||||||
|
setState({ fontSize: size });
|
||||||
|
updateDimensions();
|
||||||
|
};
|
||||||
|
|
||||||
const setCards = (cards: CardData[]) => setState({ cards, activeTab: 0 });
|
const setCards = (cards: CardData[]) => setState({ cards, activeTab: 0 });
|
||||||
const setActiveTab = (index: number) => setState({ activeTab: index });
|
const setActiveTab = (index: number) => setState({ activeTab: index });
|
||||||
|
|
@ -224,7 +251,7 @@ export function createDeckStore(
|
||||||
.filter(l => l.visible)
|
.filter(l => l.visible)
|
||||||
.map(l => `${l.prop}:${l.x1},${l.y1}-${l.x2},${l.y2}`)
|
.map(l => `${l.prop}:${l.x1},${l.y1}-${l.x2},${l.y2}`)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
return `:md-deck[${state.src}]{size="${state.size}" grid="${state.grid}" bleed="${state.bleed}" padding="${state.padding}" layers="${layersStr}"}`;
|
return `:md-deck[${state.src}]{size="${state.sizeW}x${state.sizeH}" grid="${state.gridW}x${state.gridH}" bleed="${state.bleed}" padding="${state.padding}" fontSize="${state.fontSize}" layers="${layersStr}"}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyCode = async () => {
|
const copyCode = async () => {
|
||||||
|
|
@ -239,10 +266,13 @@ export function createDeckStore(
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions: DeckActions = {
|
const actions: DeckActions = {
|
||||||
setSize,
|
setSizeW,
|
||||||
setGrid,
|
setSizeH,
|
||||||
|
setGridW,
|
||||||
|
setGridH,
|
||||||
setBleed,
|
setBleed,
|
||||||
setPadding,
|
setPadding,
|
||||||
|
setFontSize,
|
||||||
setCards,
|
setCards,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
updateCardData,
|
updateCardData,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
import type { Dimensions } from '../types';
|
import type { Dimensions } from '../types';
|
||||||
|
|
||||||
export interface DimensionOptions {
|
export interface DimensionOptions {
|
||||||
size: string;
|
sizeW: number;
|
||||||
|
sizeH: number;
|
||||||
bleed: string;
|
bleed: string;
|
||||||
padding: string;
|
padding: string;
|
||||||
grid: string;
|
gridW: number;
|
||||||
|
gridH: number;
|
||||||
|
fontSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析卡牌尺寸和网格配置
|
* 解析卡牌尺寸和网格配置
|
||||||
*/
|
*/
|
||||||
export function calculateDimensions(options: DimensionOptions): Dimensions {
|
export function calculateDimensions(options: DimensionOptions): Dimensions {
|
||||||
const [width, height] = options.size.split('x').map(Number);
|
|
||||||
const [bleedW, bleedH] = options.bleed.includes('x')
|
const [bleedW, bleedH] = options.bleed.includes('x')
|
||||||
? options.bleed.split('x').map(Number)
|
? options.bleed.split('x').map(Number)
|
||||||
: [Number(options.bleed), Number(options.bleed)];
|
: [Number(options.bleed), Number(options.bleed)];
|
||||||
|
|
@ -20,19 +22,16 @@ export function calculateDimensions(options: DimensionOptions): Dimensions {
|
||||||
: [Number(options.padding), Number(options.padding)];
|
: [Number(options.padding), Number(options.padding)];
|
||||||
|
|
||||||
// 实际卡牌尺寸(含出血)
|
// 实际卡牌尺寸(含出血)
|
||||||
const cardWidth = width + bleedW * 2;
|
const cardWidth = options.sizeW + bleedW * 2;
|
||||||
const cardHeight = height + bleedH * 2;
|
const cardHeight = options.sizeH + bleedH * 2;
|
||||||
|
|
||||||
// 网格区域尺寸(减去 padding)
|
// 网格区域尺寸(减去 padding)
|
||||||
const gridAreaWidth = width - padW * 2;
|
const gridAreaWidth = options.sizeW - padW * 2;
|
||||||
const gridAreaHeight = height - padH * 2;
|
const gridAreaHeight = options.sizeH - padH * 2;
|
||||||
|
|
||||||
// 解析网格
|
|
||||||
const [gridW, gridH] = options.grid.split('x').map(Number);
|
|
||||||
|
|
||||||
// 每个网格单元的尺寸(mm)
|
// 每个网格单元的尺寸(mm)
|
||||||
const cellWidth = gridAreaWidth / gridW;
|
const cellWidth = gridAreaWidth / options.gridW;
|
||||||
const cellHeight = gridAreaHeight / gridH;
|
const cellHeight = gridAreaHeight / options.gridH;
|
||||||
|
|
||||||
// 网格区域起点(相对于卡牌左上角,含 bleed 和 padding)
|
// 网格区域起点(相对于卡牌左上角,含 bleed 和 padding)
|
||||||
const gridOriginX = bleedW + padW;
|
const gridOriginX = bleedW + padW;
|
||||||
|
|
@ -45,10 +44,11 @@ export function calculateDimensions(options: DimensionOptions): Dimensions {
|
||||||
gridAreaHeight,
|
gridAreaHeight,
|
||||||
cellWidth,
|
cellWidth,
|
||||||
cellHeight,
|
cellHeight,
|
||||||
gridW,
|
gridW: options.gridW,
|
||||||
gridH,
|
gridH: options.gridH,
|
||||||
gridOriginX,
|
gridOriginX,
|
||||||
gridOriginY
|
gridOriginY,
|
||||||
|
fontSize: options.fontSize ?? 3
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,28 @@ import { DataEditorPanel, PropertiesEditorPanel } from './editor-panel';
|
||||||
|
|
||||||
interface DeckProps {
|
interface DeckProps {
|
||||||
size?: string;
|
size?: string;
|
||||||
|
sizeW?: number;
|
||||||
|
sizeH?: number;
|
||||||
grid?: string;
|
grid?: string;
|
||||||
|
gridW?: number;
|
||||||
|
gridH?: number;
|
||||||
bleed?: string;
|
bleed?: string;
|
||||||
padding?: string;
|
padding?: string;
|
||||||
|
fontSize?: number;
|
||||||
layers?: string;
|
layers?: string;
|
||||||
fixed?: boolean | string;
|
fixed?: boolean | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
customElement<DeckProps>('md-deck', {
|
customElement<DeckProps>('md-deck', {
|
||||||
size: '54x86',
|
size: '',
|
||||||
grid: '5x8',
|
sizeW: 54,
|
||||||
|
sizeH: 86,
|
||||||
|
grid: '',
|
||||||
|
gridW: 5,
|
||||||
|
gridH: 8,
|
||||||
bleed: '1',
|
bleed: '1',
|
||||||
padding: '2',
|
padding: '2',
|
||||||
|
fontSize: 3,
|
||||||
layers: '',
|
layers: '',
|
||||||
fixed: false
|
fixed: false
|
||||||
}, (props, { element }) => {
|
}, (props, { element }) => {
|
||||||
|
|
@ -43,11 +53,29 @@ customElement<DeckProps>('md-deck', {
|
||||||
// 创建 store 并加载数据
|
// 创建 store 并加载数据
|
||||||
const store = createDeckStore(resolvedSrc, (props.layers as string) || '');
|
const store = createDeckStore(resolvedSrc, (props.layers as string) || '');
|
||||||
|
|
||||||
// 初始化 store 属性
|
// 解析 size 属性(支持旧格式 "54x86" 和新格式)
|
||||||
store.actions.setSize(props.size || '54x86');
|
if (props.size && props.size.includes('x')) {
|
||||||
store.actions.setGrid(props.grid || '5x8');
|
const [w, h] = props.size.split('x').map(Number);
|
||||||
|
store.actions.setSizeW(w);
|
||||||
|
store.actions.setSizeH(h);
|
||||||
|
} else {
|
||||||
|
store.actions.setSizeW(props.sizeW ?? 54);
|
||||||
|
store.actions.setSizeH(props.sizeH ?? 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 grid 属性(支持旧格式 "5x8" 和新格式)
|
||||||
|
if (props.grid && props.grid.includes('x')) {
|
||||||
|
const [w, h] = props.grid.split('x').map(Number);
|
||||||
|
store.actions.setGridW(w);
|
||||||
|
store.actions.setGridH(h);
|
||||||
|
} else {
|
||||||
|
store.actions.setGridW(props.gridW ?? 5);
|
||||||
|
store.actions.setGridH(props.gridH ?? 8);
|
||||||
|
}
|
||||||
|
|
||||||
store.actions.setBleed(props.bleed || '1');
|
store.actions.setBleed(props.bleed || '1');
|
||||||
store.actions.setPadding(props.padding || '2');
|
store.actions.setPadding(props.padding || '2');
|
||||||
|
store.actions.setFontSize(props.fontSize ?? 3);
|
||||||
|
|
||||||
// 加载 CSV 数据
|
// 加载 CSV 数据
|
||||||
store.actions.loadCardsFromPath(resolvedSrc, (props.layers as string) || '');
|
store.actions.loadCardsFromPath(resolvedSrc, (props.layers as string) || '');
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export interface Dimensions {
|
||||||
gridH: number;
|
gridH: number;
|
||||||
gridOriginX: number;
|
gridOriginX: number;
|
||||||
gridOriginY: number;
|
gridOriginY: number;
|
||||||
|
fontSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectionState {
|
export interface SelectionState {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue