feat(md-deck): rework layer align/orientation pickers
Replace the flat dropdown lists with 3x3 icon grids. Alignment uses arrow icons for the 8 edge/corner positions and a center-align icon for the middle cell; orientation places its 4 arrows on a cross. Cells use fixed-width tracks so they no longer collapse inside the shrink-to-fit dropdown.
This commit is contained in:
@@ -5,8 +5,6 @@ import { createSortable, useDragDropContext } from "@thisbeyond/solid-dnd";
|
|||||||
import type { IconTypes } from "solid-icons";
|
import type { IconTypes } from "solid-icons";
|
||||||
import {
|
import {
|
||||||
TbOutlineAlignCenter,
|
TbOutlineAlignCenter,
|
||||||
TbOutlineAlignLeft,
|
|
||||||
TbOutlineAlignRight,
|
|
||||||
TbOutlineArrowDown,
|
TbOutlineArrowDown,
|
||||||
TbOutlineArrowDownLeft,
|
TbOutlineArrowDownLeft,
|
||||||
TbOutlineArrowDownRight,
|
TbOutlineArrowDownRight,
|
||||||
@@ -50,17 +48,43 @@ const ORIENTATIONS: {
|
|||||||
{ value: "w", label: "西", icon: TbOutlineArrowLeft },
|
{ value: "w", label: "西", icon: TbOutlineArrowLeft },
|
||||||
];
|
];
|
||||||
|
|
||||||
const ALIGNS: { value: Align | ""; icon: IconTypes }[] = [
|
/**
|
||||||
{ value: "", icon: TbOutlineAlignCenter },
|
* The 4 orientations placed on a 3x3 cross (row-major); the remaining
|
||||||
{ value: "l", icon: TbOutlineAlignLeft },
|
* cells are empty so the layout matches the alignment grid:
|
||||||
{ value: "c", icon: TbOutlineAlignCenter },
|
* . n .
|
||||||
{ value: "r", icon: TbOutlineAlignRight },
|
* w . e
|
||||||
{ value: "tl", icon: TbOutlineArrowUpLeft },
|
* . s .
|
||||||
{ value: "tc", icon: TbOutlineArrowUp },
|
*/
|
||||||
{ value: "tr", icon: TbOutlineArrowUpRight },
|
const ORIENT_GRID: ((typeof ORIENTATIONS)[number] | null)[] = [
|
||||||
{ value: "bl", icon: TbOutlineArrowDownLeft },
|
null,
|
||||||
{ value: "bc", icon: TbOutlineArrowDown },
|
ORIENTATIONS[0],
|
||||||
{ value: "br", icon: TbOutlineArrowDownRight },
|
null,
|
||||||
|
ORIENTATIONS[3],
|
||||||
|
null,
|
||||||
|
ORIENTATIONS[1],
|
||||||
|
null,
|
||||||
|
ORIENTATIONS[2],
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 9 alignments laid out as a 3x3 grid (row-major):
|
||||||
|
* tl tc tr
|
||||||
|
* l c r
|
||||||
|
* bl bc br
|
||||||
|
* Every icon is an arrow pointing toward the aligned corner/edge; the
|
||||||
|
* center cell uses the standard center-align icon.
|
||||||
|
*/
|
||||||
|
const ALIGNS: { value: Align; label: string; icon: IconTypes }[] = [
|
||||||
|
{ value: "tl", label: "左上", icon: TbOutlineArrowUpLeft },
|
||||||
|
{ value: "tc", label: "上", icon: TbOutlineArrowUp },
|
||||||
|
{ value: "tr", label: "右上", icon: TbOutlineArrowUpRight },
|
||||||
|
{ value: "l", label: "左", icon: TbOutlineArrowLeft },
|
||||||
|
{ value: "c", label: "居中", icon: TbOutlineAlignCenter },
|
||||||
|
{ value: "r", label: "右", icon: TbOutlineArrowRight },
|
||||||
|
{ value: "bl", label: "左下", icon: TbOutlineArrowDownLeft },
|
||||||
|
{ value: "bc", label: "下", icon: TbOutlineArrowDown },
|
||||||
|
{ value: "br", label: "右下", icon: TbOutlineArrowDownRight },
|
||||||
];
|
];
|
||||||
|
|
||||||
const FONT_PRESETS = [3, 5, 8, 12] as const;
|
const FONT_PRESETS = [3, 5, 8, 12] as const;
|
||||||
@@ -81,26 +105,7 @@ function orientIcon(v: string): IconTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function alignIcon(v: string): IconTypes {
|
function alignIcon(v: string): IconTypes {
|
||||||
switch (v) {
|
return ALIGNS.find((a) => a.value === v)?.icon ?? TbOutlineAlignCenter;
|
||||||
case "tl":
|
|
||||||
return TbOutlineArrowUpLeft;
|
|
||||||
case "tc":
|
|
||||||
return TbOutlineArrowUp;
|
|
||||||
case "tr":
|
|
||||||
return TbOutlineArrowUpRight;
|
|
||||||
case "bl":
|
|
||||||
return TbOutlineArrowDownLeft;
|
|
||||||
case "bc":
|
|
||||||
return TbOutlineArrowDown;
|
|
||||||
case "br":
|
|
||||||
return TbOutlineArrowDownRight;
|
|
||||||
case "l":
|
|
||||||
return TbOutlineAlignLeft;
|
|
||||||
case "r":
|
|
||||||
return TbOutlineAlignRight;
|
|
||||||
default:
|
|
||||||
return TbOutlineAlignCenter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LayerRow(props: LayerRowProps) {
|
export function LayerRow(props: LayerRowProps) {
|
||||||
@@ -179,20 +184,27 @@ export function LayerRow(props: LayerRowProps) {
|
|||||||
}
|
}
|
||||||
title="方向"
|
title="方向"
|
||||||
>
|
>
|
||||||
<For each={ORIENTATIONS}>
|
<div class="grid grid-cols-[repeat(3,1.75rem)] gap-1 p-2">
|
||||||
{(o) => (
|
<For each={ORIENT_GRID}>
|
||||||
<button
|
{(o) =>
|
||||||
onClick={() => props.onUpdateOrientation(o.value)}
|
o ? (
|
||||||
class="block w-full text-left px-3 py-1.5 text-sm hover:bg-gray-100 cursor-pointer whitespace-nowrap"
|
<button
|
||||||
>
|
onClick={() => props.onUpdateOrientation(o.value)}
|
||||||
<Dynamic
|
class={`w-7 h-7 rounded flex items-center justify-center cursor-pointer ${(props.layer.orientation ?? "n") === o.value ? "bg-blue-500 text-white" : "bg-gray-100 hover:bg-gray-200 text-gray-700"}`}
|
||||||
component={o.icon}
|
title={o.label}
|
||||||
class="inline-block align-[-0.125em]"
|
>
|
||||||
/>{" "}
|
<Dynamic
|
||||||
{o.label}
|
component={o.icon}
|
||||||
</button>
|
size={16}
|
||||||
)}
|
class="not-prose max-w-none"
|
||||||
</For>
|
/>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
|
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
@@ -208,20 +220,23 @@ export function LayerRow(props: LayerRowProps) {
|
|||||||
}
|
}
|
||||||
title="对齐"
|
title="对齐"
|
||||||
>
|
>
|
||||||
<For each={ALIGNS}>
|
<div class="grid grid-cols-[repeat(3,1.75rem)] gap-1 p-2">
|
||||||
{(o) => (
|
<For each={ALIGNS}>
|
||||||
<button
|
{(o) => (
|
||||||
onClick={() => props.onUpdateAlign(o.value || undefined)}
|
<button
|
||||||
class="flex items-center gap-2 w-full px-3 py-1.5 text-sm hover:bg-gray-100 cursor-pointer whitespace-nowrap"
|
onClick={() => props.onUpdateAlign(o.value)}
|
||||||
>
|
class={`w-7 h-7 rounded flex items-center justify-center cursor-pointer ${(props.layer.align ?? "c") === o.value ? "bg-blue-500 text-white" : "bg-gray-100 hover:bg-gray-200 text-gray-700"}`}
|
||||||
<Dynamic
|
title={o.label}
|
||||||
component={o.icon}
|
>
|
||||||
size={16}
|
<Dynamic
|
||||||
class="not-prose max-w-none"
|
component={o.icon}
|
||||||
/>
|
size={16}
|
||||||
</button>
|
class="not-prose max-w-none"
|
||||||
)}
|
/>
|
||||||
</For>
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
|
|
||||||
<div class="relative" ref={fontRef}>
|
<div class="relative" ref={fontRef}>
|
||||||
|
|||||||
Reference in New Issue
Block a user