payload-nested-docs-page-tree 1.2.4 → 1.4.1

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.
Files changed (43) hide show
  1. package/README.md +356 -222
  2. package/dist/components/PageTreeContext.d.ts +13 -1
  3. package/dist/components/PageTreeContext.js +11 -1
  4. package/dist/components/PageTreeContext.js.map +1 -1
  5. package/dist/components/PageTreeListView.client.d.ts +4 -5
  6. package/dist/components/PageTreeListView.client.js +668 -128
  7. package/dist/components/PageTreeListView.client.js.map +1 -1
  8. package/dist/components/PageTreeListView.module.css +399 -376
  9. package/dist/components/PageTreeListView.server.js +18 -28
  10. package/dist/components/PageTreeListView.server.js.map +1 -1
  11. package/dist/components/PageTreeTitleCell.js +80 -18
  12. package/dist/components/PageTreeTitleCell.js.map +1 -1
  13. package/dist/endpoints/createMovePageEndpoint.d.ts +3 -0
  14. package/dist/endpoints/createMovePageEndpoint.js +186 -125
  15. package/dist/endpoints/createMovePageEndpoint.js.map +1 -1
  16. package/dist/endpoints/createReorderPageEndpoint.d.ts +8 -0
  17. package/dist/endpoints/createReorderPageEndpoint.js +706 -0
  18. package/dist/endpoints/createReorderPageEndpoint.js.map +1 -0
  19. package/dist/endpoints/shared.d.ts +40 -0
  20. package/dist/endpoints/shared.js +93 -0
  21. package/dist/endpoints/shared.js.map +1 -0
  22. package/dist/index.js +110 -27
  23. package/dist/index.js.map +1 -1
  24. package/dist/types.d.ts +47 -2
  25. package/dist/types.js +22 -3
  26. package/dist/types.js.map +1 -1
  27. package/dist/utilities/diagnostics.d.ts +42 -0
  28. package/dist/utilities/diagnostics.js +83 -0
  29. package/dist/utilities/diagnostics.js.map +1 -0
  30. package/dist/utilities/dropTargets.d.ts +2 -2
  31. package/dist/utilities/dropTargets.js +9 -7
  32. package/dist/utilities/dropTargets.js.map +1 -1
  33. package/dist/utilities/moveValidation.js.map +1 -1
  34. package/dist/utilities/pageTree.js +4 -6
  35. package/dist/utilities/pageTree.js.map +1 -1
  36. package/dist/utilities/pageTreeCollision.d.ts +6 -0
  37. package/dist/utilities/pageTreeCollision.js +39 -4
  38. package/dist/utilities/pageTreeCollision.js.map +1 -1
  39. package/dist/utilities/pageTreeConfig.js.map +1 -1
  40. package/dist/utilities/status.d.ts +1 -1
  41. package/dist/utilities/status.js +7 -3
  42. package/dist/utilities/status.js.map +1 -1
  43. package/package.json +54 -32
package/dist/types.js CHANGED
@@ -4,8 +4,27 @@ export const nestedDocsPageTreePluginBadgeStatuses = [
4
4
  'draft'
5
5
  ];
6
6
  export const nestedDocsPageTreePluginCustomKey = 'nestedDocsPageTreePlugin';
7
- export const pageTreeMoveContextKey = 'pageTreeMove';
8
- export const pageTreeMoveRequestHeader = 'x-payload-nested-docs-page-tree-move';
9
- export const pageTreeMoveRequestHeaderValue = 'true';
7
+ /**
8
+ * Set on Payload's hook context for every page-tree write that leaves the
9
+ * published site unchanged: sibling reorders, and parent moves staged as
10
+ * drafts. Deploy/revalidate hooks read it to skip rebuilds they do not need.
11
+ *
12
+ * It is deliberately NOT set when `publishOnMove` publishes a move, because
13
+ * that move changed the live URL of the page and its descendants and the site
14
+ * does need rebuilding. That way one guard covers every configuration:
15
+ *
16
+ * ```ts
17
+ * if (req.context?.[pageTreeMoveContextKey]) return
18
+ * ```
19
+ *
20
+ * Note the flag describes the *effect* of the write, not its kind - it is
21
+ * absent on published moves even though those are still page-tree moves. Use
22
+ * `pageTreeWriteContextKey` when you need "was this a page-tree write at all".
23
+ */ export const pageTreeMoveContextKey = 'pageTreeMove';
24
+ /**
25
+ * Internal: set on every page-tree write regardless of whether it published.
26
+ * Diagnostics uses it so that published moves are still traced, which is the
27
+ * case most worth observing. Not part of the public API.
28
+ */ export const pageTreeWriteContextKey = 'pageTreeWrite';
10
29
 
