payload-nested-docs-page-tree 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/components/PageTreeContext.d.ts +15 -0
- package/dist/components/PageTreeContext.js +26 -0
- package/dist/components/PageTreeContext.js.map +1 -0
- package/dist/components/PageTreeListView.client.d.ts +17 -0
- package/dist/components/PageTreeListView.client.js +960 -0
- package/dist/components/PageTreeListView.client.js.map +1 -0
- package/dist/components/PageTreeListView.module.css +358 -0
- package/dist/components/PageTreeListView.server.d.ts +3 -0
- package/dist/components/PageTreeListView.server.js +390 -0
- package/dist/components/PageTreeListView.server.js.map +1 -0
- package/dist/components/PageTreeTitleCell.d.ts +6 -0
- package/dist/components/PageTreeTitleCell.js +80 -0
- package/dist/components/PageTreeTitleCell.js.map +1 -0
- package/dist/endpoints/createMovePageEndpoint.d.ts +5 -0
- package/dist/endpoints/createMovePageEndpoint.js +265 -0
- package/dist/endpoints/createMovePageEndpoint.js.map +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities/badgeConfig.d.ts +2 -0
- package/dist/utilities/badgeConfig.js +30 -0
- package/dist/utilities/badgeConfig.js.map +1 -0
- package/dist/utilities/dropTargets.d.ts +31 -0
- package/dist/utilities/dropTargets.js +73 -0
- package/dist/utilities/dropTargets.js.map +1 -0
- package/dist/utilities/moveValidation.d.ts +11 -0
- package/dist/utilities/moveValidation.js +44 -0
- package/dist/utilities/moveValidation.js.map +1 -0
- package/dist/utilities/pageTree.d.ts +37 -0
- package/dist/utilities/pageTree.js +300 -0
- package/dist/utilities/pageTree.js.map +1 -0
- package/dist/utilities/pageTreeCollision.d.ts +16 -0
- package/dist/utilities/pageTreeCollision.js +70 -0
- package/dist/utilities/pageTreeCollision.js.map +1 -0
- package/dist/utilities/pageTreeConfig.d.ts +3 -0
- package/dist/utilities/pageTreeConfig.js +21 -0
- package/dist/utilities/pageTreeConfig.js.map +1 -0
- package/dist/utilities/status.d.ts +17 -0
- package/dist/utilities/status.js +72 -0
- package/dist/utilities/status.js.map +1 -0
- package/package.json +115 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
function getInsertIndex(dropID) {
|
|
2
|
+
const insertIndex = Number.parseInt(dropID.split(':')[1] ?? '', 10);
|
|
3
|
+
return Number.isNaN(insertIndex) ? null : insertIndex;
|
|
4
|
+
}
|
|
5
|
+
function getMovingSubtreeIDs(docs, activeDoc) {
|
|
6
|
+
return new Set(docs.filter((doc)=>doc.__pageTreeID === activeDoc.__pageTreeID || doc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)).map((doc)=>doc.__pageTreeID));
|
|
7
|
+
}
|
|
8
|
+
function getSiblingID(doc) {
|
|
9
|
+
return doc?.__pageTreeID ?? null;
|
|
10
|
+
}
|
|
11
|
+
export function buildInsertDropTargets(docs) {
|
|
12
|
+
if (docs.length === 0) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return Array.from({
|
|
16
|
+
length: docs.length + 1
|
|
17
|
+
}, (_, index)=>{
|
|
18
|
+
const referenceDoc = docs[index] ?? docs[docs.length - 1];
|
|
19
|
+
return {
|
|
20
|
+
depth: referenceDoc.__pageTreeDepth,
|
|
21
|
+
dropID: `page-insert:${index}`,
|
|
22
|
+
dropType: 'insert',
|
|
23
|
+
parentID: referenceDoc.__pageTreeParentID,
|
|
24
|
+
referenceRowID: referenceDoc.__pageTreeID
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function getDropTargetParentDoc(args) {
|
|
29
|
+
const { docsByID, dropTarget } = args;
|
|
30
|
+
if (!dropTarget) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (dropTarget.dropType === 'row') {
|
|
34
|
+
return docsByID.get(dropTarget.rowID) ?? null;
|
|
35
|
+
}
|
|
36
|
+
if (dropTarget.parentID === null) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return docsByID.get(dropTarget.parentID) ?? null;
|
|
40
|
+
}
|
|
41
|
+
export function getOrderPlacementFromDropTarget(args) {
|
|
42
|
+
const { activeDoc, docs, docsByID, dropTarget } = args;
|
|
43
|
+
if (!dropTarget) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const movingSubtreeIDs = getMovingSubtreeIDs(docs, activeDoc);
|
|
47
|
+
if (dropTarget.dropType === 'row') {
|
|
48
|
+
const targetDoc = docsByID.get(dropTarget.rowID);
|
|
49
|
+
if (!targetDoc) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const childSiblings = docs.filter((doc)=>doc.__pageTreeParentID === targetDoc.__pageTreeID && !movingSubtreeIDs.has(doc.__pageTreeID));
|
|
53
|
+
return {
|
|
54
|
+
nextSiblingID: null,
|
|
55
|
+
parentID: targetDoc.__pageTreeID,
|
|
56
|
+
previousSiblingID: getSiblingID(childSiblings.at(-1))
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const insertIndex = getInsertIndex(dropTarget.dropID);
|
|
60
|
+
if (insertIndex === null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const parentID = dropTarget.parentID;
|
|
64
|
+
const beforeSiblings = docs.slice(0, insertIndex).filter((doc)=>doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID));
|
|
65
|
+
const afterSiblings = docs.slice(insertIndex).filter((doc)=>doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID));
|
|
66
|
+
return {
|
|
67
|
+
nextSiblingID: getSiblingID(afterSiblings[0]),
|
|
68
|
+
parentID,
|
|
69
|
+
previousSiblingID: getSiblingID(beforeSiblings.at(-1))
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=dropTargets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/dropTargets.ts"],"sourcesContent":["import type { PageTreeDoc } from './pageTree.js'\n\nexport type PageTreeInsertDropTarget = {\n depth: number\n dropID: string\n dropType: 'insert'\n parentID: null | string\n referenceRowID: string\n}\n\nexport type PageTreeRowDropTarget = {\n dropType: 'row'\n insertAfterDropID: string\n insertBeforeDropID: string\n rowID: string\n}\n\nexport type PageTreeDropTarget = PageTreeInsertDropTarget | PageTreeRowDropTarget\n\nexport type PageTreeOrderPlacement = {\n nextSiblingID: null | string\n parentID: null | string\n previousSiblingID: null | string\n}\n\nfunction getInsertIndex(dropID: string): null | number {\n const insertIndex = Number.parseInt(dropID.split(':')[1] ?? '', 10)\n\n return Number.isNaN(insertIndex) ? null : insertIndex\n}\n\nfunction getMovingSubtreeIDs(docs: PageTreeDoc[], activeDoc: PageTreeDoc): Set<string> {\n return new Set(\n docs\n .filter(\n (doc) =>\n doc.__pageTreeID === activeDoc.__pageTreeID ||\n doc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID),\n )\n .map((doc) => doc.__pageTreeID),\n )\n}\n\nfunction getSiblingID(doc: PageTreeDoc | undefined): null | string {\n return doc?.__pageTreeID ?? null\n}\n\nexport function buildInsertDropTargets(docs: PageTreeDoc[]): PageTreeInsertDropTarget[] {\n if (docs.length === 0) {\n return []\n }\n\n return Array.from({ length: docs.length + 1 }, (_, index) => {\n const referenceDoc = docs[index] ?? docs[docs.length - 1]\n\n return {\n depth: referenceDoc.__pageTreeDepth,\n dropID: `page-insert:${index}`,\n dropType: 'insert',\n parentID: referenceDoc.__pageTreeParentID,\n referenceRowID: referenceDoc.__pageTreeID,\n }\n })\n}\n\nexport function getDropTargetParentDoc(args: {\n docsByID: ReadonlyMap<string, PageTreeDoc>\n dropTarget: null | PageTreeDropTarget\n}): null | PageTreeDoc {\n const { docsByID, dropTarget } = args\n\n if (!dropTarget) {\n return null\n }\n\n if (dropTarget.dropType === 'row') {\n return docsByID.get(dropTarget.rowID) ?? null\n }\n\n if (dropTarget.parentID === null) {\n return null\n }\n\n return docsByID.get(dropTarget.parentID) ?? null\n}\n\nexport function getOrderPlacementFromDropTarget(args: {\n activeDoc: PageTreeDoc\n docs: PageTreeDoc[]\n docsByID: ReadonlyMap<string, PageTreeDoc>\n dropTarget: null | PageTreeDropTarget\n}): null | PageTreeOrderPlacement {\n const { activeDoc, docs, docsByID, dropTarget } = args\n\n if (!dropTarget) {\n return null\n }\n\n const movingSubtreeIDs = getMovingSubtreeIDs(docs, activeDoc)\n\n if (dropTarget.dropType === 'row') {\n const targetDoc = docsByID.get(dropTarget.rowID)\n\n if (!targetDoc) {\n return null\n }\n\n const childSiblings = docs.filter(\n (doc) =>\n doc.__pageTreeParentID === targetDoc.__pageTreeID &&\n !movingSubtreeIDs.has(doc.__pageTreeID),\n )\n\n return {\n nextSiblingID: null,\n parentID: targetDoc.__pageTreeID,\n previousSiblingID: getSiblingID(childSiblings.at(-1)),\n }\n }\n\n const insertIndex = getInsertIndex(dropTarget.dropID)\n\n if (insertIndex === null) {\n return null\n }\n\n const parentID = dropTarget.parentID\n const beforeSiblings = docs\n .slice(0, insertIndex)\n .filter((doc) => doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID))\n const afterSiblings = docs\n .slice(insertIndex)\n .filter((doc) => doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID))\n\n return {\n nextSiblingID: getSiblingID(afterSiblings[0]),\n parentID,\n previousSiblingID: getSiblingID(beforeSiblings.at(-1)),\n }\n}\n"],"names":["getInsertIndex","dropID","insertIndex","Number","parseInt","split","isNaN","getMovingSubtreeIDs","docs","activeDoc","Set","filter","doc","__pageTreeID","__pageTreeAncestorIDs","includes","map","getSiblingID","buildInsertDropTargets","length","Array","from","_","index","referenceDoc","depth","__pageTreeDepth","dropType","parentID","__pageTreeParentID","referenceRowID","getDropTargetParentDoc","args","docsByID","dropTarget","get","rowID","getOrderPlacementFromDropTarget","movingSubtreeIDs","targetDoc","childSiblings","has","nextSiblingID","previousSiblingID","at","beforeSiblings","slice","afterSiblings"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PageTreeDoc } from './pageTree.js';
|
|
2
|
+
export declare const CANCEL_DRAG_MESSAGE = "Release to cancel drag.";
|
|
3
|
+
export type PageTreeDropValidation = {
|
|
4
|
+
isValid: boolean;
|
|
5
|
+
message?: string;
|
|
6
|
+
parentID: null | string;
|
|
7
|
+
};
|
|
8
|
+
export declare function getDropValidation(args: {
|
|
9
|
+
activeDoc: PageTreeDoc;
|
|
10
|
+
targetDoc?: PageTreeDoc;
|
|
11
|
+
}): PageTreeDropValidation;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const CANCEL_DRAG_MESSAGE = 'Release to cancel drag.';
|
|
2
|
+
export function getDropValidation(args) {
|
|
3
|
+
const { activeDoc, targetDoc } = args;
|
|
4
|
+
if (!targetDoc) {
|
|
5
|
+
if (activeDoc.__pageTreeParentID === null) {
|
|
6
|
+
return {
|
|
7
|
+
isValid: false,
|
|
8
|
+
message: 'Document is already at the root.',
|
|
9
|
+
parentID: null
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
isValid: true,
|
|
14
|
+
parentID: null
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (targetDoc.__pageTreeID === activeDoc.__pageTreeID) {
|
|
18
|
+
return {
|
|
19
|
+
isValid: false,
|
|
20
|
+
message: CANCEL_DRAG_MESSAGE,
|
|
21
|
+
parentID: targetDoc.__pageTreeID
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (targetDoc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)) {
|
|
25
|
+
return {
|
|
26
|
+
isValid: false,
|
|
27
|
+
message: 'A document cannot be moved under one of its descendants.',
|
|
28
|
+
parentID: targetDoc.__pageTreeID
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (activeDoc.__pageTreeParentID === targetDoc.__pageTreeID) {
|
|
32
|
+
return {
|
|
33
|
+
isValid: false,
|
|
34
|
+
message: 'Document already has that parent.',
|
|
35
|
+
parentID: targetDoc.__pageTreeID
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
isValid: true,
|
|
40
|
+
parentID: targetDoc.__pageTreeID
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=moveValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/moveValidation.ts"],"sourcesContent":["import type { PageTreeDoc } from './pageTree.js'\r\n\r\nexport const CANCEL_DRAG_MESSAGE = 'Release to cancel drag.'\r\n\r\nexport type PageTreeDropValidation = {\r\n isValid: boolean\r\n message?: string\r\n parentID: null | string\r\n}\r\n\r\nexport function getDropValidation(args: {\r\n activeDoc: PageTreeDoc\r\n targetDoc?: PageTreeDoc\r\n}): PageTreeDropValidation {\r\n const { activeDoc, targetDoc } = args\r\n\r\n if (!targetDoc) {\r\n if (activeDoc.__pageTreeParentID === null) {\r\n return {\r\n isValid: false,\r\n message: 'Document is already at the root.',\r\n parentID: null,\r\n }\r\n }\r\n\r\n return {\r\n isValid: true,\r\n parentID: null,\r\n }\r\n }\r\n\r\n if (targetDoc.__pageTreeID === activeDoc.__pageTreeID) {\r\n return {\r\n isValid: false,\r\n message: CANCEL_DRAG_MESSAGE,\r\n parentID: targetDoc.__pageTreeID,\r\n }\r\n }\r\n\r\n if (targetDoc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)) {\r\n return {\r\n isValid: false,\r\n message: 'A document cannot be moved under one of its descendants.',\r\n parentID: targetDoc.__pageTreeID,\r\n }\r\n }\r\n\r\n if (activeDoc.__pageTreeParentID === targetDoc.__pageTreeID) {\r\n return {\r\n isValid: false,\r\n message: 'Document already has that parent.',\r\n parentID: targetDoc.__pageTreeID,\r\n }\r\n }\r\n\r\n return {\r\n isValid: true,\r\n parentID: targetDoc.__pageTreeID,\r\n }\r\n}\r\n"],"names":["CANCEL_DRAG_MESSAGE","getDropValidation","args","activeDoc","targetDoc","__pageTreeParentID","isValid","message","parentID","__pageTreeID","__pageTreeAncestorIDs","includes"],"mappings":"AAEA,OAAO,MAAMA,sBAAsB,0BAAyB;AAQ5D,OAAO,SAASC,kBAAkBC,IAGjC;IACC,MAAM,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGF;IAEjC,IAAI,CAACE,WAAW;QACd,IAAID,UAAUE,kBAAkB,KAAK,MAAM;YACzC,OAAO;gBACLC,SAAS;gBACTC,SAAS;gBACTC,UAAU;YACZ;QACF;QAEA,OAAO;YACLF,SAAS;YACTE,UAAU;QACZ;IACF;IAEA,IAAIJ,UAAUK,YAAY,KAAKN,UAAUM,YAAY,EAAE;QACrD,OAAO;YACLH,SAAS;YACTC,SAASP;YACTQ,UAAUJ,UAAUK,YAAY;QAClC;IACF;IAEA,IAAIL,UAAUM,qBAAqB,CAACC,QAAQ,CAACR,UAAUM,YAAY,GAAG;QACpE,OAAO;YACLH,SAAS;YACTC,SAAS;YACTC,UAAUJ,UAAUK,YAAY;QAClC;IACF;IAEA,IAAIN,UAAUE,kBAAkB,KAAKD,UAAUK,YAAY,EAAE;QAC3D,OAAO;YACLH,SAAS;YACTC,SAAS;YACTC,UAAUJ,UAAUK,YAAY;QAClC;IACF;IAEA,OAAO;QACLH,SAAS;QACTE,UAAUJ,UAAUK,YAAY;IAClC;AACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PageTreeSourceDoc } from '../types.js';
|
|
2
|
+
export type { PageTreeSourceDoc } from '../types.js';
|
|
3
|
+
export type PageTreeMeta = {
|
|
4
|
+
__pageTreeAncestorIDs: string[];
|
|
5
|
+
__pageTreeDepth: number;
|
|
6
|
+
__pageTreeHasChildren: boolean;
|
|
7
|
+
__pageTreeID: string;
|
|
8
|
+
__pageTreeOrderIndex: number;
|
|
9
|
+
__pageTreeParentID: null | string;
|
|
10
|
+
__pageTreeShadeLevel: number;
|
|
11
|
+
};
|
|
12
|
+
export type PageTreeDoc = PageTreeMeta & PageTreeSourceDoc;
|
|
13
|
+
export type BuildPageTreeDocsOptions = {
|
|
14
|
+
parentFieldSlug?: string;
|
|
15
|
+
sort?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function stringifyDocID(id: number | string | undefined): string;
|
|
18
|
+
export declare function getRelationshipID(value: unknown): null | string;
|
|
19
|
+
export declare function getDocParentID(doc: PageTreeSourceDoc, parentFieldSlug?: string): null | string;
|
|
20
|
+
export declare function getDocDisplayLabel(doc: PageTreeDoc): string;
|
|
21
|
+
export declare function getDocSlugSegment(doc: PageTreeDoc): string;
|
|
22
|
+
export declare function buildDocSlugPath(args: {
|
|
23
|
+
doc: PageTreeDoc;
|
|
24
|
+
docsByID: Map<string, PageTreeDoc>;
|
|
25
|
+
}): string;
|
|
26
|
+
export declare function buildProspectiveDocSlugPath(args: {
|
|
27
|
+
activeDoc: PageTreeDoc;
|
|
28
|
+
docsByID: Map<string, PageTreeDoc>;
|
|
29
|
+
targetDoc?: PageTreeDoc;
|
|
30
|
+
}): string;
|
|
31
|
+
export declare function buildChildrenByParentID(args: {
|
|
32
|
+
docs: PageTreeSourceDoc[];
|
|
33
|
+
parentFieldSlug?: string;
|
|
34
|
+
}): Map<null | string, string[]>;
|
|
35
|
+
export declare function collectDescendantIDs(rootID: string, childrenByParentID: Map<null | string, string[]>): string[];
|
|
36
|
+
export declare function buildPageTreeDocs(docs: PageTreeSourceDoc[], options?: BuildPageTreeDocsOptions): PageTreeDoc[];
|
|
37
|
+
export declare function getVisibleTreeDocs(docs: PageTreeDoc[], collapsedIDs: ReadonlySet<string>): PageTreeDoc[];
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const dateLikePattern = /^\d{4}-\d{2}-\d{2}(?:[T\s].*)?$/;
|
|
2
|
+
export function stringifyDocID(id) {
|
|
3
|
+
return String(id ?? '');
|
|
4
|
+
}
|
|
5
|
+
export function getRelationshipID(value) {
|
|
6
|
+
if (typeof value === 'number' || typeof value === 'string') {
|
|
7
|
+
return stringifyDocID(value);
|
|
8
|
+
}
|
|
9
|
+
if (value && typeof value === 'object' && 'id' in value) {
|
|
10
|
+
const relationshipID = value.id;
|
|
11
|
+
if (typeof relationshipID === 'number' || typeof relationshipID === 'string') {
|
|
12
|
+
return stringifyDocID(relationshipID);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
export function getDocParentID(doc, parentFieldSlug = 'parent') {
|
|
18
|
+
return getRelationshipID(doc[parentFieldSlug]);
|
|
19
|
+
}
|
|
20
|
+
export function getDocDisplayLabel(doc) {
|
|
21
|
+
const title = doc.title;
|
|
22
|
+
if (typeof title === 'string' && title.trim().length > 0) {
|
|
23
|
+
return title;
|
|
24
|
+
}
|
|
25
|
+
if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {
|
|
26
|
+
return doc.slug;
|
|
27
|
+
}
|
|
28
|
+
return doc.__pageTreeID;
|
|
29
|
+
}
|
|
30
|
+
export function getDocSlugSegment(doc) {
|
|
31
|
+
if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {
|
|
32
|
+
return doc.slug.trim().replace(/^\/+|\/+$/g, '');
|
|
33
|
+
}
|
|
34
|
+
return doc.__pageTreeID;
|
|
35
|
+
}
|
|
36
|
+
export function buildDocSlugPath(args) {
|
|
37
|
+
const { doc, docsByID } = args;
|
|
38
|
+
const segments = doc.__pageTreeAncestorIDs.map((ancestorID)=>docsByID.get(ancestorID)).filter((ancestorDoc)=>Boolean(ancestorDoc)).map(getDocSlugSegment);
|
|
39
|
+
segments.push(getDocSlugSegment(doc));
|
|
40
|
+
return `/${segments.filter(Boolean).join('/')}`;
|
|
41
|
+
}
|
|
42
|
+
export function buildProspectiveDocSlugPath(args) {
|
|
43
|
+
const { activeDoc, docsByID, targetDoc } = args;
|
|
44
|
+
if (!targetDoc) {
|
|
45
|
+
return `/${getDocSlugSegment(activeDoc)}`;
|
|
46
|
+
}
|
|
47
|
+
const segments = targetDoc.__pageTreeAncestorIDs.map((ancestorID)=>docsByID.get(ancestorID)).filter((ancestorDoc)=>Boolean(ancestorDoc)).map(getDocSlugSegment);
|
|
48
|
+
segments.push(getDocSlugSegment(targetDoc), getDocSlugSegment(activeDoc));
|
|
49
|
+
return `/${segments.filter(Boolean).join('/')}`;
|
|
50
|
+
}
|
|
51
|
+
export function buildChildrenByParentID(args) {
|
|
52
|
+
const { docs, parentFieldSlug = 'parent' } = args;
|
|
53
|
+
const childrenByParentID = new Map();
|
|
54
|
+
for (const doc of docs){
|
|
55
|
+
const docID = stringifyDocID(doc.id);
|
|
56
|
+
const parentID = getDocParentID(doc, parentFieldSlug);
|
|
57
|
+
const existingChildren = childrenByParentID.get(parentID) ?? [];
|
|
58
|
+
existingChildren.push(docID);
|
|
59
|
+
childrenByParentID.set(parentID, existingChildren);
|
|
60
|
+
}
|
|
61
|
+
return childrenByParentID;
|
|
62
|
+
}
|
|
63
|
+
export function collectDescendantIDs(rootID, childrenByParentID) {
|
|
64
|
+
const descendantIDs = [];
|
|
65
|
+
const stack = [
|
|
66
|
+
...childrenByParentID.get(rootID) ?? []
|
|
67
|
+
];
|
|
68
|
+
while(stack.length > 0){
|
|
69
|
+
const currentID = stack.pop();
|
|
70
|
+
if (!currentID) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
descendantIDs.push(currentID);
|
|
74
|
+
stack.push(...childrenByParentID.get(currentID) ?? []);
|
|
75
|
+
}
|
|
76
|
+
return descendantIDs;
|
|
77
|
+
}
|
|
78
|
+
function getCycleNodeIDs(nodes, nodesByID) {
|
|
79
|
+
const cycleNodeIDs = new Set();
|
|
80
|
+
const pathStack = [];
|
|
81
|
+
const visitState = new Map();
|
|
82
|
+
const walk = (node)=>{
|
|
83
|
+
const currentState = visitState.get(node.id);
|
|
84
|
+
if (currentState === 'done') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (currentState === 'visiting') {
|
|
88
|
+
const cycleStartIndex = pathStack.lastIndexOf(node.id);
|
|
89
|
+
const cycleIDs = cycleStartIndex >= 0 ? pathStack.slice(cycleStartIndex) : [
|
|
90
|
+
node.id
|
|
91
|
+
];
|
|
92
|
+
for (const cycleID of cycleIDs){
|
|
93
|
+
cycleNodeIDs.add(cycleID);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
visitState.set(node.id, 'visiting');
|
|
98
|
+
pathStack.push(node.id);
|
|
99
|
+
if (node.parentID !== null) {
|
|
100
|
+
const parentNode = nodesByID.get(node.parentID);
|
|
101
|
+
if (parentNode) {
|
|
102
|
+
walk(parentNode);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
pathStack.pop();
|
|
106
|
+
visitState.set(node.id, 'done');
|
|
107
|
+
};
|
|
108
|
+
for (const node of nodes){
|
|
109
|
+
walk(node);
|
|
110
|
+
}
|
|
111
|
+
return cycleNodeIDs;
|
|
112
|
+
}
|
|
113
|
+
function parseSortTokens(sort) {
|
|
114
|
+
if (typeof sort !== 'string' || sort.trim().length === 0) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
return sort.split(',').map((token)=>token.trim()).filter((token)=>token.length > 0).map((token)=>{
|
|
118
|
+
const descending = token.startsWith('-');
|
|
119
|
+
const normalizedToken = descending ? token.slice(1) : token;
|
|
120
|
+
return {
|
|
121
|
+
descending,
|
|
122
|
+
path: normalizedToken.split('.').filter(Boolean)
|
|
123
|
+
};
|
|
124
|
+
}).filter((token)=>token.path.length > 0);
|
|
125
|
+
}
|
|
126
|
+
function resolveSortValue(source, path) {
|
|
127
|
+
let currentValue = source;
|
|
128
|
+
for (const segment of path){
|
|
129
|
+
if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
if (!(segment in currentValue)) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
currentValue = currentValue[segment];
|
|
136
|
+
}
|
|
137
|
+
if (currentValue === null || currentValue === undefined) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (currentValue instanceof Date) {
|
|
141
|
+
const timestamp = currentValue.getTime();
|
|
142
|
+
return Number.isNaN(timestamp) ? undefined : timestamp;
|
|
143
|
+
}
|
|
144
|
+
if (typeof currentValue === 'boolean') {
|
|
145
|
+
return currentValue ? 1 : 0;
|
|
146
|
+
}
|
|
147
|
+
if (typeof currentValue === 'number') {
|
|
148
|
+
return Number.isFinite(currentValue) ? currentValue : undefined;
|
|
149
|
+
}
|
|
150
|
+
if (typeof currentValue !== 'string') {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const normalizedValue = currentValue.trim();
|
|
154
|
+
if (dateLikePattern.test(normalizedValue)) {
|
|
155
|
+
const timestamp = Date.parse(normalizedValue);
|
|
156
|
+
if (!Number.isNaN(timestamp)) {
|
|
157
|
+
return timestamp;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return normalizedValue;
|
|
161
|
+
}
|
|
162
|
+
function compareSortValues(leftValue, rightValue) {
|
|
163
|
+
if (leftValue === null && rightValue === null) {
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
if (leftValue === null) {
|
|
167
|
+
return -1;
|
|
168
|
+
}
|
|
169
|
+
if (rightValue === null) {
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
if (typeof leftValue === 'number' && typeof rightValue === 'number') {
|
|
173
|
+
if (leftValue === rightValue) {
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
return leftValue < rightValue ? -1 : 1;
|
|
177
|
+
}
|
|
178
|
+
return String(leftValue).localeCompare(String(rightValue), undefined, {
|
|
179
|
+
numeric: true,
|
|
180
|
+
sensitivity: 'base'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function createTreeNodeComparator(sort) {
|
|
184
|
+
const sortTokens = parseSortTokens(sort);
|
|
185
|
+
if (sortTokens.length === 0) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
return (leftNode, rightNode)=>{
|
|
189
|
+
for (const sortToken of sortTokens){
|
|
190
|
+
const leftValue = resolveSortValue(leftNode.doc, sortToken.path);
|
|
191
|
+
const rightValue = resolveSortValue(rightNode.doc, sortToken.path);
|
|
192
|
+
if (leftValue === undefined || rightValue === undefined) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const comparison = compareSortValues(leftValue, rightValue);
|
|
196
|
+
if (comparison !== 0) {
|
|
197
|
+
return sortToken.descending ? -comparison : comparison;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return leftNode.originalIndex - rightNode.originalIndex;
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
export function buildPageTreeDocs(docs, options = {}) {
|
|
204
|
+
const parentFieldSlug = options.parentFieldSlug ?? 'parent';
|
|
205
|
+
const nodes = docs.map((doc, index)=>({
|
|
206
|
+
doc,
|
|
207
|
+
id: stringifyDocID(doc.id),
|
|
208
|
+
originalIndex: index,
|
|
209
|
+
parentID: getDocParentID(doc, parentFieldSlug)
|
|
210
|
+
}));
|
|
211
|
+
const nodesByID = new Map(nodes.map((node)=>[
|
|
212
|
+
node.id,
|
|
213
|
+
node
|
|
214
|
+
]));
|
|
215
|
+
const cycleNodeIDs = getCycleNodeIDs(nodes, nodesByID);
|
|
216
|
+
const effectiveParentIDByNodeID = new Map();
|
|
217
|
+
const childrenByParentID = new Map();
|
|
218
|
+
const compareTreeNodes = createTreeNodeComparator(options.sort);
|
|
219
|
+
for (const node of nodes){
|
|
220
|
+
const parentExists = node.parentID !== null && nodesByID.has(node.parentID);
|
|
221
|
+
const effectiveParentID = parentExists && !cycleNodeIDs.has(node.id) ? node.parentID : null;
|
|
222
|
+
effectiveParentIDByNodeID.set(node.id, effectiveParentID);
|
|
223
|
+
const currentChildren = childrenByParentID.get(effectiveParentID) ?? [];
|
|
224
|
+
currentChildren.push(node);
|
|
225
|
+
childrenByParentID.set(effectiveParentID, currentChildren);
|
|
226
|
+
}
|
|
227
|
+
if (compareTreeNodes) {
|
|
228
|
+
for (const children of childrenByParentID.values()){
|
|
229
|
+
children.sort(compareTreeNodes);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const orderedDocs = [];
|
|
233
|
+
const subtreeDepthByID = new Map();
|
|
234
|
+
const visitedNodeIDs = new Set();
|
|
235
|
+
const getShadeLevel = (rootMaxDepth, depth)=>{
|
|
236
|
+
if (rootMaxDepth === 0) {
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
return Math.max(rootMaxDepth - depth + 1, 1);
|
|
240
|
+
};
|
|
241
|
+
const getSubtreeDepth = (node)=>{
|
|
242
|
+
const cachedDepth = subtreeDepthByID.get(node.id);
|
|
243
|
+
if (typeof cachedDepth === 'number') {
|
|
244
|
+
return cachedDepth;
|
|
245
|
+
}
|
|
246
|
+
const children = childrenByParentID.get(node.id) ?? [];
|
|
247
|
+
if (children.length === 0) {
|
|
248
|
+
subtreeDepthByID.set(node.id, 0);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
const depth = Math.max(...children.map((child)=>getSubtreeDepth(child) + 1));
|
|
252
|
+
subtreeDepthByID.set(node.id, depth);
|
|
253
|
+
return depth;
|
|
254
|
+
};
|
|
255
|
+
const visit = (node, ancestorIDs, rootMaxDepth)=>{
|
|
256
|
+
if (visitedNodeIDs.has(node.id)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
visitedNodeIDs.add(node.id);
|
|
260
|
+
const children = childrenByParentID.get(node.id) ?? [];
|
|
261
|
+
const depth = ancestorIDs.length;
|
|
262
|
+
const shadeLevel = getShadeLevel(rootMaxDepth, depth);
|
|
263
|
+
orderedDocs.push({
|
|
264
|
+
...node.doc,
|
|
265
|
+
__pageTreeAncestorIDs: ancestorIDs,
|
|
266
|
+
__pageTreeDepth: depth,
|
|
267
|
+
__pageTreeHasChildren: children.length > 0,
|
|
268
|
+
__pageTreeID: node.id,
|
|
269
|
+
__pageTreeOrderIndex: orderedDocs.length,
|
|
270
|
+
__pageTreeParentID: effectiveParentIDByNodeID.get(node.id) ?? null,
|
|
271
|
+
__pageTreeShadeLevel: shadeLevel
|
|
272
|
+
});
|
|
273
|
+
for (const child of children){
|
|
274
|
+
visit(child, [
|
|
275
|
+
...ancestorIDs,
|
|
276
|
+
node.id
|
|
277
|
+
], rootMaxDepth);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const roots = [
|
|
281
|
+
...childrenByParentID.get(null) ?? []
|
|
282
|
+
];
|
|
283
|
+
for (const root of roots){
|
|
284
|
+
visit(root, [], getSubtreeDepth(root));
|
|
285
|
+
}
|
|
286
|
+
const fallbackNodes = compareTreeNodes ? [
|
|
287
|
+
...nodes
|
|
288
|
+
].sort(compareTreeNodes) : nodes;
|
|
289
|
+
for (const node of fallbackNodes){
|
|
290
|
+
if (!visitedNodeIDs.has(node.id)) {
|
|
291
|
+
visit(node, [], getSubtreeDepth(node));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return orderedDocs;
|
|
295
|
+
}
|
|
296
|
+
export function getVisibleTreeDocs(docs, collapsedIDs) {
|
|
297
|
+
return docs.filter((doc)=>doc.__pageTreeAncestorIDs.every((ancestorID)=>!collapsedIDs.has(ancestorID)));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=pageTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/pageTree.ts"],"sourcesContent":["import type { PageTreeSourceDoc } from '../types.js'\r\nexport type { PageTreeSourceDoc } from '../types.js'\r\n\r\nexport type PageTreeMeta = {\r\n __pageTreeAncestorIDs: string[]\r\n __pageTreeDepth: number\r\n __pageTreeHasChildren: boolean\r\n __pageTreeID: string\r\n __pageTreeOrderIndex: number\r\n __pageTreeParentID: null | string\r\n __pageTreeShadeLevel: number\r\n}\r\n\r\nexport type PageTreeDoc = PageTreeMeta & PageTreeSourceDoc\r\n\r\ntype SortToken = {\r\n descending: boolean\r\n path: string[]\r\n}\r\n\r\ntype SortValue = null | number | string\r\n\r\ntype TreeNode = {\r\n doc: PageTreeSourceDoc\r\n id: string\r\n originalIndex: number\r\n parentID: null | string\r\n}\r\n\r\nconst dateLikePattern = /^\\d{4}-\\d{2}-\\d{2}(?:[T\\s].*)?$/\r\n\r\nexport type BuildPageTreeDocsOptions = {\r\n parentFieldSlug?: string\r\n sort?: string\r\n}\r\n\r\nexport function stringifyDocID(id: number | string | undefined): string {\r\n return String(id ?? '')\r\n}\r\n\r\nexport function getRelationshipID(value: unknown): null | string {\r\n if (typeof value === 'number' || typeof value === 'string') {\r\n return stringifyDocID(value)\r\n }\r\n\r\n if (value && typeof value === 'object' && 'id' in value) {\r\n const relationshipID = (value as { id?: number | string }).id\r\n\r\n if (typeof relationshipID === 'number' || typeof relationshipID === 'string') {\r\n return stringifyDocID(relationshipID)\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nexport function getDocParentID(\r\n doc: PageTreeSourceDoc,\r\n parentFieldSlug = 'parent',\r\n): null | string {\r\n return getRelationshipID(doc[parentFieldSlug])\r\n}\r\n\r\nexport function getDocDisplayLabel(doc: PageTreeDoc): string {\r\n const title = doc.title\r\n\r\n if (typeof title === 'string' && title.trim().length > 0) {\r\n return title\r\n }\r\n\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function getDocSlugSegment(doc: PageTreeDoc): string {\r\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\r\n return doc.slug.trim().replace(/^\\/+|\\/+$/g, '')\r\n }\r\n\r\n return doc.__pageTreeID\r\n}\r\n\r\nexport function buildDocSlugPath(args: {\r\n doc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n}): string {\r\n const { doc, docsByID } = args\r\n const segments = doc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(doc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildProspectiveDocSlugPath(args: {\r\n activeDoc: PageTreeDoc\r\n docsByID: Map<string, PageTreeDoc>\r\n targetDoc?: PageTreeDoc\r\n}): string {\r\n const { activeDoc, docsByID, targetDoc } = args\r\n\r\n if (!targetDoc) {\r\n return `/${getDocSlugSegment(activeDoc)}`\r\n }\r\n\r\n const segments = targetDoc.__pageTreeAncestorIDs\r\n .map((ancestorID) => docsByID.get(ancestorID))\r\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\r\n .map(getDocSlugSegment)\r\n\r\n segments.push(getDocSlugSegment(targetDoc), getDocSlugSegment(activeDoc))\r\n\r\n return `/${segments.filter(Boolean).join('/')}`\r\n}\r\n\r\nexport function buildChildrenByParentID(args: {\r\n docs: PageTreeSourceDoc[]\r\n parentFieldSlug?: string\r\n}): Map<null | string, string[]> {\r\n const { docs, parentFieldSlug = 'parent' } = args\r\n const childrenByParentID = new Map<null | string, string[]>()\r\n\r\n for (const doc of docs) {\r\n const docID = stringifyDocID(doc.id)\r\n const parentID = getDocParentID(doc, parentFieldSlug)\r\n const existingChildren = childrenByParentID.get(parentID) ?? []\r\n\r\n existingChildren.push(docID)\r\n childrenByParentID.set(parentID, existingChildren)\r\n }\r\n\r\n return childrenByParentID\r\n}\r\n\r\nexport function collectDescendantIDs(\r\n rootID: string,\r\n childrenByParentID: Map<null | string, string[]>,\r\n): string[] {\r\n const descendantIDs: string[] = []\r\n const stack = [...(childrenByParentID.get(rootID) ?? [])]\r\n\r\n while (stack.length > 0) {\r\n const currentID = stack.pop()\r\n\r\n if (!currentID) {\r\n continue\r\n }\r\n\r\n descendantIDs.push(currentID)\r\n stack.push(...(childrenByParentID.get(currentID) ?? []))\r\n }\r\n\r\n return descendantIDs\r\n}\r\n\r\nfunction getCycleNodeIDs(nodes: TreeNode[], nodesByID: Map<string, TreeNode>): Set<string> {\r\n const cycleNodeIDs = new Set<string>()\r\n const pathStack: string[] = []\r\n const visitState = new Map<string, 'done' | 'visiting'>()\r\n\r\n const walk = (node: TreeNode) => {\r\n const currentState = visitState.get(node.id)\r\n\r\n if (currentState === 'done') {\r\n return\r\n }\r\n\r\n if (currentState === 'visiting') {\r\n const cycleStartIndex = pathStack.lastIndexOf(node.id)\r\n const cycleIDs = cycleStartIndex >= 0 ? pathStack.slice(cycleStartIndex) : [node.id]\r\n\r\n for (const cycleID of cycleIDs) {\r\n cycleNodeIDs.add(cycleID)\r\n }\r\n\r\n return\r\n }\r\n\r\n visitState.set(node.id, 'visiting')\r\n pathStack.push(node.id)\r\n\r\n if (node.parentID !== null) {\r\n const parentNode = nodesByID.get(node.parentID)\r\n\r\n if (parentNode) {\r\n walk(parentNode)\r\n }\r\n }\r\n\r\n pathStack.pop()\r\n visitState.set(node.id, 'done')\r\n }\r\n\r\n for (const node of nodes) {\r\n walk(node)\r\n }\r\n\r\n return cycleNodeIDs\r\n}\r\n\r\nfunction parseSortTokens(sort?: string): SortToken[] {\r\n if (typeof sort !== 'string' || sort.trim().length === 0) {\r\n return []\r\n }\r\n\r\n return sort\r\n .split(',')\r\n .map((token) => token.trim())\r\n .filter((token) => token.length > 0)\r\n .map((token) => {\r\n const descending = token.startsWith('-')\r\n const normalizedToken = descending ? token.slice(1) : token\r\n\r\n return {\r\n descending,\r\n path: normalizedToken.split('.').filter(Boolean),\r\n }\r\n })\r\n .filter((token) => token.path.length > 0)\r\n}\r\n\r\nfunction resolveSortValue(source: unknown, path: string[]): SortValue | undefined {\r\n let currentValue = source\r\n\r\n for (const segment of path) {\r\n if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {\r\n return undefined\r\n }\r\n\r\n if (!(segment in currentValue)) {\r\n return undefined\r\n }\r\n\r\n currentValue = (currentValue as Record<string, unknown>)[segment]\r\n }\r\n\r\n if (currentValue === null || currentValue === undefined) {\r\n return null\r\n }\r\n\r\n if (currentValue instanceof Date) {\r\n const timestamp = currentValue.getTime()\r\n\r\n return Number.isNaN(timestamp) ? undefined : timestamp\r\n }\r\n\r\n if (typeof currentValue === 'boolean') {\r\n return currentValue ? 1 : 0\r\n }\r\n\r\n if (typeof currentValue === 'number') {\r\n return Number.isFinite(currentValue) ? currentValue : undefined\r\n }\r\n\r\n if (typeof currentValue !== 'string') {\r\n return undefined\r\n }\r\n\r\n const normalizedValue = currentValue.trim()\r\n\r\n if (dateLikePattern.test(normalizedValue)) {\r\n const timestamp = Date.parse(normalizedValue)\r\n\r\n if (!Number.isNaN(timestamp)) {\r\n return timestamp\r\n }\r\n }\r\n\r\n return normalizedValue\r\n}\r\n\r\nfunction compareSortValues(leftValue: SortValue, rightValue: SortValue): number {\r\n if (leftValue === null && rightValue === null) {\r\n return 0\r\n }\r\n\r\n if (leftValue === null) {\r\n return -1\r\n }\r\n\r\n if (rightValue === null) {\r\n return 1\r\n }\r\n\r\n if (typeof leftValue === 'number' && typeof rightValue === 'number') {\r\n if (leftValue === rightValue) {\r\n return 0\r\n }\r\n\r\n return leftValue < rightValue ? -1 : 1\r\n }\r\n\r\n return String(leftValue).localeCompare(String(rightValue), undefined, {\r\n numeric: true,\r\n sensitivity: 'base',\r\n })\r\n}\r\n\r\nfunction createTreeNodeComparator(sort?: string) {\r\n const sortTokens = parseSortTokens(sort)\r\n\r\n if (sortTokens.length === 0) {\r\n return null\r\n }\r\n\r\n return (leftNode: TreeNode, rightNode: TreeNode): number => {\r\n for (const sortToken of sortTokens) {\r\n const leftValue = resolveSortValue(leftNode.doc, sortToken.path)\r\n const rightValue = resolveSortValue(rightNode.doc, sortToken.path)\r\n\r\n if (leftValue === undefined || rightValue === undefined) {\r\n continue\r\n }\r\n\r\n const comparison = compareSortValues(leftValue, rightValue)\r\n\r\n if (comparison !== 0) {\r\n return sortToken.descending ? -comparison : comparison\r\n }\r\n }\r\n\r\n return leftNode.originalIndex - rightNode.originalIndex\r\n }\r\n}\r\n\r\nexport function buildPageTreeDocs(\r\n docs: PageTreeSourceDoc[],\r\n options: BuildPageTreeDocsOptions = {},\r\n): PageTreeDoc[] {\r\n const parentFieldSlug = options.parentFieldSlug ?? 'parent'\r\n const nodes: TreeNode[] = docs.map((doc, index) => ({\r\n doc,\r\n id: stringifyDocID(doc.id),\r\n originalIndex: index,\r\n parentID: getDocParentID(doc, parentFieldSlug),\r\n }))\r\n const nodesByID = new Map(nodes.map((node) => [node.id, node]))\r\n const cycleNodeIDs = getCycleNodeIDs(nodes, nodesByID)\r\n const effectiveParentIDByNodeID = new Map<string, null | string>()\r\n const childrenByParentID = new Map<null | string, TreeNode[]>()\r\n const compareTreeNodes = createTreeNodeComparator(options.sort)\r\n\r\n for (const node of nodes) {\r\n const parentExists = node.parentID !== null && nodesByID.has(node.parentID)\r\n const effectiveParentID =\r\n parentExists && !cycleNodeIDs.has(node.id) ? node.parentID : null\r\n\r\n effectiveParentIDByNodeID.set(node.id, effectiveParentID)\r\n\r\n const currentChildren = childrenByParentID.get(effectiveParentID) ?? []\r\n currentChildren.push(node)\r\n childrenByParentID.set(effectiveParentID, currentChildren)\r\n }\r\n\r\n if (compareTreeNodes) {\r\n for (const children of childrenByParentID.values()) {\r\n children.sort(compareTreeNodes)\r\n }\r\n }\r\n\r\n const orderedDocs: PageTreeDoc[] = []\r\n const subtreeDepthByID = new Map<string, number>()\r\n const visitedNodeIDs = new Set<string>()\r\n\r\n const getShadeLevel = (rootMaxDepth: number, depth: number): number => {\r\n if (rootMaxDepth === 0) {\r\n return 0\r\n }\r\n\r\n return Math.max(rootMaxDepth - depth + 1, 1)\r\n }\r\n\r\n const getSubtreeDepth = (node: TreeNode): number => {\r\n const cachedDepth = subtreeDepthByID.get(node.id)\r\n\r\n if (typeof cachedDepth === 'number') {\r\n return cachedDepth\r\n }\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n\r\n if (children.length === 0) {\r\n subtreeDepthByID.set(node.id, 0)\r\n return 0\r\n }\r\n\r\n const depth = Math.max(...children.map((child) => getSubtreeDepth(child) + 1))\r\n subtreeDepthByID.set(node.id, depth)\r\n return depth\r\n }\r\n\r\n const visit = (node: TreeNode, ancestorIDs: string[], rootMaxDepth: number) => {\r\n if (visitedNodeIDs.has(node.id)) {\r\n return\r\n }\r\n\r\n visitedNodeIDs.add(node.id)\r\n\r\n const children = childrenByParentID.get(node.id) ?? []\r\n const depth = ancestorIDs.length\r\n const shadeLevel = getShadeLevel(rootMaxDepth, depth)\r\n\r\n orderedDocs.push({\r\n ...(node.doc as PageTreeSourceDoc),\r\n __pageTreeAncestorIDs: ancestorIDs,\r\n __pageTreeDepth: depth,\r\n __pageTreeHasChildren: children.length > 0,\r\n __pageTreeID: node.id,\r\n __pageTreeOrderIndex: orderedDocs.length,\r\n __pageTreeParentID: effectiveParentIDByNodeID.get(node.id) ?? null,\r\n __pageTreeShadeLevel: shadeLevel,\r\n })\r\n\r\n for (const child of children) {\r\n visit(child, [...ancestorIDs, node.id], rootMaxDepth)\r\n }\r\n }\r\n\r\n const roots = [...(childrenByParentID.get(null) ?? [])]\r\n\r\n for (const root of roots) {\r\n visit(root, [], getSubtreeDepth(root))\r\n }\r\n\r\n const fallbackNodes = compareTreeNodes ? [...nodes].sort(compareTreeNodes) : nodes\r\n\r\n for (const node of fallbackNodes) {\r\n if (!visitedNodeIDs.has(node.id)) {\r\n visit(node, [], getSubtreeDepth(node))\r\n }\r\n }\r\n\r\n return orderedDocs\r\n}\r\n\r\nexport function getVisibleTreeDocs(\r\n docs: PageTreeDoc[],\r\n collapsedIDs: ReadonlySet<string>,\r\n): PageTreeDoc[] {\r\n return docs.filter((doc) =>\r\n doc.__pageTreeAncestorIDs.every((ancestorID) => !collapsedIDs.has(ancestorID)),\r\n )\r\n}\r\n"],"names":["dateLikePattern","stringifyDocID","id","String","getRelationshipID","value","relationshipID","getDocParentID","doc","parentFieldSlug","getDocDisplayLabel","title","trim","length","slug","__pageTreeID","getDocSlugSegment","replace","buildDocSlugPath","args","docsByID","segments","__pageTreeAncestorIDs","map","ancestorID","get","filter","ancestorDoc","Boolean","push","join","buildProspectiveDocSlugPath","activeDoc","targetDoc","buildChildrenByParentID","docs","childrenByParentID","Map","docID","parentID","existingChildren","set","collectDescendantIDs","rootID","descendantIDs","stack","currentID","pop","getCycleNodeIDs","nodes","nodesByID","cycleNodeIDs","Set","pathStack","visitState","walk","node","currentState","cycleStartIndex","lastIndexOf","cycleIDs","slice","cycleID","add","parentNode","parseSortTokens","sort","split","token","descending","startsWith","normalizedToken","path","resolveSortValue","source","currentValue","segment","Array","isArray","undefined","Date","timestamp","getTime","Number","isNaN","isFinite","normalizedValue","test","parse","compareSortValues","leftValue","rightValue","localeCompare","numeric","sensitivity","createTreeNodeComparator","sortTokens","leftNode","rightNode","sortToken","comparison","originalIndex","buildPageTreeDocs","options","index","effectiveParentIDByNodeID","compareTreeNodes","parentExists","has","effectiveParentID","currentChildren","children","values","orderedDocs","subtreeDepthByID","visitedNodeIDs","getShadeLevel","rootMaxDepth","depth","Math","max","getSubtreeDepth","cachedDepth","child","visit","ancestorIDs","shadeLevel","__pageTreeDepth","__pageTreeHasChildren","__pageTreeOrderIndex","__pageTreeParentID","__pageTreeShadeLevel","roots","root","fallbackNodes","getVisibleTreeDocs","collapsedIDs","every"],"mappings":"AA6BA,MAAMA,kBAAkB;AAOxB,OAAO,SAASC,eAAeC,EAA+B;IAC5D,OAAOC,OAAOD,MAAM;AACtB;AAEA,OAAO,SAASE,kBAAkBC,KAAc;IAC9C,IAAI,OAAOA,UAAU,YAAY,OAAOA,UAAU,UAAU;QAC1D,OAAOJ,eAAeI;IACxB;IAEA,IAAIA,SAAS,OAAOA,UAAU,YAAY,QAAQA,OAAO;QACvD,MAAMC,iBAAiB,AAACD,MAAmCH,EAAE;QAE7D,IAAI,OAAOI,mBAAmB,YAAY,OAAOA,mBAAmB,UAAU;YAC5E,OAAOL,eAAeK;QACxB;IACF;IAEA,OAAO;AACT;AAEA,OAAO,SAASC,eACdC,GAAsB,EACtBC,kBAAkB,QAAQ;IAE1B,OAAOL,kBAAkBI,GAAG,CAACC,gBAAgB;AAC/C;AAEA,OAAO,SAASC,mBAAmBF,GAAgB;IACjD,MAAMG,QAAQH,IAAIG,KAAK;IAEvB,IAAI,OAAOA,UAAU,YAAYA,MAAMC,IAAI,GAAGC,MAAM,GAAG,GAAG;QACxD,OAAOF;IACT;IAEA,IAAI,OAAOH,IAAIM,IAAI,KAAK,YAAYN,IAAIM,IAAI,CAACF,IAAI,GAAGC,MAAM,GAAG,GAAG;QAC9D,OAAOL,IAAIM,IAAI;IACjB;IAEA,OAAON,IAAIO,YAAY;AACzB;AAEA,OAAO,SAASC,kBAAkBR,GAAgB;IAChD,IAAI,OAAOA,IAAIM,IAAI,KAAK,YAAYN,IAAIM,IAAI,CAACF,IAAI,GAAGC,MAAM,GAAG,GAAG;QAC9D,OAAOL,IAAIM,IAAI,CAACF,IAAI,GAAGK,OAAO,CAAC,cAAc;IAC/C;IAEA,OAAOT,IAAIO,YAAY;AACzB;AAEA,OAAO,SAASG,iBAAiBC,IAGhC;IACC,MAAM,EAAEX,GAAG,EAAEY,QAAQ,EAAE,GAAGD;IAC1B,MAAME,WAAWb,IAAIc,qBAAqB,CACvCC,GAAG,CAAC,CAACC,aAAeJ,SAASK,GAAG,CAACD,aACjCE,MAAM,CAAC,CAACC,cAA4CC,QAAQD,cAC5DJ,GAAG,CAACP;IAEPK,SAASQ,IAAI,CAACb,kBAAkBR;IAEhC,OAAO,CAAC,CAAC,EAAEa,SAASK,MAAM,CAACE,SAASE,IAAI,CAAC,MAAM;AACjD;AAEA,OAAO,SAASC,4BAA4BZ,IAI3C;IACC,MAAM,EAAEa,SAAS,EAAEZ,QAAQ,EAAEa,SAAS,EAAE,GAAGd;IAE3C,IAAI,CAACc,WAAW;QACd,OAAO,CAAC,CAAC,EAAEjB,kBAAkBgB,YAAY;IAC3C;IAEA,MAAMX,WAAWY,UAAUX,qBAAqB,CAC7CC,GAAG,CAAC,CAACC,aAAeJ,SAASK,GAAG,CAACD,aACjCE,MAAM,CAAC,CAACC,cAA4CC,QAAQD,cAC5DJ,GAAG,CAACP;IAEPK,SAASQ,IAAI,CAACb,kBAAkBiB,YAAYjB,kBAAkBgB;IAE9D,OAAO,CAAC,CAAC,EAAEX,SAASK,MAAM,CAACE,SAASE,IAAI,CAAC,MAAM;AACjD;AAEA,OAAO,SAASI,wBAAwBf,IAGvC;IACC,MAAM,EAAEgB,IAAI,EAAE1B,kBAAkB,QAAQ,EAAE,GAAGU;IAC7C,MAAMiB,qBAAqB,IAAIC;IAE/B,KAAK,MAAM7B,OAAO2B,KAAM;QACtB,MAAMG,QAAQrC,eAAeO,IAAIN,EAAE;QACnC,MAAMqC,WAAWhC,eAAeC,KAAKC;QACrC,MAAM+B,mBAAmBJ,mBAAmBX,GAAG,CAACc,aAAa,EAAE;QAE/DC,iBAAiBX,IAAI,CAACS;QACtBF,mBAAmBK,GAAG,CAACF,UAAUC;IACnC;IAEA,OAAOJ;AACT;AAEA,OAAO,SAASM,qBACdC,MAAc,EACdP,kBAAgD;IAEhD,MAAMQ,gBAA0B,EAAE;IAClC,MAAMC,QAAQ;WAAKT,mBAAmBX,GAAG,CAACkB,WAAW,EAAE;KAAE;IAEzD,MAAOE,MAAMhC,MAAM,GAAG,EAAG;QACvB,MAAMiC,YAAYD,MAAME,GAAG;QAE3B,IAAI,CAACD,WAAW;YACd;QACF;QAEAF,cAAcf,IAAI,CAACiB;QACnBD,MAAMhB,IAAI,IAAKO,mBAAmBX,GAAG,CAACqB,cAAc,EAAE;IACxD;IAEA,OAAOF;AACT;AAEA,SAASI,gBAAgBC,KAAiB,EAAEC,SAAgC;IAC1E,MAAMC,eAAe,IAAIC;IACzB,MAAMC,YAAsB,EAAE;IAC9B,MAAMC,aAAa,IAAIjB;IAEvB,MAAMkB,OAAO,CAACC;QACZ,MAAMC,eAAeH,WAAW7B,GAAG,CAAC+B,KAAKtD,EAAE;QAE3C,IAAIuD,iBAAiB,QAAQ;YAC3B;QACF;QAEA,IAAIA,iBAAiB,YAAY;YAC/B,MAAMC,kBAAkBL,UAAUM,WAAW,CAACH,KAAKtD,EAAE;YACrD,MAAM0D,WAAWF,mBAAmB,IAAIL,UAAUQ,KAAK,CAACH,mBAAmB;gBAACF,KAAKtD,EAAE;aAAC;YAEpF,KAAK,MAAM4D,WAAWF,SAAU;gBAC9BT,aAAaY,GAAG,CAACD;YACnB;YAEA;QACF;QAEAR,WAAWb,GAAG,CAACe,KAAKtD,EAAE,EAAE;QACxBmD,UAAUxB,IAAI,CAAC2B,KAAKtD,EAAE;QAEtB,IAAIsD,KAAKjB,QAAQ,KAAK,MAAM;YAC1B,MAAMyB,aAAad,UAAUzB,GAAG,CAAC+B,KAAKjB,QAAQ;YAE9C,IAAIyB,YAAY;gBACdT,KAAKS;YACP;QACF;QAEAX,UAAUN,GAAG;QACbO,WAAWb,GAAG,CAACe,KAAKtD,EAAE,EAAE;IAC1B;IAEA,KAAK,MAAMsD,QAAQP,MAAO;QACxBM,KAAKC;IACP;IAEA,OAAOL;AACT;AAEA,SAASc,gBAAgBC,IAAa;IACpC,IAAI,OAAOA,SAAS,YAAYA,KAAKtD,IAAI,GAAGC,MAAM,KAAK,GAAG;QACxD,OAAO,EAAE;IACX;IAEA,OAAOqD,KACJC,KAAK,CAAC,KACN5C,GAAG,CAAC,CAAC6C,QAAUA,MAAMxD,IAAI,IACzBc,MAAM,CAAC,CAAC0C,QAAUA,MAAMvD,MAAM,GAAG,GACjCU,GAAG,CAAC,CAAC6C;QACJ,MAAMC,aAAaD,MAAME,UAAU,CAAC;QACpC,MAAMC,kBAAkBF,aAAaD,MAAMP,KAAK,CAAC,KAAKO;QAEtD,OAAO;YACLC;YACAG,MAAMD,gBAAgBJ,KAAK,CAAC,KAAKzC,MAAM,CAACE;QAC1C;IACF,GACCF,MAAM,CAAC,CAAC0C,QAAUA,MAAMI,IAAI,CAAC3D,MAAM,GAAG;AAC3C;AAEA,SAAS4D,iBAAiBC,MAAe,EAAEF,IAAc;IACvD,IAAIG,eAAeD;IAEnB,KAAK,MAAME,WAAWJ,KAAM;QAC1B,IAAI,CAACG,gBAAgB,OAAOA,iBAAiB,YAAYE,MAAMC,OAAO,CAACH,eAAe;YACpF,OAAOI;QACT;QAEA,IAAI,CAAEH,CAAAA,WAAWD,YAAW,GAAI;YAC9B,OAAOI;QACT;QAEAJ,eAAe,AAACA,YAAwC,CAACC,QAAQ;IACnE;IAEA,IAAID,iBAAiB,QAAQA,iBAAiBI,WAAW;QACvD,OAAO;IACT;IAEA,IAAIJ,wBAAwBK,MAAM;QAChC,MAAMC,YAAYN,aAAaO,OAAO;QAEtC,OAAOC,OAAOC,KAAK,CAACH,aAAaF,YAAYE;IAC/C;IAEA,IAAI,OAAON,iBAAiB,WAAW;QACrC,OAAOA,eAAe,IAAI;IAC5B;IAEA,IAAI,OAAOA,iBAAiB,UAAU;QACpC,OAAOQ,OAAOE,QAAQ,CAACV,gBAAgBA,eAAeI;IACxD;IAEA,IAAI,OAAOJ,iBAAiB,UAAU;QACpC,OAAOI;IACT;IAEA,MAAMO,kBAAkBX,aAAa/D,IAAI;IAEzC,IAAIZ,gBAAgBuF,IAAI,CAACD,kBAAkB;QACzC,MAAML,YAAYD,KAAKQ,KAAK,CAACF;QAE7B,IAAI,CAACH,OAAOC,KAAK,CAACH,YAAY;YAC5B,OAAOA;QACT;IACF;IAEA,OAAOK;AACT;AAEA,SAASG,kBAAkBC,SAAoB,EAAEC,UAAqB;IACpE,IAAID,cAAc,QAAQC,eAAe,MAAM;QAC7C,OAAO;IACT;IAEA,IAAID,cAAc,MAAM;QACtB,OAAO,CAAC;IACV;IAEA,IAAIC,eAAe,MAAM;QACvB,OAAO;IACT;IAEA,IAAI,OAAOD,cAAc,YAAY,OAAOC,eAAe,UAAU;QACnE,IAAID,cAAcC,YAAY;YAC5B,OAAO;QACT;QAEA,OAAOD,YAAYC,aAAa,CAAC,IAAI;IACvC;IAEA,OAAOxF,OAAOuF,WAAWE,aAAa,CAACzF,OAAOwF,aAAaZ,WAAW;QACpEc,SAAS;QACTC,aAAa;IACf;AACF;AAEA,SAASC,yBAAyB7B,IAAa;IAC7C,MAAM8B,aAAa/B,gBAAgBC;IAEnC,IAAI8B,WAAWnF,MAAM,KAAK,GAAG;QAC3B,OAAO;IACT;IAEA,OAAO,CAACoF,UAAoBC;QAC1B,KAAK,MAAMC,aAAaH,WAAY;YAClC,MAAMN,YAAYjB,iBAAiBwB,SAASzF,GAAG,EAAE2F,UAAU3B,IAAI;YAC/D,MAAMmB,aAAalB,iBAAiByB,UAAU1F,GAAG,EAAE2F,UAAU3B,IAAI;YAEjE,IAAIkB,cAAcX,aAAaY,eAAeZ,WAAW;gBACvD;YACF;YAEA,MAAMqB,aAAaX,kBAAkBC,WAAWC;YAEhD,IAAIS,eAAe,GAAG;gBACpB,OAAOD,UAAU9B,UAAU,GAAG,CAAC+B,aAAaA;YAC9C;QACF;QAEA,OAAOH,SAASI,aAAa,GAAGH,UAAUG,aAAa;IACzD;AACF;AAEA,OAAO,SAASC,kBACdnE,IAAyB,EACzBoE,UAAoC,CAAC,CAAC;IAEtC,MAAM9F,kBAAkB8F,QAAQ9F,eAAe,IAAI;IACnD,MAAMwC,QAAoBd,KAAKZ,GAAG,CAAC,CAACf,KAAKgG,QAAW,CAAA;YAClDhG;YACAN,IAAID,eAAeO,IAAIN,EAAE;YACzBmG,eAAeG;YACfjE,UAAUhC,eAAeC,KAAKC;QAChC,CAAA;IACA,MAAMyC,YAAY,IAAIb,IAAIY,MAAM1B,GAAG,CAAC,CAACiC,OAAS;YAACA,KAAKtD,EAAE;YAAEsD;SAAK;IAC7D,MAAML,eAAeH,gBAAgBC,OAAOC;IAC5C,MAAMuD,4BAA4B,IAAIpE;IACtC,MAAMD,qBAAqB,IAAIC;IAC/B,MAAMqE,mBAAmBX,yBAAyBQ,QAAQrC,IAAI;IAE9D,KAAK,MAAMV,QAAQP,MAAO;QACxB,MAAM0D,eAAenD,KAAKjB,QAAQ,KAAK,QAAQW,UAAU0D,GAAG,CAACpD,KAAKjB,QAAQ;QAC1E,MAAMsE,oBACJF,gBAAgB,CAACxD,aAAayD,GAAG,CAACpD,KAAKtD,EAAE,IAAIsD,KAAKjB,QAAQ,GAAG;QAE/DkE,0BAA0BhE,GAAG,CAACe,KAAKtD,EAAE,EAAE2G;QAEvC,MAAMC,kBAAkB1E,mBAAmBX,GAAG,CAACoF,sBAAsB,EAAE;QACvEC,gBAAgBjF,IAAI,CAAC2B;QACrBpB,mBAAmBK,GAAG,CAACoE,mBAAmBC;IAC5C;IAEA,IAAIJ,kBAAkB;QACpB,KAAK,MAAMK,YAAY3E,mBAAmB4E,MAAM,GAAI;YAClDD,SAAS7C,IAAI,CAACwC;QAChB;IACF;IAEA,MAAMO,cAA6B,EAAE;IACrC,MAAMC,mBAAmB,IAAI7E;IAC7B,MAAM8E,iBAAiB,IAAI/D;IAE3B,MAAMgE,gBAAgB,CAACC,cAAsBC;QAC3C,IAAID,iBAAiB,GAAG;YACtB,OAAO;QACT;QAEA,OAAOE,KAAKC,GAAG,CAACH,eAAeC,QAAQ,GAAG;IAC5C;IAEA,MAAMG,kBAAkB,CAACjE;QACvB,MAAMkE,cAAcR,iBAAiBzF,GAAG,CAAC+B,KAAKtD,EAAE;QAEhD,IAAI,OAAOwH,gBAAgB,UAAU;YACnC,OAAOA;QACT;QAEA,MAAMX,WAAW3E,mBAAmBX,GAAG,CAAC+B,KAAKtD,EAAE,KAAK,EAAE;QAEtD,IAAI6G,SAASlG,MAAM,KAAK,GAAG;YACzBqG,iBAAiBzE,GAAG,CAACe,KAAKtD,EAAE,EAAE;YAC9B,OAAO;QACT;QAEA,MAAMoH,QAAQC,KAAKC,GAAG,IAAIT,SAASxF,GAAG,CAAC,CAACoG,QAAUF,gBAAgBE,SAAS;QAC3ET,iBAAiBzE,GAAG,CAACe,KAAKtD,EAAE,EAAEoH;QAC9B,OAAOA;IACT;IAEA,MAAMM,QAAQ,CAACpE,MAAgBqE,aAAuBR;QACpD,IAAIF,eAAeP,GAAG,CAACpD,KAAKtD,EAAE,GAAG;YAC/B;QACF;QAEAiH,eAAepD,GAAG,CAACP,KAAKtD,EAAE;QAE1B,MAAM6G,WAAW3E,mBAAmBX,GAAG,CAAC+B,KAAKtD,EAAE,KAAK,EAAE;QACtD,MAAMoH,QAAQO,YAAYhH,MAAM;QAChC,MAAMiH,aAAaV,cAAcC,cAAcC;QAE/CL,YAAYpF,IAAI,CAAC;YACf,GAAI2B,KAAKhD,GAAG;YACZc,uBAAuBuG;YACvBE,iBAAiBT;YACjBU,uBAAuBjB,SAASlG,MAAM,GAAG;YACzCE,cAAcyC,KAAKtD,EAAE;YACrB+H,sBAAsBhB,YAAYpG,MAAM;YACxCqH,oBAAoBzB,0BAA0BhF,GAAG,CAAC+B,KAAKtD,EAAE,KAAK;YAC9DiI,sBAAsBL;QACxB;QAEA,KAAK,MAAMH,SAASZ,SAAU;YAC5Ba,MAAMD,OAAO;mBAAIE;gBAAarE,KAAKtD,EAAE;aAAC,EAAEmH;QAC1C;IACF;IAEA,MAAMe,QAAQ;WAAKhG,mBAAmBX,GAAG,CAAC,SAAS,EAAE;KAAE;IAEvD,KAAK,MAAM4G,QAAQD,MAAO;QACxBR,MAAMS,MAAM,EAAE,EAAEZ,gBAAgBY;IAClC;IAEA,MAAMC,gBAAgB5B,mBAAmB;WAAIzD;KAAM,CAACiB,IAAI,CAACwC,oBAAoBzD;IAE7E,KAAK,MAAMO,QAAQ8E,cAAe;QAChC,IAAI,CAACnB,eAAeP,GAAG,CAACpD,KAAKtD,EAAE,GAAG;YAChC0H,MAAMpE,MAAM,EAAE,EAAEiE,gBAAgBjE;QAClC;IACF;IAEA,OAAOyD;AACT;AAEA,OAAO,SAASsB,mBACdpG,IAAmB,EACnBqG,YAAiC;IAEjC,OAAOrG,KAAKT,MAAM,CAAC,CAAClB,MAClBA,IAAIc,qBAAqB,CAACmH,KAAK,CAAC,CAACjH,aAAe,CAACgH,aAAa5B,GAAG,CAACpF;AAEtE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Collision, type CollisionDetection } from '@dnd-kit/core';
|
|
2
|
+
import type { PageTreeDropTarget } from './dropTargets.js';
|
|
3
|
+
export declare const PAGE_TREE_INSERT_EDGE_SIZE_PX = 6;
|
|
4
|
+
export type PageTreePointerCollisionCandidate = {
|
|
5
|
+
dropTarget?: PageTreeDropTarget;
|
|
6
|
+
id: Collision['id'];
|
|
7
|
+
rect?: {
|
|
8
|
+
bottom: number;
|
|
9
|
+
top: number;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function resolvePageTreePointerCollisionID(args: {
|
|
13
|
+
collisions: PageTreePointerCollisionCandidate[];
|
|
14
|
+
pointerCoordinates: Parameters<CollisionDetection>[0]['pointerCoordinates'];
|
|
15
|
+
}): Collision['id'] | null;
|
|
16
|
+
export declare const pageTreeCollisionDetectionStrategy: CollisionDetection;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { closestCenter, pointerWithin } from '@dnd-kit/core';
|
|
2
|
+
export const PAGE_TREE_INSERT_EDGE_SIZE_PX = 6;
|
|
3
|
+
function getResolvedCollision(args) {
|
|
4
|
+
const { collisionID, droppableContainers, pointerCollisions } = args;
|
|
5
|
+
const existingPointerCollision = pointerCollisions.find((collision)=>collision.id === collisionID);
|
|
6
|
+
if (existingPointerCollision) {
|
|
7
|
+
return existingPointerCollision;
|
|
8
|
+
}
|
|
9
|
+
const droppableContainer = droppableContainers.find((container)=>container.id === collisionID);
|
|
10
|
+
if (!droppableContainer) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
id: collisionID,
|
|
15
|
+
data: {
|
|
16
|
+
droppableContainer,
|
|
17
|
+
value: 0
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function isRowCollision(collision) {
|
|
22
|
+
return collision.dropTarget?.dropType === 'row' && Boolean(collision.rect);
|
|
23
|
+
}
|
|
24
|
+
export function resolvePageTreePointerCollisionID(args) {
|
|
25
|
+
const { collisions, pointerCoordinates } = args;
|
|
26
|
+
if (!pointerCoordinates) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const directInsertCollision = collisions.find((collision)=>collision.dropTarget?.dropType === 'insert');
|
|
30
|
+
if (directInsertCollision) {
|
|
31
|
+
return directInsertCollision.id;
|
|
32
|
+
}
|
|
33
|
+
const rowCollision = collisions.find(isRowCollision);
|
|
34
|
+
if (!rowCollision) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
38
|
+
return rowCollision.dropTarget.insertBeforeDropID;
|
|
39
|
+
}
|
|
40
|
+
if (rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
41
|
+
return rowCollision.dropTarget.insertAfterDropID;
|
|
42
|
+
}
|
|
43
|
+
return rowCollision.id;
|
|
44
|
+
}
|
|
45
|
+
export const pageTreeCollisionDetectionStrategy = (args)=>{
|
|
46
|
+
const pointerCollisions = pointerWithin(args);
|
|
47
|
+
const resolvedCollisionID = resolvePageTreePointerCollisionID({
|
|
48
|
+
collisions: pointerCollisions.map((collision)=>({
|
|
49
|
+
id: collision.id,
|
|
50
|
+
dropTarget: collision.data?.droppableContainer.data.current,
|
|
51
|
+
rect: args.droppableRects.get(collision.id) ?? undefined
|
|
52
|
+
})),
|
|
53
|
+
pointerCoordinates: args.pointerCoordinates
|
|
54
|
+
});
|
|
55
|
+
if (resolvedCollisionID !== null) {
|
|
56
|
+
const resolvedCollision = getResolvedCollision({
|
|
57
|
+
collisionID: resolvedCollisionID,
|
|
58
|
+
droppableContainers: args.droppableContainers,
|
|
59
|
+
pointerCollisions
|
|
60
|
+
});
|
|
61
|
+
if (resolvedCollision) {
|
|
62
|
+
return [
|
|
63
|
+
resolvedCollision
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return closestCenter(args);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=pageTreeCollision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/pageTreeCollision.ts"],"sourcesContent":["import {\n closestCenter,\n type Collision,\n type CollisionDetection,\n pointerWithin,\n} from '@dnd-kit/core'\n\nimport type { PageTreeDropTarget, PageTreeRowDropTarget } from './dropTargets.js'\n\nexport const PAGE_TREE_INSERT_EDGE_SIZE_PX = 6\n\nexport type PageTreePointerCollisionCandidate = {\n dropTarget?: PageTreeDropTarget\n id: Collision['id']\n rect?: {\n bottom: number\n top: number\n }\n}\n\nfunction getResolvedCollision(args: {\n collisionID: Collision['id']\n droppableContainers: Parameters<CollisionDetection>[0]['droppableContainers']\n pointerCollisions: Collision[]\n}): Collision | null {\n const { collisionID, droppableContainers, pointerCollisions } = args\n const existingPointerCollision = pointerCollisions.find((collision) => collision.id === collisionID)\n\n if (existingPointerCollision) {\n return existingPointerCollision\n }\n\n const droppableContainer = droppableContainers.find((container) => container.id === collisionID)\n\n if (!droppableContainer) {\n return null\n }\n\n return {\n id: collisionID,\n data: {\n droppableContainer,\n value: 0,\n },\n }\n}\n\ntype PageTreeResolvedRowCollision = {\n dropTarget: PageTreeRowDropTarget\n rect: {\n bottom: number\n top: number\n }\n} & PageTreePointerCollisionCandidate\n\nfunction isRowCollision(\n collision: PageTreePointerCollisionCandidate,\n): collision is PageTreeResolvedRowCollision {\n return collision.dropTarget?.dropType === 'row' && Boolean(collision.rect)\n}\n\nexport function resolvePageTreePointerCollisionID(args: {\n collisions: PageTreePointerCollisionCandidate[]\n pointerCoordinates: Parameters<CollisionDetection>[0]['pointerCoordinates']\n}): Collision['id'] | null {\n const { collisions, pointerCoordinates } = args\n\n if (!pointerCoordinates) {\n return null\n }\n\n const directInsertCollision = collisions.find((collision) => collision.dropTarget?.dropType === 'insert')\n\n if (directInsertCollision) {\n return directInsertCollision.id\n }\n\n const rowCollision = collisions.find(isRowCollision)\n\n if (!rowCollision) {\n return null\n }\n\n if (pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {\n return rowCollision.dropTarget.insertBeforeDropID\n }\n\n if (rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {\n return rowCollision.dropTarget.insertAfterDropID\n }\n\n return rowCollision.id\n}\n\nexport const pageTreeCollisionDetectionStrategy: CollisionDetection = (args) => {\n const pointerCollisions = pointerWithin(args)\n const resolvedCollisionID = resolvePageTreePointerCollisionID({\n collisions: pointerCollisions.map((collision) => ({\n id: collision.id,\n dropTarget: collision.data?.droppableContainer.data.current as PageTreeDropTarget | undefined,\n rect: args.droppableRects.get(collision.id) ?? undefined,\n })),\n pointerCoordinates: args.pointerCoordinates,\n })\n\n if (resolvedCollisionID !== null) {\n const resolvedCollision = getResolvedCollision({\n collisionID: resolvedCollisionID,\n droppableContainers: args.droppableContainers,\n pointerCollisions,\n })\n\n if (resolvedCollision) {\n return [resolvedCollision]\n }\n }\n\n return closestCenter(args)\n}\n"],"names":["closestCenter","pointerWithin","PAGE_TREE_INSERT_EDGE_SIZE_PX","getResolvedCollision","args","collisionID","droppableContainers","pointerCollisions","existingPointerCollision","find","collision","id","droppableContainer","container","data","value","isRowCollision","dropTarget","dropType","Boolean","rect","resolvePageTreePointerCollisionID","collisions","pointerCoordinates","directInsertCollision","rowCollision","y","top","insertBeforeDropID","bottom","insertAfterDropID","pageTreeCollisionDetectionStrategy","resolvedCollisionID","map","current","droppableRects","get","undefined","resolvedCollision"],"mappings":"AAAA,SACEA,aAAa,EAGbC,aAAa,QACR,gBAAe;AAItB,OAAO,MAAMC,gCAAgC,EAAC;AAW9C,SAASC,qBAAqBC,IAI7B;IACC,MAAM,EAAEC,WAAW,EAAEC,mBAAmB,EAAEC,iBAAiB,EAAE,GAAGH;IAChE,MAAMI,2BAA2BD,kBAAkBE,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKN;IAExF,IAAIG,0BAA0B;QAC5B,OAAOA;IACT;IAEA,MAAMI,qBAAqBN,oBAAoBG,IAAI,CAAC,CAACI,YAAcA,UAAUF,EAAE,KAAKN;IAEpF,IAAI,CAACO,oBAAoB;QACvB,OAAO;IACT;IAEA,OAAO;QACLD,IAAIN;QACJS,MAAM;YACJF;YACAG,OAAO;QACT;IACF;AACF;AAUA,SAASC,eACPN,SAA4C;IAE5C,OAAOA,UAAUO,UAAU,EAAEC,aAAa,SAASC,QAAQT,UAAUU,IAAI;AAC3E;AAEA,OAAO,SAASC,kCAAkCjB,IAGjD;IACC,MAAM,EAAEkB,UAAU,EAAEC,kBAAkB,EAAE,GAAGnB;IAE3C,IAAI,CAACmB,oBAAoB;QACvB,OAAO;IACT;IAEA,MAAMC,wBAAwBF,WAAWb,IAAI,CAAC,CAACC,YAAcA,UAAUO,UAAU,EAAEC,aAAa;IAEhG,IAAIM,uBAAuB;QACzB,OAAOA,sBAAsBb,EAAE;IACjC;IAEA,MAAMc,eAAeH,WAAWb,IAAI,CAACO;IAErC,IAAI,CAACS,cAAc;QACjB,OAAO;IACT;IAEA,IAAIF,mBAAmBG,CAAC,GAAGD,aAAaL,IAAI,CAACO,GAAG,IAAIzB,+BAA+B;QACjF,OAAOuB,aAAaR,UAAU,CAACW,kBAAkB;IACnD;IAEA,IAAIH,aAAaL,IAAI,CAACS,MAAM,GAAGN,mBAAmBG,CAAC,IAAIxB,+BAA+B;QACpF,OAAOuB,aAAaR,UAAU,CAACa,iBAAiB;IAClD;IAEA,OAAOL,aAAad,EAAE;AACxB;AAEA,OAAO,MAAMoB,qCAAyD,CAAC3B;IACrE,MAAMG,oBAAoBN,cAAcG;IACxC,MAAM4B,sBAAsBX,kCAAkC;QAC5DC,YAAYf,kBAAkB0B,GAAG,CAAC,CAACvB,YAAe,CAAA;gBAChDC,IAAID,UAAUC,EAAE;gBAChBM,YAAYP,UAAUI,IAAI,EAAEF,mBAAmBE,KAAKoB;gBACpDd,MAAMhB,KAAK+B,cAAc,CAACC,GAAG,CAAC1B,UAAUC,EAAE,KAAK0B;YACjD,CAAA;QACAd,oBAAoBnB,KAAKmB,kBAAkB;IAC7C;IAEA,IAAIS,wBAAwB,MAAM;QAChC,MAAMM,oBAAoBnC,qBAAqB;YAC7CE,aAAa2B;YACb1B,qBAAqBF,KAAKE,mBAAmB;YAC7CC;QACF;QAEA,IAAI+B,mBAAmB;YACrB,OAAO;gBAACA;aAAkB;QAC5B;IACF;IAEA,OAAOtC,cAAcI;AACvB,EAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
import type { NestedDocsPageTreePluginCollectionCustom } from '../types.js';
|
|
3
|
+
export declare function getCollectionPageTreeConfig(collection: Pick<CollectionConfig, 'custom'>): null | NestedDocsPageTreePluginCollectionCustom;
|