payload-nested-docs-page-tree 1.2.3 → 1.4.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 -21
- package/README.md +136 -47
- package/dist/components/PageTreeContext.d.ts +13 -1
- package/dist/components/PageTreeContext.js +11 -1
- package/dist/components/PageTreeContext.js.map +1 -1
- package/dist/components/PageTreeListView.client.d.ts +3 -4
- package/dist/components/PageTreeListView.client.js +645 -381
- package/dist/components/PageTreeListView.client.js.map +1 -1
- package/dist/components/PageTreeListView.module.css +90 -67
- package/dist/components/PageTreeListView.server.js +18 -28
- package/dist/components/PageTreeListView.server.js.map +1 -1
- package/dist/components/PageTreeTitleCell.js +80 -18
- package/dist/components/PageTreeTitleCell.js.map +1 -1
- package/dist/endpoints/createMovePageEndpoint.d.ts +2 -0
- package/dist/endpoints/createMovePageEndpoint.js +166 -123
- package/dist/endpoints/createMovePageEndpoint.js.map +1 -1
- package/dist/endpoints/createReorderPageEndpoint.d.ts +8 -0
- package/dist/endpoints/createReorderPageEndpoint.js +703 -0
- package/dist/endpoints/createReorderPageEndpoint.js.map +1 -0
- package/dist/endpoints/shared.d.ts +40 -0
- package/dist/endpoints/shared.js +93 -0
- package/dist/endpoints/shared.js.map +1 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/rsc.js.map +1 -1
- package/dist/index.js +105 -26
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/dist/utilities/badgeConfig.js.map +1 -1
- package/dist/utilities/diagnostics.d.ts +42 -0
- package/dist/utilities/diagnostics.js +83 -0
- package/dist/utilities/diagnostics.js.map +1 -0
- package/dist/utilities/dropTargets.d.ts +2 -2
- package/dist/utilities/dropTargets.js +9 -7
- package/dist/utilities/dropTargets.js.map +1 -1
- package/dist/utilities/pageTree.js +4 -6
- package/dist/utilities/pageTree.js.map +1 -1
- package/dist/utilities/pageTreeCollision.d.ts +6 -0
- package/dist/utilities/pageTreeCollision.js +39 -4
- package/dist/utilities/pageTreeCollision.js.map +1 -1
- package/dist/utilities/pageTreeConfig.js.map +1 -1
- package/dist/utilities/status.d.ts +1 -1
- package/dist/utilities/status.js +7 -3
- package/dist/utilities/status.js.map +1 -1
- package/package.json +13 -12
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
export type NestedDocsPageTreePluginDiagnosticEvent = {
|
|
3
|
+
collection: string;
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
/** Stable identifier for grouping events from the same logical operation. */
|
|
6
|
+
flow: string;
|
|
7
|
+
level: 'error' | 'info' | 'warn';
|
|
8
|
+
message: string;
|
|
9
|
+
source: 'move-endpoint:body-rejected' | 'move-endpoint:enter' | 'move-endpoint:error' | 'move-endpoint:ok' | 'move-endpoint:snapshot-after' | 'move-endpoint:snapshot-before' | 'page-tree-change:after' | 'page-tree-change:status-flip' | 'reorder-endpoint:body-rejected' | 'reorder-endpoint:enter' | 'reorder-endpoint:error' | 'reorder-endpoint:initial-migration' | 'reorder-endpoint:ok';
|
|
10
|
+
/** ISO timestamp; included so log dumps are self-describing. */
|
|
11
|
+
t: string;
|
|
12
|
+
};
|
|
13
|
+
export type NestedDocsPageTreePluginDiagnosticsConfig = {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Custom event sink. Defaults to a single-line `console.log` per event.
|
|
17
|
+
* Useful for routing diagnostics through `req.payload.logger` or similar.
|
|
18
|
+
*/
|
|
19
|
+
logger?: (event: NestedDocsPageTreePluginDiagnosticEvent) => void;
|
|
20
|
+
} | boolean;
|
|
21
|
+
export type Diagnostics = {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
log: (event: Omit<NestedDocsPageTreePluginDiagnosticEvent, 't'>) => void;
|
|
24
|
+
};
|
|
25
|
+
/** Context key used to thread a diagnostics flow id from the move endpoint into afterChange logs. */
|
|
26
|
+
export declare const DIAGNOSTICS_FLOW_CONTEXT_KEY = "__pageTreeDiagnosticsFlow";
|
|
27
|
+
export declare function resolveDiagnostics(config: NestedDocsPageTreePluginDiagnosticsConfig | undefined): Diagnostics;
|
|
28
|
+
/** Generate a short flow id so multi-event sequences group together in logs. */
|
|
29
|
+
export declare function createFlowID(prefix: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Read a doc directly from the published main-row layer (`draft: false`).
|
|
32
|
+
* Returns null when the doc is unpublished or unreadable so callers can log
|
|
33
|
+
* the absence instead of failing the request.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readMainRowSnapshot(args: {
|
|
36
|
+
collectionSlug: string;
|
|
37
|
+
fields: ReadonlyArray<string>;
|
|
38
|
+
id: number | string;
|
|
39
|
+
req: PayloadRequest;
|
|
40
|
+
}): Promise<null | Record<string, unknown>>;
|
|
41
|
+
/** Collapse populated relationship docs to their id so logs stay compact. */
|
|
42
|
+
export declare function projectField(value: unknown): unknown;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { stringifyDocID } from './pageTree.js';
|
|
2
|
+
const NOOP_DIAGNOSTICS = {
|
|
3
|
+
enabled: false,
|
|
4
|
+
log: ()=>{}
|
|
5
|
+
};
|
|
6
|
+
const DEFAULT_TAG = '[payload-nested-docs-page-tree]';
|
|
7
|
+
const DEFAULT_TAG_COLOR = '\x1b[38;2;59;130;246m';
|
|
8
|
+
const DEFAULT_TAG_STYLE = '\x1b[1m';
|
|
9
|
+
const RESET_STYLE = '\x1b[0m';
|
|
10
|
+
/** Context key used to thread a diagnostics flow id from the move endpoint into afterChange logs. */ export const DIAGNOSTICS_FLOW_CONTEXT_KEY = '__pageTreeDiagnosticsFlow';
|
|
11
|
+
function defaultLogger(event) {
|
|
12
|
+
// One line per event keeps terminal copy-paste usable while preserving all fields.
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.log(`${DEFAULT_TAG_STYLE}${DEFAULT_TAG_COLOR}${DEFAULT_TAG} ${event.source}${RESET_STYLE} ${event.message}`, JSON.stringify(event));
|
|
15
|
+
}
|
|
16
|
+
export function resolveDiagnostics(config) {
|
|
17
|
+
if (!config) {
|
|
18
|
+
return NOOP_DIAGNOSTICS;
|
|
19
|
+
}
|
|
20
|
+
const enabled = typeof config === 'boolean' ? config : Boolean(config.enabled);
|
|
21
|
+
if (!enabled) {
|
|
22
|
+
return NOOP_DIAGNOSTICS;
|
|
23
|
+
}
|
|
24
|
+
const logger = typeof config === 'object' && config.logger ? config.logger : defaultLogger;
|
|
25
|
+
return {
|
|
26
|
+
enabled: true,
|
|
27
|
+
log (event) {
|
|
28
|
+
try {
|
|
29
|
+
logger({
|
|
30
|
+
...event,
|
|
31
|
+
t: new Date().toISOString()
|
|
32
|
+
});
|
|
33
|
+
} catch {
|
|
34
|
+
// Diagnostics must never break a request.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Generate a short flow id so multi-event sequences group together in logs. */ export function createFlowID(prefix) {
|
|
40
|
+
return `${prefix}-${Math.random().toString(36).slice(2, 8)}-${Date.now().toString(36)}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read a doc directly from the published main-row layer (`draft: false`).
|
|
44
|
+
* Returns null when the doc is unpublished or unreadable so callers can log
|
|
45
|
+
* the absence instead of failing the request.
|
|
46
|
+
*/ export async function readMainRowSnapshot(args) {
|
|
47
|
+
const { id, collectionSlug, fields, req } = args;
|
|
48
|
+
try {
|
|
49
|
+
const doc = await req.payload.findByID({
|
|
50
|
+
id: id,
|
|
51
|
+
collection: collectionSlug,
|
|
52
|
+
depth: 0,
|
|
53
|
+
disableErrors: true,
|
|
54
|
+
draft: false,
|
|
55
|
+
overrideAccess: true,
|
|
56
|
+
req
|
|
57
|
+
});
|
|
58
|
+
if (!doc) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const snapshot = {
|
|
62
|
+
id: stringifyDocID(typeof doc.id === 'number' || typeof doc.id === 'string' ? doc.id : undefined)
|
|
63
|
+
};
|
|
64
|
+
for (const field of fields){
|
|
65
|
+
snapshot[field] = projectField(doc[field]);
|
|
66
|
+
}
|
|
67
|
+
return snapshot;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Collapse populated relationship docs to their id so logs stay compact. */ export function projectField(value) {
|
|
73
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
74
|
+
const candidate = value;
|
|
75
|
+
if ('id' in candidate) {
|
|
76
|
+
const id = candidate.id;
|
|
77
|
+
return typeof id === 'number' || typeof id === 'string' ? stringifyDocID(id) : value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/diagnostics.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { stringifyDocID } from './pageTree.js'\n\nexport type NestedDocsPageTreePluginDiagnosticEvent = {\n collection: string\n data: Record<string, unknown>\n /** Stable identifier for grouping events from the same logical operation. */\n flow: string\n level: 'error' | 'info' | 'warn'\n message: string\n source:\n | 'move-endpoint:body-rejected'\n | 'move-endpoint:enter'\n | 'move-endpoint:error'\n | 'move-endpoint:ok'\n | 'move-endpoint:snapshot-after'\n | 'move-endpoint:snapshot-before'\n | 'page-tree-change:after'\n | 'page-tree-change:status-flip'\n | 'reorder-endpoint:body-rejected'\n | 'reorder-endpoint:enter'\n | 'reorder-endpoint:error'\n | 'reorder-endpoint:initial-migration'\n | 'reorder-endpoint:ok'\n /** ISO timestamp; included so log dumps are self-describing. */\n t: string\n}\n\nexport type NestedDocsPageTreePluginDiagnosticsConfig =\n | {\n enabled: boolean\n /**\n * Custom event sink. Defaults to a single-line `console.log` per event.\n * Useful for routing diagnostics through `req.payload.logger` or similar.\n */\n logger?: (event: NestedDocsPageTreePluginDiagnosticEvent) => void\n }\n | boolean\n\nexport type Diagnostics = {\n enabled: boolean\n log: (event: Omit<NestedDocsPageTreePluginDiagnosticEvent, 't'>) => void\n}\n\nconst NOOP_DIAGNOSTICS: Diagnostics = {\n enabled: false,\n log: () => {},\n}\n\nconst DEFAULT_TAG = '[payload-nested-docs-page-tree]'\nconst DEFAULT_TAG_COLOR = '\\x1b[38;2;59;130;246m'\nconst DEFAULT_TAG_STYLE = '\\x1b[1m'\nconst RESET_STYLE = '\\x1b[0m'\n\n/** Context key used to thread a diagnostics flow id from the move endpoint into afterChange logs. */\nexport const DIAGNOSTICS_FLOW_CONTEXT_KEY = '__pageTreeDiagnosticsFlow'\n\nfunction defaultLogger(event: NestedDocsPageTreePluginDiagnosticEvent): void {\n // One line per event keeps terminal copy-paste usable while preserving all fields.\n // eslint-disable-next-line no-console\n console.log(\n `${DEFAULT_TAG_STYLE}${DEFAULT_TAG_COLOR}${DEFAULT_TAG} ${event.source}${RESET_STYLE} ${event.message}`,\n JSON.stringify(event),\n )\n}\n\nexport function resolveDiagnostics(\n config: NestedDocsPageTreePluginDiagnosticsConfig | undefined,\n): Diagnostics {\n if (!config) {\n return NOOP_DIAGNOSTICS\n }\n\n const enabled = typeof config === 'boolean' ? config : Boolean(config.enabled)\n\n if (!enabled) {\n return NOOP_DIAGNOSTICS\n }\n\n const logger = typeof config === 'object' && config.logger ? config.logger : defaultLogger\n\n return {\n enabled: true,\n log(event) {\n try {\n logger({ ...event, t: new Date().toISOString() })\n } catch {\n // Diagnostics must never break a request.\n }\n },\n }\n}\n\n/** Generate a short flow id so multi-event sequences group together in logs. */\nexport function createFlowID(prefix: string): string {\n return `${prefix}-${Math.random().toString(36).slice(2, 8)}-${Date.now().toString(36)}`\n}\n\n/**\n * Read a doc directly from the published main-row layer (`draft: false`).\n * Returns null when the doc is unpublished or unreadable so callers can log\n * the absence instead of failing the request.\n */\nexport async function readMainRowSnapshot(args: {\n collectionSlug: string\n fields: ReadonlyArray<string>\n id: number | string\n req: PayloadRequest\n}): Promise<null | Record<string, unknown>> {\n const { id, collectionSlug, fields, req } = args\n\n try {\n const doc = (await req.payload.findByID({\n id: id as never,\n collection: collectionSlug as never,\n depth: 0,\n disableErrors: true,\n draft: false,\n overrideAccess: true,\n req,\n } as never)) as unknown as null | Record<string, unknown>\n\n if (!doc) {\n return null\n }\n\n const snapshot: Record<string, unknown> = {\n id: stringifyDocID(typeof doc.id === 'number' || typeof doc.id === 'string' ? doc.id : undefined),\n }\n\n for (const field of fields) {\n snapshot[field] = projectField(doc[field])\n }\n\n return snapshot\n } catch {\n return null\n }\n}\n\n/** Collapse populated relationship docs to their id so logs stay compact. */\nexport function projectField(value: unknown): unknown {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const candidate = value as { id?: unknown }\n\n if ('id' in candidate) {\n const id = candidate.id\n\n return typeof id === 'number' || typeof id === 'string' ? stringifyDocID(id) : value\n }\n }\n\n return value\n}\n"],"names":["stringifyDocID","NOOP_DIAGNOSTICS","enabled","log","DEFAULT_TAG","DEFAULT_TAG_COLOR","DEFAULT_TAG_STYLE","RESET_STYLE","DIAGNOSTICS_FLOW_CONTEXT_KEY","defaultLogger","event","console","source","message","JSON","stringify","resolveDiagnostics","config","Boolean","logger","t","Date","toISOString","createFlowID","prefix","Math","random","toString","slice","now","readMainRowSnapshot","args","id","collectionSlug","fields","req","doc","payload","findByID","collection","depth","disableErrors","draft","overrideAccess","snapshot","undefined","field","projectField","value","Array","isArray","candidate"],"mappings":"AAEA,SAASA,cAAc,QAAQ,gBAAe;AA2C9C,MAAMC,mBAAgC;IACpCC,SAAS;IACTC,KAAK,KAAO;AACd;AAEA,MAAMC,cAAc;AACpB,MAAMC,oBAAoB;AAC1B,MAAMC,oBAAoB;AAC1B,MAAMC,cAAc;AAEpB,mGAAmG,GACnG,OAAO,MAAMC,+BAA+B,4BAA2B;AAEvE,SAASC,cAAcC,KAA8C;IACnE,mFAAmF;IACnF,sCAAsC;IACtCC,QAAQR,GAAG,CACT,GAAGG,oBAAoBD,oBAAoBD,YAAY,CAAC,EAAEM,MAAME,MAAM,GAAGL,YAAY,CAAC,EAAEG,MAAMG,OAAO,EAAE,EACvGC,KAAKC,SAAS,CAACL;AAEnB;AAEA,OAAO,SAASM,mBACdC,MAA6D;IAE7D,IAAI,CAACA,QAAQ;QACX,OAAOhB;IACT;IAEA,MAAMC,UAAU,OAAOe,WAAW,YAAYA,SAASC,QAAQD,OAAOf,OAAO;IAE7E,IAAI,CAACA,SAAS;QACZ,OAAOD;IACT;IAEA,MAAMkB,SAAS,OAAOF,WAAW,YAAYA,OAAOE,MAAM,GAAGF,OAAOE,MAAM,GAAGV;IAE7E,OAAO;QACLP,SAAS;QACTC,KAAIO,KAAK;YACP,IAAI;gBACFS,OAAO;oBAAE,GAAGT,KAAK;oBAAEU,GAAG,IAAIC,OAAOC,WAAW;gBAAG;YACjD,EAAE,OAAM;YACN,0CAA0C;YAC5C;QACF;IACF;AACF;AAEA,8EAA8E,GAC9E,OAAO,SAASC,aAAaC,MAAc;IACzC,OAAO,GAAGA,OAAO,CAAC,EAAEC,KAAKC,MAAM,GAAGC,QAAQ,CAAC,IAAIC,KAAK,CAAC,GAAG,GAAG,CAAC,EAAEP,KAAKQ,GAAG,GAAGF,QAAQ,CAAC,KAAK;AACzF;AAEA;;;;CAIC,GACD,OAAO,eAAeG,oBAAoBC,IAKzC;IACC,MAAM,EAAEC,EAAE,EAAEC,cAAc,EAAEC,MAAM,EAAEC,GAAG,EAAE,GAAGJ;IAE5C,IAAI;QACF,MAAMK,MAAO,MAAMD,IAAIE,OAAO,CAACC,QAAQ,CAAC;YACtCN,IAAIA;YACJO,YAAYN;YACZO,OAAO;YACPC,eAAe;YACfC,OAAO;YACPC,gBAAgB;YAChBR;QACF;QAEA,IAAI,CAACC,KAAK;YACR,OAAO;QACT;QAEA,MAAMQ,WAAoC;YACxCZ,IAAIhC,eAAe,OAAOoC,IAAIJ,EAAE,KAAK,YAAY,OAAOI,IAAIJ,EAAE,KAAK,WAAWI,IAAIJ,EAAE,GAAGa;QACzF;QAEA,KAAK,MAAMC,SAASZ,OAAQ;YAC1BU,QAAQ,CAACE,MAAM,GAAGC,aAAaX,GAAG,CAACU,MAAM;QAC3C;QAEA,OAAOF;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,2EAA2E,GAC3E,OAAO,SAASG,aAAaC,KAAc;IACzC,IAAIA,SAAS,OAAOA,UAAU,YAAY,CAACC,MAAMC,OAAO,CAACF,QAAQ;QAC/D,MAAMG,YAAYH;QAElB,IAAI,QAAQG,WAAW;YACrB,MAAMnB,KAAKmB,UAAUnB,EAAE;YAEvB,OAAO,OAAOA,OAAO,YAAY,OAAOA,OAAO,WAAWhC,eAAegC,MAAMgB;QACjF;IACF;IAEA,OAAOA;AACT"}
|
|
@@ -8,8 +8,8 @@ export type PageTreeInsertDropTarget = {
|
|
|
8
8
|
};
|
|
9
9
|
export type PageTreeRowDropTarget = {
|
|
10
10
|
dropType: 'row';
|
|
11
|
-
insertAfterDropID: string;
|
|
12
|
-
insertBeforeDropID: string;
|
|
11
|
+
insertAfterDropID: null | string;
|
|
12
|
+
insertBeforeDropID: null | string;
|
|
13
13
|
rowID: string;
|
|
14
14
|
};
|
|
15
15
|
export type PageTreeDropTarget = PageTreeInsertDropTarget | PageTreeRowDropTarget;
|
|
@@ -9,18 +9,19 @@ function getSiblingID(doc) {
|
|
|
9
9
|
return doc?.__pageTreeID ?? null;
|
|
10
10
|
}
|
|
11
11
|
export function buildInsertDropTargets(docs) {
|
|
12
|
-
|
|
12
|
+
const rootDocs = docs.filter((doc)=>doc.__pageTreeParentID === null);
|
|
13
|
+
if (rootDocs.length === 0) {
|
|
13
14
|
return [];
|
|
14
15
|
}
|
|
15
16
|
return Array.from({
|
|
16
|
-
length:
|
|
17
|
+
length: rootDocs.length + 1
|
|
17
18
|
}, (_, index)=>{
|
|
18
|
-
const referenceDoc =
|
|
19
|
+
const referenceDoc = rootDocs[index] ?? rootDocs[rootDocs.length - 1];
|
|
19
20
|
return {
|
|
20
|
-
depth:
|
|
21
|
+
depth: 0,
|
|
21
22
|
dropID: `page-insert:${index}`,
|
|
22
23
|
dropType: 'insert',
|
|
23
|
-
parentID:
|
|
24
|
+
parentID: null,
|
|
24
25
|
referenceRowID: referenceDoc.__pageTreeID
|
|
25
26
|
};
|
|
26
27
|
});
|
|
@@ -61,8 +62,9 @@ export function getOrderPlacementFromDropTarget(args) {
|
|
|
61
62
|
return null;
|
|
62
63
|
}
|
|
63
64
|
const parentID = dropTarget.parentID;
|
|
64
|
-
const
|
|
65
|
-
const
|
|
65
|
+
const siblingDocs = docs.filter((doc)=>doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID));
|
|
66
|
+
const beforeSiblings = siblingDocs.slice(0, insertIndex);
|
|
67
|
+
const afterSiblings = siblingDocs.slice(insertIndex);
|
|
66
68
|
return {
|
|
67
69
|
nextSiblingID: getSiblingID(afterSiblings[0]),
|
|
68
70
|
parentID,
|
|
@@ -1 +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 (
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/dropTargets.ts"],"sourcesContent":["import type { PageTreeDoc } from './pageTree.js'\r\n\r\nexport type PageTreeInsertDropTarget = {\r\n depth: number\r\n dropID: string\r\n dropType: 'insert'\r\n parentID: null | string\r\n referenceRowID: string\r\n}\r\n\r\nexport type PageTreeRowDropTarget = {\r\n dropType: 'row'\r\n insertAfterDropID: null | string\r\n insertBeforeDropID: null | string\r\n rowID: string\r\n}\r\n\r\nexport type PageTreeDropTarget = PageTreeInsertDropTarget | PageTreeRowDropTarget\r\n\r\nexport type PageTreeOrderPlacement = {\r\n nextSiblingID: null | string\r\n parentID: null | string\r\n previousSiblingID: null | string\r\n}\r\n\r\nfunction getInsertIndex(dropID: string): null | number {\r\n const insertIndex = Number.parseInt(dropID.split(':')[1] ?? '', 10)\r\n\r\n return Number.isNaN(insertIndex) ? null : insertIndex\r\n}\r\n\r\nfunction getMovingSubtreeIDs(docs: PageTreeDoc[], activeDoc: PageTreeDoc): Set<string> {\r\n return new Set(\r\n docs\r\n .filter(\r\n (doc) =>\r\n doc.__pageTreeID === activeDoc.__pageTreeID ||\r\n doc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID),\r\n )\r\n .map((doc) => doc.__pageTreeID),\r\n )\r\n}\r\n\r\nfunction getSiblingID(doc: PageTreeDoc | undefined): null | string {\r\n return doc?.__pageTreeID ?? null\r\n}\r\n\r\nexport function buildInsertDropTargets(docs: PageTreeDoc[]): PageTreeInsertDropTarget[] {\r\n const rootDocs = docs.filter((doc) => doc.__pageTreeParentID === null)\r\n\r\n if (rootDocs.length === 0) {\r\n return []\r\n }\r\n\r\n return Array.from({ length: rootDocs.length + 1 }, (_, index) => {\r\n const referenceDoc = rootDocs[index] ?? rootDocs[rootDocs.length - 1]\r\n\r\n return {\r\n depth: 0,\r\n dropID: `page-insert:${index}`,\r\n dropType: 'insert',\r\n parentID: null,\r\n referenceRowID: referenceDoc.__pageTreeID,\r\n }\r\n })\r\n}\r\n\r\nexport function getDropTargetParentDoc(args: {\r\n docsByID: ReadonlyMap<string, PageTreeDoc>\r\n dropTarget: null | PageTreeDropTarget\r\n}): null | PageTreeDoc {\r\n const { docsByID, dropTarget } = args\r\n\r\n if (!dropTarget) {\r\n return null\r\n }\r\n\r\n if (dropTarget.dropType === 'row') {\r\n return docsByID.get(dropTarget.rowID) ?? null\r\n }\r\n\r\n if (dropTarget.parentID === null) {\r\n return null\r\n }\r\n\r\n return docsByID.get(dropTarget.parentID) ?? null\r\n}\r\n\r\nexport function getOrderPlacementFromDropTarget(args: {\r\n activeDoc: PageTreeDoc\r\n docs: PageTreeDoc[]\r\n docsByID: ReadonlyMap<string, PageTreeDoc>\r\n dropTarget: null | PageTreeDropTarget\r\n}): null | PageTreeOrderPlacement {\r\n const { activeDoc, docs, docsByID, dropTarget } = args\r\n\r\n if (!dropTarget) {\r\n return null\r\n }\r\n\r\n const movingSubtreeIDs = getMovingSubtreeIDs(docs, activeDoc)\r\n\r\n if (dropTarget.dropType === 'row') {\r\n const targetDoc = docsByID.get(dropTarget.rowID)\r\n\r\n if (!targetDoc) {\r\n return null\r\n }\r\n\r\n const childSiblings = docs.filter(\r\n (doc) =>\r\n doc.__pageTreeParentID === targetDoc.__pageTreeID &&\r\n !movingSubtreeIDs.has(doc.__pageTreeID),\r\n )\r\n\r\n return {\r\n nextSiblingID: null,\r\n parentID: targetDoc.__pageTreeID,\r\n previousSiblingID: getSiblingID(childSiblings.at(-1)),\r\n }\r\n }\r\n\r\n const insertIndex = getInsertIndex(dropTarget.dropID)\r\n\r\n if (insertIndex === null) {\r\n return null\r\n }\r\n\r\n const parentID = dropTarget.parentID\r\n const siblingDocs = docs.filter(\r\n (doc) => doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID),\r\n )\r\n const beforeSiblings = siblingDocs.slice(0, insertIndex)\r\n const afterSiblings = siblingDocs.slice(insertIndex)\r\n\r\n return {\r\n nextSiblingID: getSiblingID(afterSiblings[0]),\r\n parentID,\r\n previousSiblingID: getSiblingID(beforeSiblings.at(-1)),\r\n }\r\n}\r\n"],"names":["getInsertIndex","dropID","insertIndex","Number","parseInt","split","isNaN","getMovingSubtreeIDs","docs","activeDoc","Set","filter","doc","__pageTreeID","__pageTreeAncestorIDs","includes","map","getSiblingID","buildInsertDropTargets","rootDocs","__pageTreeParentID","length","Array","from","_","index","referenceDoc","depth","dropType","parentID","referenceRowID","getDropTargetParentDoc","args","docsByID","dropTarget","get","rowID","getOrderPlacementFromDropTarget","movingSubtreeIDs","targetDoc","childSiblings","has","nextSiblingID","previousSiblingID","at","siblingDocs","beforeSiblings","slice","afterSiblings"],"mappings":"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"}
|
|
@@ -175,12 +175,10 @@ function compareSortValues(leftValue, rightValue) {
|
|
|
175
175
|
}
|
|
176
176
|
return leftValue < rightValue ? -1 : 1;
|
|
177
177
|
}
|
|
178
|
-
//
|
|
179
|
-
// values like `a5`, `a53`, `a5i`)
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
// `a53 < a5i` lexicographically). It also matches MongoDB's default string ordering,
|
|
183
|
-
// so children sort consistently between server-side and client-side.
|
|
178
|
+
// Payload orderable keys use fractional indexing strings (for example `_order`
|
|
179
|
+
// values like `a5`, `a53`, `a5i`). These must sort by raw lexicographic order
|
|
180
|
+
// to match the database; numeric-aware locale comparison places `a53` after
|
|
181
|
+
// `a5i`, which is wrong for these keys.
|
|
184
182
|
const leftString = String(leftValue);
|
|
185
183
|
const rightString = String(rightValue);
|
|
186
184
|
if (leftString === rightString) {
|
|
@@ -1 +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 // Plain lexicographic comparison. Required for fractional-indexing keys (e.g. `_order`\r\n // values like `a5`, `a53`, `a5i`), which encode position as raw lex order — using\r\n // `localeCompare` with `numeric: true` would interpret digit runs as numbers and\r\n // produce a different ordering than the database (e.g. `a53 > a5i` numerically vs\r\n // `a53 < a5i` lexicographically). It also matches MongoDB's default string ordering,\r\n // so children sort consistently between server-side and client-side.\r\n const leftString = String(leftValue)\r\n const rightString = String(rightValue)\r\n\r\n if (leftString === rightString) {\r\n return 0\r\n }\r\n\r\n return leftString < rightString ? -1 : 1\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","leftString","rightString","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":"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"}
|
|
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 // Payload orderable keys use fractional indexing strings (for example `_order`\r\n // values like `a5`, `a53`, `a5i`). These must sort by raw lexicographic order\r\n // to match the database; numeric-aware locale comparison places `a53` after\r\n // `a5i`, which is wrong for these keys.\r\n const leftString = String(leftValue)\r\n const rightString = String(rightValue)\r\n\r\n if (leftString === rightString) {\r\n return 0\r\n }\r\n\r\n return leftString < rightString ? -1 : 1\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","leftString","rightString","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,+EAA+E;IAC/E,8EAA8E;IAC9E,4EAA4E;IAC5E,wCAAwC;IACxC,MAAMC,aAAazF,OAAOuF;IAC1B,MAAMG,cAAc1F,OAAOwF;IAE3B,IAAIC,eAAeC,aAAa;QAC9B,OAAO;IACT;IAEA,OAAOD,aAAaC,cAAc,CAAC,IAAI;AACzC;AAEA,SAASC,yBAAyB5B,IAAa;IAC7C,MAAM6B,aAAa9B,gBAAgBC;IAEnC,IAAI6B,WAAWlF,MAAM,KAAK,GAAG;QAC3B,OAAO;IACT;IAEA,OAAO,CAACmF,UAAoBC;QAC1B,KAAK,MAAMC,aAAaH,WAAY;YAClC,MAAML,YAAYjB,iBAAiBuB,SAASxF,GAAG,EAAE0F,UAAU1B,IAAI;YAC/D,MAAMmB,aAAalB,iBAAiBwB,UAAUzF,GAAG,EAAE0F,UAAU1B,IAAI;YAEjE,IAAIkB,cAAcX,aAAaY,eAAeZ,WAAW;gBACvD;YACF;YAEA,MAAMoB,aAAaV,kBAAkBC,WAAWC;YAEhD,IAAIQ,eAAe,GAAG;gBACpB,OAAOD,UAAU7B,UAAU,GAAG,CAAC8B,aAAaA;YAC9C;QACF;QAEA,OAAOH,SAASI,aAAa,GAAGH,UAAUG,aAAa;IACzD;AACF;AAEA,OAAO,SAASC,kBACdlE,IAAyB,EACzBmE,UAAoC,CAAC,CAAC;IAEtC,MAAM7F,kBAAkB6F,QAAQ7F,eAAe,IAAI;IACnD,MAAMwC,QAAoBd,KAAKZ,GAAG,CAAC,CAACf,KAAK+F,QAAW,CAAA;YAClD/F;YACAN,IAAID,eAAeO,IAAIN,EAAE;YACzBkG,eAAeG;YACfhE,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,MAAMsD,4BAA4B,IAAInE;IACtC,MAAMD,qBAAqB,IAAIC;IAC/B,MAAMoE,mBAAmBX,yBAAyBQ,QAAQpC,IAAI;IAE9D,KAAK,MAAMV,QAAQP,MAAO;QACxB,MAAMyD,eAAelD,KAAKjB,QAAQ,KAAK,QAAQW,UAAUyD,GAAG,CAACnD,KAAKjB,QAAQ;QAC1E,MAAMqE,oBACJF,gBAAgB,CAACvD,aAAawD,GAAG,CAACnD,KAAKtD,EAAE,IAAIsD,KAAKjB,QAAQ,GAAG;QAE/DiE,0BAA0B/D,GAAG,CAACe,KAAKtD,EAAE,EAAE0G;QAEvC,MAAMC,kBAAkBzE,mBAAmBX,GAAG,CAACmF,sBAAsB,EAAE;QACvEC,gBAAgBhF,IAAI,CAAC2B;QACrBpB,mBAAmBK,GAAG,CAACmE,mBAAmBC;IAC5C;IAEA,IAAIJ,kBAAkB;QACpB,KAAK,MAAMK,YAAY1E,mBAAmB2E,MAAM,GAAI;YAClDD,SAAS5C,IAAI,CAACuC;QAChB;IACF;IAEA,MAAMO,cAA6B,EAAE;IACrC,MAAMC,mBAAmB,IAAI5E;IAC7B,MAAM6E,iBAAiB,IAAI9D;IAE3B,MAAM+D,gBAAgB,CAACC,cAAsBC;QAC3C,IAAID,iBAAiB,GAAG;YACtB,OAAO;QACT;QAEA,OAAOE,KAAKC,GAAG,CAACH,eAAeC,QAAQ,GAAG;IAC5C;IAEA,MAAMG,kBAAkB,CAAChE;QACvB,MAAMiE,cAAcR,iBAAiBxF,GAAG,CAAC+B,KAAKtD,EAAE;QAEhD,IAAI,OAAOuH,gBAAgB,UAAU;YACnC,OAAOA;QACT;QAEA,MAAMX,WAAW1E,mBAAmBX,GAAG,CAAC+B,KAAKtD,EAAE,KAAK,EAAE;QAEtD,IAAI4G,SAASjG,MAAM,KAAK,GAAG;YACzBoG,iBAAiBxE,GAAG,CAACe,KAAKtD,EAAE,EAAE;YAC9B,OAAO;QACT;QAEA,MAAMmH,QAAQC,KAAKC,GAAG,IAAIT,SAASvF,GAAG,CAAC,CAACmG,QAAUF,gBAAgBE,SAAS;QAC3ET,iBAAiBxE,GAAG,CAACe,KAAKtD,EAAE,EAAEmH;QAC9B,OAAOA;IACT;IAEA,MAAMM,QAAQ,CAACnE,MAAgBoE,aAAuBR;QACpD,IAAIF,eAAeP,GAAG,CAACnD,KAAKtD,EAAE,GAAG;YAC/B;QACF;QAEAgH,eAAenD,GAAG,CAACP,KAAKtD,EAAE;QAE1B,MAAM4G,WAAW1E,mBAAmBX,GAAG,CAAC+B,KAAKtD,EAAE,KAAK,EAAE;QACtD,MAAMmH,QAAQO,YAAY/G,MAAM;QAChC,MAAMgH,aAAaV,cAAcC,cAAcC;QAE/CL,YAAYnF,IAAI,CAAC;YACf,GAAI2B,KAAKhD,GAAG;YACZc,uBAAuBsG;YACvBE,iBAAiBT;YACjBU,uBAAuBjB,SAASjG,MAAM,GAAG;YACzCE,cAAcyC,KAAKtD,EAAE;YACrB8H,sBAAsBhB,YAAYnG,MAAM;YACxCoH,oBAAoBzB,0BAA0B/E,GAAG,CAAC+B,KAAKtD,EAAE,KAAK;YAC9DgI,sBAAsBL;QACxB;QAEA,KAAK,MAAMH,SAASZ,SAAU;YAC5Ba,MAAMD,OAAO;mBAAIE;gBAAapE,KAAKtD,EAAE;aAAC,EAAEkH;QAC1C;IACF;IAEA,MAAMe,QAAQ;WAAK/F,mBAAmBX,GAAG,CAAC,SAAS,EAAE;KAAE;IAEvD,KAAK,MAAM2G,QAAQD,MAAO;QACxBR,MAAMS,MAAM,EAAE,EAAEZ,gBAAgBY;IAClC;IAEA,MAAMC,gBAAgB5B,mBAAmB;WAAIxD;KAAM,CAACiB,IAAI,CAACuC,oBAAoBxD;IAE7E,KAAK,MAAMO,QAAQ6E,cAAe;QAChC,IAAI,CAACnB,eAAeP,GAAG,CAACnD,KAAKtD,EAAE,GAAG;YAChCyH,MAAMnE,MAAM,EAAE,EAAEgE,gBAAgBhE;QAClC;IACF;IAEA,OAAOwD;AACT;AAEA,OAAO,SAASsB,mBACdnG,IAAmB,EACnBoG,YAAiC;IAEjC,OAAOpG,KAAKT,MAAM,CAAC,CAAClB,MAClBA,IAAIc,qBAAqB,CAACkH,KAAK,CAAC,CAAChH,aAAe,CAAC+G,aAAa5B,GAAG,CAACnF;AAEtE"}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { type Collision, type CollisionDetection } from '@dnd-kit/core';
|
|
2
2
|
import type { PageTreeDropTarget } from './dropTargets.js';
|
|
3
3
|
export declare const PAGE_TREE_INSERT_EDGE_SIZE_PX = 6;
|
|
4
|
+
type PageTreeDragType = 'move' | 'order';
|
|
4
5
|
export type PageTreePointerCollisionCandidate = {
|
|
6
|
+
dragType?: PageTreeDragType;
|
|
5
7
|
dropTarget?: PageTreeDropTarget;
|
|
6
8
|
id: Collision['id'];
|
|
9
|
+
parentID?: null | string;
|
|
7
10
|
rect?: {
|
|
8
11
|
bottom: number;
|
|
9
12
|
top: number;
|
|
10
13
|
};
|
|
11
14
|
};
|
|
12
15
|
export declare function resolvePageTreePointerCollisionID(args: {
|
|
16
|
+
activeParentID?: null | string;
|
|
13
17
|
collisions: PageTreePointerCollisionCandidate[];
|
|
18
|
+
dragType?: PageTreeDragType;
|
|
14
19
|
pointerCoordinates: Parameters<CollisionDetection>[0]['pointerCoordinates'];
|
|
15
20
|
}): Collision['id'] | null;
|
|
16
21
|
export declare const pageTreeCollisionDetectionStrategy: CollisionDetection;
|
|
22
|
+
export {};
|
|
@@ -21,11 +21,36 @@ function getResolvedCollision(args) {
|
|
|
21
21
|
function isRowCollision(collision) {
|
|
22
22
|
return collision.dropTarget?.dropType === 'row' && Boolean(collision.rect);
|
|
23
23
|
}
|
|
24
|
+
function getDragData(value) {
|
|
25
|
+
if (!value || typeof value !== 'object') {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
const data = value;
|
|
29
|
+
const dragType = data.dragType === 'move' || data.dragType === 'order' ? data.dragType : undefined;
|
|
30
|
+
const parentID = data.parentID === null || typeof data.parentID === 'string' ? data.parentID : undefined;
|
|
31
|
+
return {
|
|
32
|
+
dragType,
|
|
33
|
+
parentID
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getCollisionDropTarget(collision) {
|
|
37
|
+
return collision.data?.droppableContainer.data.current;
|
|
38
|
+
}
|
|
39
|
+
function getCollisionDragData(collision) {
|
|
40
|
+
return getDragData(collision.data?.droppableContainer.data.current);
|
|
41
|
+
}
|
|
42
|
+
function isSortableOrderCollision(collision, activeDragData) {
|
|
43
|
+
const collisionDragData = getCollisionDragData(collision);
|
|
44
|
+
return collisionDragData.dragType === 'order' && collisionDragData.parentID === activeDragData.parentID;
|
|
45
|
+
}
|
|
24
46
|
export function resolvePageTreePointerCollisionID(args) {
|
|
25
|
-
const { collisions, pointerCoordinates } = args;
|
|
47
|
+
const { activeParentID, collisions, dragType, pointerCoordinates } = args;
|
|
26
48
|
if (!pointerCoordinates) {
|
|
27
49
|
return null;
|
|
28
50
|
}
|
|
51
|
+
if (dragType === 'order') {
|
|
52
|
+
return collisions.find((collision)=>collision.dragType === 'order' && collision.parentID === activeParentID)?.id ?? null;
|
|
53
|
+
}
|
|
29
54
|
const directInsertCollision = collisions.find((collision)=>collision.dropTarget?.dropType === 'insert');
|
|
30
55
|
if (directInsertCollision) {
|
|
31
56
|
return directInsertCollision.id;
|
|
@@ -34,22 +59,32 @@ export function resolvePageTreePointerCollisionID(args) {
|
|
|
34
59
|
if (!rowCollision) {
|
|
35
60
|
return null;
|
|
36
61
|
}
|
|
37
|
-
if (pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
62
|
+
if (rowCollision.dropTarget.insertBeforeDropID && pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
38
63
|
return rowCollision.dropTarget.insertBeforeDropID;
|
|
39
64
|
}
|
|
40
|
-
if (rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
65
|
+
if (rowCollision.dropTarget.insertAfterDropID && rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX) {
|
|
41
66
|
return rowCollision.dropTarget.insertAfterDropID;
|
|
42
67
|
}
|
|
43
68
|
return rowCollision.id;
|
|
44
69
|
}
|
|
45
70
|
export const pageTreeCollisionDetectionStrategy = (args)=>{
|
|
71
|
+
const activeDragData = getDragData(args.active.data.current);
|
|
72
|
+
if (activeDragData.dragType === 'order') {
|
|
73
|
+
const closestRowCollision = closestCenter(args).find((collision)=>isSortableOrderCollision(collision, activeDragData));
|
|
74
|
+
return closestRowCollision ? [
|
|
75
|
+
closestRowCollision
|
|
76
|
+
] : [];
|
|
77
|
+
}
|
|
46
78
|
const pointerCollisions = pointerWithin(args);
|
|
47
79
|
const resolvedCollisionID = resolvePageTreePointerCollisionID({
|
|
80
|
+
activeParentID: activeDragData.parentID,
|
|
48
81
|
collisions: pointerCollisions.map((collision)=>({
|
|
49
82
|
id: collision.id,
|
|
50
|
-
|
|
83
|
+
...getCollisionDragData(collision),
|
|
84
|
+
dropTarget: getCollisionDropTarget(collision),
|
|
51
85
|
rect: args.droppableRects.get(collision.id) ?? undefined
|
|
52
86
|
})),
|
|
87
|
+
dragType: activeDragData.dragType,
|
|
53
88
|
pointerCoordinates: args.pointerCoordinates
|
|
54
89
|
});
|
|
55
90
|
if (resolvedCollisionID !== null) {
|
|
@@ -1 +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
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/pageTreeCollision.ts"],"sourcesContent":["import {\r\n closestCenter,\r\n type Collision,\r\n type CollisionDetection,\r\n pointerWithin,\r\n} from '@dnd-kit/core'\r\n\r\nimport type { PageTreeDropTarget, PageTreeRowDropTarget } from './dropTargets.js'\r\n\r\nexport const PAGE_TREE_INSERT_EDGE_SIZE_PX = 6\r\ntype PageTreeDragType = 'move' | 'order'\r\ntype PageTreeDragData = {\r\n dragType?: PageTreeDragType\r\n parentID?: null | string\r\n}\r\n\r\nexport type PageTreePointerCollisionCandidate = {\r\n dragType?: PageTreeDragType\r\n dropTarget?: PageTreeDropTarget\r\n id: Collision['id']\r\n parentID?: null | string\r\n rect?: {\r\n bottom: number\r\n top: number\r\n }\r\n}\r\n\r\nfunction getResolvedCollision(args: {\r\n collisionID: Collision['id']\r\n droppableContainers: Parameters<CollisionDetection>[0]['droppableContainers']\r\n pointerCollisions: Collision[]\r\n}): Collision | null {\r\n const { collisionID, droppableContainers, pointerCollisions } = args\r\n const existingPointerCollision = pointerCollisions.find(\r\n (collision) => collision.id === collisionID,\r\n )\r\n\r\n if (existingPointerCollision) {\r\n return existingPointerCollision\r\n }\r\n\r\n const droppableContainer = droppableContainers.find((container) => container.id === collisionID)\r\n\r\n if (!droppableContainer) {\r\n return null\r\n }\r\n\r\n return {\r\n id: collisionID,\r\n data: {\r\n droppableContainer,\r\n value: 0,\r\n },\r\n }\r\n}\r\n\r\ntype PageTreeResolvedRowCollision = {\r\n dropTarget: PageTreeRowDropTarget\r\n rect: {\r\n bottom: number\r\n top: number\r\n }\r\n} & PageTreePointerCollisionCandidate\r\n\r\nfunction isRowCollision(\r\n collision: PageTreePointerCollisionCandidate,\r\n): collision is PageTreeResolvedRowCollision {\r\n return collision.dropTarget?.dropType === 'row' && Boolean(collision.rect)\r\n}\r\n\r\nfunction getDragData(value: unknown): PageTreeDragData {\r\n if (!value || typeof value !== 'object') {\r\n return {}\r\n }\r\n\r\n const data = value as { dragType?: unknown; parentID?: unknown }\r\n const dragType = data.dragType === 'move' || data.dragType === 'order' ? data.dragType : undefined\r\n const parentID =\r\n data.parentID === null || typeof data.parentID === 'string' ? data.parentID : undefined\r\n\r\n return {\r\n dragType,\r\n parentID,\r\n }\r\n}\r\n\r\nfunction getCollisionDropTarget(collision: Collision): PageTreeDropTarget | undefined {\r\n return collision.data?.droppableContainer.data.current as PageTreeDropTarget | undefined\r\n}\r\n\r\nfunction getCollisionDragData(collision: Collision): PageTreeDragData {\r\n return getDragData(collision.data?.droppableContainer.data.current)\r\n}\r\n\r\nfunction isSortableOrderCollision(collision: Collision, activeDragData: PageTreeDragData): boolean {\r\n const collisionDragData = getCollisionDragData(collision)\r\n\r\n return (\r\n collisionDragData.dragType === 'order' && collisionDragData.parentID === activeDragData.parentID\r\n )\r\n}\r\n\r\nexport function resolvePageTreePointerCollisionID(args: {\r\n activeParentID?: null | string\r\n collisions: PageTreePointerCollisionCandidate[]\r\n dragType?: PageTreeDragType\r\n pointerCoordinates: Parameters<CollisionDetection>[0]['pointerCoordinates']\r\n}): Collision['id'] | null {\r\n const { activeParentID, collisions, dragType, pointerCoordinates } = args\r\n\r\n if (!pointerCoordinates) {\r\n return null\r\n }\r\n\r\n if (dragType === 'order') {\r\n return (\r\n collisions.find(\r\n (collision) => collision.dragType === 'order' && collision.parentID === activeParentID,\r\n )?.id ?? null\r\n )\r\n }\r\n\r\n const directInsertCollision = collisions.find(\r\n (collision) => collision.dropTarget?.dropType === 'insert',\r\n )\r\n\r\n if (directInsertCollision) {\r\n return directInsertCollision.id\r\n }\r\n\r\n const rowCollision = collisions.find(isRowCollision)\r\n\r\n if (!rowCollision) {\r\n return null\r\n }\r\n\r\n if (\r\n rowCollision.dropTarget.insertBeforeDropID &&\r\n pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX\r\n ) {\r\n return rowCollision.dropTarget.insertBeforeDropID\r\n }\r\n\r\n if (\r\n rowCollision.dropTarget.insertAfterDropID &&\r\n rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX\r\n ) {\r\n return rowCollision.dropTarget.insertAfterDropID\r\n }\r\n\r\n return rowCollision.id\r\n}\r\n\r\nexport const pageTreeCollisionDetectionStrategy: CollisionDetection = (args) => {\n const activeDragData = getDragData(args.active.data.current)\n\n if (activeDragData.dragType === 'order') {\n const closestRowCollision = closestCenter(args).find((collision) =>\n isSortableOrderCollision(collision, activeDragData),\n )\n\n return closestRowCollision ? [closestRowCollision] : []\n }\n\n const pointerCollisions = pointerWithin(args)\n const resolvedCollisionID = resolvePageTreePointerCollisionID({\n activeParentID: activeDragData.parentID,\n collisions: pointerCollisions.map((collision) => ({\r\n id: collision.id,\r\n ...getCollisionDragData(collision),\r\n dropTarget: getCollisionDropTarget(collision),\r\n rect: args.droppableRects.get(collision.id) ?? undefined,\r\n })),\r\n dragType: activeDragData.dragType,\r\n pointerCoordinates: args.pointerCoordinates,\r\n })\r\n\r\n if (resolvedCollisionID !== null) {\r\n const resolvedCollision = getResolvedCollision({\r\n collisionID: resolvedCollisionID,\r\n droppableContainers: args.droppableContainers,\r\n pointerCollisions,\r\n })\r\n\r\n if (resolvedCollision) {\r\n return [resolvedCollision]\r\n }\r\n }\r\n\r\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","getDragData","dragType","undefined","parentID","getCollisionDropTarget","current","getCollisionDragData","isSortableOrderCollision","activeDragData","collisionDragData","resolvePageTreePointerCollisionID","activeParentID","collisions","pointerCoordinates","directInsertCollision","rowCollision","insertBeforeDropID","y","top","insertAfterDropID","bottom","pageTreeCollisionDetectionStrategy","active","closestRowCollision","resolvedCollisionID","map","droppableRects","get","resolvedCollision"],"mappings":"AAAA,SACEA,aAAa,EAGbC,aAAa,QACR,gBAAe;AAItB,OAAO,MAAMC,gCAAgC,EAAC;AAkB9C,SAASC,qBAAqBC,IAI7B;IACC,MAAM,EAAEC,WAAW,EAAEC,mBAAmB,EAAEC,iBAAiB,EAAE,GAAGH;IAChE,MAAMI,2BAA2BD,kBAAkBE,IAAI,CACrD,CAACC,YAAcA,UAAUC,EAAE,KAAKN;IAGlC,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,SAASC,YAAYN,KAAc;IACjC,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU;QACvC,OAAO,CAAC;IACV;IAEA,MAAMD,OAAOC;IACb,MAAMO,WAAWR,KAAKQ,QAAQ,KAAK,UAAUR,KAAKQ,QAAQ,KAAK,UAAUR,KAAKQ,QAAQ,GAAGC;IACzF,MAAMC,WACJV,KAAKU,QAAQ,KAAK,QAAQ,OAAOV,KAAKU,QAAQ,KAAK,WAAWV,KAAKU,QAAQ,GAAGD;IAEhF,OAAO;QACLD;QACAE;IACF;AACF;AAEA,SAASC,uBAAuBf,SAAoB;IAClD,OAAOA,UAAUI,IAAI,EAAEF,mBAAmBE,KAAKY;AACjD;AAEA,SAASC,qBAAqBjB,SAAoB;IAChD,OAAOW,YAAYX,UAAUI,IAAI,EAAEF,mBAAmBE,KAAKY;AAC7D;AAEA,SAASE,yBAAyBlB,SAAoB,EAAEmB,cAAgC;IACtF,MAAMC,oBAAoBH,qBAAqBjB;IAE/C,OACEoB,kBAAkBR,QAAQ,KAAK,WAAWQ,kBAAkBN,QAAQ,KAAKK,eAAeL,QAAQ;AAEpG;AAEA,OAAO,SAASO,kCAAkC3B,IAKjD;IACC,MAAM,EAAE4B,cAAc,EAAEC,UAAU,EAAEX,QAAQ,EAAEY,kBAAkB,EAAE,GAAG9B;IAErE,IAAI,CAAC8B,oBAAoB;QACvB,OAAO;IACT;IAEA,IAAIZ,aAAa,SAAS;QACxB,OACEW,WAAWxB,IAAI,CACb,CAACC,YAAcA,UAAUY,QAAQ,KAAK,WAAWZ,UAAUc,QAAQ,KAAKQ,iBACvErB,MAAM;IAEb;IAEA,MAAMwB,wBAAwBF,WAAWxB,IAAI,CAC3C,CAACC,YAAcA,UAAUO,UAAU,EAAEC,aAAa;IAGpD,IAAIiB,uBAAuB;QACzB,OAAOA,sBAAsBxB,EAAE;IACjC;IAEA,MAAMyB,eAAeH,WAAWxB,IAAI,CAACO;IAErC,IAAI,CAACoB,cAAc;QACjB,OAAO;IACT;IAEA,IACEA,aAAanB,UAAU,CAACoB,kBAAkB,IAC1CH,mBAAmBI,CAAC,GAAGF,aAAahB,IAAI,CAACmB,GAAG,IAAIrC,+BAChD;QACA,OAAOkC,aAAanB,UAAU,CAACoB,kBAAkB;IACnD;IAEA,IACED,aAAanB,UAAU,CAACuB,iBAAiB,IACzCJ,aAAahB,IAAI,CAACqB,MAAM,GAAGP,mBAAmBI,CAAC,IAAIpC,+BACnD;QACA,OAAOkC,aAAanB,UAAU,CAACuB,iBAAiB;IAClD;IAEA,OAAOJ,aAAazB,EAAE;AACxB;AAEA,OAAO,MAAM+B,qCAAyD,CAACtC;IACrE,MAAMyB,iBAAiBR,YAAYjB,KAAKuC,MAAM,CAAC7B,IAAI,CAACY,OAAO;IAE3D,IAAIG,eAAeP,QAAQ,KAAK,SAAS;QACvC,MAAMsB,sBAAsB5C,cAAcI,MAAMK,IAAI,CAAC,CAACC,YACpDkB,yBAAyBlB,WAAWmB;QAGtC,OAAOe,sBAAsB;YAACA;SAAoB,GAAG,EAAE;IACzD;IAEA,MAAMrC,oBAAoBN,cAAcG;IACxC,MAAMyC,sBAAsBd,kCAAkC;QAC5DC,gBAAgBH,eAAeL,QAAQ;QACvCS,YAAY1B,kBAAkBuC,GAAG,CAAC,CAACpC,YAAe,CAAA;gBAChDC,IAAID,UAAUC,EAAE;gBAChB,GAAGgB,qBAAqBjB,UAAU;gBAClCO,YAAYQ,uBAAuBf;gBACnCU,MAAMhB,KAAK2C,cAAc,CAACC,GAAG,CAACtC,UAAUC,EAAE,KAAKY;YACjD,CAAA;QACAD,UAAUO,eAAeP,QAAQ;QACjCY,oBAAoB9B,KAAK8B,kBAAkB;IAC7C;IAEA,IAAIW,wBAAwB,MAAM;QAChC,MAAMI,oBAAoB9C,qBAAqB;YAC7CE,aAAawC;YACbvC,qBAAqBF,KAAKE,mBAAmB;YAC7CC;QACF;QAEA,IAAI0C,mBAAmB;YACrB,OAAO;gBAACA;aAAkB;QAC5B;IACF;IAEA,OAAOjD,cAAcI;AACvB,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/pageTreeConfig.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\r\n\r\nimport type { NestedDocsPageTreePluginCollectionCustom } from '../types.js'\r\nimport { nestedDocsPageTreePluginCustomKey } from '../types.js'\r\nimport { normalizeNestedDocsPageTreePluginBadgeConfig } from './badgeConfig.js'\r\n\r\nexport function getCollectionPageTreeConfig(\r\n collection: Pick<CollectionConfig, 'custom'>,\r\n): null | NestedDocsPageTreePluginCollectionCustom {\r\n const value = collection.custom?.[nestedDocsPageTreePluginCustomKey]\r\n\r\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\r\n return null\r\n }\r\n\r\n const config = value as Partial<NestedDocsPageTreePluginCollectionCustom>\r\n\r\n if (\
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/pageTreeConfig.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\r\n\r\nimport type { NestedDocsPageTreePluginCollectionCustom } from '../types.js'\r\nimport { nestedDocsPageTreePluginCustomKey } from '../types.js'\r\nimport { normalizeNestedDocsPageTreePluginBadgeConfig } from './badgeConfig.js'\r\n\r\nexport function getCollectionPageTreeConfig(\r\n collection: Pick<CollectionConfig, 'custom'>,\r\n): null | NestedDocsPageTreePluginCollectionCustom {\r\n const value = collection.custom?.[nestedDocsPageTreePluginCustomKey]\r\n\r\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\r\n return null\r\n }\r\n\r\n const config = value as Partial<NestedDocsPageTreePluginCollectionCustom>\r\n\r\n if (\n typeof config.breadcrumbsFieldSlug !== 'string' ||\n typeof config.defaultLimit !== 'number' ||\n typeof config.hideBreadcrumbs !== 'boolean' ||\n !config.homeIndicator ||\n typeof config.homeIndicator !== 'object' ||\n Array.isArray(config.homeIndicator) ||\n typeof config.homeIndicator.enabled !== 'boolean' ||\n typeof config.parentFieldSlug !== 'string'\n ) {\n return null\n }\n\r\n return {\r\n badges: normalizeNestedDocsPageTreePluginBadgeConfig(config.badges),\r\n breadcrumbsFieldSlug: config.breadcrumbsFieldSlug,\n defaultLimit: config.defaultLimit,\n hideBreadcrumbs: config.hideBreadcrumbs,\n homeIndicator: {\n enabled: config.homeIndicator.enabled,\n },\n parentFieldSlug: config.parentFieldSlug,\n }\n}\n"],"names":["nestedDocsPageTreePluginCustomKey","normalizeNestedDocsPageTreePluginBadgeConfig","getCollectionPageTreeConfig","collection","value","custom","Array","isArray","config","breadcrumbsFieldSlug","defaultLimit","hideBreadcrumbs","homeIndicator","enabled","parentFieldSlug","badges"],"mappings":"AAGA,SAASA,iCAAiC,QAAQ,cAAa;AAC/D,SAASC,4CAA4C,QAAQ,mBAAkB;AAE/E,OAAO,SAASC,4BACdC,UAA4C;IAE5C,MAAMC,QAAQD,WAAWE,MAAM,EAAE,CAACL,kCAAkC;IAEpE,IAAI,CAACI,SAAS,OAAOA,UAAU,YAAYE,MAAMC,OAAO,CAACH,QAAQ;QAC/D,OAAO;IACT;IAEA,MAAMI,SAASJ;IAEf,IACE,OAAOI,OAAOC,oBAAoB,KAAK,YACvC,OAAOD,OAAOE,YAAY,KAAK,YAC/B,OAAOF,OAAOG,eAAe,KAAK,aAClC,CAACH,OAAOI,aAAa,IACrB,OAAOJ,OAAOI,aAAa,KAAK,YAChCN,MAAMC,OAAO,CAACC,OAAOI,aAAa,KAClC,OAAOJ,OAAOI,aAAa,CAACC,OAAO,KAAK,aACxC,OAAOL,OAAOM,eAAe,KAAK,UAClC;QACA,OAAO;IACT;IAEA,OAAO;QACLC,QAAQd,6CAA6CO,OAAOO,MAAM;QAClEN,sBAAsBD,OAAOC,oBAAoB;QACjDC,cAAcF,OAAOE,YAAY;QACjCC,iBAAiBH,OAAOG,eAAe;QACvCC,eAAe;YACbC,SAASL,OAAOI,aAAa,CAACC,OAAO;QACvC;QACAC,iBAAiBN,OAAOM,eAAe;IACzC;AACF"}
|
|
@@ -12,6 +12,6 @@ export declare function getPageTreeBadgeLabel(args: {
|
|
|
12
12
|
t: (key: ReturnType<typeof getPageTreeDisplayStatusLabelKey>) => string;
|
|
13
13
|
}): string;
|
|
14
14
|
export declare function withPageTreeDisplayStatuses(args: {
|
|
15
|
+
currentDocs: Pick<PageTreeSourceDoc, '_status' | 'id'>[];
|
|
15
16
|
draftDocs: PageTreeSourceDoc[];
|
|
16
|
-
publishedIDs: ReadonlySet<string>;
|
|
17
17
|
}): PageTreeSourceDoc[];
|
package/dist/utilities/status.js
CHANGED
|
@@ -50,11 +50,15 @@ export function getPageTreeBadgeLabel(args) {
|
|
|
50
50
|
return t(getPageTreeDisplayStatusLabelKey(status));
|
|
51
51
|
}
|
|
52
52
|
export function withPageTreeDisplayStatuses(args) {
|
|
53
|
-
const {
|
|
53
|
+
const { currentDocs, draftDocs } = args;
|
|
54
|
+
const currentStatusByID = new Map(currentDocs.map((doc)=>[
|
|
55
|
+
String(doc.id ?? ''),
|
|
56
|
+
doc._status ?? null
|
|
57
|
+
]));
|
|
54
58
|
return draftDocs.map((doc)=>{
|
|
55
59
|
const docID = String(doc.id ?? '');
|
|
56
|
-
const
|
|
57
|
-
const displayStatus = doc._status === 'draft' &&
|
|
60
|
+
const currentStatus = currentStatusByID.get(docID);
|
|
61
|
+
const displayStatus = doc._status === 'draft' && currentStatus === 'published' ? 'changed' : doc._status === 'draft' || doc._status === 'published' ? doc._status : undefined;
|
|
58
62
|
if (doc._displayStatus === displayStatus) {
|
|
59
63
|
return doc;
|
|
60
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/status.ts"],"sourcesContent":["import type {\n PageTreeSourceDoc,\n NestedDocsPageTreePluginBadgeMap,\n NestedDocsPageTreePluginBadgeStatus,\n} from '../types.js'\n\nexport type PageTreeDisplayStatus = 'changed' | 'draft' | 'published' | 'unknown'\n\nfunction isBadgeStatus(status: PageTreeDisplayStatus): status is NestedDocsPageTreePluginBadgeStatus {\n return status === 'changed' || status === 'draft' || status === 'published'\n}\n\nexport function getPageTreeDisplayStatus(\n doc: Pick<PageTreeSourceDoc, '_displayStatus' | '_status'>,\n): PageTreeDisplayStatus {\n if (doc._displayStatus === 'changed') {\n return 'changed'\n }\n\n if (doc._displayStatus === 'draft') {\n return 'draft'\n }\n\n if (doc._displayStatus === 'published') {\n return 'published'\n }\n\n if (doc._status === 'draft') {\n return 'draft'\n }\n\n if (doc._status === 'published') {\n return 'published'\n }\n\n return 'unknown'\n}\n\nexport function getPageTreeDisplayStatusLabelKey(status: PageTreeDisplayStatus):\n | 'version:draft'\n | 'version:changed'\n | 'version:published'\n | 'general:noValue' {\n if (status === 'changed') {\n return 'version:changed'\n }\n\n if (status === 'draft') {\n return 'version:draft'\n }\n\n if (status === 'published') {\n return 'version:published'\n }\n\n return 'general:noValue'\n}\n\nexport function getPageTreeBadgeColor(args: {\n badgeColors: NestedDocsPageTreePluginBadgeMap\n status: PageTreeDisplayStatus\n}): string | undefined {\n const { badgeColors, status } = args\n\n if (!isBadgeStatus(status)) {\n return undefined\n }\n\n const badgeColor = badgeColors[status]\n\n return typeof badgeColor === 'string' && badgeColor.trim().length > 0\n ? badgeColor.trim()\n : undefined\n}\n\nexport function getPageTreeBadgeLabel(args: {\n badgeLabels: NestedDocsPageTreePluginBadgeMap\n status: PageTreeDisplayStatus\n t: (key: ReturnType<typeof getPageTreeDisplayStatusLabelKey>) => string\n}): string {\n const { badgeLabels, status, t } = args\n\n if (isBadgeStatus(status)) {\n const badgeLabel = badgeLabels[status]\n\n if (typeof badgeLabel === 'string' && badgeLabel.trim().length > 0) {\n return badgeLabel.trim()\n }\n }\n\n return t(getPageTreeDisplayStatusLabelKey(status))\n}\n\nexport function withPageTreeDisplayStatuses(args: {\n
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/status.ts"],"sourcesContent":["import type {\r\n PageTreeSourceDoc,\r\n NestedDocsPageTreePluginBadgeMap,\r\n NestedDocsPageTreePluginBadgeStatus,\r\n} from '../types.js'\r\n\r\nexport type PageTreeDisplayStatus = 'changed' | 'draft' | 'published' | 'unknown'\r\n\r\nfunction isBadgeStatus(status: PageTreeDisplayStatus): status is NestedDocsPageTreePluginBadgeStatus {\r\n return status === 'changed' || status === 'draft' || status === 'published'\r\n}\r\n\r\nexport function getPageTreeDisplayStatus(\r\n doc: Pick<PageTreeSourceDoc, '_displayStatus' | '_status'>,\r\n): PageTreeDisplayStatus {\r\n if (doc._displayStatus === 'changed') {\r\n return 'changed'\r\n }\r\n\r\n if (doc._displayStatus === 'draft') {\r\n return 'draft'\r\n }\r\n\r\n if (doc._displayStatus === 'published') {\r\n return 'published'\r\n }\r\n\r\n if (doc._status === 'draft') {\r\n return 'draft'\r\n }\r\n\r\n if (doc._status === 'published') {\r\n return 'published'\r\n }\r\n\r\n return 'unknown'\r\n}\r\n\r\nexport function getPageTreeDisplayStatusLabelKey(status: PageTreeDisplayStatus):\r\n | 'version:draft'\r\n | 'version:changed'\r\n | 'version:published'\r\n | 'general:noValue' {\r\n if (status === 'changed') {\r\n return 'version:changed'\r\n }\r\n\r\n if (status === 'draft') {\r\n return 'version:draft'\r\n }\r\n\r\n if (status === 'published') {\r\n return 'version:published'\r\n }\r\n\r\n return 'general:noValue'\r\n}\r\n\r\nexport function getPageTreeBadgeColor(args: {\r\n badgeColors: NestedDocsPageTreePluginBadgeMap\r\n status: PageTreeDisplayStatus\r\n}): string | undefined {\r\n const { badgeColors, status } = args\r\n\r\n if (!isBadgeStatus(status)) {\r\n return undefined\r\n }\r\n\r\n const badgeColor = badgeColors[status]\r\n\r\n return typeof badgeColor === 'string' && badgeColor.trim().length > 0\r\n ? badgeColor.trim()\r\n : undefined\r\n}\r\n\r\nexport function getPageTreeBadgeLabel(args: {\r\n badgeLabels: NestedDocsPageTreePluginBadgeMap\r\n status: PageTreeDisplayStatus\r\n t: (key: ReturnType<typeof getPageTreeDisplayStatusLabelKey>) => string\r\n}): string {\r\n const { badgeLabels, status, t } = args\r\n\r\n if (isBadgeStatus(status)) {\r\n const badgeLabel = badgeLabels[status]\r\n\r\n if (typeof badgeLabel === 'string' && badgeLabel.trim().length > 0) {\r\n return badgeLabel.trim()\r\n }\r\n }\r\n\r\n return t(getPageTreeDisplayStatusLabelKey(status))\r\n}\r\n\r\nexport function withPageTreeDisplayStatuses(args: {\r\n currentDocs: Pick<PageTreeSourceDoc, '_status' | 'id'>[]\r\n draftDocs: PageTreeSourceDoc[]\r\n}): PageTreeSourceDoc[] {\r\n const { currentDocs, draftDocs } = args\r\n const currentStatusByID = new Map(\r\n currentDocs.map((doc) => [String(doc.id ?? ''), doc._status ?? null]),\r\n )\r\n\r\n return draftDocs.map((doc) => {\r\n const docID = String(doc.id ?? '')\r\n const currentStatus = currentStatusByID.get(docID)\r\n const displayStatus =\r\n doc._status === 'draft' && currentStatus === 'published'\r\n ? 'changed'\r\n : doc._status === 'draft' || doc._status === 'published'\r\n ? doc._status\r\n : undefined\r\n\r\n if (doc._displayStatus === displayStatus) {\r\n return doc\r\n }\r\n\r\n return {\r\n ...doc,\r\n _displayStatus: displayStatus,\r\n }\r\n })\r\n}\r\n"],"names":["isBadgeStatus","status","getPageTreeDisplayStatus","doc","_displayStatus","_status","getPageTreeDisplayStatusLabelKey","getPageTreeBadgeColor","args","badgeColors","undefined","badgeColor","trim","length","getPageTreeBadgeLabel","badgeLabels","t","badgeLabel","withPageTreeDisplayStatuses","currentDocs","draftDocs","currentStatusByID","Map","map","String","id","docID","currentStatus","get","displayStatus"],"mappings":"AAQA,SAASA,cAAcC,MAA6B;IAClD,OAAOA,WAAW,aAAaA,WAAW,WAAWA,WAAW;AAClE;AAEA,OAAO,SAASC,yBACdC,GAA0D;IAE1D,IAAIA,IAAIC,cAAc,KAAK,WAAW;QACpC,OAAO;IACT;IAEA,IAAID,IAAIC,cAAc,KAAK,SAAS;QAClC,OAAO;IACT;IAEA,IAAID,IAAIC,cAAc,KAAK,aAAa;QACtC,OAAO;IACT;IAEA,IAAID,IAAIE,OAAO,KAAK,SAAS;QAC3B,OAAO;IACT;IAEA,IAAIF,IAAIE,OAAO,KAAK,aAAa;QAC/B,OAAO;IACT;IAEA,OAAO;AACT;AAEA,OAAO,SAASC,iCAAiCL,MAA6B;IAK5E,IAAIA,WAAW,WAAW;QACxB,OAAO;IACT;IAEA,IAAIA,WAAW,SAAS;QACtB,OAAO;IACT;IAEA,IAAIA,WAAW,aAAa;QAC1B,OAAO;IACT;IAEA,OAAO;AACT;AAEA,OAAO,SAASM,sBAAsBC,IAGrC;IACC,MAAM,EAAEC,WAAW,EAAER,MAAM,EAAE,GAAGO;IAEhC,IAAI,CAACR,cAAcC,SAAS;QAC1B,OAAOS;IACT;IAEA,MAAMC,aAAaF,WAAW,CAACR,OAAO;IAEtC,OAAO,OAAOU,eAAe,YAAYA,WAAWC,IAAI,GAAGC,MAAM,GAAG,IAChEF,WAAWC,IAAI,KACfF;AACN;AAEA,OAAO,SAASI,sBAAsBN,IAIrC;IACC,MAAM,EAAEO,WAAW,EAAEd,MAAM,EAAEe,CAAC,EAAE,GAAGR;IAEnC,IAAIR,cAAcC,SAAS;QACzB,MAAMgB,aAAaF,WAAW,CAACd,OAAO;QAEtC,IAAI,OAAOgB,eAAe,YAAYA,WAAWL,IAAI,GAAGC,MAAM,GAAG,GAAG;YAClE,OAAOI,WAAWL,IAAI;QACxB;IACF;IAEA,OAAOI,EAAEV,iCAAiCL;AAC5C;AAEA,OAAO,SAASiB,4BAA4BV,IAG3C;IACC,MAAM,EAAEW,WAAW,EAAEC,SAAS,EAAE,GAAGZ;IACnC,MAAMa,oBAAoB,IAAIC,IAC5BH,YAAYI,GAAG,CAAC,CAACpB,MAAQ;YAACqB,OAAOrB,IAAIsB,EAAE,IAAI;YAAKtB,IAAIE,OAAO,IAAI;SAAK;IAGtE,OAAOe,UAAUG,GAAG,CAAC,CAACpB;QACpB,MAAMuB,QAAQF,OAAOrB,IAAIsB,EAAE,IAAI;QAC/B,MAAME,gBAAgBN,kBAAkBO,GAAG,CAACF;QAC5C,MAAMG,gBACJ1B,IAAIE,OAAO,KAAK,WAAWsB,kBAAkB,cACzC,YACAxB,IAAIE,OAAO,KAAK,WAAWF,IAAIE,OAAO,KAAK,cACzCF,IAAIE,OAAO,GACXK;QAER,IAAIP,IAAIC,cAAc,KAAKyB,eAAe;YACxC,OAAO1B;QACT;QAEA,OAAO;YACL,GAAGA,GAAG;YACNC,gBAAgByB;QAClB;IACF;AACF"}
|