alinea
Version:
Headless git-based CMS
261 lines (259 loc) • 7.71 kB
JavaScript
import {
require_dataloader
} from "../../chunks/chunk-3TNMMA3W.js";
import {
useAtomValue
} from "../../chunks/chunk-TOJF2G3X.js";
import {
atom
} from "../../chunks/chunk-WJ67RR7S.js";
import {
PLazy
} from "../../chunks/chunk-IKINPSS5.js";
import {
__toESM
} from "../../chunks/chunk-NZLE2WMY.js";
// src/dashboard/atoms/EntryAtoms.ts
var import_dataloader = __toESM(require_dataloader(), 1);
import { Config } from "alinea/core/Config";
import { Entry } from "alinea/core/Entry";
import { getRoot, getType } from "alinea/core/Internal";
import { Type } from "alinea/core/Type";
import { entries } from "alinea/core/util/Objects";
import { parents, translations } from "alinea/query";
import { useMemo } from "react";
import { configAtom } from "./DashboardAtoms.js";
import { dbAtom } from "./DbAtoms.js";
import { localeAtom, rootAtom, workspaceAtom } from "./NavigationAtoms.js";
var ROOT_ID = "@alinea/root";
var visibleTypesAtom = atom((get) => {
const { schema } = get(configAtom);
return entries(schema).filter(([_, type]) => !Type.isHidden(type)).map(([name]) => name);
});
async function getHasChildren(graph, workspace, root, parentId, visibleTypes) {
return Boolean(
await graph.first({
workspace,
root,
parentId,
filter: {
_type: { in: visibleTypes }
},
status: "preferDraft"
})
);
}
function childrenOf(graph, locale, workspace, root, parentId, visibleTypes, orderBy) {
return PLazy.from(async () => {
const children = await graph.find({
select: {
id: Entry.id,
locale: Entry.locale
},
orderBy,
workspace,
root,
parentId,
filter: {
_type: { in: visibleTypes }
},
status: "preferDraft"
});
const untranslated = /* @__PURE__ */ new Set();
const translatedChildren = new Set(
children.filter((child) => child.locale === locale).map((child) => child.id)
);
const orderedChildren = children.filter((child) => {
if (translatedChildren.has(child.id)) return child.locale === locale;
if (untranslated.has(child.id)) return false;
untranslated.add(child.id);
return true;
});
return [...new Set(orderedChildren.map((child) => child.id))];
});
}
async function entryTreeRoot(graph, locale, workspace, rootName, visibleTypes) {
const root = graph.config.workspaces[workspace][rootName];
const orderBy = getRoot(root).orderChildrenBy;
return {
id: ROOT_ID,
index: "",
type: ROOT_ID,
isFolder: true,
isRoot: true,
entries: [],
hasChildren: true,
children: childrenOf(
graph,
locale,
workspace,
rootName,
null,
visibleTypes,
orderBy
)
};
}
var loaderAtom = atom((get) => {
const graph = get(dbAtom);
const locale = get(localeAtom);
const visibleTypes = get(visibleTypesAtom);
const { schema } = get(configAtom);
const root = get(rootAtom);
const workspace = get(workspaceAtom);
return new import_dataloader.default(async (ids) => {
const indexed = /* @__PURE__ */ new Map();
const search = ids.filter((id) => id !== ROOT_ID);
const data = {
id: Entry.id,
type: Entry.type,
title: Entry.title,
status: Entry.status,
locale: Entry.locale,
workspace: Entry.workspace,
main: Entry.main,
root: Entry.root,
path: Entry.path,
parents: parents({
select: {
path: Entry.path,
type: Entry.type
}
})
};
const rows = await graph.find({
groupBy: Entry.id,
select: {
id: Entry.id,
index: Entry.index,
type: Entry.type,
data,
translations: translations({
select: data
})
},
id: { in: search },
status: "preferDraft"
});
for (const row of rows) {
const canDrag = row.data.parents.length > 0 ? !getType(schema[row.data.parents.at(-1).type]).orderChildrenBy : true;
const type = schema[row.type];
const typeConfig = getType(type);
const orderBy = typeConfig.orderChildrenBy;
const children = childrenOf(
graph,
locale,
row.data.workspace,
row.data.root,
row.id,
visibleTypes,
orderBy
);
const hasChildren = typeConfig.contains ? await getHasChildren(
graph,
row.data.workspace,
row.data.root,
row.id,
visibleTypes
) : false;
const entries2 = [row.data].concat(row.translations);
indexed.set(row.id, {
id: row.id,
type: row.type,
index: row.index,
entries: entries2,
canDrag,
hasChildren,
children
});
}
const res = [];
for (const id of ids) {
if (id === ROOT_ID) {
res.push(
await entryTreeRoot(
graph,
locale,
workspace.name,
root.name,
visibleTypes
)
);
continue;
}
const entry = indexed.get(id);
if (!entry) {
res.push(void 0);
continue;
}
const typeName = entry.entries[0].type;
const type = schema[typeName];
const isFolder = Type.isContainer(type);
res.push({ ...entry, isFolder });
}
return res;
});
});
function useEntryTreeProvider() {
const loader = useAtomValue(loaderAtom);
const db = useAtomValue(dbAtom);
return useMemo(() => {
return {
canDrag(items) {
return items.every((item) => {
const data = item.getItemData();
return data.canDrag;
});
},
canDrop(items, target) {
const { item: parent } = target;
if (items.length !== 1) return false;
const [dropping] = items;
const newParent = dropping.getParent() !== parent;
const parentData = parent.getItemData();
const droppingData = dropping.getItemData();
if (!droppingData) return false;
const childType = db.config.schema[droppingData.type];
if (!parentData) return false;
if (parentData.type === ROOT_ID) {
const entry = droppingData.entries[0];
const root = db.config.workspaces[entry.workspace][entry.root];
const orderBy2 = getRoot(root).orderChildrenBy;
return !newParent ? !orderBy2 : Config.rootContains(db.config, root, childType);
}
const parentType = db.config.schema[parentData.type];
const orderBy = getType(parentType).orderChildrenBy;
return !newParent ? !orderBy : Config.typeContains(db.config, parentType, childType);
},
onDrop(items, target) {
const { item: parent } = target;
if (items.length !== 1) return;
const [dropping] = items;
const children = parent.getChildren();
const previous = "childIndex" in target ? children[target.childIndex - 1] : null;
const after = previous ? previous.getId() : null;
const newParent = dropping.getParent() !== parent;
const toRoot = parent.getId().startsWith("@alinea") ? dropping.getItemData().entries[0].root : void 0;
const toParent = !toRoot && newParent ? parent.getId() : void 0;
db.move({
id: dropping.getId(),
after,
toParent,
toRoot
});
},
async getItem(id) {
const data = await loader.clear(id).load(id);
if (!data) throw new Error(`Item ${id} not found`);
return data;
},
async getChildren(id) {
return this.getItem(id).then((item) => item?.children ?? []);
}
};
}, [loader]);
}
export {
ROOT_ID,
useEntryTreeProvider
};