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

130 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-02-27 14:19:26 +08:00
import { For } from 'solid-js';
2026-02-27 14:58:44 +08:00
import type { DeckStore } from './stores/deckStore';
2026-02-27 12:24:51 +08:00
export interface DataEditorPanelProps {
activeTab: number;
2026-02-27 14:58:44 +08:00
cards: DeckStore['state']['cards'];
updateCardData: DeckStore['actions']['updateCardData'];
2026-02-27 12:24:51 +08:00
}
export interface PropertiesEditorPanelProps {
2026-02-27 14:19:26 +08:00
store: DeckStore;
2026-02-27 12:24:51 +08:00
}
/**
* CSV
*/
export function DataEditorPanel(props: DataEditorPanelProps) {
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 14:02:11 +08:00
<For each={Object.keys(props.cards[props.activeTab] || {})}>
{(key) => (
<div>
<label class="block text-sm font-medium text-gray-700">{key}</label>
<textarea
class="w-full border border-gray-300 rounded px-2 py-1 text-sm"
rows={3}
value={props.cards[props.activeTab]?.[key] || ''}
2026-02-27 14:19:26 +08:00
onInput={(e) => props.updateCardData(props.activeTab, key, e.target.value)}
2026-02-27 14:02:11 +08:00
/>
</div>
)}
</For>
2026-02-27 12:24:51 +08:00
</div>
</div>
);
}
/**
*
*/
export function PropertiesEditorPanel(props: PropertiesEditorPanelProps) {
2026-02-27 14:19:26 +08:00
const { store } = props;
2026-02-27 14:58:44 +08:00
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-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"
2026-02-27 14:58:44 +08:00
value={store.state.size}
onInput={(e) => store.actions.setSize(e.target.value)}
2026-02-27 12:24:51 +08:00
/>
</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"
2026-02-27 14:58:44 +08:00
value={store.state.grid}
onInput={(e) => store.actions.setGrid(e.target.value)}
2026-02-27 12:24:51 +08:00
/>
</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"
2026-02-27 14:58:44 +08:00
value={store.state.bleed}
onInput={(e) => store.actions.setBleed(e.target.value)}
2026-02-27 12:24:51 +08:00
/>
</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"
2026-02-27 14:58:44 +08:00
value={store.state.padding}
onInput={(e) => store.actions.setPadding(e.target.value)}
2026-02-27 12:24:51 +08:00
/>
</div>
<hr class="my-4" />
<h4 class="font-medium text-sm text-gray-700"></h4>
2026-02-27 14:58:44 +08:00
<For each={store.state.layerConfigs}>
2026-02-27 12:24:51 +08:00
{(layer) => (
<div class="flex items-center gap-2">
<input
type="checkbox"
checked={layer.visible}
2026-02-27 14:58:44 +08:00
onChange={() => store.actions.toggleLayerVisible(layer.prop)}
2026-02-27 12:24:51 +08:00
class="cursor-pointer"
/>
<span class="text-sm flex-1">{layer.prop}</span>
<button
2026-02-27 14:58:44 +08:00
onClick={() => store.actions.setEditingLayer(layer.prop)}
2026-02-27 12:24:51 +08:00
class={`text-xs px-2 py-0.5 rounded cursor-pointer ${
2026-02-27 14:58:44 +08:00
store.state.editingLayer === layer.prop
2026-02-27 12:24:51 +08:00
? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
2026-02-27 14:58:44 +08:00
{store.state.editingLayer === layer.prop ? '✓ 框选' : '编辑位置'}
2026-02-27 12:24:51 +08:00
</button>
</div>
)}
</For>
<hr class="my-4" />
<button
2026-02-27 14:58:44 +08:00
onClick={store.actions.copyCode}
2026-02-27 12:24:51 +08:00
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>
);
}