@sanity/hierarchical-document-list 0.1.0-next.1 → 0.1.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/README.md +91 -40
- package/lib/TreeDeskStructure.js +56 -28
- package/lib/TreeInputComponent.d.ts +3 -3
- package/lib/TreeInputComponent.js +14 -3
- package/lib/components/DeskWarning.d.ts +6 -0
- package/lib/components/DeskWarning.js +25 -0
- package/lib/components/DocumentInNode.d.ts +2 -2
- package/lib/components/DocumentInNode.js +29 -17
- package/lib/components/DocumentPreviewStatus.js +26 -7
- package/lib/components/NodeActions.d.ts +2 -2
- package/lib/components/NodeActions.js +24 -10
- package/lib/components/NodeContentRenderer.js +42 -40
- package/lib/components/PlaceholderDropzone.js +19 -8
- package/lib/components/TreeEditor.d.ts +2 -2
- package/lib/components/TreeEditor.js +29 -16
- package/lib/components/TreeEditorErrorBoundary.d.ts +1 -15
- package/lib/components/TreeEditorErrorBoundary.js +26 -28
- package/lib/components/TreeNodeRenderer.js +25 -12
- package/lib/components/TreeNodeRendererScaffold.js +12 -134
- package/lib/createDeskHierarchy.d.ts +4 -0
- package/lib/createDeskHierarchy.js +34 -19
- package/lib/createHierarchicalField.js +40 -19
- package/lib/{hiearchy.tree.d.ts → schemas/hierarchy.tree.d.ts} +0 -0
- package/lib/schemas/hierarchy.tree.js +31 -0
- package/lib/utils/flatDataToTree.d.ts +3 -3
- package/lib/utils/flatDataToTree.js +15 -6
- package/lib/utils/getAdjescentNodes.js +7 -6
- package/lib/utils/getCommonTreeProps.js +14 -2
- package/lib/utils/getTreeHeight.js +3 -3
- package/lib/utils/gradientPatchAdapter.js +16 -15
- package/lib/utils/idUtils.js +4 -2
- package/lib/utils/moveItemInArray.js +15 -5
- package/lib/utils/treeData.d.ts +7 -7
- package/lib/utils/treeData.js +65 -36
- package/lib/utils/treePatches.d.ts +10 -8
- package/lib/utils/treePatches.js +58 -50
- package/lib/utils/useAllItems.js +43 -41
- package/lib/utils/useLocalTree.d.ts +5 -5
- package/lib/utils/useLocalTree.js +21 -11
- package/lib/utils/useTreeOperations.js +1 -1
- package/lib/utils/useTreeOperationsProvider.d.ts +7 -8
- package/lib/utils/useTreeOperationsProvider.js +25 -17
- package/package.json +11 -9
- package/sanity.json +1 -1
- package/screenshot-1.jpg +0 -0
- package/tsconfig.json +1 -1
- package/lib/hiearchy.tree.js +0 -28
package/README.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# sanity-plugin-hierarchical-document-list
|
|
2
2
|
|
|
3
|
-
Plugin for
|
|
3
|
+
Plugin for visually organizing documents as hierarchies in the [Sanity studio](https://www.sanity.io/docs/sanity-studio). Applications include:
|
|
4
|
+
|
|
5
|
+
- Tables of content - such as a book's sections and chapters
|
|
6
|
+
- Navigational structure & menus - a website mega-menu with multiple levels, for example
|
|
7
|
+
- Taxonomy inheritance - "_Carbs_ is a parent of _Legumes_ which is a parent of _Beans_"
|
|
4
8
|
|
|
5
9
|

|
|
6
10
|
|
|
11
|
+
⚠️ **Compatibility:** This plugin requires Sanity Studio [version 2.25.0](https://github.com/sanity-io/sanity/releases/tag/v2.25.0) or higher.
|
|
12
|
+
|
|
13
|
+
If you're looking for a way to order documents on a flat list, refer to [@sanity/orderable-document-list](https://github.com/sanity-io/orderable-document-list).
|
|
14
|
+
|
|
7
15
|
## Getting started
|
|
8
16
|
|
|
9
17
|
```bash
|
|
@@ -11,9 +19,9 @@ Plugin for editing hierarchical references in the [Sanity studio](https://www.sa
|
|
|
11
19
|
sanity install @sanity/hierarchical-document-list
|
|
12
20
|
```
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
Once you've installed the plugin the next step is to add one or more hierarchy documents to your Structure Builder.
|
|
15
23
|
|
|
16
|
-
💡
|
|
24
|
+
💡 _To learn about custom desk structures, refer to the [Structure Builder docs](https://www.sanity.io/docs/overview-structure-builder)._
|
|
17
25
|
|
|
18
26
|
```js
|
|
19
27
|
// deskStructure.js
|
|
@@ -28,7 +36,7 @@ export default () => {
|
|
|
28
36
|
createDeskHierarchy({
|
|
29
37
|
title: 'Main table of contents',
|
|
30
38
|
|
|
31
|
-
// The
|
|
39
|
+
// The hierarchy will be stored in this document ID 👇
|
|
32
40
|
documentId: 'main-table-of-contents',
|
|
33
41
|
|
|
34
42
|
// Document types editors should be able to include in the hierarchy
|
|
@@ -48,13 +56,15 @@ export default () => {
|
|
|
48
56
|
|
|
49
57
|
## How it works
|
|
50
58
|
|
|
51
|
-
The hierarchical data is stored in
|
|
59
|
+
The hierarchical data is stored in a centralized document with the `documentId` of your choosing. As compared to storing parent/child relationships in each individual document in the hierarchy, this makes it easier to implement different hierarchies for the same content according to the context.
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
This approach also simplifies querying the full structure - as you'll see in [querying data](#querying-data) below.
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
Keep in mind that this specified **document is live-edited**, meaning it has no draft and every change by editors will directly affect its published version.
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
Instead of requiring editors to manually add items one-by-one, the plugin will create a [GROQ](https://www.sanity.io/docs/overview-groq) query that matches all documents with a `_type` in the `referenceTo` option you specify, that also match the optional `referenceOptions.filter`. From these documents, editors are able to drag, nest and re-order them at will from the "Add more items" list.
|
|
66
|
+
|
|
67
|
+
If a document in the tree doesn't match the filters set, it'll keep existing in the data. This can happen if the document has a new, unfitting value, the configuration changed or it was deleted. Although the tree will still be publishable, editors will get a warning and won't be able to drag these faulty entries around.
|
|
58
68
|
|
|
59
69
|
## Querying data
|
|
60
70
|
|
|
@@ -65,20 +75,26 @@ The plugin stores flat arrays which represent your hierarchical data through `pa
|
|
|
65
75
|
{
|
|
66
76
|
"_key": "741b9edde2ba",
|
|
67
77
|
"_type": "hierarchy.node",
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
"value": {
|
|
79
|
+
"reference": {
|
|
80
|
+
"_ref": "75c47994-e6bb-487a-b8c9-b283f2436031",
|
|
81
|
+
"_type": "reference",
|
|
82
|
+
"_weak": true // This plugin includes weak references by default
|
|
83
|
+
},
|
|
84
|
+
"docType": "docs.article"
|
|
72
85
|
}
|
|
73
86
|
// no `parent`, this item is top-level
|
|
74
87
|
},
|
|
75
88
|
{
|
|
76
89
|
"_key": "f92eaeec96f7",
|
|
77
90
|
"_type": "hierarchy.node",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
"value": {
|
|
92
|
+
"reference": {
|
|
93
|
+
"_ref": "7ad60a02-5d6e-47d8-92e2-6724cc130058",
|
|
94
|
+
"_type": "reference",
|
|
95
|
+
"_weak": true
|
|
96
|
+
},
|
|
97
|
+
"docType": "site.post"
|
|
82
98
|
},
|
|
83
99
|
// The `parent` property points to the _key of the parent node where this one is nested
|
|
84
100
|
"parent": "741b9edde2ba"
|
|
@@ -98,10 +114,12 @@ From the the above, we know how to expand referenced documents in GROQ:
|
|
|
98
114
|
parent,
|
|
99
115
|
|
|
100
116
|
// "Expand" the reference to the node
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
value {
|
|
118
|
+
reference->{
|
|
119
|
+
// Get whatever property you need from your documents
|
|
120
|
+
title,
|
|
121
|
+
slug,
|
|
122
|
+
}
|
|
105
123
|
}
|
|
106
124
|
}
|
|
107
125
|
}
|
|
@@ -119,13 +137,13 @@ Find a given document in a hierarchy and get its parent - useful for rendering b
|
|
|
119
137
|
// From the tree, get the 1st node that references a given document _id
|
|
120
138
|
tree[node._ref == "my-book-section"][0] {
|
|
121
139
|
_key,
|
|
122
|
-
"section": node->{
|
|
140
|
+
"section": node.reference->{
|
|
123
141
|
title,
|
|
124
142
|
},
|
|
125
143
|
// Then, from the tree get the element matching the `parent` _key of the found node
|
|
126
144
|
"parentChapter": ^.tree[_key == ^.parent][0]{
|
|
127
145
|
_key,
|
|
128
|
-
"chapter": node->{
|
|
146
|
+
"chapter": node.reference->{
|
|
129
147
|
title,
|
|
130
148
|
contributors,
|
|
131
149
|
}
|
|
@@ -148,14 +166,47 @@ const hierarchyDocument = await client.fetch(`*[_id == "book-v3-review-a"][0]{
|
|
|
148
166
|
// Make sure you include each item's _key and parent
|
|
149
167
|
_key,
|
|
150
168
|
parent,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
169
|
+
value {
|
|
170
|
+
reference->{
|
|
171
|
+
title,
|
|
172
|
+
slug,
|
|
173
|
+
content,
|
|
174
|
+
}
|
|
155
175
|
}
|
|
156
176
|
}
|
|
157
177
|
}`)
|
|
158
178
|
const tree = flatDataToTree(data.tree)
|
|
179
|
+
|
|
180
|
+
/* Results in a recursively nested structure. Using the example data above:
|
|
181
|
+
{
|
|
182
|
+
"_key": "741b9edde2ba",
|
|
183
|
+
"_type": "hierarchy.node",
|
|
184
|
+
"value": {
|
|
185
|
+
"reference": {
|
|
186
|
+
"_ref": "75c47994-e6bb-487a-b8c9-b283f2436031",
|
|
187
|
+
"_type": "reference",
|
|
188
|
+
"_weak": true
|
|
189
|
+
},
|
|
190
|
+
"docType": "docs.article"
|
|
191
|
+
},
|
|
192
|
+
"parent": null,
|
|
193
|
+
"children": [
|
|
194
|
+
{
|
|
195
|
+
"_key": "f92eaeec96f7",
|
|
196
|
+
"_type": "hierarchy.node",
|
|
197
|
+
"value": {
|
|
198
|
+
"reference": {
|
|
199
|
+
"_ref": "7ad60a02-5d6e-47d8-92e2-6724cc130058",
|
|
200
|
+
"_type": "reference",
|
|
201
|
+
"_weak": true
|
|
202
|
+
},
|
|
203
|
+
"docType": "site.post"
|
|
204
|
+
},
|
|
205
|
+
"parent": "741b9edde2ba"
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
*/
|
|
159
210
|
```
|
|
160
211
|
|
|
161
212
|
After the transformation above, nodes with nested entries will include a `children` array. This data structure is recursive.
|
|
@@ -173,9 +224,10 @@ export default {
|
|
|
173
224
|
name: 'myCustomHierarchicalType',
|
|
174
225
|
title: 'Custom document type for holding hierarchical data',
|
|
175
226
|
type: 'document',
|
|
227
|
+
liveEdit: true, // 👉 Important: set liveEdit to `true` to ensure the UI works properly
|
|
176
228
|
fields: [
|
|
177
229
|
createHierarchicalField({
|
|
178
|
-
name: '
|
|
230
|
+
name: 'customTreeDataKey', // key for the tree field in the document
|
|
179
231
|
title: 'Custom tree',
|
|
180
232
|
options: {
|
|
181
233
|
referenceTo: ['category']
|
|
@@ -185,26 +237,25 @@ export default {
|
|
|
185
237
|
}
|
|
186
238
|
```
|
|
187
239
|
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
📌 **Note:** you can also use the method above to add hierarchies inside the schema of documents and objects. We're considering adapting this input to support any type of nest-able data, not only references. Until then, avoid `createHierarchicalField` for fields in nested schemas as, in these contexts, it lacks the necessary affordances for a good editing experience.
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
240
|
Then, in your desk structure where you added the hierarchical document(s), include the right `documentType` and `fieldKeyInDocument` properties:
|
|
195
241
|
|
|
196
242
|
```js
|
|
197
243
|
createDeskHierarchy({
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
// Include whatever values you defined in your schema
|
|
203
|
-
documentType: 'myCustomHierarchicalType',
|
|
204
|
-
fieldKeyInDocument: 'treeData'
|
|
244
|
+
// Include whatever values you defined in your schema in the step above
|
|
245
|
+
documentType: 'myCustomHierarchicalType', // the name of your document type
|
|
246
|
+
fieldKeyInDocument: 'customTreeDataKey' // the name of the hierarchical field
|
|
247
|
+
// ...
|
|
205
248
|
})
|
|
206
249
|
```
|
|
207
250
|
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
📌 **Note:** you can also use the method above to add hierarchies inside the schema of documents and objects, which would be editable outside the desk structure.
|
|
254
|
+
|
|
255
|
+
We're considering adapting this input to support any type of nest-able data, not only references. Until then, avoid `createHierarchicalField` for fields in nested schemas as, in these contexts, it lacks the necessary affordances for a good editing experience.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
208
259
|
## License
|
|
209
260
|
|
|
210
261
|
MIT-licensed. See LICENSE.
|
package/lib/TreeDeskStructure.js
CHANGED
|
@@ -1,43 +1,71 @@
|
|
|
1
|
-
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
13
|
+
var t = {};
|
|
14
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
15
|
+
t[p] = s[p];
|
|
16
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
17
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
18
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
19
|
+
t[p[i]] = s[p[i]];
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
};
|
|
23
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
24
|
import { PublishIcon } from '@sanity/icons';
|
|
3
25
|
import { useDocumentOperation, useEditState } from '@sanity/react-hooks';
|
|
4
|
-
import { Box, Button,
|
|
26
|
+
import { Box, Button, Flex, Spinner, useToast } from '@sanity/ui';
|
|
5
27
|
import React from 'react';
|
|
28
|
+
import DeskWarning from './components/DeskWarning';
|
|
6
29
|
import TreeEditor from './components/TreeEditor';
|
|
7
30
|
import { toGradient } from './utils/gradientPatchAdapter';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!patch
|
|
31
|
+
var DEFAULT_TREE_FIELD_KEY = 'tree';
|
|
32
|
+
var DEFAULT_TREE_DOC_TYPE = 'hierarchy.tree';
|
|
33
|
+
var TreeDeskStructure = function (props) {
|
|
34
|
+
var treeDocType = props.options.documentType || DEFAULT_TREE_DOC_TYPE;
|
|
35
|
+
var treeFieldKey = props.options.fieldKeyInDocument || DEFAULT_TREE_FIELD_KEY;
|
|
36
|
+
var _a = useEditState(props.options.documentId, treeDocType), published = _a.published, draft = _a.draft, liveEdit = _a.liveEdit;
|
|
37
|
+
var _b = useDocumentOperation(props.options.documentId, treeDocType), patch = _b.patch, ops = __rest(_b, ["patch"]);
|
|
38
|
+
var push = useToast().push;
|
|
39
|
+
var treeValue = ((published === null || published === void 0 ? void 0 : published[treeFieldKey]) || []);
|
|
40
|
+
var handleChange = React.useCallback(function (patchEvent) {
|
|
41
|
+
if (!(patch === null || patch === void 0 ? void 0 : patch.execute)) {
|
|
19
42
|
return;
|
|
20
43
|
}
|
|
21
44
|
patch.execute(toGradient(patchEvent.patches));
|
|
22
45
|
}, [patch]);
|
|
23
|
-
React.useEffect(()
|
|
24
|
-
|
|
46
|
+
React.useEffect(function () {
|
|
47
|
+
var _a;
|
|
48
|
+
if (!(published === null || published === void 0 ? void 0 : published._id) && (patch === null || patch === void 0 ? void 0 : patch.execute) && !(patch === null || patch === void 0 ? void 0 : patch.disabled)) {
|
|
25
49
|
// If no published document, create it
|
|
26
|
-
patch.execute([{ setIfMissing: { [treeFieldKey]
|
|
50
|
+
patch.execute([{ setIfMissing: (_a = {}, _a[treeFieldKey] = [], _a) }]);
|
|
27
51
|
}
|
|
28
|
-
}, [published
|
|
29
|
-
if (
|
|
30
|
-
return (_jsx(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
}, [published === null || published === void 0 ? void 0 : published._id, patch]);
|
|
53
|
+
if (!liveEdit) {
|
|
54
|
+
return (_jsx(DeskWarning, { title: "Invalid configuration", subtitle: "The `documentType` passed to `createDeskHiearchy` isn't live editable. \\nTo continue using this plugin, add `liveEdit: true` to your custom schema type or unset `documentType` in your hierarchy configuration." }, void 0));
|
|
55
|
+
}
|
|
56
|
+
if (draft === null || draft === void 0 ? void 0 : draft._id) {
|
|
57
|
+
return (_jsx(DeskWarning, __assign({ title: "This hierarchy tree contains a draft", subtitle: "Click on the button below to publish your draft in order to continue editing the live\r\n published document." }, { children: _jsx(Button, { fontSize: 1, tone: "positive", text: "Publish draft", icon: PublishIcon, onClick: function () {
|
|
58
|
+
var _a, _b;
|
|
59
|
+
(_b = (_a = ops.publish) === null || _a === void 0 ? void 0 : _a.execute) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
60
|
+
push({
|
|
61
|
+
status: 'info',
|
|
62
|
+
title: 'Publishing draft...'
|
|
63
|
+
});
|
|
64
|
+
} }, void 0) }), void 0));
|
|
37
65
|
}
|
|
38
|
-
if (!published
|
|
39
|
-
return (_jsx(Flex, { padding: 5, align: 'center', justify: 'center', height: 'fill', children: _jsx(Spinner, { width: 4, muted: true }, void 0) }, void 0));
|
|
66
|
+
if (!(published === null || published === void 0 ? void 0 : published._id)) {
|
|
67
|
+
return (_jsx(Flex, __assign({ padding: 5, align: 'center', justify: 'center', height: 'fill' }, { children: _jsx(Spinner, { width: 4, muted: true }, void 0) }), void 0));
|
|
40
68
|
}
|
|
41
|
-
return (_jsx(Box, { paddingBottom: 5, paddingRight: 2, children: _jsx(TreeEditor, { options: props.options, tree: treeValue, onChange: handleChange, patchPrefix: treeFieldKey }, void 0) }, void 0));
|
|
69
|
+
return (_jsx(Box, __assign({ paddingBottom: 5, paddingRight: 2 }, { children: _jsx(TreeEditor, { options: props.options, tree: treeValue, onChange: handleChange, patchPrefix: treeFieldKey }, void 0) }), void 0));
|
|
42
70
|
};
|
|
43
71
|
export default TreeDeskStructure;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { FormFieldPresence } from '@sanity/base/presence';
|
|
2
2
|
import { Marker, Path } from '@sanity/types';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { StoredTreeItem, TreeFieldSchema } from './types';
|
|
5
5
|
export interface TreeInputComponentProps {
|
|
6
6
|
type: TreeFieldSchema;
|
|
7
|
-
value:
|
|
8
|
-
compareValue:
|
|
7
|
+
value: StoredTreeItem[];
|
|
8
|
+
compareValue: StoredTreeItem[];
|
|
9
9
|
markers: Marker[];
|
|
10
10
|
level: number;
|
|
11
11
|
onChange: (event: unknown) => void;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
13
|
import React from 'react';
|
|
3
14
|
import { FormField } from '@sanity/base/components';
|
|
4
15
|
import TreeEditor from './components/TreeEditor';
|
|
5
|
-
|
|
6
|
-
return (_jsx(FormField, { description: props.type.description, title: props.type.title, __unstable_markers: props.markers, __unstable_presence: props.presence,
|
|
16
|
+
var TreeInputComponent = React.forwardRef(function (props, _ref) {
|
|
17
|
+
return (_jsx(FormField, __assign({ description: props.type.description, title: props.type.title, __unstable_markers: props.markers, __unstable_presence: props.presence,
|
|
7
18
|
// @ts-expect-error FormField's TS definitions are off - it doesn't include compareValue
|
|
8
|
-
compareValue: props.compareValue, children: _jsx(TreeEditor, { options: props.type.options, tree: props.value || [], onChange: props.onChange }, void 0) }, void 0));
|
|
19
|
+
compareValue: props.compareValue }, { children: _jsx(TreeEditor, { options: props.type.options, tree: props.value || [], onChange: props.onChange }, void 0) }), void 0));
|
|
9
20
|
});
|
|
10
21
|
export default TreeInputComponent;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Box, Card, Container, Heading, Stack, Text } from '@sanity/ui';
|
|
14
|
+
import React from 'react';
|
|
15
|
+
// React component that wraps text between two delimiters in a <pre> tag
|
|
16
|
+
var WrapCodeBlocks = function (_a) {
|
|
17
|
+
var text = _a.text;
|
|
18
|
+
return (_jsx(_Fragment, { children: text.split('`').map(function (part, i) { return (_jsx(React.Fragment, { children: i % 2 === 0 ? part : _jsx("code", { children: part }, void 0) }, i)); }) }, void 0));
|
|
19
|
+
};
|
|
20
|
+
var DeskWarning = function (_a) {
|
|
21
|
+
var subtitle = _a.subtitle, title = _a.title, children = _a.children;
|
|
22
|
+
return (_jsx(Container, __assign({ padding: 5, style: { maxWidth: '25rem' }, sizing: 'content' }, { children: _jsx(Card, __assign({ padding: 4, border: true, radius: 2, width: 0, tone: "caution" }, { children: _jsxs(Stack, __assign({ space: 3 }, { children: [_jsx(Heading, __assign({ size: 1 }, { children: title }), void 0), subtitle &&
|
|
23
|
+
subtitle.split('\\n').map(function (line) { return (_jsx(Text, __assign({ size: 1 }, { children: _jsx(WrapCodeBlocks, { text: line }, void 0) }), void 0)); }), children && _jsx(Box, __assign({ marginTop: 2 }, { children: children }), void 0)] }), void 0) }), void 0) }), void 0));
|
|
24
|
+
};
|
|
25
|
+
export default DeskWarning;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { LocalTreeItem } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Renders a preview for each referenced document.
|
|
5
5
|
* Nested inside TreeNode.tsx
|
|
6
6
|
*/
|
|
7
7
|
declare const DocumentInNode: React.FC<{
|
|
8
|
-
item:
|
|
8
|
+
item: LocalTreeItem;
|
|
9
9
|
action?: React.ReactNode;
|
|
10
10
|
}>;
|
|
11
11
|
export default DocumentInNode;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { TextWithTone } from '@sanity/base/components';
|
|
3
14
|
import { usePaneRouter } from '@sanity/desk-tool';
|
|
@@ -12,35 +23,36 @@ import DocumentPreviewStatus from './DocumentPreviewStatus';
|
|
|
12
23
|
* Renders a preview for each referenced document.
|
|
13
24
|
* Nested inside TreeNode.tsx
|
|
14
25
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
var DocumentInNode = function (props) {
|
|
27
|
+
var _a = props.item, _b = _a.value, _c = _b === void 0 ? {} : _b, reference = _c.reference, docType = _c.docType, draftId = _a.draftId, publishedId = _a.publishedId;
|
|
28
|
+
var _d = usePaneRouter(), routerPanesState = _d.routerPanesState, ChildLink = _d.ChildLink;
|
|
29
|
+
var allItemsStatus = useTreeOperations().allItemsStatus;
|
|
30
|
+
var isActive = React.useMemo(function () {
|
|
20
31
|
// If some pane is active with the current document `_id`, it's active
|
|
21
|
-
return routerPanesState.some((pane)
|
|
32
|
+
return routerPanesState.some(function (pane) { return pane.some(function (group) { return group.id === (reference === null || reference === void 0 ? void 0 : reference._ref); }); });
|
|
22
33
|
}, [routerPanesState]);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
var schemaType = React.useMemo(function () { return schema.get(docType); }, [docType]);
|
|
35
|
+
var LinkComponent = React.useMemo(function () {
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
37
|
+
return React.forwardRef(function (linkProps, ref) { return (_jsx(ChildLink, __assign({}, linkProps, { childId: reference === null || reference === void 0 ? void 0 : reference._ref, ref: ref, childParameters: {
|
|
38
|
+
type: docType
|
|
39
|
+
} }), void 0)); });
|
|
40
|
+
}, [ChildLink, reference === null || reference === void 0 ? void 0 : reference._ref]);
|
|
41
|
+
if (!(reference === null || reference === void 0 ? void 0 : reference._ref)) {
|
|
30
42
|
return null;
|
|
31
43
|
}
|
|
32
|
-
return (_jsxs(Flex, { gap: 2, align: "center", style: { flex: 1 }, children: [publishedId || allItemsStatus !== 'success' ? (
|
|
44
|
+
return (_jsxs(Flex, __assign({ gap: 2, align: "center", style: { flex: 1 } }, { children: [publishedId || allItemsStatus !== 'success' ? (
|
|
33
45
|
/* Card loosely copied from @sanity/desk-tool's PaneItem.tsx */
|
|
34
|
-
_jsx(Card, { __unstable_focusRing: true, as: LinkComponent, tone: isActive ? 'primary' : 'default', padding: 1, radius: 2, flex: 1, "data-as": "a", "data-ui": "PaneItem", children: _jsx(Preview, { layout: "default", type: schemaType, value: { _ref: draftId || reference
|
|
46
|
+
_jsx(Card, __assign({ __unstable_focusRing: true, as: LinkComponent, tone: isActive ? 'primary' : 'default', padding: 1, radius: 2, flex: 1, "data-as": "a", "data-ui": "PaneItem" }, { children: _jsx(Preview, { layout: "default", type: schemaType, value: { _ref: draftId || (reference === null || reference === void 0 ? void 0 : reference._ref) }, status: _jsx(DocumentPreviewStatus, { draft: draftId
|
|
35
47
|
? {
|
|
36
48
|
_id: draftId,
|
|
37
49
|
_type: docType,
|
|
38
50
|
_updatedAt: props.item.draftUpdatedAt
|
|
39
51
|
}
|
|
40
52
|
: undefined, published: {
|
|
41
|
-
_id: reference
|
|
53
|
+
_id: reference === null || reference === void 0 ? void 0 : reference._ref,
|
|
42
54
|
_type: docType,
|
|
43
55
|
_updatedAt: props.item.publishedUpdatedAt
|
|
44
|
-
} }, void 0) }, void 0) }, void 0)) : (_jsx(Card, { padding: 3, radius: 1, flex: 1, children: _jsxs(Flex, { align: "center", children: [_jsx(Text, { size: 2, muted: true, style: { flex: 1 }, children: "Invalid document" }, void 0), _jsx(Tooltip, { placement: "left", portal: true, content: _jsx(Box, { padding: 3, children: _jsxs(Flex, { align: "flex-start", gap: 3, children: [_jsx(TextWithTone, { tone: "default", size: 3, children: _jsx(HelpCircleIcon, {}, void 0) }, void 0), _jsxs(Stack, { space: 3, children: [_jsx(Text, { as: "h2", size: 1, weight: "semibold", children: "This document is not valid" }, void 0), _jsxs(Text, { size: 1, children: ["ID: ", reference
|
|
56
|
+
} }, void 0) }, void 0) }), void 0)) : (_jsx(Card, __assign({ padding: 3, radius: 1, flex: 1 }, { children: _jsxs(Flex, __assign({ align: "center" }, { children: [_jsx(Text, __assign({ size: 2, muted: true, style: { flex: 1 } }, { children: "Invalid document" }), void 0), _jsx(Tooltip, __assign({ placement: "left", portal: true, content: _jsx(Box, __assign({ padding: 3 }, { children: _jsxs(Flex, __assign({ align: "flex-start", gap: 3 }, { children: [_jsx(TextWithTone, __assign({ tone: "default", size: 3 }, { children: _jsx(HelpCircleIcon, {}, void 0) }), void 0), _jsxs(Stack, __assign({ space: 3 }, { children: [_jsx(Text, __assign({ as: "h2", size: 1, weight: "semibold" }, { children: "This document is not valid" }), void 0), _jsxs(Text, __assign({ size: 1 }, { children: ["ID: ", reference === null || reference === void 0 ? void 0 : reference._ref] }), void 0)] }), void 0)] }), void 0) }), void 0) }, { children: _jsx(TextWithTone, __assign({ tone: "default", size: 2 }, { children: _jsx(HelpCircleIcon, {}, void 0) }), void 0) }), void 0)] }), void 0) }), void 0)), props.action] }), void 0));
|
|
45
57
|
};
|
|
46
58
|
export default DocumentInNode;
|
|
@@ -1,16 +1,35 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
13
|
import { TextWithTone } from '@sanity/base/components';
|
|
3
14
|
import { useTimeAgo } from '@sanity/base/hooks';
|
|
4
15
|
import { EditIcon, PublishIcon } from '@sanity/icons';
|
|
5
16
|
import { Box, Inline, Text, Tooltip } from '@sanity/ui';
|
|
6
|
-
export function TimeAgo(
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
export function TimeAgo(_a) {
|
|
18
|
+
var time = _a.time;
|
|
19
|
+
var timeAgo = useTimeAgo(time);
|
|
20
|
+
return (_jsxs("span", __assign({ title: timeAgo }, { children: [timeAgo, timeAgo.toLowerCase().trim().startsWith('just now') ? '' : ' ago'] }), void 0));
|
|
9
21
|
}
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
var PublishedStatus = function (_a) {
|
|
23
|
+
var document = _a.document;
|
|
24
|
+
return (_jsx(Tooltip, __assign({ portal: true, content: _jsx(Box, __assign({ padding: 2 }, { children: _jsx(Text, __assign({ size: 1 }, { children: document ? (_jsxs(_Fragment, { children: ["Published ", document._updatedAt && _jsx(TimeAgo, { time: document._updatedAt }, void 0)] }, void 0)) : (_jsx(_Fragment, { children: "Not published" }, void 0)) }), void 0) }), void 0) }, { children: _jsx(TextWithTone, __assign({ tone: "positive", dimmed: !document, muted: !document, size: 1 }, { children: _jsx(PublishIcon, {}, void 0) }), void 0) }), void 0));
|
|
25
|
+
};
|
|
26
|
+
var DraftStatus = function (_a) {
|
|
27
|
+
var document = _a.document;
|
|
28
|
+
return (_jsx(Tooltip, __assign({ portal: true, content: _jsx(Box, __assign({ padding: 2 }, { children: _jsx(Text, __assign({ size: 1 }, { children: document ? (_jsxs(_Fragment, { children: ["Edited ", (document === null || document === void 0 ? void 0 : document._updatedAt) && _jsx(TimeAgo, { time: document === null || document === void 0 ? void 0 : document._updatedAt }, void 0)] }, void 0)) : (_jsx(_Fragment, { children: "No unpublished edits" }, void 0)) }), void 0) }), void 0) }, { children: _jsx(TextWithTone, __assign({ tone: "caution", dimmed: !document, muted: !document, size: 1 }, { children: _jsx(EditIcon, {}, void 0) }), void 0) }), void 0));
|
|
29
|
+
};
|
|
12
30
|
// Adapted from @sanity\desk-tool\src\components\paneItem\helpers.tsx
|
|
13
|
-
|
|
14
|
-
|
|
31
|
+
var DocumentPreviewStatus = function (_a) {
|
|
32
|
+
var draft = _a.draft, published = _a.published;
|
|
33
|
+
return (_jsxs(Inline, __assign({ space: 4 }, { children: [_jsx(PublishedStatus, { document: published }, void 0), _jsx(DraftStatus, { document: draft }, void 0)] }), void 0));
|
|
15
34
|
};
|
|
16
35
|
export default DocumentPreviewStatus;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { NodeProps } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Applicable only to nodes inside the main tree.
|
|
5
5
|
* Unadded items have their actions defined in TreeEditor.
|
|
6
6
|
*/
|
|
7
7
|
declare const NodeActions: React.FC<{
|
|
8
|
-
nodeProps:
|
|
8
|
+
nodeProps: NodeProps;
|
|
9
9
|
}>;
|
|
10
10
|
export default NodeActions;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { IntentLink } from '@sanity/base/components';
|
|
3
14
|
import { CopyIcon, EllipsisVerticalIcon, LaunchIcon, RemoveCircleIcon } from '@sanity/icons';
|
|
@@ -8,16 +19,19 @@ import useTreeOperations from '../utils/useTreeOperations';
|
|
|
8
19
|
* Applicable only to nodes inside the main tree.
|
|
9
20
|
* Unadded items have their actions defined in TreeEditor.
|
|
10
21
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
var NodeActions = function (_a) {
|
|
23
|
+
var nodeProps = _a.nodeProps;
|
|
24
|
+
var operations = useTreeOperations();
|
|
25
|
+
var node = nodeProps.node;
|
|
26
|
+
var _b = (node === null || node === void 0 ? void 0 : node.value) || {}, reference = _b.reference, docType = _b.docType;
|
|
14
27
|
// Adapted from @sanity\form-builder\src\inputs\ReferenceInput\ArrayItemReferenceInput.tsx
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
var OpenLink = React.useMemo(function () {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
30
|
+
return React.forwardRef(function OpenLinkInner(restProps, _ref) {
|
|
31
|
+
return (_jsx(IntentLink, __assign({}, restProps, { intent: "edit", params: { id: reference === null || reference === void 0 ? void 0 : reference._ref, type: docType }, target: "_blank", rel: "noopener noreferrer", ref: _ref }), void 0));
|
|
32
|
+
});
|
|
33
|
+
}, [reference === null || reference === void 0 ? void 0 : reference._ref, docType]);
|
|
34
|
+
var isValid = !!node.publishedId;
|
|
35
|
+
return (_jsx(MenuButton, { button: _jsx(Button, { padding: 2, mode: "bleed", icon: EllipsisVerticalIcon }, void 0), id: "hiearchical-doc-list--".concat(node._key, "-menuButton"), menu: _jsxs(Menu, { children: [_jsx(MenuItem, { text: "Remove from list", tone: "critical", icon: RemoveCircleIcon, onClick: function () { return operations.removeItem(nodeProps); } }, void 0), _jsx(MenuItem, { text: "Duplicate item", icon: CopyIcon, disabled: !isValid, onClick: function () { return operations.duplicateItem(nodeProps); } }, void 0), _jsx(MenuDivider, {}, void 0), _jsx(MenuItem, { text: "Open in new tab", icon: LaunchIcon, disabled: !isValid, as: OpenLink, "data-as": "a" }, void 0)] }, void 0), placement: "right", popover: { portal: true, tone: 'default' } }, void 0));
|
|
22
36
|
};
|
|
23
37
|
export default NodeActions;
|