sanity-plugin-taxonomy-manager 4.7.0 → 4.7.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-taxonomy-manager",
3
- "version": "4.7.0",
3
+ "version": "4.7.2",
4
4
  "description": "Create and manage SKOS compliant taxonomies, thesauri, and classification schemes in Sanity Studio.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -15,7 +15,7 @@ import {
15
15
  } from '@sanity/ui'
16
16
  import {useState, useEffect, useCallback} from 'react'
17
17
  import type {ArrayFieldProps, ObjectOptions} from 'sanity'
18
- import {useClient, useFormValue, isVersionId, isDraftId, usePerspective} from 'sanity'
18
+ import {FormField, useClient, useFormValue, isVersionId, isDraftId, usePerspective} from 'sanity'
19
19
 
20
20
  import {useEmbeddingsRecs} from '../../hooks'
21
21
  import NodeTree from '../../static/NodeTree'
@@ -44,14 +44,142 @@ type ArrayHierarchyInputProps = ArrayFieldProps & {
44
44
  type FilterResult = Awaited<ReturnType<ReferenceOptions['filter']>>
45
45
 
46
46
  /**
47
- * #### Hierarchy View Input Component
48
- * Allows Studio users to browse and select taxonomy
49
- * terms from a hierarchical tree structure. It is designed to be
50
- * used as an input for taxonomy array fields in Sanity Studio.
47
+ * Input component that replaces Sanity's default array field input with a
48
+ * hierarchical taxonomy tree browser. Studio users can browse taxonomy terms
49
+ * organized in their scheme hierarchy and select terms to add to the array field.
51
50
  *
52
- * Hierarchy view must be used in conjunction with the Taxonomy Manager
53
- * plugin `schemeFilter` or `branchFilter` options.
51
+ * @remarks
52
+ * - Must be used with a `schemeFilter` or `branchFilter` helper in the field's
53
+ * `options.filter`. Rendering without a filter will display a configuration warning.
54
+ * - Supports only **single-schema arrays** (i.e., `of: [{type: 'reference'}]`). Arrays
55
+ * with multiple schema types will render a warning and fall back to the default input.
56
+ * - Taxonomy selection is disabled when viewing the published perspective.
57
+ * - When `browseOnly` is set in the filter configuration, Sanity's default search input
58
+ * is suppressed and only the tree browser is available for term selection.
59
+ * - When `expanded` is set in the filter configuration, the hierarchy tree loads open
60
+ * by default instead of collapsed.
54
61
  *
62
+ * @param props - Standard Sanity `ArrayFieldProps` extended with an optional
63
+ * `embeddingsIndex` configuration object.
64
+ * @param props.embeddingsIndex - Optional configuration for AI-assisted term
65
+ * recommendations via a Sanity Embeddings Index. When provided, opening the tree
66
+ * browser queries the specified index and annotates matching taxonomy terms with
67
+ * a relevance score to help authors identify the most appropriate terms.
68
+ * @param props.embeddingsIndex.indexName - The name of the Sanity Embeddings Index
69
+ * to query. Must be an index that includes `skosConcept` documents.
70
+ * @param props.embeddingsIndex.fieldReferences - An array of field names from the
71
+ * current document whose values are concatenated and sent as the embeddings search
72
+ * query. All listed fields must contain values when the tree browser is opened;
73
+ * empty fields will display an error message in the tree view rather than scores.
74
+ * @param props.embeddingsIndex.maxResults - Maximum number of semantically matching
75
+ * terms to return from the embeddings index. Defaults to `3`.
76
+ *
77
+ * @example
78
+ * Basic usage with a scheme filter:
79
+ * ```js
80
+ * import {ArrayHierarchyInput, schemeFilter} from 'sanity-plugin-taxonomy-manager'
81
+ *
82
+ * defineField({
83
+ * name: 'categories',
84
+ * title: 'Categories',
85
+ * type: 'array',
86
+ * of: [
87
+ * {
88
+ * type: 'reference',
89
+ * to: {type: 'skosConcept'},
90
+ * options: {
91
+ * filter: schemeFilter({schemeId: 'f3deba'}),
92
+ * disableNew: true,
93
+ * },
94
+ * },
95
+ * ],
96
+ * components: {field: ArrayHierarchyInput},
97
+ * })
98
+ * ```
99
+ *
100
+ * @example
101
+ * Branch filter with tree expanded by default:
102
+ * ```js
103
+ * import {ArrayHierarchyInput, branchFilter} from 'sanity-plugin-taxonomy-manager'
104
+ *
105
+ * defineField({
106
+ * name: 'habitats',
107
+ * title: 'Habitats',
108
+ * type: 'array',
109
+ * of: [
110
+ * {
111
+ * type: 'reference',
112
+ * to: {type: 'skosConcept'},
113
+ * options: {
114
+ * filter: branchFilter({schemeId: 'cf76c1', branchId: '1e5e6c', expanded: true}),
115
+ * disableNew: true,
116
+ * },
117
+ * },
118
+ * ],
119
+ * components: {field: ArrayHierarchyInput},
120
+ * })
121
+ * ```
122
+ *
123
+ * @example
124
+ * Browse-only mode (suppresses the default Sanity search input):
125
+ * ```js
126
+ * import {ArrayHierarchyInput, branchFilter} from 'sanity-plugin-taxonomy-manager'
127
+ *
128
+ * defineField({
129
+ * name: 'habitats',
130
+ * title: 'Habitats',
131
+ * type: 'array',
132
+ * of: [
133
+ * {
134
+ * type: 'reference',
135
+ * to: {type: 'skosConcept'},
136
+ * options: {
137
+ * filter: branchFilter({schemeId: 'cf76c1', branchId: '1e5e6c', browseOnly: true}),
138
+ * disableNew: true,
139
+ * },
140
+ * },
141
+ * ],
142
+ * components: {field: ArrayHierarchyInput},
143
+ * })
144
+ * ```
145
+ *
146
+ * @example
147
+ * AI-assisted recommendations via an embeddings index:
148
+ * ```jsx
149
+ * import {ArrayHierarchyInput, branchFilter} from 'sanity-plugin-taxonomy-manager'
150
+ *
151
+ * defineField({
152
+ * name: 'categories',
153
+ * title: 'Categories',
154
+ * type: 'array',
155
+ * of: [
156
+ * {
157
+ * type: 'reference',
158
+ * to: [{type: 'skosConcept'}],
159
+ * options: {
160
+ * filter: branchFilter({schemeId: 'f3deba', branchId: '25f826'}),
161
+ * disableNew: true,
162
+ * },
163
+ * },
164
+ * ],
165
+ * components: {
166
+ * field: (props) => (
167
+ * <ArrayHierarchyInput
168
+ * {...props}
169
+ * embeddingsIndex={{
170
+ * indexName: 'my-taxonomy-index',
171
+ * fieldReferences: ['title', 'description'],
172
+ * maxResults: 4,
173
+ * }}
174
+ * />
175
+ * ),
176
+ * },
177
+ * })
178
+ * ```
179
+ *
180
+ * @see {@link ReferenceHierarchyInput} for single-value `reference` fields
181
+ * @see {@link schemeFilter} for filtering by a full concept scheme
182
+ * @see {@link branchFilter} for filtering by a branch within a concept scheme
55
183
  */
56
184
  export function ArrayHierarchyInput(props: ArrayHierarchyInputProps) {
57
185
  const client = useClient({apiVersion: '2025-02-19'})
@@ -279,19 +407,24 @@ export function ArrayHierarchyInput(props: ArrayHierarchyInputProps) {
279
407
  return props.renderDefault(props)
280
408
  }
281
409
 
410
+ // Wrap the empty state in `FormField` so the field exposes the same
411
+ // DOM target Sanity's Validation panel uses for scroll-to-field /
412
+ // highlight on click. Without this wrapper, clicking a required
413
+ // browse-only field's validation error has no effect.
282
414
  return (
283
- <Stack space={2}>
284
- <Box paddingY={3}>
285
- <Text size={1} weight={'medium'}>
286
- {title}
287
- </Text>
288
- </Box>
415
+ <FormField
416
+ title={title}
417
+ description={props.description}
418
+ level={props.level}
419
+ validation={props.validation}
420
+ __unstable_presence={props.presence}
421
+ >
289
422
  <Card padding={3} radius={2} border>
290
423
  <Text muted align="center" size={1}>
291
424
  No items
292
425
  </Text>
293
426
  </Card>
294
- </Stack>
427
+ </FormField>
295
428
  )
296
429
  }
297
430
 
@@ -4,7 +4,7 @@ import type {DocumentId} from '@sanity/id-utils'
4
4
  import {Grid, Stack, Button, Dialog, Box, Spinner, Text, Flex, Card} from '@sanity/ui'
5
5
  import {useState, useEffect, useCallback} from 'react'
6
6
  import type {ObjectFieldProps, ObjectOptions, Reference} from 'sanity'
7
- import {isDraftId, useClient, useFormValue, usePerspective} from 'sanity'
7
+ import {FormField, isDraftId, useClient, useFormValue, usePerspective} from 'sanity'
8
8
 
9
9
  import {useEmbeddingsRecs} from '../../hooks'
10
10
  import NodeTree from '../../static/NodeTree'
@@ -33,13 +33,120 @@ type HierarchyInput = ObjectFieldProps<Reference> & {
33
33
  type FilterResult = Awaited<ReturnType<ReferenceOptions['filter']>>
34
34
 
35
35
  /**
36
- * #### Hierarchy View Input Component for Reference Fields
37
- * Allows Studio users to browse and select taxonomy
38
- * terms from a hierarchical tree structure. It is designed to be
39
- * used as an input for taxonomy reference fields in Sanity Studio.
36
+ * Input component that replaces Sanity's default reference field input with a
37
+ * hierarchical taxonomy tree browser. Studio users can browse taxonomy terms
38
+ * organized in their scheme hierarchy and select a single term for the field.
40
39
  *
41
- * Hierarchy view must be used in conjunction with the Taxonomy Manager
42
- * plugin `schemeFilter` or `branchFilter` options.
40
+ * @remarks
41
+ * - Must be used with a `schemeFilter` or `branchFilter` helper in the field's
42
+ * `options.filter`. Rendering without a filter will display a configuration warning.
43
+ * - Taxonomy selection is disabled when viewing the published perspective.
44
+ * - When `browseOnly` is set in the filter configuration, Sanity's default search input
45
+ * is suppressed and only the tree browser is available for term selection.
46
+ * - When `expanded` is set in the filter configuration, the hierarchy tree loads open
47
+ * by default instead of collapsed.
48
+ *
49
+ * @param props - Standard Sanity `ObjectFieldProps<Reference>` extended with an optional
50
+ * `embeddingsIndex` configuration object.
51
+ * @param props.embeddingsIndex - Optional configuration for AI-assisted term
52
+ * recommendations via a Sanity Embeddings Index. When provided, opening the tree
53
+ * browser queries the specified index and annotates matching taxonomy terms with
54
+ * a relevance score to help authors identify the most appropriate term.
55
+ * @param props.embeddingsIndex.indexName - The name of the Sanity Embeddings Index
56
+ * to query. Must be an index that includes `skosConcept` documents.
57
+ * @param props.embeddingsIndex.fieldReferences - An array of field names from the
58
+ * current document whose values are concatenated and sent as the embeddings search
59
+ * query. All listed fields must contain values when the tree browser is opened;
60
+ * empty fields will display an error message in the tree view rather than scores.
61
+ * @param props.embeddingsIndex.maxResults - Maximum number of semantically matching
62
+ * terms to return from the embeddings index. Defaults to `3`.
63
+ *
64
+ * @example
65
+ * Basic usage with a scheme filter:
66
+ * ```js
67
+ * import {ReferenceHierarchyInput, schemeFilter} from 'sanity-plugin-taxonomy-manager'
68
+ *
69
+ * defineField({
70
+ * name: 'gradeLevel',
71
+ * title: 'Grade Level',
72
+ * type: 'reference',
73
+ * to: {type: 'skosConcept'},
74
+ * options: {
75
+ * filter: schemeFilter({schemeId: 'f3deba'}),
76
+ * disableNew: true,
77
+ * },
78
+ * components: {field: ReferenceHierarchyInput},
79
+ * })
80
+ * ```
81
+ *
82
+ * @example
83
+ * Branch filter with tree expanded by default:
84
+ * ```js
85
+ * import {ReferenceHierarchyInput, branchFilter} from 'sanity-plugin-taxonomy-manager'
86
+ *
87
+ * defineField({
88
+ * name: 'topics',
89
+ * title: 'Topics',
90
+ * type: 'reference',
91
+ * to: {type: 'skosConcept'},
92
+ * options: {
93
+ * filter: branchFilter({schemeId: 'cf76c1', branchId: '1e5e6c', expanded: true}),
94
+ * disableNew: true,
95
+ * },
96
+ * components: {field: ReferenceHierarchyInput},
97
+ * })
98
+ * ```
99
+ *
100
+ * @example
101
+ * Browse-only mode (suppresses the default Sanity search input):
102
+ * ```js
103
+ * import {ReferenceHierarchyInput, branchFilter} from 'sanity-plugin-taxonomy-manager'
104
+ *
105
+ * defineField({
106
+ * name: 'topics',
107
+ * title: 'Topics',
108
+ * type: 'reference',
109
+ * to: {type: 'skosConcept'},
110
+ * options: {
111
+ * filter: branchFilter({schemeId: 'cf76c1', branchId: '1e5e6c', browseOnly: true}),
112
+ * disableNew: true,
113
+ * },
114
+ * components: {field: ReferenceHierarchyInput},
115
+ * })
116
+ * ```
117
+ *
118
+ * @example
119
+ * AI-assisted recommendations via an embeddings index:
120
+ * ```jsx
121
+ * import {ReferenceHierarchyInput, schemeFilter} from 'sanity-plugin-taxonomy-manager'
122
+ *
123
+ * defineField({
124
+ * name: 'topics',
125
+ * title: 'Topics',
126
+ * type: 'reference',
127
+ * to: [{type: 'skosConcept'}],
128
+ * options: {
129
+ * filter: schemeFilter({schemeId: 'f3deba'}),
130
+ * disableNew: true,
131
+ * },
132
+ * components: {
133
+ * field: (props) => (
134
+ * <ReferenceHierarchyInput
135
+ * {...props}
136
+ * embeddingsIndex={{
137
+ * indexName: 'my-taxonomy-index',
138
+ * fieldReferences: ['title', 'metaDescription'],
139
+ * maxResults: 4,
140
+ * }}
141
+ * />
142
+ * ),
143
+ * },
144
+ * })
145
+ * ```
146
+ *
147
+ * @see {@link ArrayHierarchyInput} for multi-value `array` fields
148
+ * @see {@link schemeFilter} for filtering by a full concept scheme
149
+ * @see {@link branchFilter} for filtering by a branch within a concept scheme
43
150
  */
44
151
  export function ReferenceHierarchyInput(props: HierarchyInput) {
45
152
  const client = useClient({apiVersion: 'vX'})
@@ -223,19 +330,24 @@ export function ReferenceHierarchyInput(props: HierarchyInput) {
223
330
  if (value) {
224
331
  return props.renderDefault(props)
225
332
  }
333
+ // Wrap the empty state in `FormField` so the field exposes the same
334
+ // DOM target Sanity's Validation panel uses for scroll-to-field /
335
+ // highlight on click. Without this wrapper, clicking a required
336
+ // browse-only field's validation error has no effect.
226
337
  return (
227
- <Stack space={2}>
228
- <Box paddingY={3}>
229
- <Text size={1} weight={'medium'}>
230
- {title}
231
- </Text>
232
- </Box>
338
+ <FormField
339
+ title={title}
340
+ description={props.description}
341
+ level={props.level}
342
+ validation={props.validation}
343
+ __unstable_presence={props.presence}
344
+ >
233
345
  <Card padding={3} radius={2} border>
234
346
  <Text muted align="center" size={1}>
235
347
  No items
236
348
  </Text>
237
349
  </Card>
238
- </Stack>
350
+ </FormField>
239
351
  )
240
352
  }
241
353