@sanity/hierarchical-document-list 0.1.0-next.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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/lib/TreeDeskStructure.d.ts +7 -0
  4. package/lib/TreeDeskStructure.js +43 -0
  5. package/lib/TreeInputComponent.d.ts +19 -0
  6. package/lib/TreeInputComponent.js +10 -0
  7. package/lib/components/DocumentInNode.d.ts +11 -0
  8. package/lib/components/DocumentInNode.js +46 -0
  9. package/lib/components/DocumentPreviewStatus.d.ts +7 -0
  10. package/lib/components/DocumentPreviewStatus.js +16 -0
  11. package/lib/components/NodeActions.d.ts +10 -0
  12. package/lib/components/NodeActions.js +23 -0
  13. package/lib/components/NodeContentRenderer.d.ts +8 -0
  14. package/lib/components/NodeContentRenderer.js +79 -0
  15. package/lib/components/PlaceholderDropzone.d.ts +9 -0
  16. package/lib/components/PlaceholderDropzone.js +17 -0
  17. package/lib/components/TreeEditor.d.ts +12 -0
  18. package/lib/components/TreeEditor.js +41 -0
  19. package/lib/components/TreeEditorErrorBoundary.d.ts +17 -0
  20. package/lib/components/TreeEditorErrorBoundary.js +40 -0
  21. package/lib/components/TreeNodeRenderer.d.ts +3 -0
  22. package/lib/components/TreeNodeRenderer.js +22 -0
  23. package/lib/components/TreeNodeRendererScaffold.d.ts +4 -0
  24. package/lib/components/TreeNodeRendererScaffold.js +164 -0
  25. package/lib/createDeskHierarchy.d.ts +10 -0
  26. package/lib/createDeskHierarchy.js +52 -0
  27. package/lib/createHierarchicalField.d.ts +8 -0
  28. package/lib/createHierarchicalField.js +30 -0
  29. package/lib/hiearchy.tree.d.ts +23 -0
  30. package/lib/hiearchy.tree.js +28 -0
  31. package/lib/index.d.ts +3 -0
  32. package/lib/index.js +3 -0
  33. package/lib/utils/flatDataToTree.d.ts +6 -0
  34. package/lib/utils/flatDataToTree.js +14 -0
  35. package/lib/utils/getAdjescentNodes.d.ts +12 -0
  36. package/lib/utils/getAdjescentNodes.js +15 -0
  37. package/lib/utils/getCommonTreeProps.d.ts +7 -0
  38. package/lib/utils/getCommonTreeProps.js +15 -0
  39. package/lib/utils/getTreeHeight.d.ts +3 -0
  40. package/lib/utils/getTreeHeight.js +7 -0
  41. package/lib/utils/gradientPatchAdapter.d.ts +4 -0
  42. package/lib/utils/gradientPatchAdapter.js +34 -0
  43. package/lib/utils/idUtils.d.ts +2 -0
  44. package/lib/utils/idUtils.js +6 -0
  45. package/lib/utils/moveItemInArray.d.ts +5 -0
  46. package/lib/utils/moveItemInArray.js +13 -0
  47. package/lib/utils/treeData.d.ts +18 -0
  48. package/lib/utils/treeData.js +77 -0
  49. package/lib/utils/treePatches.d.ts +13 -0
  50. package/lib/utils/treePatches.js +133 -0
  51. package/lib/utils/useAllItems.d.ts +7 -0
  52. package/lib/utils/useAllItems.js +92 -0
  53. package/lib/utils/useLocalTree.d.ts +17 -0
  54. package/lib/utils/useLocalTree.js +27 -0
  55. package/lib/utils/useTreeOperations.d.ts +9 -0
  56. package/lib/utils/useTreeOperations.js +16 -0
  57. package/lib/utils/useTreeOperationsProvider.d.ts +15 -0
  58. package/lib/utils/useTreeOperationsProvider.js +52 -0
  59. package/package.json +54 -0
  60. package/sanity.json +12 -0
  61. package/screenshot-1.jpg +0 -0
  62. package/tsconfig.json +20 -0
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useToast } from '@sanity/ui';
3
+ import React from 'react';
4
+ const ErrorToast = ({ error }) => {
5
+ const { push } = useToast();
6
+ React.useEffect(() => {
7
+ if (error?.title) {
8
+ push({
9
+ title: error.title,
10
+ description: error.description,
11
+ closable: true,
12
+ status: 'error',
13
+ id: 'hierarchical-error'
14
+ });
15
+ }
16
+ }, [error]);
17
+ return null;
18
+ };
19
+ class TreeEditorErrorBoundary extends React.Component {
20
+ constructor(props) {
21
+ super(props);
22
+ this.state = { error: undefined };
23
+ }
24
+ static getDerivedStateFromError(error) {
25
+ if (!error) {
26
+ return {
27
+ error: undefined
28
+ };
29
+ }
30
+ return {
31
+ error: {
32
+ title: 'Something went wrong'
33
+ }
34
+ };
35
+ }
36
+ render() {
37
+ return (_jsxs(React.Fragment, { children: [_jsx(ErrorToast, { error: this.state.error }, void 0), this.props.children] }, void 0));
38
+ }
39
+ }
40
+ export default TreeEditorErrorBoundary;
@@ -0,0 +1,3 @@
1
+ import { TreeRenderer } from 'react-sortable-tree';
2
+ declare const TreeNodeRenderer: TreeRenderer;
3
+ export default TreeNodeRenderer;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import TreeNodeRendererScaffold from './TreeNodeRendererScaffold';
4
+ /**
5
+ * To prevent expand buttons from overflowing on the left, we add a minimum left padding to all entries
6
+ */
7
+ const BASE_LEFT_PADDING = 10;
8
+ const NESTING_PADDING = 14;
9
+ const TreeNodeRenderer = (props) => {
10
+ const { children, lowerSiblingCounts, connectDropTarget, isOver, draggedNode, canDrop } = props;
11
+ // Construct the scaffold representing the structure of the tree
12
+ const scaffoldBlockCount = lowerSiblingCounts.length;
13
+ return connectDropTarget(_jsxs("div", { style: props.style, children: [_jsx("div", { style: {
14
+ // prettier-ignore
15
+ paddingLeft: `${BASE_LEFT_PADDING + (NESTING_PADDING * scaffoldBlockCount)}px`
16
+ }, children: React.Children.map(children, (child) => React.cloneElement(child, {
17
+ isOver,
18
+ canDrop,
19
+ draggedNode
20
+ })) }, void 0), _jsx(TreeNodeRendererScaffold, { ...props }, void 0)] }, void 0));
21
+ };
22
+ export default TreeNodeRenderer;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { TreeRendererProps } from 'react-sortable-tree';
3
+ declare const TreeNodeRendererScaffold: React.FC<TreeRendererProps>;
4
+ export default TreeNodeRendererScaffold;
@@ -0,0 +1,164 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { blue } from '@sanity/color';
3
+ import { createGlobalStyle } from 'styled-components';
4
+ // Adapted from react-sortable-tree/src/tree-node.js
5
+ const ScaffoldStyles = createGlobalStyle `
6
+ .rst__lineBlock,
7
+ .rst__absoluteLineBlock {
8
+ height: 100%;
9
+ position: relative;
10
+ display: inline-block;
11
+ --stroke-width: 3px;
12
+ }
13
+
14
+ .rst__absoluteLineBlock {
15
+ position: absolute;
16
+ top: 0;
17
+ }
18
+
19
+ /* Highlight line for pointing to dragged row destination
20
+ ========================================================================== */
21
+ /**
22
+ * +--+--+
23
+ * | | |
24
+ * | | |
25
+ * | | |
26
+ * +--+--+
27
+ */
28
+ .rst__highlightLineVertical {
29
+ z-index: 3;
30
+ }
31
+ .rst__highlightLineVertical::before {
32
+ position: absolute;
33
+ content: '';
34
+ background-color: ${blue[400].hex};
35
+ width: calc(var(--stroke-width) * 2);
36
+ margin-left: calc(var(--stroke-width) * -1);
37
+ left: 50%;
38
+ top: 0;
39
+ height: 100%;
40
+ }
41
+
42
+ @keyframes arrow-pulse {
43
+ 0% {
44
+ transform: translate(0, 0);
45
+ opacity: 0;
46
+ }
47
+ 30% {
48
+ transform: translate(0, 300%);
49
+ opacity: 1;
50
+ }
51
+ 70% {
52
+ transform: translate(0, 700%);
53
+ opacity: 1;
54
+ }
55
+ 100% {
56
+ transform: translate(0, 1000%);
57
+ opacity: 0;
58
+ }
59
+ }
60
+ .rst__highlightLineVertical::after {
61
+ content: '';
62
+ position: absolute;
63
+ height: 0;
64
+ margin-left: calc(var(--stroke-width) * -1);
65
+ left: 50%;
66
+ top: 0;
67
+ border-left: var(--stroke-width) solid transparent;
68
+ border-right: var(--stroke-width) solid transparent;
69
+ border-top: var(--stroke-width) solid white;
70
+ animation: arrow-pulse 1s infinite linear both;
71
+ }
72
+
73
+ /**
74
+ * +-----+
75
+ * | |
76
+ * | +--+
77
+ * | | |
78
+ * +--+--+
79
+ */
80
+ .rst__highlightTopLeftCorner::before {
81
+ z-index: 3;
82
+ content: '';
83
+ position: absolute;
84
+ border-top: solid calc(var(--stroke-width) * 2) ${blue[400].hex};
85
+ border-left: solid calc(var(--stroke-width) * 2) ${blue[400].hex};
86
+ box-sizing: border-box;
87
+ height: calc(50% + var(--stroke-width));
88
+ top: 50%;
89
+ margin-top: calc(var(--stroke-width) * -1);
90
+ right: 0;
91
+ width: calc(50% + var(--stroke-width));
92
+ }
93
+
94
+ /**
95
+ * +--+--+
96
+ * | | |
97
+ * | | |
98
+ * | +->|
99
+ * +-----+
100
+ */
101
+ .rst__highlightBottomLeftCorner {
102
+ z-index: 3;
103
+ }
104
+ .rst__highlightBottomLeftCorner::before {
105
+ content: '';
106
+ position: absolute;
107
+ border-bottom: solid calc(var(--stroke-width) * 2) ${blue[400].hex};
108
+ border-left: solid calc(var(--stroke-width) * 2) ${blue[400].hex};
109
+ box-sizing: border-box;
110
+ height: calc(100% + var(--stroke-width));
111
+ top: 0;
112
+ right: calc(var(--stroke-width) * 3);
113
+ width: calc(50% - calc(var(--stroke-width) * 2));
114
+ }
115
+
116
+ .rst__highlightBottomLeftCorner::after {
117
+ content: '';
118
+ position: absolute;
119
+ height: 0;
120
+ right: 0;
121
+ top: 100%;
122
+ margin-top: calc(var(--stroke-width) * -3);
123
+ border-top: calc(var(--stroke-width) * 3) solid transparent;
124
+ border-bottom: calc(var(--stroke-width) * 3) solid transparent;
125
+ border-left: calc(var(--stroke-width) * 3) solid ${blue[400].hex};
126
+ }
127
+
128
+ .rst__unclickable {
129
+ pointer-events: none;
130
+ margin-top: -calc(var(--stroke-width) * 3);
131
+ }
132
+ `;
133
+ const TreeNodeRendererScaffold = (props) => {
134
+ const { lowerSiblingCounts, scaffoldBlockPxWidth, listIndex, swapDepth, swapFrom, swapLength, treeIndex } = props;
135
+ // Construct the scaffold representing the structure of the tree
136
+ const scaffold = lowerSiblingCounts.map((lowerSiblingCount, i) => {
137
+ if (lowerSiblingCount < 0 || treeIndex === listIndex || i !== swapDepth) {
138
+ return null;
139
+ }
140
+ // This row has been shifted, and is at the depth of
141
+ // the line pointing to the new destination
142
+ let highlightLineClass = '';
143
+ if (listIndex === (swapFrom || 0) + (swapLength || 0) - 1) {
144
+ // This block is on the bottom (target) line
145
+ // This block points at the target block (where the row will go when released)
146
+ highlightLineClass = 'rst__highlightBottomLeftCorner';
147
+ }
148
+ else if (treeIndex === swapFrom) {
149
+ // This block is on the top (source) line
150
+ highlightLineClass = 'rst__highlightTopLeftCorner';
151
+ }
152
+ else {
153
+ // This block is between the bottom and top
154
+ highlightLineClass = 'rst__highlightLineVertical';
155
+ }
156
+ const style = {
157
+ width: scaffoldBlockPxWidth,
158
+ left: scaffoldBlockPxWidth * i
159
+ };
160
+ return (_jsx("div", { style: style, className: `rst__unclickable rst__absoluteLineBlock ${highlightLineClass || ''}`, tabIndex: -1 }, i));
161
+ });
162
+ return (_jsxs(_Fragment, { children: [scaffold, _jsx(ScaffoldStyles, {}, void 0)] }, void 0));
163
+ };
164
+ export default TreeNodeRendererScaffold;
@@ -0,0 +1,10 @@
1
+ import { TreeDeskStructureProps } from './types';
2
+ interface TreeProps extends TreeDeskStructureProps {
3
+ /**
4
+ * Visible title above the tree.
5
+ * Also used as the label in the desk list item.
6
+ */
7
+ title: string;
8
+ }
9
+ export default function createDeskHierarchy(props: TreeProps): import("@sanity/structure/dist/dts/ListItem").ListItemBuilder;
10
+ export {};
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import S from '@sanity/desk-tool/structure-builder';
3
+ import { AddIcon } from '@sanity/icons';
4
+ import TreeDeskStructure from './TreeDeskStructure';
5
+ const deskTreeValidator = (props) => {
6
+ const { documentId, referenceTo } = props;
7
+ if (typeof documentId !== 'string' && !documentId) {
8
+ throw new Error('[hierarchical input] Please add a documentId to your tree');
9
+ }
10
+ if (!Array.isArray(referenceTo)) {
11
+ throw new Error(`[hierarchical input] Missing valid 'referenceTo' in createDeskHierarchy (documentId "${documentId}")`);
12
+ }
13
+ return (deskProps) => _jsx(TreeDeskStructure, { ...deskProps, options: props }, void 0);
14
+ };
15
+ export default function createDeskHierarchy(props) {
16
+ const { documentId, referenceTo, referenceOptions } = props;
17
+ let mainList = (referenceTo?.length === 1
18
+ ? S.documentTypeList(referenceTo[0]).schemaType(referenceTo[0])
19
+ : S.documentList())
20
+ .id(documentId)
21
+ .menuItems((referenceTo || []).map((schemaType) => S.menuItem()
22
+ .intent({
23
+ type: 'create',
24
+ params: { type: schemaType, template: schemaType }
25
+ })
26
+ // @TODO: get the title for each schema type
27
+ .title(schemaType)
28
+ .icon(AddIcon)))
29
+ .canHandleIntent((intent, context) => {
30
+ if (intent === 'edit' && context.id === props.documentId) {
31
+ return true;
32
+ }
33
+ if (intent === 'create' && referenceTo.includes(context.type)) {
34
+ return true;
35
+ }
36
+ return false;
37
+ });
38
+ if (referenceOptions?.filter) {
39
+ mainList = mainList.filter(referenceOptions.filter);
40
+ }
41
+ if (referenceOptions?.filterParams) {
42
+ mainList = mainList.params(referenceOptions.filterParams);
43
+ }
44
+ return S.listItem()
45
+ .id(documentId)
46
+ .title(props.title || documentId)
47
+ .child(Object.assign(mainList.serialize(), {
48
+ type: 'component',
49
+ component: deskTreeValidator(props),
50
+ options: props
51
+ }, props.title ? { title: props.title } : {}));
52
+ }
@@ -0,0 +1,8 @@
1
+ import { ArraySchemaType } from '@sanity/types';
2
+ import React from 'react';
3
+ import { TreeFieldSchema } from './types';
4
+ export default function createHierarchicalField({ name, title, options, ...rest }: TreeFieldSchema): Omit<ArraySchemaType, 'type' | 'jsonType' | 'of'> & {
5
+ type: string;
6
+ inputComponent: React.FC<any>;
7
+ of: any[];
8
+ };
@@ -0,0 +1,30 @@
1
+ import TreeInputComponent from './TreeInputComponent';
2
+ export default function createHierarchicalField({ name, title, options, ...rest }) {
3
+ if (!Array.isArray(options?.referenceTo)) {
4
+ throw new Error(`[hierarchical input] Missing valid options.referenceTo in createHierarchicalField (field of name "${name}")`);
5
+ }
6
+ return {
7
+ ...rest,
8
+ options,
9
+ name,
10
+ title,
11
+ type: 'array',
12
+ of: [
13
+ {
14
+ type: 'object',
15
+ fields: [
16
+ { name: 'parent', type: 'string' },
17
+ { name: 'nodeDocType', type: 'string' },
18
+ {
19
+ name: 'node',
20
+ type: 'reference',
21
+ weak: true,
22
+ to: options.referenceTo.map((type) => ({ type })),
23
+ options: options.referenceOptions
24
+ }
25
+ ]
26
+ }
27
+ ],
28
+ inputComponent: TreeInputComponent
29
+ };
30
+ }
@@ -0,0 +1,23 @@
1
+ /// <reference types="react" />
2
+ declare const _default: {
3
+ name: string;
4
+ title: string;
5
+ type: string;
6
+ liveEdit: boolean;
7
+ fields: (Omit<import("@sanity/types/dist/dts").ArraySchemaType<unknown>, "type" | "of" | "jsonType"> & {
8
+ type: string;
9
+ inputComponent: import("react").FC<any>;
10
+ of: any[];
11
+ })[];
12
+ preview: {
13
+ select: {
14
+ id: string;
15
+ tree: string;
16
+ };
17
+ prepare({ id, tree }: {
18
+ id: string;
19
+ tree: unknown[];
20
+ }): Record<string, string>;
21
+ };
22
+ };
23
+ export default _default;
@@ -0,0 +1,28 @@
1
+ import createHierarchicalField from './createHierarchicalField';
2
+ export default {
3
+ name: 'hierarchy.tree',
4
+ title: 'Hierarchical tree',
5
+ type: 'document',
6
+ liveEdit: true,
7
+ fields: [
8
+ createHierarchicalField({
9
+ name: 'tree',
10
+ title: 'Tree',
11
+ options: {
12
+ referenceTo: ['document']
13
+ }
14
+ })
15
+ ],
16
+ preview: {
17
+ select: {
18
+ id: '_id',
19
+ tree: 'tree'
20
+ },
21
+ prepare({ id, tree }) {
22
+ return {
23
+ title: `Hierarchical documents (ID: ${id})`,
24
+ subtitle: `${tree?.length || 0} document(s) in its list.`
25
+ };
26
+ }
27
+ }
28
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default as createHierarchicalField } from './createHierarchicalField';
2
+ export { default as createDeskHierarchy } from './createDeskHierarchy';
3
+ export { default as flatDataToTree } from './utils/flatDataToTree';
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { default as createHierarchicalField } from './createHierarchicalField';
2
+ export { default as createDeskHierarchy } from './createDeskHierarchy';
3
+ export { default as flatDataToTree } from './utils/flatDataToTree';
@@ -0,0 +1,6 @@
1
+ import { SanityTreeItem } from '../types';
2
+ interface TreeItemWithChildren extends SanityTreeItem {
3
+ children?: TreeItemWithChildren[];
4
+ }
5
+ export default function flatDataToTree(data: SanityTreeItem[]): TreeItemWithChildren[];
6
+ export {};
@@ -0,0 +1,14 @@
1
+ import { getTreeFromFlatData } from 'react-sortable-tree';
2
+ export default function flatDataToTree(data) {
3
+ return getTreeFromFlatData({
4
+ flatData: data.map((item) => ({
5
+ ...item,
6
+ // if parent: undefined, the tree won't be constructed
7
+ parent: item.parent || null
8
+ })),
9
+ getKey: (item) => item._key,
10
+ getParentKey: (item) => item.parent,
11
+ // without rootKey, the tree won't be constructed
12
+ rootKey: null
13
+ });
14
+ }
@@ -0,0 +1,12 @@
1
+ import { FlatDataItem, TreeItem } from 'react-sortable-tree';
2
+ /**
3
+ * Gets adjescent non-children nodes of a given treeIndex.
4
+ */
5
+ export default function getAdjescentNodes({ flatTree, node, treeIndex }: {
6
+ flatTree: FlatDataItem[];
7
+ node: TreeItem;
8
+ treeIndex: number;
9
+ }): {
10
+ leadingNode?: FlatDataItem;
11
+ followingNode?: FlatDataItem;
12
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Gets adjescent non-children nodes of a given treeIndex.
3
+ */
4
+ export default function getAdjescentNodes({ flatTree, node, treeIndex }) {
5
+ const leadingNode = flatTree
6
+ .slice(0, treeIndex)
7
+ .reverse()
8
+ // Disregard children nodes - these include the current node's key in their `path` array
9
+ .find((item) => !item.path.includes(node._key));
10
+ const followingNode = flatTree.slice(treeIndex + 1).find((item) => !item.path.includes(node._key));
11
+ return {
12
+ leadingNode,
13
+ followingNode
14
+ };
15
+ }
@@ -0,0 +1,7 @@
1
+ import { ReactSortableTreeProps } from 'react-sortable-tree';
2
+ export default function getCommonTreeProps({ placeholder }: {
3
+ placeholder: {
4
+ title: string;
5
+ subtitle?: string;
6
+ };
7
+ }): Partial<ReactSortableTreeProps>;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import NodeContentRenderer from '../components/NodeContentRenderer';
3
+ import PlaceholderDropzone from '../components/PlaceholderDropzone';
4
+ import TreeNodeRenderer from '../components/TreeNodeRenderer';
5
+ import { ROW_HEIGHT } from './getTreeHeight';
6
+ export default function getCommonTreeProps({ placeholder }) {
7
+ return {
8
+ theme: {
9
+ nodeContentRenderer: NodeContentRenderer,
10
+ placeholderRenderer: (props) => _jsx(PlaceholderDropzone, { ...placeholder, ...props }, void 0),
11
+ treeNodeRenderer: TreeNodeRenderer,
12
+ rowHeight: ROW_HEIGHT
13
+ }
14
+ };
15
+ }
@@ -0,0 +1,3 @@
1
+ import { TreeItem } from 'react-sortable-tree';
2
+ export declare const ROW_HEIGHT = 51;
3
+ export default function getTreeHeight(treeData: TreeItem[]): string;
@@ -0,0 +1,7 @@
1
+ import { getVisibleNodeCount } from 'react-sortable-tree';
2
+ export const ROW_HEIGHT = 51;
3
+ export default function getTreeHeight(treeData) {
4
+ const visibleNodeCount = getVisibleNodeCount({ treeData });
5
+ // prettier-ignore
6
+ return `${50 + (ROW_HEIGHT * visibleNodeCount)}px`;
7
+ }
@@ -0,0 +1,4 @@
1
+ declare type Patch = Record<string, any>;
2
+ declare type GradientPatch = Record<string, any>;
3
+ export declare function toGradient(patches: Patch[]): GradientPatch[];
4
+ export {};
@@ -0,0 +1,34 @@
1
+ // Adapted from @sanity/form-builder/src/sanity/utils/gradientPatchAdapter.ts
2
+ import assert from 'assert';
3
+ import { arrayToJSONMatchPath } from '@sanity/mutator';
4
+ export function toGradient(patches) {
5
+ return patches.map(toGradientPatch);
6
+ }
7
+ function toGradientPatch(patch) {
8
+ const matchPath = arrayToJSONMatchPath(patch.path || []);
9
+ if (patch.type === 'insert') {
10
+ const { position, items } = patch;
11
+ return {
12
+ insert: {
13
+ [position]: matchPath,
14
+ items: items
15
+ }
16
+ };
17
+ }
18
+ if (patch.type === 'unset') {
19
+ return {
20
+ unset: [matchPath]
21
+ };
22
+ }
23
+ assert(patch.type, `Missing patch type in patch ${JSON.stringify(patch)}`);
24
+ if (matchPath) {
25
+ return {
26
+ [patch.type]: {
27
+ [matchPath]: patch.value
28
+ }
29
+ };
30
+ }
31
+ return {
32
+ [patch.type]: patch.value
33
+ };
34
+ }
@@ -0,0 +1,2 @@
1
+ export declare function unprefixId(_id?: string): string;
2
+ export declare function isDraft(_id?: string): boolean;
@@ -0,0 +1,6 @@
1
+ export function unprefixId(_id = '') {
2
+ return _id.replace('drafts.', '');
3
+ }
4
+ export function isDraft(_id = '') {
5
+ return _id.startsWith('drafts.');
6
+ }
@@ -0,0 +1,5 @@
1
+ export default function moveItemInArray<ItemType = unknown>({ array, fromIndex, toIndex }: {
2
+ array: ItemType[];
3
+ fromIndex: number;
4
+ toIndex: number;
5
+ }): ItemType[];
@@ -0,0 +1,13 @@
1
+ export default function moveItemInArray({ array, fromIndex, toIndex }) {
2
+ if (fromIndex === toIndex) {
3
+ return array;
4
+ }
5
+ const newArray = [...array];
6
+ const target = newArray[fromIndex];
7
+ const inc = toIndex < fromIndex ? -1 : 1;
8
+ for (let i = fromIndex; i !== toIndex; i += inc) {
9
+ newArray[i] = newArray[i + inc];
10
+ }
11
+ newArray[toIndex] = target;
12
+ return newArray;
13
+ }
@@ -0,0 +1,18 @@
1
+ import { SanityDocument } from '@sanity/client';
2
+ import { TreeItem } from 'react-sortable-tree';
3
+ import { AllItems, SanityTreeItem, VisibilityMap } from '../types';
4
+ export declare const dataToEditorTree: ({ tree, allItems, visibilityMap }: {
5
+ tree: SanityTreeItem[];
6
+ allItems: AllItems;
7
+ visibilityMap: VisibilityMap;
8
+ }) => TreeItem[];
9
+ export declare const flatTree: (tree: TreeItem[]) => TreeItem[];
10
+ export interface FetchData {
11
+ mainTree?: SanityTreeItem[];
12
+ allItems?: SanityDocument[];
13
+ }
14
+ export declare const getUnaddedItems: (data: {
15
+ allItems: AllItems;
16
+ tree: SanityTreeItem[];
17
+ }) => SanityTreeItem[];
18
+ export declare function normalizeNodeForStorage(item: TreeItem): SanityTreeItem;
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { randomKey } from '@sanity/util/content';
3
+ import DocumentInNode from '../components/DocumentInNode';
4
+ import NodeActions from '../components/NodeActions';
5
+ import flatDataToTree from './flatDataToTree';
6
+ export const dataToEditorTree = ({ tree, allItems, visibilityMap }) => {
7
+ const itemsWithTitle = tree
8
+ .filter((item) => item?.value?.reference?._ref)
9
+ .map((item) => {
10
+ const refId = item.value?.reference?._ref;
11
+ const docPair = refId ? allItems[refId] : undefined;
12
+ const draftDoc = docPair?.draft;
13
+ const publishedDoc = docPair?.published;
14
+ const enhancedItem = {
15
+ ...item,
16
+ expanded: visibilityMap[item._key] !== false,
17
+ draftId: draftDoc?._id,
18
+ publishedId: publishedDoc?._id,
19
+ draftUpdatedAt: draftDoc?._updatedAt,
20
+ publishedUpdatedAt: publishedDoc?._updatedAt
21
+ };
22
+ return {
23
+ ...enhancedItem,
24
+ title: (nodeProps) => (_jsx(DocumentInNode, { item: enhancedItem, action: _jsx(NodeActions, { nodeProps: nodeProps }, void 0) }, void 0)),
25
+ children: []
26
+ };
27
+ });
28
+ return flatDataToTree(itemsWithTitle);
29
+ };
30
+ const documentPairToNode = (doc) => {
31
+ if (!doc?.published?._id) {
32
+ return undefined;
33
+ }
34
+ return {
35
+ _key: randomKey(12),
36
+ _type: 'hierarchy.node',
37
+ draftId: doc.draft?._id,
38
+ draftUpdatedAt: doc.draft?._updatedAt,
39
+ publishedId: doc.published._id,
40
+ publishedUpdatedAt: doc.published?._updatedAt,
41
+ value: {
42
+ reference: {
43
+ _ref: doc.published._id,
44
+ _type: 'reference',
45
+ _weak: true
46
+ },
47
+ docType: doc.published._type
48
+ }
49
+ };
50
+ };
51
+ export const flatTree = (tree) => {
52
+ return tree.reduce((flattened, item) => {
53
+ const { children, ...node } = item;
54
+ return [...flattened, node, ...(Array.isArray(children) ? flatTree(children) : [])];
55
+ }, []);
56
+ };
57
+ export const getUnaddedItems = (data) => {
58
+ if (!data.tree) {
59
+ return Object.entries(data.allItems)
60
+ .map((value) => documentPairToNode(value[1]))
61
+ .filter(Boolean);
62
+ }
63
+ return Object.entries(data.allItems)
64
+ .filter(([publishedId]) => publishedId &&
65
+ // unadded items shouldn't be in the tree
66
+ !data.tree.some((treeItem) => treeItem?.value?.reference?._ref === publishedId))
67
+ .map(([_publishedId, documentPair]) => documentPairToNode(documentPair))
68
+ .filter(Boolean);
69
+ };
70
+ export function normalizeNodeForStorage(item) {
71
+ return {
72
+ _key: item._key,
73
+ _type: item._type || 'hierarchy.node',
74
+ value: item.value,
75
+ parent: item.parent
76
+ };
77
+ }