ttrpg-tools/src/components/editor-panel.tsx

155 lines
4.7 KiB
TypeScript
Raw Normal View History

2026-02-27 12:24:51 +08:00
import { Show, For } from 'solid-js';
import type { CardData, LayerConfig } from './types';
2026-02-27 13:00:24 +08:00
import { FieldEditor, PropertyForm } from './FieldEditor';
import type { FieldSchema } from './hooks/types';
2026-02-27 12:24:51 +08:00
export interface DataEditorPanelProps {
cards: CardData[];
activeTab: number;
updateCardData: (key: string, value: string) => void;
}
export interface PropertiesEditorPanelProps {
localSize: string;
localGrid: string;
localBleed: string;
localPadding: string;
layerConfigs: LayerConfig[];
editingLayer: string | null;
onSizeChange: (value: string) => void;
onGridChange: (value: string) => void;
onBleedChange: (value: string) => void;
onPaddingChange: (value: string) => void;
onToggleLayerVisible: (prop: string) => void;
onStartEditingLayer: (prop: string) => void;
onCopyCode: () => void;
}
2026-02-27 13:00:24 +08:00
/**
* CardData Schema
*/
function createCardDataSchema(keys: string[]): FieldSchema[] {
return keys.map((key) => ({
key,
label: key.charAt(0).toUpperCase() + key.slice(1),
type: 'text',
rows: 3
}));
}
2026-02-27 12:24:51 +08:00
/**
* CSV
2026-02-27 13:00:24 +08:00
* 使 FieldEditor
2026-02-27 12:24:51 +08:00
*/
export function DataEditorPanel(props: DataEditorPanelProps) {
2026-02-27 13:00:24 +08:00
const currentCard = () => props.cards[props.activeTab] || {};
const fieldKeys = () => Object.keys(currentCard());
const schema = () => ({ fields: createCardDataSchema(fieldKeys()) });
const handleChange = (key: string, value: string | number | boolean) => {
props.updateCardData(key, String(value));
};
2026-02-27 12:24:51 +08:00
return (
<div class="w-64 flex-shrink-0">
<h3 class="font-bold mb-2"></h3>
<div class="space-y-2 max-h-96 overflow-y-auto">
2026-02-27 13:00:24 +08:00
<PropertyForm
schema={schema()}
values={currentCard()}
onChange={handleChange}
/>
2026-02-27 12:24:51 +08:00
</div>
</div>
);
}
/**
*
*/
export function PropertiesEditorPanel(props: PropertiesEditorPanelProps) {
return (
<div class="w-64 flex-shrink-0">
<h3 class="font-bold mb-2"></h3>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-gray-700"> (mm)</label>
<input
type="text"
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
value={props.localSize}
onInput={(e) => props.onSizeChange(e.target.value)}
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"></label>
<input
type="text"
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
value={props.localGrid}
onInput={(e) => props.onGridChange(e.target.value)}
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"> (mm)</label>
<input
type="text"
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
value={props.localBleed}
onInput={(e) => props.onBleedChange(e.target.value)}
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"> (mm)</label>
<input
type="text"
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
value={props.localPadding}
onInput={(e) => props.onPaddingChange(e.target.value)}
/>
</div>
<hr class="my-4" />
<h4 class="font-medium text-sm text-gray-700"></h4>
<For each={props.layerConfigs}>
{(layer) => (
<div class="flex items-center gap-2">
<input
type="checkbox"
checked={layer.visible}
onChange={() => props.onToggleLayerVisible(layer.prop)}
class="cursor-pointer"
/>
<span class="text-sm flex-1">{layer.prop}</span>
<button
onClick={() => props.onStartEditingLayer(layer.prop)}
class={`text-xs px-2 py-0.5 rounded cursor-pointer ${
props.editingLayer === layer.prop
? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
{props.editingLayer === layer.prop ? '✓ 框选' : '编辑位置'}
</button>
</div>
)}
</For>
<hr class="my-4" />
<button
onClick={props.onCopyCode}
class="w-full bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-sm font-medium cursor-pointer"
>
📋
</button>
</div>
</div>
);
}