wip fix
This commit is contained in:
parent
4711bafef1
commit
51d0f5accd
|
|
@ -146,24 +146,25 @@ model org_user {
|
||||||
}
|
}
|
||||||
|
|
||||||
model page {
|
model page {
|
||||||
id String @id(map: "page_id") @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
id String @id(map: "page_id") @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
name String
|
name String
|
||||||
url String
|
url String
|
||||||
content_tree Json
|
content_tree Json
|
||||||
id_site String @db.Uuid
|
id_site String @db.Uuid
|
||||||
created_at DateTime? @default(now()) @db.Timestamp(6)
|
created_at DateTime? @default(now()) @db.Timestamp(6)
|
||||||
js_compiled String?
|
js_compiled String?
|
||||||
js String?
|
js String?
|
||||||
updated_at DateTime? @default(now()) @db.Timestamp(6)
|
updated_at DateTime? @default(now()) @db.Timestamp(6)
|
||||||
id_folder String? @db.Uuid
|
id_folder String? @db.Uuid
|
||||||
is_deleted Boolean @default(false)
|
is_deleted Boolean @default(false)
|
||||||
id_layout String? @db.Uuid
|
id_layout String? @db.Uuid
|
||||||
is_default_layout Boolean @default(false)
|
is_default_layout Boolean @default(false)
|
||||||
code_assign code_assign[]
|
code_assign code_assign[]
|
||||||
page_folder page_folder? @relation(fields: [id_folder], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
page_folder page_folder? @relation(fields: [id_folder], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
page page? @relation("pageTopage", fields: [id_layout], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
page page? @relation("pageTopage", fields: [id_layout], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
other_page page[] @relation("pageTopage")
|
other_page page[] @relation("pageTopage")
|
||||||
site site @relation(fields: [id_site], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
site site @relation(fields: [id_site], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
|
page_history page_history[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model page_folder {
|
model page_folder {
|
||||||
|
|
@ -307,3 +308,11 @@ model code_assign {
|
||||||
component_group component_group? @relation(fields: [id_component_group], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
component_group component_group? @relation(fields: [id_component_group], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
page page? @relation(fields: [id_page], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
page page? @relation(fields: [id_page], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model page_history {
|
||||||
|
id String @id(map: "page_history_id") @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
|
id_page String @db.Uuid
|
||||||
|
content_tree Bytes
|
||||||
|
ts String
|
||||||
|
page page @relation(fields: [id_page], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { SyncConnection } from "../type";
|
||||||
import { parseJs } from "../editor/parser/parse-js";
|
import { parseJs } from "../editor/parser/parse-js";
|
||||||
import { snapshot } from "../entity/snapshot";
|
import { snapshot } from "../entity/snapshot";
|
||||||
import { validate } from "uuid";
|
import { validate } from "uuid";
|
||||||
|
import { gzipAsync } from "utils/diff";
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
const timeout = {
|
const timeout = {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import { SAction } from "../actions";
|
||||||
import { docs } from "../entity/docs";
|
import { docs } from "../entity/docs";
|
||||||
import { gunzipAsync } from "../entity/zlib";
|
import { gunzipAsync } from "../entity/zlib";
|
||||||
import { SyncConnection } from "../type";
|
import { SyncConnection } from "../type";
|
||||||
|
import { gzipAsync } from "utils/diff";
|
||||||
|
|
||||||
|
const history = {} as Record<string, string>;
|
||||||
|
|
||||||
export const yjs_diff_local: SAction["yjs"]["diff_local"] = async function (
|
export const yjs_diff_local: SAction["yjs"]["diff_local"] = async function (
|
||||||
this: SyncConnection,
|
this: SyncConnection,
|
||||||
|
|
@ -25,6 +28,32 @@ export const yjs_diff_local: SAction["yjs"]["diff_local"] = async function (
|
||||||
if (root) {
|
if (root) {
|
||||||
if (mode === "page") {
|
if (mode === "page") {
|
||||||
if (validate(id) && id) {
|
if (validate(id) && id) {
|
||||||
|
let mode = "create" as "create" | "update";
|
||||||
|
const cur = Math.round(Date.now() / 5000) + "";
|
||||||
|
if (history[id] && history[id] === cur) {
|
||||||
|
mode = "update";
|
||||||
|
}
|
||||||
|
history[id] = cur;
|
||||||
|
if (mode === "create") {
|
||||||
|
await _db.page_history.create({
|
||||||
|
data: {
|
||||||
|
id_page: id,
|
||||||
|
content_tree: await gzipAsync(JSON.stringify(root.toJSON())),
|
||||||
|
ts: history[id],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await _db.page_history.updateMany({
|
||||||
|
data: {
|
||||||
|
content_tree: await gzipAsync(JSON.stringify(root.toJSON())),
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
id_page: id,
|
||||||
|
ts: history[id],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await _db.page.update({
|
await _db.page.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import { EdPopComp } from "./panel/popup/comp/comp-popup";
|
||||||
import { EdPopPage } from "./panel/popup/page/page-popup";
|
import { EdPopPage } from "./panel/popup/page/page-popup";
|
||||||
import { EdPopScript } from "./panel/popup/script/pop-script";
|
import { EdPopScript } from "./panel/popup/script/pop-script";
|
||||||
import { EdPopSite } from "./panel/popup/site/site-popup";
|
import { EdPopSite } from "./panel/popup/site/site-popup";
|
||||||
|
import { EdPageHistoryMain } from "./panel/main/main-history";
|
||||||
|
|
||||||
export const EdBase = () => {
|
export const EdBase = () => {
|
||||||
const p = useGlobal(EDGlobal, "EDITOR");
|
const p = useGlobal(EDGlobal, "EDITOR");
|
||||||
|
|
@ -51,22 +52,27 @@ export const EdBase = () => {
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-1 flex-col items-stretch">
|
<div className="flex flex-1 flex-col items-stretch">
|
||||||
<EdMid />
|
<EdMid />
|
||||||
<div
|
|
||||||
className={cx(
|
{p.page.history.id ? (
|
||||||
"flex flex-1 items-stretch",
|
<EdPageHistoryMain />
|
||||||
p.mode === "mobile" ? mobileCSS : "bg-white"
|
) : (
|
||||||
)}
|
<div
|
||||||
>
|
className={cx(
|
||||||
{p.status !== "ready" ? (
|
"flex flex-1 items-stretch",
|
||||||
<Loading note={`page-${p.status}`} />
|
p.mode === "mobile" ? mobileCSS : "bg-white"
|
||||||
) : (
|
)}
|
||||||
<>
|
>
|
||||||
<EdMain />
|
{p.status !== "ready" ? (
|
||||||
<EdPane type="right" min_size={240} />
|
<Loading note={`page-${p.status}`} />
|
||||||
<EdRight />
|
) : (
|
||||||
</>
|
<>
|
||||||
)}
|
<EdMain />
|
||||||
</div>
|
<EdPane type="right" min_size={240} />
|
||||||
|
<EdRight />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,17 @@ import { EdApi } from "./panel/header/left/api";
|
||||||
import { EdSiteJS } from "./panel/header/left/js";
|
import { EdSiteJS } from "./panel/header/left/js";
|
||||||
import { EdSitePicker } from "./panel/header/left/site-picker";
|
import { EdSitePicker } from "./panel/header/left/site-picker";
|
||||||
import { EdTreeBody } from "./panel/tree/body";
|
import { EdTreeBody } from "./panel/tree/body";
|
||||||
|
import { EdPageHistoryBtn } from "./panel/tree/history-btn";
|
||||||
|
import { EdPageHistoryList } from "./panel/tree/history-list";
|
||||||
import { EdTreeSearch } from "./panel/tree/search";
|
import { EdTreeSearch } from "./panel/tree/search";
|
||||||
|
import { treeRebuild } from "./logic/tree/build";
|
||||||
|
|
||||||
export const EdLeft = () => {
|
export const EdLeft = () => {
|
||||||
const p = useGlobal(EDGlobal, "EDITOR");
|
const p = useGlobal(EDGlobal, "EDITOR");
|
||||||
const local = useLocal({ tree: null as any, timeout: null as any });
|
const local = useLocal({
|
||||||
|
tree: null as any,
|
||||||
|
timeout: null as any,
|
||||||
|
});
|
||||||
|
|
||||||
if (!local.tree) {
|
if (!local.tree) {
|
||||||
clearTimeout(local.timeout);
|
clearTimeout(local.timeout);
|
||||||
|
|
@ -40,27 +46,46 @@ export const EdLeft = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EdTreeSearch />
|
<div className="flex flex-row items-stretch border-b">
|
||||||
|
<EdPageHistoryBtn
|
||||||
|
show={p.page.history.show}
|
||||||
|
onShow={async (show) => {
|
||||||
|
p.page.history.id = "";
|
||||||
|
p.page.history.show = show;
|
||||||
|
if (!show) {
|
||||||
|
await treeRebuild(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.render();
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!p.page.history.show && <EdTreeSearch />}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="tree-body flex relative flex-1 overflow-y-auto overflow-x-hidden"
|
className="tree-body flex relative flex-1 overflow-y-auto overflow-x-hidden"
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
if (ref) local.tree = ref;
|
if (ref) local.tree = ref;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 flex flex-col">
|
{p.page.history.show ? (
|
||||||
{local.tree && (
|
<EdPageHistoryList />
|
||||||
<DndProvider
|
) : (
|
||||||
backend={HTML5Backend}
|
<div className="absolute inset-0 flex flex-col">
|
||||||
options={getBackendOptions({
|
{local.tree && (
|
||||||
html5: {
|
<DndProvider
|
||||||
rootElement: local.tree,
|
backend={HTML5Backend}
|
||||||
},
|
options={getBackendOptions({
|
||||||
})}
|
html5: {
|
||||||
>
|
rootElement: local.tree,
|
||||||
<EdTreeBody />
|
},
|
||||||
</DndProvider>
|
})}
|
||||||
)}
|
>
|
||||||
</div>
|
<EdTreeBody />
|
||||||
|
</DndProvider>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { getCompMeta } from "../comp/comp-meta";
|
||||||
import { IMeta, PG, active } from "../ed-global";
|
import { IMeta, PG, active } from "../ed-global";
|
||||||
|
|
||||||
export const isMetaActive = (p: PG, meta: IMeta) => {
|
export const isMetaActive = (p: PG, meta: IMeta) => {
|
||||||
|
if (!meta.item) return false;
|
||||||
|
|
||||||
let is_active: boolean = active.item_id === meta.item.id;
|
let is_active: boolean = active.item_id === meta.item.id;
|
||||||
if (active.comp_id) {
|
if (active.comp_id) {
|
||||||
if (meta.parent?.comp_id === active.comp_id) {
|
if (meta.parent?.comp_id === active.comp_id) {
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,10 @@ export const EDGlobal = {
|
||||||
init_local_effect: {} as Record<string, boolean>,
|
init_local_effect: {} as Record<string, boolean>,
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
|
history: {
|
||||||
|
id: "",
|
||||||
|
show: false
|
||||||
|
},
|
||||||
root_id: "root",
|
root_id: "root",
|
||||||
cur: EmptyPage,
|
cur: EmptyPage,
|
||||||
doc: null as null | DPage,
|
doc: null as null | DPage,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { useGlobal, useLocal } from "web-utils";
|
||||||
|
import { EDGlobal } from "../../logic/ed-global";
|
||||||
|
import { FC, useEffect } from "react";
|
||||||
|
import { decompress } from "wasm-gzip";
|
||||||
|
import { Vi } from "../../../vi/vi";
|
||||||
|
import { genMeta } from "../../../vi/meta/meta";
|
||||||
|
import { IRoot } from "../../../../utils/types/root";
|
||||||
|
import { IContent } from "../../../../utils/types/general";
|
||||||
|
import { initLoadComp } from "../../../vi/meta/comp/init-comp-load";
|
||||||
|
import { loadCompSnapshot } from "../../logic/comp/load";
|
||||||
|
import { IItem } from "../../../../utils/types/item";
|
||||||
|
import { mainStyle } from "./main";
|
||||||
|
import { Loading } from "../../../../utils/ui/loading";
|
||||||
|
import { treeRebuild } from "../../logic/tree/build";
|
||||||
|
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
export const EdPageHistoryMain: FC<{}> = ({}) => {
|
||||||
|
const p = useGlobal(EDGlobal, "EDITOR");
|
||||||
|
const local = useLocal({
|
||||||
|
loading: true,
|
||||||
|
root: null as any,
|
||||||
|
meta: {} as any,
|
||||||
|
entry: [] as any,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
local.loading = true;
|
||||||
|
local.render();
|
||||||
|
_db.page_history
|
||||||
|
.findFirst({
|
||||||
|
where: { id: p.page.history.id },
|
||||||
|
select: {
|
||||||
|
content_tree: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (e) => {
|
||||||
|
if (e) {
|
||||||
|
const zip = new Uint8Array((e.content_tree as any).data);
|
||||||
|
const root = JSON.parse(decoder.decode(decompress(zip))) as IRoot;
|
||||||
|
local.root = root;
|
||||||
|
await initLoadComp(
|
||||||
|
{
|
||||||
|
comps: p.comp.loaded,
|
||||||
|
meta: local.meta,
|
||||||
|
mode: "page",
|
||||||
|
},
|
||||||
|
root as unknown as IItem,
|
||||||
|
{
|
||||||
|
async load(comp_ids) {
|
||||||
|
if (!p.sync) return;
|
||||||
|
|
||||||
|
const ids = comp_ids.filter((id) => !p.comp.loaded[id]);
|
||||||
|
const comps = await p.sync.comp.load(ids, true);
|
||||||
|
let result = Object.entries(comps);
|
||||||
|
|
||||||
|
for (const [id_comp, comp] of result) {
|
||||||
|
if (comp && comp.snapshot && !p.comp.list[id_comp]) {
|
||||||
|
if (!p.comp.loaded[id_comp]) {
|
||||||
|
await loadCompSnapshot(p, id_comp, comp.snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
local.meta = {};
|
||||||
|
local.entry = [];
|
||||||
|
for (const item of root.childs) {
|
||||||
|
local.entry.push(item.id);
|
||||||
|
genMeta(
|
||||||
|
{
|
||||||
|
note: "cache-rebuild",
|
||||||
|
comps: p.comp.loaded,
|
||||||
|
meta: local.meta,
|
||||||
|
mode: "page",
|
||||||
|
},
|
||||||
|
{ item: item as IContent }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
local.loading = false;
|
||||||
|
local.render();
|
||||||
|
p.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [p.page.history.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-1 flex-col items-stretch">
|
||||||
|
<div className="border-b p-1 text-sm flex">
|
||||||
|
<div
|
||||||
|
className="border px-2 cursor-pointer hover:bg-blue-200 border border-blue-700 hover:bg-blue-700 hover:text-white transition-all "
|
||||||
|
onClick={async () => {
|
||||||
|
if (confirm("Are you sure ?")) {
|
||||||
|
p.page.history.id = "";
|
||||||
|
p.page.history.show = false;
|
||||||
|
|
||||||
|
p.page.doc?.transact(() => {
|
||||||
|
const root = p.page.doc?.getMap("map").get("root");
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
syncronize(root as any, local.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
p.render();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Revert to this version
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"flex flex-1 relative overflow-auto",
|
||||||
|
p.mode === "mobile" ? "flex-col items-center" : ""
|
||||||
|
)}
|
||||||
|
ref={(el) => {
|
||||||
|
if (el) {
|
||||||
|
const bound = el.getBoundingClientRect();
|
||||||
|
if (local.width !== bound.width || local.height !== bound.height) {
|
||||||
|
local.width = bound.width;
|
||||||
|
local.height = bound.height;
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{local.loading ? (
|
||||||
|
<Loading backdrop={true} />
|
||||||
|
) : (
|
||||||
|
<div className={mainStyle(p, local.meta)}>
|
||||||
|
<Vi
|
||||||
|
meta={local.meta}
|
||||||
|
mode={p.mode}
|
||||||
|
api_url={p.site.config.api_url}
|
||||||
|
site_id={p.site.id}
|
||||||
|
page_id={p.page.cur.id}
|
||||||
|
entry={local.entry}
|
||||||
|
api={p.script.api}
|
||||||
|
db={p.script.db}
|
||||||
|
script={{ init_local_effect: p.script.init_local_effect }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -107,7 +107,7 @@ export const EdMain = () => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mainStyle = (p: PG, meta?: IMeta) => {
|
export const mainStyle = (p: PG, meta?: IMeta) => {
|
||||||
let is_active = meta ? isMetaActive(p, meta) : false;
|
let is_active = meta ? isMetaActive(p, meta) : false;
|
||||||
|
|
||||||
const scale = parseInt(p.ui.zoom.replace("%", "")) / 100;
|
const scale = parseInt(p.ui.zoom.replace("%", "")) / 100;
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ export const EdPopComp = () => {
|
||||||
className={cx(
|
className={cx(
|
||||||
"border cursor-pointer -mb-[1px] px-2 hover:text-blue-500 hover:border-blue-500 hover:border-b-transparent select-none",
|
"border cursor-pointer -mb-[1px] px-2 hover:text-blue-500 hover:border-blue-500 hover:border-b-transparent select-none",
|
||||||
local.tab === e &&
|
local.tab === e &&
|
||||||
"bg-white border-b-transparent",
|
"bg-white border-b-transparent",
|
||||||
local.tab !== e &&
|
local.tab !== e &&
|
||||||
"text-slate-400 border-b-slate-200 border-transparent bg-transparent"
|
"text-slate-400 border-b-slate-200 border-transparent bg-transparent"
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
local.tab = e;
|
local.tab = e;
|
||||||
|
|
@ -101,7 +101,7 @@ export const EdPopComp = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 mr-1 justify-end">
|
<div className="flex flex-1 mr-1">
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
|
|
@ -128,19 +128,23 @@ export const EdPopComp = () => {
|
||||||
background: #efefff;
|
background: #efefff;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
compPicker.search ? css`
|
compPicker.search
|
||||||
> .tree-root {
|
? css`
|
||||||
display: flex;
|
> .tree-root {
|
||||||
flex-direction: row;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-direction: row;
|
||||||
position: relative;
|
flex-wrap: wrap;
|
||||||
}` : css`
|
position: relative;
|
||||||
> .tree-root > .listitem > .container {
|
}
|
||||||
display: flex;
|
`
|
||||||
flex-direction: row;
|
: css`
|
||||||
flex-wrap: wrap;
|
> .tree-root > .listitem > .container {
|
||||||
position: relative;
|
display: flex;
|
||||||
}`
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{compPicker.ref && compPicker.status === "ready" && (
|
{compPicker.ref && compPicker.status === "ready" && (
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
export const EdPageHistoryBtn: FC<{
|
||||||
|
show: boolean;
|
||||||
|
onShow: (show: boolean) => void;
|
||||||
|
}> = ({ show, onShow }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"border-r min-w-[25px] px-2 flex items-center justify-center cursor-pointer select-none",
|
||||||
|
show && "bg-blue-700 text-white flex-1",
|
||||||
|
!show && "hover:bg-blue-100"
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
onShow(!show);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-history"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/></svg>`,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
{show && (
|
||||||
|
<>
|
||||||
|
<div className="text-xs ml-1 flex-1">History</div>
|
||||||
|
<div className="ml-3 border-l border-l-blue-100/20 pl-2">
|
||||||
|
×
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { useGlobal, useLocal } from "web-utils";
|
||||||
|
import { EDGlobal } from "../../logic/ed-global";
|
||||||
|
import { Loading } from "../../../../utils/ui/loading";
|
||||||
|
import { format, formatDistance } from "date-fns";
|
||||||
|
export const EdPageHistoryList = () => {
|
||||||
|
const p = useGlobal(EDGlobal, "EDITOR");
|
||||||
|
const local = useLocal(
|
||||||
|
{ loading: true, list: [] as Awaited<ReturnType<typeof queryList>> },
|
||||||
|
async () => {
|
||||||
|
console.log("query list");
|
||||||
|
local.list = await queryList(p.page.cur.id);
|
||||||
|
local.loading = false;
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{local.loading ? (
|
||||||
|
<Loading backdrop={false} />
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-1 flex-col items-stretch">
|
||||||
|
{local.list.map((e) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"flex justify-between items-center text-sm px-2 py-1 cursor-pointer hover:bg-blue-100 border-b transition-all select-none",
|
||||||
|
e.id === p.page.history.id &&
|
||||||
|
"border-r-4 bg-blue-50 border-r-blue-700"
|
||||||
|
)}
|
||||||
|
key={e.id}
|
||||||
|
onClick={() => {
|
||||||
|
p.page.history.id = e.id;
|
||||||
|
p.render();
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex-1">
|
||||||
|
{format(parseInt(e.ts) * 5000, "yyyy-MM-dd HH:mm:ss")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right text-[11px]">
|
||||||
|
{formatDistance(Date.now(), parseInt(e.ts) * 5000) + " ago"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryList = async (page_id: string) => {
|
||||||
|
return await _db.page_history.findMany({
|
||||||
|
where: { id_page: page_id },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
ts: true,
|
||||||
|
},
|
||||||
|
orderBy: { ts: "desc" },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -22,7 +22,6 @@ export const EdTreeSearch = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-stretch border-b"
|
|
||||||
onMouseOver={() => {
|
onMouseOver={() => {
|
||||||
if (local.focus) {
|
if (local.focus) {
|
||||||
local.hover = true;
|
local.hover = true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue