feat : improve extra section

This commit is contained in:
2026-09-12 15:28:47 +07:00 Unverified
parent e3eba63071
commit 6d12863860
2 changed files with 49 additions and 132 deletions
+5
View File
@@ -159,6 +159,11 @@
color: var(--accent-foreground);
}
.extra-sheet-grid .Spreadsheet__data-viewer,
.extra-sheet-grid .Spreadsheet__data-viewer * {
color: #000 !important;
}
@media (pointer: coarse) {
[data-slot="button"],
[data-slot="toggle-group-item"],
+44 -132
View File
@@ -1,18 +1,12 @@
"use client";
import {
ArrowDownIcon,
ArrowLeftIcon,
ArrowRightIcon,
ArrowUpIcon,
Columns3Icon,
GripHorizontalIcon,
GripVerticalIcon,
PlusIcon,
PencilIcon,
PaletteIcon,
Redo2Icon,
Rows3Icon,
Trash2Icon,
Undo2Icon,
} from "lucide-react";
@@ -57,23 +51,12 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Kbd } from "@/components/ui/kbd";
import {
addExtraSheetColumn,
addExtraSheetRow,
deleteExtraSheetColumn,
deleteExtraSheetRow,
extraSheetToMatrix,
matrixToExtraSheet,
moveExtraSheetColumn,
@@ -92,7 +75,6 @@ import {
} from "@/lib/rich-text";
import { cn } from "@/lib/utils";
type DeleteTarget = "column" | "row" | null;
interface SheetHistory {
items: ExtraSheet[];
index: number;
@@ -132,20 +114,36 @@ function LiteralDataViewer({ cell }: { cell?: { value?: unknown; backgroundColor
);
}
function DialogDataEditor({
function InlineDataEditor({
cell,
row,
column,
exitEditMode,
onEdit,
}: DataEditorProps<{ value: string }> & {
onEdit: (point: Point, value: string) => void;
}) {
onChange,
}: DataEditorProps<{ value: string }>) {
const inputRef = useRef<HTMLInputElement>(null);
const text = useMemo(() => {
const document = new DOMParser().parseFromString(
normalizeExtraRichText(String(cell?.value ?? "")),
"text/html",
);
document.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
document.querySelectorAll("p + p").forEach((p) => p.prepend("\n"));
return document.body.textContent ?? "";
}, [cell?.value]);
useEffect(() => {
onEdit({ row, column }, String(cell?.value ?? ""));
exitEditMode();
}, [cell?.value, column, exitEditMode, onEdit, row]);
return <LiteralDataViewer cell={cell} />;
const input = inputRef.current;
input?.focus();
input?.setSelectionRange(input.value.length, input.value.length);
}, []);
return (
<div className="Spreadsheet__data-editor">
<input
ref={inputRef}
type="text"
aria-label="แก้ไขข้อความเซลล์"
value={text}
onChange={(event) => onChange({ ...cell, value: event.target.value })}
/>
</div>
);
}
function EditableColumnIndicator({
@@ -244,7 +242,6 @@ export function ExtraSheetEditor({
const [labelDialogOpen, setLabelDialogOpen] = useState(false);
const [renaming, setRenaming] = useState(false);
const [columnLabel, setColumnLabel] = useState("");
const [deleteTarget, setDeleteTarget] = useState<DeleteTarget>(null);
const [removeOpen, setRemoveOpen] = useState(false);
const [cellDialogOpen, setCellDialogOpen] = useState(false);
const [cellDraft, setCellDraft] = useState("");
@@ -411,9 +408,6 @@ export function ExtraSheetEditor({
const rowIndicator = useCallback((props: RowIndicatorProps) => (
<SortableRowIndicator {...props} id={rowDragId(value.rows[props.row].id)} />
), [value.rows]);
const dataEditor = useCallback((props: DataEditorProps<{ value: string }>) => (
<DialogDataEditor {...props} onEdit={openCellEditorAt} />
), [openCellEditorAt]);
return (
<div
@@ -494,92 +488,27 @@ export function ExtraSheetEditor({
>
ล้างสีพื้นหลัง
</Button>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button type="button" variant="outline" size="sm" />}
>
<Rows3Icon data-icon="inline-start" />
แถว {active.row + 1}
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuLabel>จัดการแถว {active.row + 1}</DropdownMenuLabel>
<DropdownMenuItem
disabled={!activeRow || active.row === 0}
onClick={() => {
moveRow(active.row, active.row - 1);
}}
>
<ArrowUpIcon />ย้ายขึ้น
</DropdownMenuItem>
<DropdownMenuItem
disabled={!activeRow || active.row === value.rows.length - 1}
onClick={() => {
moveRow(active.row, active.row + 1);
}}
>
<ArrowDownIcon />ย้ายลง
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem
variant="destructive"
disabled={value.rows.length === 1}
onClick={() => setDeleteTarget("row")}
>
<Trash2Icon />ลบแถว
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button type="button" variant="outline" size="sm" />}
>
<Columns3Icon data-icon="inline-start" />
{activeColumn?.label ?? "คอลัมน์"}
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuLabel>{activeColumn?.label}</DropdownMenuLabel>
<DropdownMenuItem onClick={() => openRenameColumn()}>แก้ไขชื่อ</DropdownMenuItem>
<DropdownMenuItem
disabled={!activeColumn || active.column === 0}
onClick={() => {
moveColumn(active.column, active.column - 1);
}}
>
<ArrowLeftIcon />ย้ายไปทางซ้าย
</DropdownMenuItem>
<DropdownMenuItem
disabled={!activeColumn || active.column === value.columns.length - 1}
onClick={() => {
moveColumn(active.column, active.column + 1);
}}
>
<ArrowRightIcon />ย้ายไปทางขวา
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem
variant="destructive"
disabled={value.columns.length === 1}
onClick={() => setDeleteTarget("column")}
>
<Trash2Icon />ลบคอลัมน์
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
<Button type="button" variant="ghost" size="sm" onClick={() => setRemoveOpen(true)}>
<Trash2Icon data-icon="inline-start" />
ลบตาราง
</Button>
</div>
<div className="overflow-x-auto rounded-lg border">
<div
className="overflow-x-auto rounded-lg border"
onClickCapture={(event) => {
if ((event.target as HTMLElement).closest(".Spreadsheet__active-cell--view")) {
event.stopPropagation();
}
}}
onDoubleClickCapture={(event) => {
if ((event.target as HTMLElement).closest(".Spreadsheet__cell, .Spreadsheet__active-cell")) {
event.preventDefault();
event.stopPropagation();
openCellEditorAt(active);
}
}}
>
<DndContext
sensors={sensors}
collisionDetection={sameAxisCollision}
@@ -594,7 +523,7 @@ export function ExtraSheetEditor({
ColumnIndicator={columnIndicator}
RowIndicator={rowIndicator}
DataViewer={LiteralDataViewer}
DataEditor={dataEditor}
DataEditor={InlineDataEditor}
onActivate={(point) => {
setActive(point);
setSelectedCount(1);
@@ -682,23 +611,6 @@ export function ExtraSheetEditor({
</DialogContent>
</Dialog>
<DeleteConfirmationDialog
open={deleteTarget !== null}
onOpenChange={(open) => { if (!open) setDeleteTarget(null); }}
title={deleteTarget === "column" ? `ลบ ${activeColumn?.label}?` : `ลบแถว ${active.row + 1}?`}
description="ข้อมูลในรายการนี้จะถูกลบออกจากตาราง"
showSuccessToast={false}
onConfirm={() => {
if (deleteTarget === "column") {
commitSheet(deleteExtraSheetColumn(value, active.column));
activateOnly({ ...active, column: Math.max(0, active.column - 1) });
} else if (deleteTarget === "row") {
commitSheet(deleteExtraSheetRow(value, active.row));
activateOnly({ ...active, row: Math.max(0, active.row - 1) });
}
setDeleteTarget(null);
}}
/>
<DeleteConfirmationDialog
open={removeOpen}
onOpenChange={setRemoveOpen}