11
30
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug } from 'payload'\r\n\r\nexport const nestedDocsPageTreePluginBadgeStatuses = ['published', 'changed', 'draft'] as const\r\n\r\nexport type NestedDocsPageTreePluginBadgeStatus =\r\n (typeof nestedDocsPageTreePluginBadgeStatuses)[number]\r\n\r\nexport type NestedDocsPageTreePluginBadgeMap = Partial<\r\n Record<NestedDocsPageTreePluginBadgeStatus, string>\r\n>\r\n\r\nexport type NestedDocsPageTreePluginBadgeConfig = {\r\n colors?: NestedDocsPageTreePluginBadgeMap\r\n labels?: NestedDocsPageTreePluginBadgeMap\r\n}\r\n\r\nexport type NestedDocsPageTreePluginResolvedBadgeConfig = {\r\n colors: NestedDocsPageTreePluginBadgeMap\r\n labels: NestedDocsPageTreePluginBadgeMap\r\n}\r\n\r\nexport type NestedDocsPageTreePluginHomeIndicatorConfig =\r\n | false\r\n | {\r\n collections?: CollectionSlug[]\r\n }\r\n\r\nexport type NestedDocsPageTreePluginResolvedHomeIndicatorConfig = {\r\n enabled: boolean\r\n}\r\n\r\nexport type NestedDocsPageTreePluginConfig = {\r\n badges?: NestedDocsPageTreePluginBadgeConfig\r\n breadcrumbsFieldSlug?: string\r\n collections: CollectionSlug[]\r\n defaultLimit?: number\r\n disabled?: boolean\r\n hideBreadcrumbs?: boolean\r\n homeIndicator?: NestedDocsPageTreePluginHomeIndicatorConfig\r\n parentFieldSlug?: string\r\n}\r\n\r\nexport type NestedDocsPageTreePluginCollectionCustom = {\r\n badges: NestedDocsPageTreePluginResolvedBadgeConfig\r\n breadcrumbsFieldSlug: string\r\n defaultLimit: number\r\n hideBreadcrumbs: boolean\r\n homeIndicator: NestedDocsPageTreePluginResolvedHomeIndicatorConfig\r\n parentFieldSlug: string\r\n}\r\n\r\nexport type PageTreeSourceDoc = Record<string, unknown> & {\r\n _displayStatus?: null | string\r\n _status?: null | string\r\n id?: number | string\r\n slug?: null | string\r\n}\r\n\r\nexport const nestedDocsPageTreePluginCustomKey = 'nestedDocsPageTreePlugin'\r\n\r\nexport const pageTreeMoveContextKey = 'pageTreeMove'\r\n\r\nexport const pageTreeMoveRequestHeader = 'x-payload-nested-docs-page-tree-move'\r\n\r\nexport const pageTreeMoveRequestHeaderValue = 'true'\r\n\r\nexport type PageTreeMoveContext = {\r\n [pageTreeMoveContextKey]?: boolean\r\n}\r\n"],"names":["nestedDocsPageTreePluginBadgeStatuses","nestedDocsPageTreePluginCustomKey","pageTreeMoveContextKey","pageTreeMoveRequestHeader","pageTreeMoveRequestHeaderValue"],"mappings":"AAEA,OAAO,MAAMA,wCAAwC;IAAC;IAAa;IAAW;CAAQ,CAAS;AAwD/F,OAAO,MAAMC,oCAAoC,2BAA0B;AAE3E,OAAO,MAAMC,yBAAyB,eAAc;AAEpD,OAAO,MAAMC,4BAA4B,uCAAsC;AAE/E,OAAO,MAAMC,iCAAiC,OAAM"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug } from 'payload'\n\nimport type { NestedDocsPageTreePluginDiagnosticsConfig } from './utilities/diagnostics.js'\n\nexport type {\n NestedDocsPageTreePluginDiagnosticEvent,\n NestedDocsPageTreePluginDiagnosticsConfig,\n} from './utilities/diagnostics.js'\n\nexport const nestedDocsPageTreePluginBadgeStatuses = ['published', 'changed', 'draft'] as const\n\nexport type NestedDocsPageTreePluginBadgeStatus =\n (typeof nestedDocsPageTreePluginBadgeStatuses)[number]\n\nexport type NestedDocsPageTreePluginBadgeMap = Partial<\n Record<NestedDocsPageTreePluginBadgeStatus, string>\n>\n\nexport type NestedDocsPageTreePluginBadgeConfig = {\n colors?: NestedDocsPageTreePluginBadgeMap\n labels?: NestedDocsPageTreePluginBadgeMap\n}\n\nexport type NestedDocsPageTreePluginResolvedBadgeConfig = {\n colors: NestedDocsPageTreePluginBadgeMap\n labels: NestedDocsPageTreePluginBadgeMap\n}\n\nexport type NestedDocsPageTreePluginHomeIndicatorConfig =\n | false\n | {\n collections?: CollectionSlug[]\n }\n\nexport type NestedDocsPageTreePluginResolvedHomeIndicatorConfig = {\n enabled: boolean\n}\n\nexport type NestedDocsPageTreePluginConfig = {\n badges?: NestedDocsPageTreePluginBadgeConfig\n breadcrumbsFieldSlug?: string\n collections: CollectionSlug[]\n defaultLimit?: number\n /**\n * When set, the plugin emits structured diagnostic events for page-tree\n * triggered moves, including before/after snapshots of the published main row.\n * Disabled by default. Pass `true` to log to `console`, or pass an object\n * with a custom `logger` to route events elsewhere.\n */\n diagnostics?: NestedDocsPageTreePluginDiagnosticsConfig\n disabled?: boolean\n hideBreadcrumbs?: boolean\n homeIndicator?: NestedDocsPageTreePluginHomeIndicatorConfig\n parentFieldSlug?: string\n /**\n * Publish a hierarchy move immediately instead of staging it as a draft — but\n * ONLY when the moved document had no unpublished changes before the move (its\n * latest version is already published). A document with pending draft edits (a\n * \"changed\" or draft-only doc) always stays staged, so in-progress edits are\n * never published as a side effect of a move. Only affects collections with\n * drafts enabled; collections without drafts move live regardless.\n *\n * Disabled by default: every move is staged as a draft and the live URL/path\n * changes only when the document is next published.\n *\n * @default false\n */\n publishOnMove?: boolean\n}\n\nexport type NestedDocsPageTreePluginCollectionCustom = {\n badges: NestedDocsPageTreePluginResolvedBadgeConfig\n breadcrumbsFieldSlug: string\n defaultLimit: number\n hideBreadcrumbs: boolean\n homeIndicator: NestedDocsPageTreePluginResolvedHomeIndicatorConfig\n parentFieldSlug: string\n}\n\nexport type PageTreeSourceDoc = Record<string, unknown> & {\n _displayStatus?: null | string\n _status?: null | string\n id?: number | string\n slug?: null | string\n}\n\nexport const nestedDocsPageTreePluginCustomKey = 'nestedDocsPageTreePlugin'\n\n/**\n * Set on Payload's hook context for every page-tree write that leaves the\n * published site unchanged: sibling reorders, and parent moves staged as\n * drafts. Deploy/revalidate hooks read it to skip rebuilds they do not need.\n *\n * It is deliberately NOT set when `publishOnMove` publishes a move, because\n * that move changed the live URL of the page and its descendants and the site\n * does need rebuilding. That way one guard covers every configuration:\n *\n * ```ts\n * if (req.context?.[pageTreeMoveContextKey]) return\n * ```\n *\n * Note the flag describes the *effect* of the write, not its kind - it is\n * absent on published moves even though those are still page-tree moves. Use\n * `pageTreeWriteContextKey` when you need \"was this a page-tree write at all\".\n */\nexport const pageTreeMoveContextKey = 'pageTreeMove'\n\n/**\n * Internal: set on every page-tree write regardless of whether it published.\n * Diagnostics uses it so that published moves are still traced, which is the\n * case most worth observing. Not part of the public API.\n */\nexport const pageTreeWriteContextKey = 'pageTreeWrite'\n\nexport type PageTreeMoveContext = {\n [pageTreeMoveContextKey]?: boolean\n [pageTreeWriteContextKey]?: boolean\n}\n"],"names":["nestedDocsPageTreePluginBadgeStatuses","nestedDocsPageTreePluginCustomKey","pageTreeMoveContextKey","pageTreeWriteContextKey"],"mappings":"AASA,OAAO,MAAMA,wCAAwC;IAAC;IAAa;IAAW;CAAQ,CAAS;AA6E/F,OAAO,MAAMC,oCAAoC,2BAA0B;AAE3E;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,MAAMC,yBAAyB,eAAc;AAEpD;;;;CAIC,GACD,OAAO,MAAMC,0BAA0B,gBAAe"}
@@ -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
- if (docs.length === 0) {
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: docs.length + 1
17
+ length: rootDocs.length + 1
17
18
  }, (_, index)=>{
18
- const referenceDoc = docs[index] ?? docs[docs.length - 1];
19
+ const referenceDoc = rootDocs[index] ?? rootDocs[rootDocs.length - 1];
19
20
  return {
20
- depth: referenceDoc.__pageTreeDepth,
21
+ depth: 0,
21
22
  dropID: `page-insert:${index}`,
22
23
  dropType: 'insert',
23
- parentID: referenceDoc.__pageTreeParentID,
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 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));
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 (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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"}
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: null | string\n insertBeforeDropID: null | 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 const rootDocs = docs.filter((doc) => doc.__pageTreeParentID === null)\n\n if (rootDocs.length === 0) {\n return []\n }\n\n return Array.from({ length: rootDocs.length + 1 }, (_, index) => {\n const referenceDoc = rootDocs[index] ?? rootDocs[rootDocs.length - 1]\n\n return {\n depth: 0,\n dropID: `page-insert:${index}`,\n dropType: 'insert',\n parentID: null,\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 siblingDocs = docs.filter(\n (doc) => doc.__pageTreeParentID === parentID && !movingSubtreeIDs.has(doc.__pageTreeID),\n )\n const beforeSiblings = siblingDocs.slice(0, insertIndex)\n const afterSiblings = siblingDocs.slice(insertIndex)\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","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":"AAyBA,SAASA,eAAeC,MAAc;IACpC,MAAMC,cAAcC,OAAOC,QAAQ,CAACH,OAAOI,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI;IAEhE,OAAOF,OAAOG,KAAK,CAACJ,eAAe,OAAOA;AAC5C;AAEA,SAASK,oBAAoBC,IAAmB,EAAEC,SAAsB;IACtE,OAAO,IAAIC,IACTF,KACGG,MAAM,CACL,CAACC,MACCA,IAAIC,YAAY,KAAKJ,UAAUI,YAAY,IAC3CD,IAAIE,qBAAqB,CAACC,QAAQ,CAACN,UAAUI,YAAY,GAE5DG,GAAG,CAAC,CAACJ,MAAQA,IAAIC,YAAY;AAEpC;AAEA,SAASI,aAAaL,GAA4B;IAChD,OAAOA,KAAKC,gBAAgB;AAC9B;AAEA,OAAO,SAASK,uBAAuBV,IAAmB;IACxD,MAAMW,WAAWX,KAAKG,MAAM,CAAC,CAACC,MAAQA,IAAIQ,kBAAkB,KAAK;IAEjE,IAAID,SAASE,MAAM,KAAK,GAAG;QACzB,OAAO,EAAE;IACX;IAEA,OAAOC,MAAMC,IAAI,CAAC;QAAEF,QAAQF,SAASE,MAAM,GAAG;IAAE,GAAG,CAACG,GAAGC;QACrD,MAAMC,eAAeP,QAAQ,CAACM,MAAM,IAAIN,QAAQ,CAACA,SAASE,MAAM,GAAG,EAAE;QAErE,OAAO;YACLM,OAAO;YACP1B,QAAQ,CAAC,YAAY,EAAEwB,OAAO;YAC9BG,UAAU;YACVC,UAAU;YACVC,gBAAgBJ,aAAab,YAAY;QAC3C;IACF;AACF;AAEA,OAAO,SAASkB,uBAAuBC,IAGtC;IACC,MAAM,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGF;IAEjC,IAAI,CAACE,YAAY;QACf,OAAO;IACT;IAEA,IAAIA,WAAWN,QAAQ,KAAK,OAAO;QACjC,OAAOK,SAASE,GAAG,CAACD,WAAWE,KAAK,KAAK;IAC3C;IAEA,IAAIF,WAAWL,QAAQ,KAAK,MAAM;QAChC,OAAO;IACT;IAEA,OAAOI,SAASE,GAAG,CAACD,WAAWL,QAAQ,KAAK;AAC9C;AAEA,OAAO,SAASQ,gCAAgCL,IAK/C;IACC,MAAM,EAAEvB,SAAS,EAAED,IAAI,EAAEyB,QAAQ,EAAEC,UAAU,EAAE,GAAGF;IAElD,IAAI,CAACE,YAAY;QACf,OAAO;IACT;IAEA,MAAMI,mBAAmB/B,oBAAoBC,MAAMC;IAEnD,IAAIyB,WAAWN,QAAQ,KAAK,OAAO;QACjC,MAAMW,YAAYN,SAASE,GAAG,CAACD,WAAWE,KAAK;QAE/C,IAAI,CAACG,WAAW;YACd,OAAO;QACT;QAEA,MAAMC,gBAAgBhC,KAAKG,MAAM,CAC/B,CAACC,MACCA,IAAIQ,kBAAkB,KAAKmB,UAAU1B,YAAY,IACjD,CAACyB,iBAAiBG,GAAG,CAAC7B,IAAIC,YAAY;QAG1C,OAAO;YACL6B,eAAe;YACfb,UAAUU,UAAU1B,YAAY;YAChC8B,mBAAmB1B,aAAauB,cAAcI,EAAE,CAAC,CAAC;QACpD;IACF;IAEA,MAAM1C,cAAcF,eAAekC,WAAWjC,MAAM;IAEpD,IAAIC,gBAAgB,MAAM;QACxB,OAAO;IACT;IAEA,MAAM2B,WAAWK,WAAWL,QAAQ;IACpC,MAAMgB,cAAcrC,KAAKG,MAAM,CAC7B,CAACC,MAAQA,IAAIQ,kBAAkB,KAAKS,YAAY,CAACS,iBAAiBG,GAAG,CAAC7B,IAAIC,YAAY;IAExF,MAAMiC,iBAAiBD,YAAYE,KAAK,CAAC,GAAG7C;IAC5C,MAAM8C,gBAAgBH,YAAYE,KAAK,CAAC7C;IAExC,OAAO;QACLwC,eAAezB,aAAa+B,aAAa,CAAC,EAAE;QAC5CnB;QACAc,mBAAmB1B,aAAa6B,eAAeF,EAAE,CAAC,CAAC;IACrD;AACF"}
@@ -1 +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"}
1
+ {"version":3,"sources":["../../src/utilities/moveValidation.ts"],"sourcesContent":["import type { PageTreeDoc } from './pageTree.js'\n\nexport const CANCEL_DRAG_MESSAGE = 'Release to cancel drag.'\n\nexport type PageTreeDropValidation = {\n isValid: boolean\n message?: string\n parentID: null | string\n}\n\nexport function getDropValidation(args: {\n activeDoc: PageTreeDoc\n targetDoc?: PageTreeDoc\n}): PageTreeDropValidation {\n const { activeDoc, targetDoc } = args\n\n if (!targetDoc) {\n if (activeDoc.__pageTreeParentID === null) {\n return {\n isValid: false,\n message: 'Document is already at the root.',\n parentID: null,\n }\n }\n\n return {\n isValid: true,\n parentID: null,\n }\n }\n\n if (targetDoc.__pageTreeID === activeDoc.__pageTreeID) {\n return {\n isValid: false,\n message: CANCEL_DRAG_MESSAGE,\n parentID: targetDoc.__pageTreeID,\n }\n }\n\n if (targetDoc.__pageTreeAncestorIDs.includes(activeDoc.__pageTreeID)) {\n return {\n isValid: false,\n message: 'A document cannot be moved under one of its descendants.',\n parentID: targetDoc.__pageTreeID,\n }\n }\n\n if (activeDoc.__pageTreeParentID === targetDoc.__pageTreeID) {\n return {\n isValid: false,\n message: 'Document already has that parent.',\n parentID: targetDoc.__pageTreeID,\n }\n }\n\n return {\n isValid: true,\n parentID: targetDoc.__pageTreeID,\n }\n}\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"}
@@ -175,12 +175,10 @@ function compareSortValues(leftValue, rightValue) {
175
175
  }
176
176
  return leftValue < rightValue ? -1 : 1;
177
177
  }
178
- // Plain lexicographic comparison. Required for fractional-indexing keys (e.g. `_order`
179
- // values like `a5`, `a53`, `a5i`), which encode position as raw lex order — using
180
- // `localeCompare` with `numeric: true` would interpret digit runs as numbers and
181
- // produce a different ordering than the database (e.g. `a53 > a5i` numerically vs
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'\nexport type { PageTreeSourceDoc } from '../types.js'\n\nexport type PageTreeMeta = {\n __pageTreeAncestorIDs: string[]\n __pageTreeDepth: number\n __pageTreeHasChildren: boolean\n __pageTreeID: string\n __pageTreeOrderIndex: number\n __pageTreeParentID: null | string\n __pageTreeShadeLevel: number\n}\n\nexport type PageTreeDoc = PageTreeMeta & PageTreeSourceDoc\n\ntype SortToken = {\n descending: boolean\n path: string[]\n}\n\ntype SortValue = null | number | string\n\ntype TreeNode = {\n doc: PageTreeSourceDoc\n id: string\n originalIndex: number\n parentID: null | string\n}\n\nconst dateLikePattern = /^\\d{4}-\\d{2}-\\d{2}(?:[T\\s].*)?$/\n\nexport type BuildPageTreeDocsOptions = {\n parentFieldSlug?: string\n sort?: string\n}\n\nexport function stringifyDocID(id: number | string | undefined): string {\n return String(id ?? '')\n}\n\nexport function getRelationshipID(value: unknown): null | string {\n if (typeof value === 'number' || typeof value === 'string') {\n return stringifyDocID(value)\n }\n\n if (value && typeof value === 'object' && 'id' in value) {\n const relationshipID = (value as { id?: number | string }).id\n\n if (typeof relationshipID === 'number' || typeof relationshipID === 'string') {\n return stringifyDocID(relationshipID)\n }\n }\n\n return null\n}\n\nexport function getDocParentID(\n doc: PageTreeSourceDoc,\n parentFieldSlug = 'parent',\n): null | string {\n return getRelationshipID(doc[parentFieldSlug])\n}\n\nexport function getDocDisplayLabel(doc: PageTreeDoc): string {\n const title = doc.title\n\n if (typeof title === 'string' && title.trim().length > 0) {\n return title\n }\n\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\n return doc.slug\n }\n\n return doc.__pageTreeID\n}\n\nexport function getDocSlugSegment(doc: PageTreeDoc): string {\n if (typeof doc.slug === 'string' && doc.slug.trim().length > 0) {\n return doc.slug.trim().replace(/^\\/+|\\/+$/g, '')\n }\n\n return doc.__pageTreeID\n}\n\nexport function buildDocSlugPath(args: {\n doc: PageTreeDoc\n docsByID: Map<string, PageTreeDoc>\n}): string {\n const { doc, docsByID } = args\n const segments = doc.__pageTreeAncestorIDs\n .map((ancestorID) => docsByID.get(ancestorID))\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\n .map(getDocSlugSegment)\n\n segments.push(getDocSlugSegment(doc))\n\n return `/${segments.filter(Boolean).join('/')}`\n}\n\nexport function buildProspectiveDocSlugPath(args: {\n activeDoc: PageTreeDoc\n docsByID: Map<string, PageTreeDoc>\n targetDoc?: PageTreeDoc\n}): string {\n const { activeDoc, docsByID, targetDoc } = args\n\n if (!targetDoc) {\n return `/${getDocSlugSegment(activeDoc)}`\n }\n\n const segments = targetDoc.__pageTreeAncestorIDs\n .map((ancestorID) => docsByID.get(ancestorID))\n .filter((ancestorDoc): ancestorDoc is PageTreeDoc => Boolean(ancestorDoc))\n .map(getDocSlugSegment)\n\n segments.push(getDocSlugSegment(targetDoc), getDocSlugSegment(activeDoc))\n\n return `/${segments.filter(Boolean).join('/')}`\n}\n\nexport function buildChildrenByParentID(args: {\n docs: PageTreeSourceDoc[]\n parentFieldSlug?: string\n}): Map<null | string, string[]> {\n const { docs, parentFieldSlug = 'parent' } = args\n const childrenByParentID = new Map<null | string, string[]>()\n\n for (const doc of docs) {\n const docID = stringifyDocID(doc.id)\n const parentID = getDocParentID(doc, parentFieldSlug)\n const existingChildren = childrenByParentID.get(parentID) ?? []\n\n existingChildren.push(docID)\n childrenByParentID.set(parentID, existingChildren)\n }\n\n return childrenByParentID\n}\n\nexport function collectDescendantIDs(\n rootID: string,\n childrenByParentID: Map<null | string, string[]>,\n): string[] {\n const descendantIDs: string[] = []\n const stack = [...(childrenByParentID.get(rootID) ?? [])]\n\n while (stack.length > 0) {\n const currentID = stack.pop()\n\n if (!currentID) {\n continue\n }\n\n descendantIDs.push(currentID)\n stack.push(...(childrenByParentID.get(currentID) ?? []))\n }\n\n return descendantIDs\n}\n\nfunction getCycleNodeIDs(nodes: TreeNode[], nodesByID: Map<string, TreeNode>): Set<string> {\n const cycleNodeIDs = new Set<string>()\n const pathStack: string[] = []\n const visitState = new Map<string, 'done' | 'visiting'>()\n\n const walk = (node: TreeNode) => {\n const currentState = visitState.get(node.id)\n\n if (currentState === 'done') {\n return\n }\n\n if (currentState === 'visiting') {\n const cycleStartIndex = pathStack.lastIndexOf(node.id)\n const cycleIDs = cycleStartIndex >= 0 ? pathStack.slice(cycleStartIndex) : [node.id]\n\n for (const cycleID of cycleIDs) {\n cycleNodeIDs.add(cycleID)\n }\n\n return\n }\n\n visitState.set(node.id, 'visiting')\n pathStack.push(node.id)\n\n if (node.parentID !== null) {\n const parentNode = nodesByID.get(node.parentID)\n\n if (parentNode) {\n walk(parentNode)\n }\n }\n\n pathStack.pop()\n visitState.set(node.id, 'done')\n }\n\n for (const node of nodes) {\n walk(node)\n }\n\n return cycleNodeIDs\n}\n\nfunction parseSortTokens(sort?: string): SortToken[] {\n if (typeof sort !== 'string' || sort.trim().length === 0) {\n return []\n }\n\n return sort\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0)\n .map((token) => {\n const descending = token.startsWith('-')\n const normalizedToken = descending ? token.slice(1) : token\n\n return {\n descending,\n path: normalizedToken.split('.').filter(Boolean),\n }\n })\n .filter((token) => token.path.length > 0)\n}\n\nfunction resolveSortValue(source: unknown, path: string[]): SortValue | undefined {\n let currentValue = source\n\n for (const segment of path) {\n if (!currentValue || typeof currentValue !== 'object' || Array.isArray(currentValue)) {\n return undefined\n }\n\n if (!(segment in currentValue)) {\n return undefined\n }\n\n currentValue = (currentValue as Record<string, unknown>)[segment]\n }\n\n if (currentValue === null || currentValue === undefined) {\n return null\n }\n\n if (currentValue instanceof Date) {\n const timestamp = currentValue.getTime()\n\n return Number.isNaN(timestamp) ? undefined : timestamp\n }\n\n if (typeof currentValue === 'boolean') {\n return currentValue ? 1 : 0\n }\n\n if (typeof currentValue === 'number') {\n return Number.isFinite(currentValue) ? currentValue : undefined\n }\n\n if (typeof currentValue !== 'string') {\n return undefined\n }\n\n const normalizedValue = currentValue.trim()\n\n if (dateLikePattern.test(normalizedValue)) {\n const timestamp = Date.parse(normalizedValue)\n\n if (!Number.isNaN(timestamp)) {\n return timestamp\n }\n }\n\n return normalizedValue\n}\n\nfunction compareSortValues(leftValue: SortValue, rightValue: SortValue): number {\n if (leftValue === null && rightValue === null) {\n return 0\n }\n\n if (leftValue === null) {\n return -1\n }\n\n if (rightValue === null) {\n return 1\n }\n\n if (typeof leftValue === 'number' && typeof rightValue === 'number') {\n if (leftValue === rightValue) {\n return 0\n }\n\n return leftValue < rightValue ? -1 : 1\n }\n\n // Payload orderable keys use fractional indexing strings (for example `_order`\n // values like `a5`, `a53`, `a5i`). These must sort by raw lexicographic order\n // to match the database; numeric-aware locale comparison places `a53` after\n // `a5i`, which is wrong for these keys.\n const leftString = String(leftValue)\n const rightString = String(rightValue)\n\n if (leftString === rightString) {\n return 0\n }\n\n return leftString < rightString ? -1 : 1\n}\n\nfunction createTreeNodeComparator(sort?: string) {\n const sortTokens = parseSortTokens(sort)\n\n if (sortTokens.length === 0) {\n return null\n }\n\n return (leftNode: TreeNode, rightNode: TreeNode): number => {\n for (const sortToken of sortTokens) {\n const leftValue = resolveSortValue(leftNode.doc, sortToken.path)\n const rightValue = resolveSortValue(rightNode.doc, sortToken.path)\n\n if (leftValue === undefined || rightValue === undefined) {\n continue\n }\n\n const comparison = compareSortValues(leftValue, rightValue)\n\n if (comparison !== 0) {\n return sortToken.descending ? -comparison : comparison\n }\n }\n\n return leftNode.originalIndex - rightNode.originalIndex\n }\n}\n\nexport function buildPageTreeDocs(\n docs: PageTreeSourceDoc[],\n options: BuildPageTreeDocsOptions = {},\n): PageTreeDoc[] {\n const parentFieldSlug = options.parentFieldSlug ?? 'parent'\n const nodes: TreeNode[] = docs.map((doc, index) => ({\n doc,\n id: stringifyDocID(doc.id),\n originalIndex: index,\n parentID: getDocParentID(doc, parentFieldSlug),\n }))\n const nodesByID = new Map(nodes.map((node) => [node.id, node]))\n const cycleNodeIDs = getCycleNodeIDs(nodes, nodesByID)\n const effectiveParentIDByNodeID = new Map<string, null | string>()\n const childrenByParentID = new Map<null | string, TreeNode[]>()\n const compareTreeNodes = createTreeNodeComparator(options.sort)\n\n for (const node of nodes) {\n const parentExists = node.parentID !== null && nodesByID.has(node.parentID)\n const effectiveParentID =\n parentExists && !cycleNodeIDs.has(node.id) ? node.parentID : null\n\n effectiveParentIDByNodeID.set(node.id, effectiveParentID)\n\n const currentChildren = childrenByParentID.get(effectiveParentID) ?? []\n currentChildren.push(node)\n childrenByParentID.set(effectiveParentID, currentChildren)\n }\n\n if (compareTreeNodes) {\n for (const children of childrenByParentID.values()) {\n children.sort(compareTreeNodes)\n }\n }\n\n const orderedDocs: PageTreeDoc[] = []\n const subtreeDepthByID = new Map<string, number>()\n const visitedNodeIDs = new Set<string>()\n\n const getShadeLevel = (rootMaxDepth: number, depth: number): number => {\n if (rootMaxDepth === 0) {\n return 0\n }\n\n return Math.max(rootMaxDepth - depth + 1, 1)\n }\n\n const getSubtreeDepth = (node: TreeNode): number => {\n const cachedDepth = subtreeDepthByID.get(node.id)\n\n if (typeof cachedDepth === 'number') {\n return cachedDepth\n }\n\n const children = childrenByParentID.get(node.id) ?? []\n\n if (children.length === 0) {\n subtreeDepthByID.set(node.id, 0)\n return 0\n }\n\n const depth = Math.max(...children.map((child) => getSubtreeDepth(child) + 1))\n subtreeDepthByID.set(node.id, depth)\n return depth\n }\n\n const visit = (node: TreeNode, ancestorIDs: string[], rootMaxDepth: number) => {\n if (visitedNodeIDs.has(node.id)) {\n return\n }\n\n visitedNodeIDs.add(node.id)\n\n const children = childrenByParentID.get(node.id) ?? []\n const depth = ancestorIDs.length\n const shadeLevel = getShadeLevel(rootMaxDepth, depth)\n\n orderedDocs.push({\n ...(node.doc as PageTreeSourceDoc),\n __pageTreeAncestorIDs: ancestorIDs,\n __pageTreeDepth: depth,\n __pageTreeHasChildren: children.length > 0,\n __pageTreeID: node.id,\n __pageTreeOrderIndex: orderedDocs.length,\n __pageTreeParentID: effectiveParentIDByNodeID.get(node.id) ?? null,\n __pageTreeShadeLevel: shadeLevel,\n })\n\n for (const child of children) {\n visit(child, [...ancestorIDs, node.id], rootMaxDepth)\n }\n }\n\n const roots = [...(childrenByParentID.get(null) ?? [])]\n\n for (const root of roots) {\n visit(root, [], getSubtreeDepth(root))\n }\n\n const fallbackNodes = compareTreeNodes ? [...nodes].sort(compareTreeNodes) : nodes\n\n for (const node of fallbackNodes) {\n if (!visitedNodeIDs.has(node.id)) {\n visit(node, [], getSubtreeDepth(node))\n }\n }\n\n return orderedDocs\n}\n\nexport function getVisibleTreeDocs(\n docs: PageTreeDoc[],\n collapsedIDs: ReadonlySet<string>,\n): PageTreeDoc[] {\n return docs.filter((doc) =>\n doc.__pageTreeAncestorIDs.every((ancestorID) => !collapsedIDs.has(ancestorID)),\n )\n}\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
- dropTarget: collision.data?.droppableContainer.data.current,
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.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"}
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\ntype PageTreeDragType = 'move' | 'order'\ntype PageTreeDragData = {\n dragType?: PageTreeDragType\n parentID?: null | string\n}\n\nexport type PageTreePointerCollisionCandidate = {\n dragType?: PageTreeDragType\n dropTarget?: PageTreeDropTarget\n id: Collision['id']\n parentID?: null | string\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(\n (collision) => collision.id === collisionID,\n )\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\nfunction getDragData(value: unknown): PageTreeDragData {\n if (!value || typeof value !== 'object') {\n return {}\n }\n\n const data = value as { dragType?: unknown; parentID?: unknown }\n const dragType = data.dragType === 'move' || data.dragType === 'order' ? data.dragType : undefined\n const parentID =\n data.parentID === null || typeof data.parentID === 'string' ? data.parentID : undefined\n\n return {\n dragType,\n parentID,\n }\n}\n\nfunction getCollisionDropTarget(collision: Collision): PageTreeDropTarget | undefined {\n return collision.data?.droppableContainer.data.current as PageTreeDropTarget | undefined\n}\n\nfunction getCollisionDragData(collision: Collision): PageTreeDragData {\n return getDragData(collision.data?.droppableContainer.data.current)\n}\n\nfunction isSortableOrderCollision(collision: Collision, activeDragData: PageTreeDragData): boolean {\n const collisionDragData = getCollisionDragData(collision)\n\n return (\n collisionDragData.dragType === 'order' && collisionDragData.parentID === activeDragData.parentID\n )\n}\n\nexport function resolvePageTreePointerCollisionID(args: {\n activeParentID?: null | string\n collisions: PageTreePointerCollisionCandidate[]\n dragType?: PageTreeDragType\n pointerCoordinates: Parameters<CollisionDetection>[0]['pointerCoordinates']\n}): Collision['id'] | null {\n const { activeParentID, collisions, dragType, pointerCoordinates } = args\n\n if (!pointerCoordinates) {\n return null\n }\n\n if (dragType === 'order') {\n return (\n collisions.find(\n (collision) => collision.dragType === 'order' && collision.parentID === activeParentID,\n )?.id ?? null\n )\n }\n\n const directInsertCollision = collisions.find(\n (collision) => collision.dropTarget?.dropType === 'insert',\n )\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 (\n rowCollision.dropTarget.insertBeforeDropID &&\n pointerCoordinates.y - rowCollision.rect.top <= PAGE_TREE_INSERT_EDGE_SIZE_PX\n ) {\n return rowCollision.dropTarget.insertBeforeDropID\n }\n\n if (\n rowCollision.dropTarget.insertAfterDropID &&\n rowCollision.rect.bottom - pointerCoordinates.y <= PAGE_TREE_INSERT_EDGE_SIZE_PX\n ) {\n return rowCollision.dropTarget.insertAfterDropID\n }\n\n return rowCollision.id\n}\n\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) => ({\n id: collision.id,\n ...getCollisionDragData(collision),\n dropTarget: getCollisionDropTarget(collision),\n rect: args.droppableRects.get(collision.id) ?? undefined,\n })),\n dragType: activeDragData.dragType,\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","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":"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"}
@@ -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 (\r\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"}
1
+ {"version":3,"sources":["../../src/utilities/pageTreeConfig.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { NestedDocsPageTreePluginCollectionCustom } from '../types.js'\nimport { nestedDocsPageTreePluginCustomKey } from '../types.js'\nimport { normalizeNestedDocsPageTreePluginBadgeConfig } from './badgeConfig.js'\n\nexport function getCollectionPageTreeConfig(\n collection: Pick<CollectionConfig, 'custom'>,\n): null | NestedDocsPageTreePluginCollectionCustom {\n const value = collection.custom?.[nestedDocsPageTreePluginCustomKey]\n\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null\n }\n\n const config = value as Partial<NestedDocsPageTreePluginCollectionCustom>\n\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\n return {\n badges: normalizeNestedDocsPageTreePluginBadgeConfig(config.badges),\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[];