@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.
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/lib/TreeDeskStructure.d.ts +7 -0
- package/lib/TreeDeskStructure.js +43 -0
- package/lib/TreeInputComponent.d.ts +19 -0
- package/lib/TreeInputComponent.js +10 -0
- package/lib/components/DocumentInNode.d.ts +11 -0
- package/lib/components/DocumentInNode.js +46 -0
- package/lib/components/DocumentPreviewStatus.d.ts +7 -0
- package/lib/components/DocumentPreviewStatus.js +16 -0
- package/lib/components/NodeActions.d.ts +10 -0
- package/lib/components/NodeActions.js +23 -0
- package/lib/components/NodeContentRenderer.d.ts +8 -0
- package/lib/components/NodeContentRenderer.js +79 -0
- package/lib/components/PlaceholderDropzone.d.ts +9 -0
- package/lib/components/PlaceholderDropzone.js +17 -0
- package/lib/components/TreeEditor.d.ts +12 -0
- package/lib/components/TreeEditor.js +41 -0
- package/lib/components/TreeEditorErrorBoundary.d.ts +17 -0
- package/lib/components/TreeEditorErrorBoundary.js +40 -0
- package/lib/components/TreeNodeRenderer.d.ts +3 -0
- package/lib/components/TreeNodeRenderer.js +22 -0
- package/lib/components/TreeNodeRendererScaffold.d.ts +4 -0
- package/lib/components/TreeNodeRendererScaffold.js +164 -0
- package/lib/createDeskHierarchy.d.ts +10 -0
- package/lib/createDeskHierarchy.js +52 -0
- package/lib/createHierarchicalField.d.ts +8 -0
- package/lib/createHierarchicalField.js +30 -0
- package/lib/hiearchy.tree.d.ts +23 -0
- package/lib/hiearchy.tree.js +28 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/utils/flatDataToTree.d.ts +6 -0
- package/lib/utils/flatDataToTree.js +14 -0
- package/lib/utils/getAdjescentNodes.d.ts +12 -0
- package/lib/utils/getAdjescentNodes.js +15 -0
- package/lib/utils/getCommonTreeProps.d.ts +7 -0
- package/lib/utils/getCommonTreeProps.js +15 -0
- package/lib/utils/getTreeHeight.d.ts +3 -0
- package/lib/utils/getTreeHeight.js +7 -0
- package/lib/utils/gradientPatchAdapter.d.ts +4 -0
- package/lib/utils/gradientPatchAdapter.js +34 -0
- package/lib/utils/idUtils.d.ts +2 -0
- package/lib/utils/idUtils.js +6 -0
- package/lib/utils/moveItemInArray.d.ts +5 -0
- package/lib/utils/moveItemInArray.js +13 -0
- package/lib/utils/treeData.d.ts +18 -0
- package/lib/utils/treeData.js +77 -0
- package/lib/utils/treePatches.d.ts +13 -0
- package/lib/utils/treePatches.js +133 -0
- package/lib/utils/useAllItems.d.ts +7 -0
- package/lib/utils/useAllItems.js +92 -0
- package/lib/utils/useLocalTree.d.ts +17 -0
- package/lib/utils/useLocalTree.js +27 -0
- package/lib/utils/useTreeOperations.d.ts +9 -0
- package/lib/utils/useTreeOperations.js +16 -0
- package/lib/utils/useTreeOperationsProvider.d.ts +15 -0
- package/lib/utils/useTreeOperationsProvider.js +52 -0
- package/package.json +54 -0
- package/sanity.json +12 -0
- package/screenshot-1.jpg +0 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FullTree, NodeData, NodeRendererProps, OnMovePreviousAndNextLocation, TreeItem } from 'react-sortable-tree';
|
|
2
|
+
import { SanityTreeItem } from '../types';
|
|
3
|
+
export declare type HandleMovedNodeData = Omit<NodeData & FullTree & OnMovePreviousAndNextLocation, 'prevPath' | 'prevTreeIndex' | 'path' | 'treeIndex'>;
|
|
4
|
+
export declare type HandleMovedNode = (moveData: HandleMovedNodeData) => void;
|
|
5
|
+
export declare function getAddItemPatch(item: SanityTreeItem): unknown[];
|
|
6
|
+
export declare function getDuplicateItemPatch(nodeProps: NodeRendererProps): unknown[];
|
|
7
|
+
export declare function getRemoveItemPatch({ node }: Pick<NodeRendererProps, 'node'>): unknown[];
|
|
8
|
+
export declare function getMovedNodePatch(data: HandleMovedNodeData): unknown[];
|
|
9
|
+
export declare function getMoveItemPatch({ nodeProps: { node, treeIndex, parentNode }, localTree, direction }: {
|
|
10
|
+
nodeProps: NodeRendererProps;
|
|
11
|
+
localTree: TreeItem[];
|
|
12
|
+
direction: 'up' | 'down';
|
|
13
|
+
}): unknown[];
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as Patch from '@sanity/form-builder/lib/patch/patches';
|
|
2
|
+
import { randomKey } from '@sanity/util/content';
|
|
3
|
+
import { getFlatDataFromTree } from 'react-sortable-tree';
|
|
4
|
+
import getAdjescentNodes from './getAdjescentNodes';
|
|
5
|
+
import moveItemInArray from './moveItemInArray';
|
|
6
|
+
import { normalizeNodeForStorage } from './treeData';
|
|
7
|
+
export function getAddItemPatch(item) {
|
|
8
|
+
const normalizedNode = normalizeNodeForStorage(item);
|
|
9
|
+
return [
|
|
10
|
+
// Add the node to the end of the tree
|
|
11
|
+
Patch.insert([normalizedNode], 'after', [-1])
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
export function getDuplicateItemPatch(nodeProps) {
|
|
15
|
+
const newItem = {
|
|
16
|
+
...nodeProps.node,
|
|
17
|
+
_key: randomKey(12)
|
|
18
|
+
};
|
|
19
|
+
const normalizedNode = normalizeNodeForStorage(newItem);
|
|
20
|
+
return [
|
|
21
|
+
// Add duplicated node before the existing one
|
|
22
|
+
Patch.insert([normalizedNode], 'before', [{ _key: nodeProps.node._key }])
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
export function getRemoveItemPatch({ node }) {
|
|
26
|
+
const keyPath = { _key: node._key };
|
|
27
|
+
const children = getChildrenPaths(node);
|
|
28
|
+
return [
|
|
29
|
+
// 1. Unset the removed node
|
|
30
|
+
Patch.unset([keyPath]),
|
|
31
|
+
// 2. Unset its children
|
|
32
|
+
...children.map((path) => Patch.unset([{ _key: path }]))
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
export function getMovedNodePatch(data) {
|
|
36
|
+
const { nextParentNode } = data;
|
|
37
|
+
const keyPath = { _key: data.node._key };
|
|
38
|
+
// === REMOVING NODE FROM TREE ===
|
|
39
|
+
// `nextPath` will be null if the item is removed from tree
|
|
40
|
+
if (!Array.isArray(data.nextPath)) {
|
|
41
|
+
return getRemoveItemPatch({ node: data.node });
|
|
42
|
+
}
|
|
43
|
+
const nextFlatTree = getFlatDataFromTree({
|
|
44
|
+
treeData: data.treeData,
|
|
45
|
+
getNodeKey: (t) => t.node._key
|
|
46
|
+
});
|
|
47
|
+
const normalizedNode = normalizeNodeForStorage(data.node);
|
|
48
|
+
const { leadingNode, followingNode } = getAdjescentNodes({
|
|
49
|
+
flatTree: nextFlatTree,
|
|
50
|
+
node: data.node,
|
|
51
|
+
treeIndex: data.nextTreeIndex
|
|
52
|
+
});
|
|
53
|
+
return [
|
|
54
|
+
// 1. Unset the moved node
|
|
55
|
+
// (will be ignored by Content Lake on new nodes with _key not yet in tree)
|
|
56
|
+
Patch.unset([keyPath]),
|
|
57
|
+
// 2. SIBLING-BASED PLACEMENT
|
|
58
|
+
// If we were to place solely based on nextTreeIndex, concurrent changes from other editors could put the new node in an unexpected position.
|
|
59
|
+
// Let's instead anchor it to the _key of the sibling coming before or after it.
|
|
60
|
+
leadingNode?.node?._key
|
|
61
|
+
? // After the sibling before it
|
|
62
|
+
Patch.insert([normalizedNode], 'after', [{ _key: leadingNode.node._key }])
|
|
63
|
+
: // Or before the sibling right after it, in case there's no leading sibling node
|
|
64
|
+
Patch.insert([normalizedNode], 'before', [
|
|
65
|
+
followingNode?.node?._key ? { _key: followingNode.node._key } : data.nextTreeIndex
|
|
66
|
+
]),
|
|
67
|
+
// 3. Patch the new node with its new `parent`
|
|
68
|
+
nextParentNode
|
|
69
|
+
? // If it has a parent node, set that parent's _key
|
|
70
|
+
Patch.set(nextParentNode._key, [keyPath, 'parent'])
|
|
71
|
+
: // Else remove the parent key entirely
|
|
72
|
+
Patch.unset([keyPath, 'parent'])
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
function getChildrenPaths(node) {
|
|
76
|
+
if (!Array.isArray(node.children)) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
return node.children
|
|
80
|
+
.reduce((keyPaths, child) => [...keyPaths, child._key, ...getChildrenPaths(child)], [])
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
}
|
|
83
|
+
export function getMoveItemPatch({ nodeProps: { node, treeIndex, parentNode }, localTree, direction = 'up' }) {
|
|
84
|
+
const keyPath = { _key: node._key };
|
|
85
|
+
const nextTreeIndex = treeIndex + (direction === 'up' ? -1 : 1);
|
|
86
|
+
const flatTree = getFlatDataFromTree({
|
|
87
|
+
treeData: localTree,
|
|
88
|
+
getNodeKey: (t) => t.node._key
|
|
89
|
+
});
|
|
90
|
+
const nextFlatTree = moveItemInArray({
|
|
91
|
+
array: flatTree,
|
|
92
|
+
fromIndex: treeIndex,
|
|
93
|
+
toIndex: nextTreeIndex
|
|
94
|
+
});
|
|
95
|
+
const { leadingNode, followingNode } = getAdjescentNodes({
|
|
96
|
+
flatTree: nextFlatTree,
|
|
97
|
+
node,
|
|
98
|
+
treeIndex: nextTreeIndex
|
|
99
|
+
});
|
|
100
|
+
const normalizedNode = normalizeNodeForStorage(node);
|
|
101
|
+
console.log(`Move ${direction}`, {
|
|
102
|
+
node,
|
|
103
|
+
treeIndex,
|
|
104
|
+
parentNode,
|
|
105
|
+
nextFlatTree,
|
|
106
|
+
flatTree,
|
|
107
|
+
localTree,
|
|
108
|
+
leadingSibling: leadingNode,
|
|
109
|
+
followingSibling: followingNode
|
|
110
|
+
});
|
|
111
|
+
// When moving up, look at following node to figure out what is the next parent.
|
|
112
|
+
const nodeToInheritParent = direction === 'up' ? followingNode : leadingNode;
|
|
113
|
+
const nextParentNode = nodeToInheritParent?.parentNode;
|
|
114
|
+
return [
|
|
115
|
+
// 1. Unset the moved node
|
|
116
|
+
// (will be ignored by Content Lake on new nodes with _key not yet in tree)
|
|
117
|
+
Patch.unset([keyPath]),
|
|
118
|
+
// 2. SIBLING-BASED PLACEMENT
|
|
119
|
+
leadingNode?.node?._key
|
|
120
|
+
? // After the sibling before it
|
|
121
|
+
Patch.insert([normalizedNode], 'after', [{ _key: leadingNode.node._key }])
|
|
122
|
+
: // Or before the sibling right after it, in case there's no leading sibling node
|
|
123
|
+
Patch.insert([normalizedNode], 'before', [
|
|
124
|
+
followingNode?.node?._key ? { _key: followingNode.node._key } : nextTreeIndex
|
|
125
|
+
]),
|
|
126
|
+
// 3. Patch the new node with its new `parent`
|
|
127
|
+
nextParentNode
|
|
128
|
+
? // If it has a parent node, set that parent's _key
|
|
129
|
+
Patch.set(nextParentNode._key, [keyPath, 'parent'])
|
|
130
|
+
: // Else remove the parent key entirely
|
|
131
|
+
Patch.unset([keyPath, 'parent'])
|
|
132
|
+
];
|
|
133
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import sanityClient from 'part:@sanity/base/client';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { isDraft, unprefixId } from './idUtils';
|
|
4
|
+
const client = sanityClient.withConfig({
|
|
5
|
+
apiVersion: '2021-09-01'
|
|
6
|
+
});
|
|
7
|
+
function getDeskFilter({ referenceTo, referenceOptions }) {
|
|
8
|
+
const filterParts = ['_type in $docTypes'];
|
|
9
|
+
if (referenceOptions?.filter) {
|
|
10
|
+
filterParts.push(referenceOptions.filter);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
filter: filterParts.join(' && '),
|
|
14
|
+
params: {
|
|
15
|
+
...(referenceOptions?.filterParams || {}),
|
|
16
|
+
docTypes: referenceTo.map((schemaType) => schemaType)
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function updateItemInState(state, item) {
|
|
21
|
+
const newState = { ...state };
|
|
22
|
+
const publishedId = unprefixId(item._id);
|
|
23
|
+
newState[publishedId] = {
|
|
24
|
+
...(newState[publishedId] || {}),
|
|
25
|
+
[isDraft(item._id) ? 'draft' : 'published']: item
|
|
26
|
+
};
|
|
27
|
+
return newState;
|
|
28
|
+
}
|
|
29
|
+
function allItemsReducer(state, action) {
|
|
30
|
+
if (action.type === 'addOrEditItem' && action.item?._id) {
|
|
31
|
+
return updateItemInState(state, action.item);
|
|
32
|
+
}
|
|
33
|
+
if (action.type === 'removeItem') {
|
|
34
|
+
const publishedId = unprefixId(action.itemId);
|
|
35
|
+
return {
|
|
36
|
+
...state,
|
|
37
|
+
[publishedId]: isDraft(action.itemId)
|
|
38
|
+
? // If a draft, keep only published
|
|
39
|
+
{
|
|
40
|
+
published: state[publishedId]?.published
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
draft: state[publishedId]?.draft
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (action.type === 'setInitialData') {
|
|
48
|
+
return action.items.reduce(updateItemInState, {});
|
|
49
|
+
}
|
|
50
|
+
return state;
|
|
51
|
+
}
|
|
52
|
+
export default function useAllItems(options) {
|
|
53
|
+
const [status, setStatus] = React.useState('loading');
|
|
54
|
+
const [allItems, dispatch] = React.useReducer(allItemsReducer, {});
|
|
55
|
+
function handleListener(event) {
|
|
56
|
+
if (event.type !== 'mutation') {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (event.result) {
|
|
60
|
+
dispatch({ type: 'addOrEditItem', item: event.result });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
dispatch({ type: 'removeItem', itemId: event.documentId });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function handleFirstLoad(items) {
|
|
67
|
+
dispatch({ type: 'setInitialData', items });
|
|
68
|
+
setStatus('success');
|
|
69
|
+
}
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
const { filter, params } = getDeskFilter(options);
|
|
72
|
+
const query = `*[${filter}] {
|
|
73
|
+
_id,
|
|
74
|
+
_type,
|
|
75
|
+
_updatedAt,
|
|
76
|
+
}`;
|
|
77
|
+
client
|
|
78
|
+
.fetch(query, params)
|
|
79
|
+
.then(handleFirstLoad)
|
|
80
|
+
.catch(() => {
|
|
81
|
+
setStatus('error');
|
|
82
|
+
});
|
|
83
|
+
const listener = client.listen(query, params).subscribe(handleListener);
|
|
84
|
+
return () => {
|
|
85
|
+
listener.unsubscribe();
|
|
86
|
+
};
|
|
87
|
+
}, []);
|
|
88
|
+
return {
|
|
89
|
+
status,
|
|
90
|
+
allItems
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OnVisibilityToggleData, TreeItem } from 'react-sortable-tree';
|
|
2
|
+
import { AllItems, SanityTreeItem } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Enhances tree data with information on:
|
|
5
|
+
* - `expanded` - native property of react-sortable-tree to determine collapsing & expanding of a node's children
|
|
6
|
+
* - `draftId` & `publishedId` - refer to SanityTreeItem's type annotations
|
|
7
|
+
*
|
|
8
|
+
* Doesn't modify the main tree or has side-effects on data.
|
|
9
|
+
* Has the added benefit of being local to the user, so external changes won't affect local visibility.
|
|
10
|
+
*/
|
|
11
|
+
export default function useLocalTree({ tree, allItems }: {
|
|
12
|
+
tree: SanityTreeItem[];
|
|
13
|
+
allItems: AllItems;
|
|
14
|
+
}): {
|
|
15
|
+
handleVisibilityToggle: (data: OnVisibilityToggleData) => void;
|
|
16
|
+
localTree: TreeItem[];
|
|
17
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { dataToEditorTree } from './treeData';
|
|
3
|
+
/**
|
|
4
|
+
* Enhances tree data with information on:
|
|
5
|
+
* - `expanded` - native property of react-sortable-tree to determine collapsing & expanding of a node's children
|
|
6
|
+
* - `draftId` & `publishedId` - refer to SanityTreeItem's type annotations
|
|
7
|
+
*
|
|
8
|
+
* Doesn't modify the main tree or has side-effects on data.
|
|
9
|
+
* Has the added benefit of being local to the user, so external changes won't affect local visibility.
|
|
10
|
+
*/
|
|
11
|
+
export default function useLocalTree({ tree, allItems }) {
|
|
12
|
+
const [visibilityMap, setVisibilityMap] = React.useState({});
|
|
13
|
+
function handleVisibilityToggle(data) {
|
|
14
|
+
setVisibilityMap({
|
|
15
|
+
...visibilityMap,
|
|
16
|
+
[data.node._key]: data.expanded
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
localTree: dataToEditorTree({
|
|
21
|
+
tree,
|
|
22
|
+
allItems,
|
|
23
|
+
visibilityMap
|
|
24
|
+
}),
|
|
25
|
+
handleVisibilityToggle
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import useAllItems from './useAllItems';
|
|
3
|
+
import useTreeOperationsProvider from './useTreeOperationsProvider';
|
|
4
|
+
declare type ContextValue = ReturnType<typeof useTreeOperationsProvider> & {
|
|
5
|
+
allItemsStatus: ReturnType<typeof useAllItems>['status'];
|
|
6
|
+
};
|
|
7
|
+
export declare const TreeOperationsContext: React.Context<ContextValue>;
|
|
8
|
+
export default function useTreeOperations(): ContextValue;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
function placeholder() {
|
|
3
|
+
// no-op
|
|
4
|
+
}
|
|
5
|
+
export const TreeOperationsContext = React.createContext({
|
|
6
|
+
addItem: placeholder,
|
|
7
|
+
duplicateItem: placeholder,
|
|
8
|
+
removeItem: placeholder,
|
|
9
|
+
handleMovedNode: placeholder,
|
|
10
|
+
moveItemDown: placeholder,
|
|
11
|
+
moveItemUp: placeholder,
|
|
12
|
+
allItemsStatus: 'loading'
|
|
13
|
+
});
|
|
14
|
+
export default function useTreeOperations() {
|
|
15
|
+
return React.useContext(TreeOperationsContext);
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NodeRendererProps, TreeItem } from 'react-sortable-tree';
|
|
2
|
+
import { SanityTreeItem } from '../types';
|
|
3
|
+
import { HandleMovedNode } from './treePatches';
|
|
4
|
+
export default function useTreeOperationsProvider(props: {
|
|
5
|
+
patchPrefix?: string;
|
|
6
|
+
onChange: (patch: unknown) => void;
|
|
7
|
+
localTree: TreeItem[];
|
|
8
|
+
}): {
|
|
9
|
+
handleMovedNode: HandleMovedNode;
|
|
10
|
+
addItem: (item: SanityTreeItem) => void;
|
|
11
|
+
duplicateItem: (nodeProps: NodeRendererProps) => void;
|
|
12
|
+
removeItem: (nodeProps: NodeRendererProps) => void;
|
|
13
|
+
moveItemUp: (nodeProps: NodeRendererProps) => void;
|
|
14
|
+
moveItemDown: (nodeProps: NodeRendererProps) => void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as Patch from '@sanity/form-builder/lib/patch/patches';
|
|
2
|
+
import PatchEvent from '@sanity/form-builder/PatchEvent';
|
|
3
|
+
import { getAddItemPatch, getDuplicateItemPatch, getMovedNodePatch, getMoveItemPatch, getRemoveItemPatch } from './treePatches';
|
|
4
|
+
export default function useTreeOperationsProvider(props) {
|
|
5
|
+
const { localTree } = props;
|
|
6
|
+
function runPatches(patches) {
|
|
7
|
+
const finalPatches = [
|
|
8
|
+
// Ensure tree array exists before any operation
|
|
9
|
+
Patch.setIfMissing([]),
|
|
10
|
+
...(patches || [])
|
|
11
|
+
];
|
|
12
|
+
let patchEvent = PatchEvent.from(finalPatches);
|
|
13
|
+
if (props.patchPrefix) {
|
|
14
|
+
patchEvent = PatchEvent.from(finalPatches.map((patch) => Patch.prefixPath(patch, props.patchPrefix)));
|
|
15
|
+
}
|
|
16
|
+
props.onChange(patchEvent);
|
|
17
|
+
}
|
|
18
|
+
function handleMovedNode(data) {
|
|
19
|
+
runPatches(getMovedNodePatch(data));
|
|
20
|
+
}
|
|
21
|
+
function addItem(item) {
|
|
22
|
+
runPatches(getAddItemPatch(item));
|
|
23
|
+
}
|
|
24
|
+
function duplicateItem(nodeProps) {
|
|
25
|
+
runPatches(getDuplicateItemPatch(nodeProps));
|
|
26
|
+
}
|
|
27
|
+
function removeItem(nodeProps) {
|
|
28
|
+
runPatches(getRemoveItemPatch(nodeProps));
|
|
29
|
+
}
|
|
30
|
+
function moveItemUp(nodeProps) {
|
|
31
|
+
runPatches(getMoveItemPatch({
|
|
32
|
+
nodeProps,
|
|
33
|
+
localTree,
|
|
34
|
+
direction: 'up'
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function moveItemDown(nodeProps) {
|
|
38
|
+
runPatches(getMoveItemPatch({
|
|
39
|
+
nodeProps,
|
|
40
|
+
localTree,
|
|
41
|
+
direction: 'down'
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
handleMovedNode,
|
|
46
|
+
addItem,
|
|
47
|
+
removeItem,
|
|
48
|
+
moveItemUp,
|
|
49
|
+
moveItemDown,
|
|
50
|
+
duplicateItem
|
|
51
|
+
};
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sanity/hierarchical-document-list",
|
|
3
|
+
"version": "0.1.0-next.1",
|
|
4
|
+
"author": "Sanity <hello@sanity.io>",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/sanity-io/hierarchical-document-list.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/sanity-io/hierarchical-document-list/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/sanity-io/hierarchical-document-list#readme",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "tsc -w -d",
|
|
17
|
+
"build": "tsc -d",
|
|
18
|
+
"format": "prettier src -w",
|
|
19
|
+
"lint": "eslint src"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/assert": "^1.5.6",
|
|
23
|
+
"@types/react": "^17.0.38",
|
|
24
|
+
"@types/react-dom": "^17.0.11",
|
|
25
|
+
"@types/react-sortable-tree": "^0.3.14",
|
|
26
|
+
"@types/styled-components": "^5.1.21",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
|
28
|
+
"@typescript-eslint/parser": "^5.10.1",
|
|
29
|
+
"eslint": "^8.7.0",
|
|
30
|
+
"eslint-config-prettier": "^8.3.0",
|
|
31
|
+
"eslint-config-sanity": "^5.1.0",
|
|
32
|
+
"prettier": "^2.5.1",
|
|
33
|
+
"styled-components": "^5.3.3",
|
|
34
|
+
"typescript": "^4.5.5"
|
|
35
|
+
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@sanity/base": ">= 2.25.0",
|
|
39
|
+
"@sanity/color": "^2.1.6",
|
|
40
|
+
"@sanity/desk-tool": ">= 2.25.0",
|
|
41
|
+
"@sanity/form-builder": "^2.25.0",
|
|
42
|
+
"@sanity/icons": ">= 1.2.0",
|
|
43
|
+
"@sanity/ui": ">= 0.37.0",
|
|
44
|
+
"assert": "^2.0.0",
|
|
45
|
+
"react": "^17.0.2",
|
|
46
|
+
"react-dom": "^17.0.2",
|
|
47
|
+
"react-sortable-tree": "^2.8.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@sanity/base": ">= 2.25.0",
|
|
51
|
+
"@sanity/desk-tool": ">= 2.25.0",
|
|
52
|
+
"styled-components": ">= 5.2.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/sanity.json
ADDED
package/screenshot-1.jpg
ADDED
|
Binary file
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
6
|
+
"allowJs": false,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": false,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"moduleResolution": "Node",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
"outDir": "lib"
|
|
18
|
+
},
|
|
19
|
+
"include": ["./src"]
|
|
20
|
+
}
|