2023-12-16 11:39:13 +08:00
|
|
|
import { useContext, useState } from "react";
|
2023-09-19 20:48:55 +08:00
|
|
|
import {
|
|
|
|
Empty,
|
|
|
|
Row,
|
|
|
|
Col,
|
|
|
|
AutoComplete,
|
|
|
|
Button,
|
|
|
|
Input,
|
|
|
|
Popover,
|
|
|
|
Toast,
|
|
|
|
} from "@douyinfe/semi-ui";
|
|
|
|
import {
|
|
|
|
IllustrationNoContent,
|
|
|
|
IllustrationNoContentDark,
|
|
|
|
} from "@douyinfe/semi-illustrations";
|
|
|
|
import {
|
|
|
|
IconPlus,
|
|
|
|
IconSearch,
|
|
|
|
IconCheckboxTick,
|
|
|
|
IconDeleteStroked,
|
|
|
|
} from "@douyinfe/semi-icons";
|
2023-09-19 20:50:06 +08:00
|
|
|
import {
|
|
|
|
defaultTableTheme,
|
|
|
|
tableThemes,
|
|
|
|
Action,
|
|
|
|
ObjectType,
|
|
|
|
} from "../data/data";
|
2023-10-19 16:36:46 +08:00
|
|
|
import { AreaContext, UndoRedoContext } from "../pages/Editor";
|
2023-09-19 20:48:55 +08:00
|
|
|
|
2023-12-16 11:39:13 +08:00
|
|
|
export default function AreaOverview() {
|
2023-09-19 20:50:15 +08:00
|
|
|
const { areas, addArea, deleteArea, updateArea } = useContext(AreaContext);
|
2023-09-19 20:50:06 +08:00
|
|
|
const { setUndoStack, setRedoStack } = useContext(UndoRedoContext);
|
|
|
|
const [editField, setEditField] = useState({});
|
2023-09-19 20:48:55 +08:00
|
|
|
const [value, setValue] = useState("");
|
|
|
|
const [filteredResult, setFilteredResult] = useState(
|
|
|
|
areas.map((t) => {
|
|
|
|
return t.name;
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleStringSearch = (value) => {
|
|
|
|
setFilteredResult(
|
|
|
|
areas
|
|
|
|
.map((t) => {
|
|
|
|
return t.name;
|
|
|
|
})
|
|
|
|
.filter((i) => i.includes(value))
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<Row gutter={6}>
|
|
|
|
<Col span={16}>
|
|
|
|
<AutoComplete
|
|
|
|
data={filteredResult}
|
|
|
|
value={value}
|
|
|
|
showClear
|
|
|
|
prefix={<IconSearch />}
|
|
|
|
placeholder="Search..."
|
2023-09-19 20:51:08 +08:00
|
|
|
emptyContent={
|
|
|
|
<div className="p-3 popover-theme">No areas found</div>
|
|
|
|
}
|
2023-09-19 20:48:55 +08:00
|
|
|
onSearch={(v) => handleStringSearch(v)}
|
|
|
|
onChange={(v) => setValue(v)}
|
|
|
|
onSelect={(v) => {
|
|
|
|
const { id } = areas.find((t) => t.name === v);
|
|
|
|
document
|
|
|
|
.getElementById(`scroll_area_${id}`)
|
|
|
|
.scrollIntoView({ behavior: "smooth" });
|
|
|
|
}}
|
|
|
|
className="w-full"
|
|
|
|
/>
|
|
|
|
</Col>
|
|
|
|
<Col span={8}>
|
2023-09-19 20:49:57 +08:00
|
|
|
<Button icon={<IconPlus />} block onClick={() => addArea()}>
|
2023-09-19 20:48:55 +08:00
|
|
|
Add area
|
|
|
|
</Button>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
{areas.length <= 0 ? (
|
2023-09-19 20:51:08 +08:00
|
|
|
<div className="select-none mt-2">
|
2023-09-19 20:48:55 +08:00
|
|
|
<Empty
|
|
|
|
image={
|
2023-09-19 20:51:08 +08:00
|
|
|
<IllustrationNoContent style={{ width: 154, height: 154 }} />
|
2023-09-19 20:48:55 +08:00
|
|
|
}
|
|
|
|
darkModeImage={
|
2023-09-19 20:51:08 +08:00
|
|
|
<IllustrationNoContentDark style={{ width: 154, height: 154 }} />
|
2023-09-19 20:48:55 +08:00
|
|
|
}
|
|
|
|
title="No subject areas"
|
|
|
|
description="Add subject areas to compartmentalize tables!"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<div className="p-2">
|
|
|
|
{areas.map((a, i) => (
|
|
|
|
<Row
|
|
|
|
gutter={6}
|
|
|
|
type="flex"
|
|
|
|
justify="start"
|
|
|
|
align="middle"
|
|
|
|
key={i}
|
|
|
|
id={`scroll_area_${a.id}`}
|
|
|
|
className="my-3"
|
|
|
|
>
|
|
|
|
<Col span={18}>
|
|
|
|
<Input
|
|
|
|
value={a.name}
|
2023-09-19 20:49:07 +08:00
|
|
|
placeholder="Name"
|
2023-09-19 20:50:06 +08:00
|
|
|
onChange={(value) => updateArea(a.id, { name: value })}
|
|
|
|
onFocus={(e) => setEditField({ name: e.target.value })}
|
|
|
|
onBlur={(e) => {
|
|
|
|
if (e.target.value === editField.name) return;
|
|
|
|
setUndoStack((prev) => [
|
|
|
|
...prev,
|
|
|
|
{
|
|
|
|
action: Action.EDIT,
|
|
|
|
element: ObjectType.AREA,
|
|
|
|
aid: i,
|
|
|
|
undo: editField,
|
|
|
|
redo: { name: e.target.value },
|
2023-09-19 20:50:52 +08:00
|
|
|
message: `Edit area name to ${e.target.value}`,
|
2023-09-19 20:50:06 +08:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
setRedoStack([]);
|
|
|
|
}}
|
2023-09-19 20:48:55 +08:00
|
|
|
/>
|
|
|
|
</Col>
|
|
|
|
<Col span={3}>
|
|
|
|
<Popover
|
|
|
|
content={
|
2023-09-19 20:51:08 +08:00
|
|
|
<div className="popover-theme">
|
2023-09-19 20:48:55 +08:00
|
|
|
<div className="flex justify-between items-center p-2">
|
|
|
|
<div className="font-medium">Theme</div>
|
|
|
|
<Button
|
|
|
|
type="tertiary"
|
|
|
|
size="small"
|
|
|
|
onClick={() =>
|
|
|
|
updateArea(i, { color: defaultTableTheme })
|
|
|
|
}
|
|
|
|
>
|
|
|
|
Clear
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
<div className="py-3">
|
|
|
|
<div>
|
|
|
|
{tableThemes
|
|
|
|
.slice(0, Math.ceil(tableThemes.length / 2))
|
|
|
|
.map((c) => (
|
|
|
|
<button
|
|
|
|
key={c}
|
|
|
|
style={{ backgroundColor: c }}
|
|
|
|
className="p-3 rounded-full mx-1"
|
2023-09-19 20:50:06 +08:00
|
|
|
onClick={() => {
|
|
|
|
setUndoStack((prev) => [
|
|
|
|
...prev,
|
|
|
|
{
|
|
|
|
action: Action.EDIT,
|
|
|
|
element: ObjectType.AREA,
|
|
|
|
aid: i,
|
|
|
|
undo: { color: a.color },
|
|
|
|
redo: { color: c },
|
2023-09-19 20:50:52 +08:00
|
|
|
message: `Edit area color to ${c}`,
|
2023-09-19 20:50:06 +08:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
setRedoStack([]);
|
|
|
|
updateArea(i, { color: c });
|
|
|
|
}}
|
2023-09-19 20:48:55 +08:00
|
|
|
>
|
|
|
|
{a.color === c ? (
|
|
|
|
<IconCheckboxTick
|
|
|
|
style={{ color: "white" }}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<IconCheckboxTick style={{ color: c }} />
|
|
|
|
)}
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
<div className="mt-3">
|
|
|
|
{tableThemes
|
|
|
|
.slice(Math.ceil(tableThemes.length / 2))
|
|
|
|
.map((c) => (
|
|
|
|
<button
|
|
|
|
key={c}
|
|
|
|
style={{ backgroundColor: c }}
|
|
|
|
className="p-3 rounded-full mx-1"
|
2023-09-19 20:50:06 +08:00
|
|
|
onClick={() => {
|
|
|
|
setUndoStack((prev) => [
|
|
|
|
...prev,
|
|
|
|
{
|
|
|
|
action: Action.EDIT,
|
|
|
|
element: ObjectType.AREA,
|
|
|
|
aid: i,
|
|
|
|
undo: { color: a.color },
|
|
|
|
redo: { color: c },
|
2023-09-19 20:50:52 +08:00
|
|
|
message: `Edit area color to ${c}`,
|
2023-09-19 20:50:06 +08:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
setRedoStack([]);
|
|
|
|
updateArea(i, { color: c });
|
|
|
|
}}
|
2023-09-19 20:48:55 +08:00
|
|
|
>
|
|
|
|
<IconCheckboxTick
|
|
|
|
style={{
|
|
|
|
color: a.color === c ? "white" : c,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
trigger="click"
|
|
|
|
position="bottomLeft"
|
|
|
|
showArrow
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
className="h-[32px] w-[32px] rounded"
|
|
|
|
style={{ backgroundColor: a.color }}
|
|
|
|
/>
|
|
|
|
</Popover>
|
|
|
|
</Col>
|
|
|
|
<Col span={3}>
|
|
|
|
<Button
|
|
|
|
icon={<IconDeleteStroked />}
|
|
|
|
type="danger"
|
2023-09-19 20:50:18 +08:00
|
|
|
onClick={() => {
|
2023-09-19 20:48:55 +08:00
|
|
|
Toast.success(`Area deleted!`);
|
2023-09-19 20:49:57 +08:00
|
|
|
deleteArea(i, true);
|
2023-09-19 20:48:55 +08:00
|
|
|
}}
|
|
|
|
></Button>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|