sanity-plugin-taxonomy-manager 2.0.0-beta.0 → 2.0.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 CHANGED
@@ -1,4 +1,4 @@
1
- # Sanity Plugin Taxonomy Manager
1
+ # Sanity Taxonomy Manager Plugin
2
2
  ![NPM Version](https://img.shields.io/npm/v/sanity-plugin-taxonomy-manager?style=flat-square)
3
3
  ![License](https://img.shields.io/npm/l/sanity-plugin-taxonomy-manager?style=flat-square)
4
4
 
@@ -11,7 +11,7 @@ Taxonomies are crucial tools for organization and interoperability between and a
11
11
 
12
12
  The Taxonomy Manager document schema is based on the [World Wide Web Consortium](https://www.w3.org/) (W3C) [Simple Knowledge Organization Scheme](https://www.w3.org/TR/skos-reference/) (SKOS) recommendation. Concept and concept scheme editor tools include standard SKOS properties, hints for creating consistent concepts and vocabularies, and validation functions for preventing consistency errors.
13
13
 
14
- <!-- Add screenshot of hierarchy view from a demo studio -->
14
+ <img src="https://user-images.githubusercontent.com/3710835/212743871-14760a60-0689-4cc3-a13e-55dd7a4ef19a.png" width="700">
15
15
 
16
16
  ## Features
17
17
 
@@ -32,23 +32,25 @@ $ npm i sanity-plugin-taxonomy-manager
32
32
  Add the plugin to your project configuration to make the skosConcept and skosConceptScheme document types available in your studio.
33
33
 
34
34
  ```js
35
- // sanity.config.ts
35
+ // sanity.config.js
36
36
 
37
- import { defineConfig } from 'sanity';
38
-
39
- import {taxonomyManager} from 'sanity-plugin-taxonomy-manager';
37
+ import {defineConfig} from 'sanity'
38
+ import {deskTool} from 'sanity/desk'
39
+ import {structure} from "./deskStructure"
40
+ import {taxonomyManager} from 'sanity-plugin-taxonomy-manager'
41
+ import {schemaTypes} from './schemas'
40
42
 
41
43
  export default defineConfig({
42
44
  name: 'default',
43
- title: 'My Cool Project',
44
- projectId: 'my-project-id',
45
+ title: 'Sanity Studio',
46
+ projectId: 'project-id',
45
47
  dataset: 'production',
46
48
  plugins: [
47
- // Include the taxonomy manager plugin
48
- taxonomyManager(),
49
49
  deskTool({
50
- structure: deskStructure,
51
- })
50
+ structure
51
+ }),
52
+ // Include the taxonomy manager plugin
53
+ taxonomyManager()
52
54
  ],
53
55
  schema: {
54
56
  types: schemaTypes,
@@ -62,36 +64,34 @@ Use [Structure Builder](https://www.sanity.io/docs/structure-builder-reference)
62
64
  // ./deskStructure.js
63
65
  import {TreeView} from 'sanity-plugin-taxonomy-manager'
64
66
 
65
- export const myStructure = (S) =>
66
-
67
- // ... other structure builder items
68
-
69
- S.divider(),
70
- S.listItem()
71
- .title('Concept Schemes')
72
- .schemaType('skosConceptScheme')
73
- .child(
74
- S.documentTypeList('skosConceptScheme')
67
+ export const structure = (S) =>
68
+ S.list()
69
+ .title('Content')
70
+ .items([
71
+ S.listItem()
75
72
  .title('Concept Schemes')
76
- .child(id =>
77
- S.document()
78
- .schemaType('skosConceptScheme')
79
- .documentId(id)
80
- .views([
81
- S.view.component(TreeView).title('Tree View'),
82
- S.view.form()
83
- ])
84
- )
85
- ),
86
- S.documentTypeListItem("skosConcept").title("Concepts"),
87
- S.divider(),
88
-
89
- // ... other structure builder items
90
-
91
- // Be sure to remove Taxonomy Manager types from the main list
92
- ...S.documentTypeListItems().filter(
93
- (listItem) => !['skosConcept', 'skosConceptScheme'].includes(listItem.getId())
94
- )
73
+ .schemaType('skosConceptScheme')
74
+ .child(
75
+ S.documentTypeList('skosConceptScheme')
76
+ .title('Concept Schemes')
77
+ .child(id =>
78
+ S.document()
79
+ .schemaType('skosConceptScheme')
80
+ .documentId(id)
81
+ .views([
82
+ S.view.component(TreeView).title('Tree View'),
83
+ S.view.form()
84
+ ])
85
+ )
86
+ ),
87
+ S.documentTypeListItem("skosConcept").title("Concepts"),
88
+ S.divider(),
89
+
90
+ // Remove Taxonomy Manager types from the default document type list
91
+ ...S.documentTypeListItems().filter(
92
+ (listItem) => !['skosConcept', 'skosConceptScheme'].includes(listItem.getId())
93
+ ),
94
+ ])
95
95
  ```
96
96
 
97
97
  ## Usage
@@ -107,23 +107,23 @@ export const myStructure = (S) =>
107
107
 
108
108
  The concept editor includes filtering and validation to help you create consistent SKOS vocabularies:
109
109
 
110
- - **SKOS Broader and Related Concepts**
110
+ **SKOS Broader and Related Concepts**
111
111
  Adding the same concept to Broader and Related fields is not allowed, and the editor validates disjunction of Related concepts with Broader Transitive up to five hierarchical levels in either direction.
112
112
 
113
- - **Preferred, Alternative, and Hidden Labels**
113
+ **Preferred, Alternative, and Hidden Labels**
114
114
  Preferred Labels are validated for uniqueness across concepts, and Preferred, Alternative, and Hidden are validated to prevent duplicates and overlap.
115
115
 
116
- - **Scope Notes, Definition, and Examples**
116
+ **Scope Notes, Definition, and Examples**
117
117
  Standard optional SKOS documentation fields are included by default.
118
118
 
119
- - **Support for Single or Multiple Taxonomy Schemes (or none)**
119
+ **Support for Single or Multiple Taxonomy Schemes (or none)**
120
120
  For cases where more than one taxonomy is needed, multiple [SKOS Concept Schemes](https://www.w3.org/TR/skos-reference/#schemes) are supported. Schemes can be used to configure filtered views of concepts in Sanity Structure Builder and can be used to scope values for reference arrays.
121
121
 
122
122
  <!-- Concept Scheme views show a hierarchical list (Tree View) of the concepts included in a given scheme. This list allows for easy visualization of Top Concepts, polyhierarchy (concepts that appear in more than one place in the hierarchy), and "Orphan" terms (top level concepts not denoted as a "Top Concept"). -->
123
123
 
124
- ## [SKOS Overview](https://www.w3.org/TR/skos-reference/)
124
+ ## SKOS Overview
125
125
 
126
- > The Simple Knowledge Organization System (SKOS) is a common data model for sharing and linking knowledge organization systems via the Web.
126
+ > The [Simple Knowledge Organization System (SKOS)](https://www.w3.org/TR/skos-reference/) is a common data model for sharing and linking knowledge organization systems via the Web.
127
127
  >
128
128
  > Many knowledge organization systems, such as thesauri, taxonomies, classification schemes and subject heading systems, share a similar structure, and are used in similar applications. SKOS captures much of this similarity and makes it explicit, to enable data and technology sharing across diverse applications.
129
129
  >
package/lib/index.esm.js CHANGED
@@ -1 +1 @@
1
- import{useFormValue as e,defineType as t,defineField as i,defineArrayMember as n,useClient as o,definePlugin as r}from"sanity";import{jsxs as a,jsx as s,Fragment as c}from"react/jsx-runtime";import{AiFillTags as l,AiOutlineTag as d,AiFillTag as p}from"react-icons/ai";import{Stack as h,Text as u,Flex as m,Spinner as f,Inline as b,Container as y,Box as v}from"@sanity/ui";import{RiNodeTree as g}from"react-icons/ri";import{useState as C,useEffect as T}from"react";var R=t({name:"skosConcept",title:"Concept",type:"document",icon:l,initialValue:async(e,t)=>{var i;const{getClient:n}=t,o=n({apiVersion:"2021-03-25"});return{baseIri:null!=(i=await o.fetch("\n *[_type == 'skosConcept' && defined(baseIri)]| order(_createdAt desc)[0].baseIri\n "))?i:void 0,broader:[],related:[]}},groups:[{name:"label",title:"Labels",default:!0},{name:"relationship",title:"Relationships"},{name:"note",title:"Documentation"}],fields:[i({name:"prefLabel",title:"Preferred Label",group:"label",type:"string",description:"The preferred lexical label for this concept. This label is also used to unambiguously represent this concept via the concept IRI.",components:{input:function(t){var i;const n=e(["baseIri"]);return a(h,{space:2,children:[t.renderDefault(t),s(u,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:a(c,{children:[s("strong",{children:"Concept IRI: "}),n||"[base URI not defined] ",null==(i=t.value)?void 0:i.replaceAll(" ","")]})})]})}},validation:e=>e.required().custom(((e,t)=>{const{getClient:i}=t;return i({apiVersion:"2022-12-14"}).fetch('*[_type == "skosConcept" && prefLabel == "'.concat(e,'" && !(_id in path("drafts.**"))][0]._id')).then((e=>{var i;return!e||e===(null==(i=t.document)?void 0:i._id.replace("drafts.",""))||"Preferred Label must be unique."}))}))}),i({name:"baseIri",title:"Base IRI",type:"url",group:"label",validation:e=>e.required().error("Please supply a base IRI."),description:"The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies.",options:{collapsible:!0}}),i({name:"conceptIriBase",title:"Edit the base IRI",type:"baseIri",group:"label"}),i({name:"altLabel",title:"Alternate Label(s)",group:"label",type:"array",description:"Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),i({name:"hiddenLabel",title:"Hidden Label(s)",group:"label",type:"array",description:"Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),i({name:"broader",title:"Broader Concept(s)",description:"Broader relationships create a hierarchy between concepts, for example to create category/subcategory, part/whole, or class/instance relationships.",group:"relationship",type:"array",of:[{type:"reference",to:{type:"skosConcept"},options:{filter:e=>{let{document:t}=e;return{filter:'!(_id in $broader || _id in $related || _id in path("drafts.**") || _id == $self)',params:{self:t._id.replace("drafts.",""),broader:t.broader.map((e=>{let{_ref:t}=e;return t})),related:t.related.map((e=>{let{_ref:t}=e;return t}))}}}}}]}),i({name:"related",title:"Related Concept(s)",description:'Associative links between concepts indicate that the two are inherently "related", but that one is not in any way more general than the other. Broader and Associated relationships are mutually exclusive.',group:"relationship",type:"array",of:[{type:"reference",to:[{type:"skosConcept"}]}]}),i({name:"scopeNote",title:"Scope Note",type:"text",description:"A brief statement on the intended meaning of this concept, especially as an indication of how the use of the concept is limited in indexing practice",rows:3,group:"note"}),i({name:"definition",title:"Definition",type:"text",description:"A complete explanation of the intended meaning of the concept",rows:3,group:"note"}),i({name:"example",title:"Examples",type:"text",description:"An example of the use of the concept.",rows:3,group:"note"}),i({name:"topConcept",title:"Top Concept",group:"relationship",type:"boolean",description:a(c,{children:['NOTE: Top Concepts are determined at the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, set Top Concept to "false."',s("br",{}),s("br",{}),"Description: Top concepts provide an efficient entry point to broader/narrower concept hierarchies and/or top level facets. By convention, resources can be a Top Concept, or have Broader relationships, but not both."]}),hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.topConcept)}}),i({name:"scheme",title:"Concept Scheme(s)",group:"relationship",type:"reference",hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.scheme)},description:a(c,{children:["NOTE: Concept Scheme inclusion is are determined from the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",s("br",{}),s("br",{}),"Description: Concept schemes are used to group concepts into defined sets, such as thesauri, classification schemes, or facets."]}),to:[{type:"skosConceptScheme"}],options:{disableNew:!0}})],orderings:[{title:"Top Concepts",name:"topConcept",by:[{field:"topConcept",direction:"desc"},{field:"prefLabel",direction:"asc"}]},{title:"Preferred Label",name:"prefLabel",by:[{field:"prefLabel",direction:"asc"}]}],preview:{select:{title:"prefLabel",topConcept:"topConcept",broader:"broader.0.prefLabel",broaderPlusOne:"broader.0.broader.0.prefLabel",broaderPlusTwo:"broader.0.broader.0.broader.0.prefLabel"},prepare(e){let{title:t,topConcept:i,broader:n,broaderPlusOne:o,broaderPlusTwo:r}=e;const a=[o,n].filter(Boolean),s=a.length>0?"".concat(a.join(" ▷ ")," ▶︎ ").concat(t):"",c=r?"... ".concat(s):s;return{title:t,subtitle:i?"Top Concept":c,media:i?d:p}}}}),_=t({name:"skosConceptScheme",title:"Concept Scheme",type:"document",icon:g,fields:[i({name:"title",title:"Title",type:"string",description:"Taxonomy schemes group concepts into defined sets, such as thesauri, classification schemes, or facets. Concepts may belong on many (or no) concept schemes, and you may create as many (or few) concept schemes as you like"}),i({name:"description",title:"Description",type:"text",rows:5,description:"Describe the intended use of this scheme."}),i({name:"topConcepts",title:"Top Concepts",type:"array",validation:e=>e.unique(),of:[n({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}}),i({name:"concepts",title:"Concepts",type:"array",validation:e=>e.unique(),of:[n({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}})],preview:{select:{title:"title"},prepare(e){let{title:t}=e;return{title:t,media:g}}}}),I=t({name:"baseIri",title:"Base IRI",type:"object",description:"NOTE: conceptIriBase.iriValue is deprecated in version 2 of this plugin. Please migrate this value to the baseIri field above. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",hidden:e=>{let{document:t}=e;var i;return!(null==(i=null==t?void 0:t.conceptIriBase)?void 0:i.iriValue)},fields:[i({name:"iriValue",title:"IRI Value",type:"url",description:"Description: The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies."})],options:{collapsible:!0,collapsed:!1}});const w=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return e>6?"":'"childConcepts": *[_id in coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]).concepts[]._ref && ^._id in broader[]._ref ]|order(prefLabel){\n "id": _id,\n "level": '.concat(e,",\n prefLabel,\n ").concat(w(e+1),"\n }")},z=e=>{let{concepts:t}=e;return s("ul",{style:{listStyle:"none"},children:t.map((e=>{var t;return a("li",{style:{fontWeight:"normal",marginTop:".75rem"},children:[e.prefLabel,(null==(t=e.childConcepts)?void 0:t.length)>0&&s(z,{concepts:e.childConcepts})]},e.id)}))})},L=e=>{let{document:t,documentId:i}=e;const n=o({apiVersion:"2021-10-21"}),[r,c]=C([]),[l,d]=C(!0),[p,h]=C(!1),[y,v]=C(!1),[g,R]=C(i);return T((()=>{if(void 0===t.displayed._id)return;(async()=>{var e;console.log(r),R(i),d(!0),h(!1),v(!1),0!=r.length&&g==i&&await(e=1e3,console.log("waiting"),new Promise((t=>setTimeout(t,e)))),await n.fetch('coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]) {\n "topConcepts":topConcepts[]->|order(prefLabel){\n "id": _id,\n "level": 0,\n prefLabel,\n '.concat(w(),"\n },\n \"orphans\": *[\n // filter to concepts in this scheme only:\n _id in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref // filter to concepts in this scheme only\n // filter out concepts that reference a topConcept in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).topConcepts[]._ref]) < 1\n // filter out concepts that reference other concepts in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref]) < 1\n ]|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n prefLabel,\n ").concat(w(),"\n }\n }"),{id:i}).then((e=>{null==e.topConcepts&&e.orphans.length<1?(d(!1),h(!0)):(d(!1),c(e))})).catch((e=>{console.log("Error: ",e),d(!1),v(!0)}))})()}),[i,t.displayed.concepts,t.displayed.topConcepts]),1==y?s("p",{children:"Sorry, could not get concepts."}):1==p?s("p",{children:"This scheme does not yet have any concepts assigned to it."}):l?a(m,{align:"center",direction:"column",gap:5,height:"fill",justify:"center",style:{paddingTop:"1rem"},onResize:void 0,onResizeCapture:void 0,children:[s(f,{muted:!0,onResize:void 0,onResizeCapture:void 0}),s(u,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:"Loading hierarchy…"})]}):a("ul",{style:{listStyle:"none",paddingLeft:"0",marginTop:"1rem"},children:[r.topConcepts.map((e=>{var t;if(null==e?void 0:e.id)return a("li",{style:{paddingTop:".5rem",fontWeight:"bold",marginTop:".75rem"},children:[a(b,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"top concept"})]}),(null==(t=null==e?void 0:e.childConcepts)?void 0:t.length)>0&&s(z,{concepts:e.childConcepts})]},e.id)})),r.orphans.map((e=>{var t;return a("li",{style:{paddingTop:".5rem",fontWeight:"normal",marginTop:".75rem"},children:[a(b,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==(t=e.childConcepts)?void 0:t.length)>0&&s(z,{concepts:e.childConcepts})]},e.id)}))]})},x=e=>{let{document:t,documentId:i}=e;return s(y,{width:1,style:{paddingTop:"1.25rem"},onResize:void 0,onResizeCapture:void 0,children:s(v,{padding:4,onResize:void 0,onResizeCapture:void 0,children:a(h,{space:2,children:[s(u,{size:1,weight:"semibold",onResize:void 0,onResizeCapture:void 0,children:"Hierarchy Tree"}),s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"Concept hierarchy is determined by 'Broader' relationships assigned to each concept.."}),s(L,{document:t,documentId:i})]})})})},P=r({name:"taxonomyManager",schema:{types:[R,_,I]}});export{x as TreeView,P as taxonomyManager};//# sourceMappingURL=index.esm.js.map
1
+ import{useFormValue as e,defineType as t,defineField as i,defineArrayMember as n,useClient as o,definePlugin as r}from"sanity";import{jsxs as a,jsx as s,Fragment as c}from"react/jsx-runtime";import{AiFillTags as l,AiOutlineTag as d,AiFillTag as p}from"react-icons/ai";import{Stack as h,Text as u,Flex as m,Spinner as f,Inline as b,Container as y,Box as v}from"@sanity/ui";import{RiNodeTree as g}from"react-icons/ri";import{useState as C,useEffect as T}from"react";var _=t({name:"skosConcept",title:"Concept",type:"document",icon:l,initialValue:async(e,t)=>{var i;const{getClient:n}=t,o=n({apiVersion:"2021-03-25"});return{baseIri:null!=(i=await o.fetch("\n *[_type == 'skosConcept' && defined(baseIri)]| order(_createdAt desc)[0].baseIri\n "))?i:void 0,broader:[],related:[]}},groups:[{name:"label",title:"Labels",default:!0},{name:"relationship",title:"Relationships"},{name:"note",title:"Documentation"}],fields:[i({name:"prefLabel",title:"Preferred Label",group:"label",type:"string",description:"The preferred lexical label for this concept. This label is also used to unambiguously represent this concept via the concept IRI.",components:{input:function(t){var i;const n=e(["baseIri"]);return a(h,{space:2,children:[t.renderDefault(t),s(u,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:a(c,{children:[s("strong",{children:"Concept IRI: "}),n||"[base URI not defined] ",null==(i=t.value)?void 0:i.replaceAll(" ","")]})})]})}},validation:e=>e.required().custom(((e,t)=>{const{getClient:i}=t;return i({apiVersion:"2022-12-14"}).fetch('*[_type == "skosConcept" && prefLabel == "'.concat(e,'" && !(_id in path("drafts.**"))][0]._id')).then((e=>{var i;return!e||e===(null==(i=t.document)?void 0:i._id.replace("drafts.",""))||"Preferred Label must be unique."}))}))}),i({name:"baseIri",title:"Base IRI",type:"url",group:"label",validation:e=>e.required().error("Please supply a base IRI."),description:"The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies.",options:{collapsible:!0}}),i({name:"conceptIriBase",title:"Edit the base IRI",type:"baseIri",group:"label"}),i({name:"altLabel",title:"Alternate Label(s)",group:"label",type:"array",description:"Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),i({name:"hiddenLabel",title:"Hidden Label(s)",group:"label",type:"array",description:"Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),i({name:"broader",title:"Broader Concept(s)",description:"Broader relationships create a hierarchy between concepts, for example to create category/subcategory, part/whole, or class/instance relationships.",group:"relationship",type:"array",of:[{type:"reference",to:{type:"skosConcept"},options:{filter:e=>{let{document:t}=e;return{filter:'!(_id in $broader || _id in $related || _id in path("drafts.**") || _id == $self)',params:{self:t._id.replace("drafts.",""),broader:t.broader.map((e=>{let{_ref:t}=e;return t})),related:t.related.map((e=>{let{_ref:t}=e;return t}))}}}}}]}),i({name:"related",title:"Related Concept(s)",description:'Associative links between concepts indicate that the two are inherently "related", but that one is not in any way more general than the other. Broader and Associated relationships are mutually exclusive.',group:"relationship",type:"array",of:[{type:"reference",to:[{type:"skosConcept"}]}]}),i({name:"scopeNote",title:"Scope Note",type:"text",description:"A brief statement on the intended meaning of this concept, especially as an indication of how the use of the concept is limited in indexing practice",rows:3,group:"note"}),i({name:"definition",title:"Definition",type:"text",description:"A complete explanation of the intended meaning of the concept",rows:3,group:"note"}),i({name:"example",title:"Examples",type:"text",description:"An example of the use of the concept.",rows:3,group:"note"}),i({name:"topConcept",title:"Top Concept",group:"relationship",type:"boolean",description:a(c,{children:['NOTE: Top Concepts are determined at the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, set Top Concept to "false."',s("br",{}),s("br",{}),"Description: Top concepts provide an efficient entry point to broader/narrower concept hierarchies and/or top level facets. By convention, resources can be a Top Concept, or have Broader relationships, but not both."]}),hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.topConcept)}}),i({name:"scheme",title:"Concept Scheme(s)",group:"relationship",type:"reference",hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.scheme)},description:a(c,{children:["NOTE: Concept Scheme inclusion is are determined from the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",s("br",{}),s("br",{}),"Description: Concept schemes are used to group concepts into defined sets, such as thesauri, classification schemes, or facets."]}),to:[{type:"skosConceptScheme"}],options:{disableNew:!0}})],orderings:[{title:"Top Concepts",name:"topConcept",by:[{field:"topConcept",direction:"desc"},{field:"prefLabel",direction:"asc"}]},{title:"Preferred Label",name:"prefLabel",by:[{field:"prefLabel",direction:"asc"}]}],preview:{select:{title:"prefLabel",topConcept:"topConcept",broader:"broader.0.prefLabel",broaderPlusOne:"broader.0.broader.0.prefLabel",broaderPlusTwo:"broader.0.broader.0.broader.0.prefLabel"},prepare(e){let{title:t,topConcept:i,broader:n,broaderPlusOne:o,broaderPlusTwo:r}=e;const a=[o,n].filter(Boolean),s=a.length>0?"".concat(a.join(" ▷ ")," ▶︎ ").concat(t):"",c=r?"... ".concat(s):s;return{title:t,subtitle:i?"Top Concept":c,media:i?d:p}}}}),R=t({name:"skosConceptScheme",title:"Concept Scheme",type:"document",icon:g,fields:[i({name:"title",title:"Title",type:"string",description:"Taxonomy schemes group concepts into defined sets, such as thesauri, classification schemes, or facets. Concepts may belong on many (or no) concept schemes, and you may create as many (or few) concept schemes as you like"}),i({name:"description",title:"Description",type:"text",rows:5,description:"Describe the intended use of this scheme."}),i({name:"topConcepts",title:"Top Concepts",type:"array",validation:e=>e.unique(),of:[n({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}}),i({name:"concepts",title:"Concepts",type:"array",validation:e=>e.unique(),of:[n({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}})],preview:{select:{title:"title"},prepare(e){let{title:t}=e;return{title:t,media:g}}}}),I=t({name:"baseIri",title:"Base IRI",type:"object",description:"NOTE: conceptIriBase.iriValue is deprecated in version 2 of this plugin. Please migrate this value to the baseIri field above. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",hidden:e=>{let{document:t}=e;var i;return!(null==(i=null==t?void 0:t.conceptIriBase)?void 0:i.iriValue)},fields:[i({name:"iriValue",title:"IRI Value",type:"url",description:"Description: The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies."})],options:{collapsible:!0,collapsed:!1}});const z=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return e>6?"":'"childConcepts": *[_id in coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]).concepts[]._ref && ^._id in broader[]._ref ]|order(prefLabel){\n "id": _id,\n "level": '.concat(e,",\n prefLabel,\n ").concat(z(e+1),"\n }")},L=e=>{let{concepts:t}=e;return s("ul",{style:{listStyle:"none"},children:t.map((e=>{var t;return a("li",{style:{fontWeight:"normal",marginTop:".75rem"},children:[e.prefLabel,(null==(t=e.childConcepts)?void 0:t.length)>0&&s(L,{concepts:e.childConcepts})]},e.id)}))})},w=e=>{let{document:t,documentId:i}=e;const n=o({apiVersion:"2021-10-21"}),[r,c]=C([]),[l,d]=C(!0),[p,h]=C(!1),[y,v]=C(!1);return T((()=>{if(void 0===t.displayed._id)return;if(!(i===t.displayed._id.replace(/^drafts\./,"")))return void console.log("document not loaded yet.");(async()=>{d(!0),h(!1),v(!1);try{const e=await n.fetch('coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]) {\n "topConcepts":topConcepts[]->|order(prefLabel){\n "id": _id,\n "level": 0,\n prefLabel,\n '.concat(z(),"\n },\n \"orphans\": *[\n // filter to concepts in this scheme only:\n _id in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref // filter to concepts in this scheme only\n // filter out concepts that reference a topConcept in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).topConcepts[]._ref]) < 1\n // filter out concepts that reference other concepts in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref]) < 1\n ]|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n prefLabel,\n ").concat(z(),"\n }\n }"),{id:i});null==e.topConcepts&&e.orphans.length<1?h(!0):(d(!1),c(e))}catch(e){console.log("Error: ",e),v(!0)}d(!1)})()}),[i,t.displayed._id,t.displayed.concepts,t.displayed.topConcepts]),1==y?s("p",{children:"Sorry, could not get concepts."}):1==p?s("p",{children:"This scheme does not yet have any concepts assigned to it."}):l?a(m,{align:"center",direction:"column",gap:5,height:"fill",justify:"center",style:{paddingTop:"1rem"},onResize:void 0,onResizeCapture:void 0,children:[s(f,{muted:!0,onResize:void 0,onResizeCapture:void 0}),s(u,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:"Loading hierarchy…"})]}):a("ul",{style:{listStyle:"none",paddingLeft:"0",marginTop:"1rem"},children:[r.topConcepts.map((e=>{var t;if(null==e?void 0:e.id)return a("li",{style:{paddingTop:".5rem",fontWeight:"bold",marginTop:".75rem"},children:[a(b,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"top concept"})]}),(null==(t=null==e?void 0:e.childConcepts)?void 0:t.length)>0&&s(L,{concepts:e.childConcepts})]},e.id)})),r.orphans.map((e=>{var t;return a("li",{style:{paddingTop:".5rem",fontWeight:"normal",marginTop:".75rem"},children:[a(b,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==(t=e.childConcepts)?void 0:t.length)>0&&s(L,{concepts:e.childConcepts})]},e.id)}))]})},x=e=>{let{document:t,documentId:i}=e;return s(y,{width:1,style:{paddingTop:"1.25rem"},onResize:void 0,onResizeCapture:void 0,children:s(v,{padding:4,onResize:void 0,onResizeCapture:void 0,children:a(h,{space:2,children:[s(u,{size:1,weight:"semibold",onResize:void 0,onResizeCapture:void 0,children:"Hierarchy Tree"}),s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"Concept hierarchy is determined by 'Broader' relationships assigned to each concept."}),s(w,{document:t,documentId:i})]})})})},P=r({name:"taxonomyManager",schema:{types:[_,R,I]}});export{x as TreeView,P as taxonomyManager};//# sourceMappingURL=index.esm.js.map
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("sanity"),t=require("react/jsx-runtime"),i=require("react-icons/ai"),n=require("@sanity/ui"),o=require("react-icons/ri"),r=require("react");var s=e.defineType({name:"skosConcept",title:"Concept",type:"document",icon:i.AiFillTags,initialValue:async(e,t)=>{var i;const{getClient:n}=t,o=n({apiVersion:"2021-03-25"});return{baseIri:null!=(i=await o.fetch("\n *[_type == 'skosConcept' && defined(baseIri)]| order(_createdAt desc)[0].baseIri\n "))?i:void 0,broader:[],related:[]}},groups:[{name:"label",title:"Labels",default:!0},{name:"relationship",title:"Relationships"},{name:"note",title:"Documentation"}],fields:[e.defineField({name:"prefLabel",title:"Preferred Label",group:"label",type:"string",description:"The preferred lexical label for this concept. This label is also used to unambiguously represent this concept via the concept IRI.",components:{input:function(i){var o;const r=e.useFormValue(["baseIri"]);return t.jsxs(n.Stack,{space:2,children:[i.renderDefault(i),t.jsx(n.Text,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:t.jsxs(t.Fragment,{children:[t.jsx("strong",{children:"Concept IRI: "}),r||"[base URI not defined] ",null==(o=i.value)?void 0:o.replaceAll(" ","")]})})]})}},validation:e=>e.required().custom(((e,t)=>{const{getClient:i}=t;return i({apiVersion:"2022-12-14"}).fetch('*[_type == "skosConcept" && prefLabel == "'.concat(e,'" && !(_id in path("drafts.**"))][0]._id')).then((e=>{var i;return!e||e===(null==(i=t.document)?void 0:i._id.replace("drafts.",""))||"Preferred Label must be unique."}))}))}),e.defineField({name:"baseIri",title:"Base IRI",type:"url",group:"label",validation:e=>e.required().error("Please supply a base IRI."),description:"The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies.",options:{collapsible:!0}}),e.defineField({name:"conceptIriBase",title:"Edit the base IRI",type:"baseIri",group:"label"}),e.defineField({name:"altLabel",title:"Alternate Label(s)",group:"label",type:"array",description:"Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),e.defineField({name:"hiddenLabel",title:"Hidden Label(s)",group:"label",type:"array",description:"Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),e.defineField({name:"broader",title:"Broader Concept(s)",description:"Broader relationships create a hierarchy between concepts, for example to create category/subcategory, part/whole, or class/instance relationships.",group:"relationship",type:"array",of:[{type:"reference",to:{type:"skosConcept"},options:{filter:e=>{let{document:t}=e;return{filter:'!(_id in $broader || _id in $related || _id in path("drafts.**") || _id == $self)',params:{self:t._id.replace("drafts.",""),broader:t.broader.map((e=>{let{_ref:t}=e;return t})),related:t.related.map((e=>{let{_ref:t}=e;return t}))}}}}}]}),e.defineField({name:"related",title:"Related Concept(s)",description:'Associative links between concepts indicate that the two are inherently "related", but that one is not in any way more general than the other. Broader and Associated relationships are mutually exclusive.',group:"relationship",type:"array",of:[{type:"reference",to:[{type:"skosConcept"}]}]}),e.defineField({name:"scopeNote",title:"Scope Note",type:"text",description:"A brief statement on the intended meaning of this concept, especially as an indication of how the use of the concept is limited in indexing practice",rows:3,group:"note"}),e.defineField({name:"definition",title:"Definition",type:"text",description:"A complete explanation of the intended meaning of the concept",rows:3,group:"note"}),e.defineField({name:"example",title:"Examples",type:"text",description:"An example of the use of the concept.",rows:3,group:"note"}),e.defineField({name:"topConcept",title:"Top Concept",group:"relationship",type:"boolean",description:t.jsxs(t.Fragment,{children:['NOTE: Top Concepts are determined at the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, set Top Concept to "false."',t.jsx("br",{}),t.jsx("br",{}),"Description: Top concepts provide an efficient entry point to broader/narrower concept hierarchies and/or top level facets. By convention, resources can be a Top Concept, or have Broader relationships, but not both."]}),hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.topConcept)}}),e.defineField({name:"scheme",title:"Concept Scheme(s)",group:"relationship",type:"reference",hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.scheme)},description:t.jsxs(t.Fragment,{children:["NOTE: Concept Scheme inclusion is are determined from the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",t.jsx("br",{}),t.jsx("br",{}),"Description: Concept schemes are used to group concepts into defined sets, such as thesauri, classification schemes, or facets."]}),to:[{type:"skosConceptScheme"}],options:{disableNew:!0}})],orderings:[{title:"Top Concepts",name:"topConcept",by:[{field:"topConcept",direction:"desc"},{field:"prefLabel",direction:"asc"}]},{title:"Preferred Label",name:"prefLabel",by:[{field:"prefLabel",direction:"asc"}]}],preview:{select:{title:"prefLabel",topConcept:"topConcept",broader:"broader.0.prefLabel",broaderPlusOne:"broader.0.broader.0.prefLabel",broaderPlusTwo:"broader.0.broader.0.broader.0.prefLabel"},prepare(e){let{title:t,topConcept:n,broader:o,broaderPlusOne:r,broaderPlusTwo:s}=e;const a=[r,o].filter(Boolean),l=a.length>0?"".concat(a.join(" ▷ ")," ▶︎ ").concat(t):"",d=s?"... ".concat(l):l;return{title:t,subtitle:n?"Top Concept":d,media:n?i.AiOutlineTag:i.AiFillTag}}}}),a=e.defineType({name:"skosConceptScheme",title:"Concept Scheme",type:"document",icon:o.RiNodeTree,fields:[e.defineField({name:"title",title:"Title",type:"string",description:"Taxonomy schemes group concepts into defined sets, such as thesauri, classification schemes, or facets. Concepts may belong on many (or no) concept schemes, and you may create as many (or few) concept schemes as you like"}),e.defineField({name:"description",title:"Description",type:"text",rows:5,description:"Describe the intended use of this scheme."}),e.defineField({name:"topConcepts",title:"Top Concepts",type:"array",validation:e=>e.unique(),of:[e.defineArrayMember({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}}),e.defineField({name:"concepts",title:"Concepts",type:"array",validation:e=>e.unique(),of:[e.defineArrayMember({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}})],preview:{select:{title:"title"},prepare(e){let{title:t}=e;return{title:t,media:o.RiNodeTree}}}}),l=e.defineType({name:"baseIri",title:"Base IRI",type:"object",description:"NOTE: conceptIriBase.iriValue is deprecated in version 2 of this plugin. Please migrate this value to the baseIri field above. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",hidden:e=>{let{document:t}=e;var i;return!(null==(i=null==t?void 0:t.conceptIriBase)?void 0:i.iriValue)},fields:[e.defineField({name:"iriValue",title:"IRI Value",type:"url",description:"Description: The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies."})],options:{collapsible:!0,collapsed:!1}});const d=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return e>6?"":'"childConcepts": *[_id in coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]).concepts[]._ref && ^._id in broader[]._ref ]|order(prefLabel){\n "id": _id,\n "level": '.concat(e,",\n prefLabel,\n ").concat(d(e+1),"\n }")},c=e=>{let{concepts:i}=e;return t.jsx("ul",{style:{listStyle:"none"},children:i.map((e=>{var i;return t.jsxs("li",{style:{fontWeight:"normal",marginTop:".75rem"},children:[e.prefLabel,(null==(i=e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)}))})},p=i=>{let{document:o,documentId:s}=i;const a=e.useClient({apiVersion:"2021-10-21"}),[l,p]=r.useState([]),[u,h]=r.useState(!0),[f,m]=r.useState(!1),[b,y]=r.useState(!1),[v,g]=r.useState(s);return r.useEffect((()=>{if(void 0===o.displayed._id)return;(async()=>{var e;console.log(l),g(s),h(!0),m(!1),y(!1),0!=l.length&&v==s&&await(e=1e3,console.log("waiting"),new Promise((t=>setTimeout(t,e)))),await a.fetch('coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]) {\n "topConcepts":topConcepts[]->|order(prefLabel){\n "id": _id,\n "level": 0,\n prefLabel,\n '.concat(d(),"\n },\n \"orphans\": *[\n // filter to concepts in this scheme only:\n _id in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref // filter to concepts in this scheme only\n // filter out concepts that reference a topConcept in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).topConcepts[]._ref]) < 1\n // filter out concepts that reference other concepts in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref]) < 1\n ]|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n prefLabel,\n ").concat(d(),"\n }\n }"),{id:s}).then((e=>{null==e.topConcepts&&e.orphans.length<1?(h(!1),m(!0)):(h(!1),p(e))})).catch((e=>{console.log("Error: ",e),h(!1),y(!0)}))})()}),[s,o.displayed.concepts,o.displayed.topConcepts]),1==b?t.jsx("p",{children:"Sorry, could not get concepts."}):1==f?t.jsx("p",{children:"This scheme does not yet have any concepts assigned to it."}):u?t.jsxs(n.Flex,{align:"center",direction:"column",gap:5,height:"fill",justify:"center",style:{paddingTop:"1rem"},onResize:void 0,onResizeCapture:void 0,children:[t.jsx(n.Spinner,{muted:!0,onResize:void 0,onResizeCapture:void 0}),t.jsx(n.Text,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:"Loading hierarchy…"})]}):t.jsxs("ul",{style:{listStyle:"none",paddingLeft:"0",marginTop:"1rem"},children:[l.topConcepts.map((e=>{var i;if(null==e?void 0:e.id)return t.jsxs("li",{style:{paddingTop:".5rem",fontWeight:"bold",marginTop:".75rem"},children:[t.jsxs(n.Inline,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"top concept"})]}),(null==(i=null==e?void 0:e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)})),l.orphans.map((e=>{var i;return t.jsxs("li",{style:{paddingTop:".5rem",fontWeight:"normal",marginTop:".75rem"},children:[t.jsxs(n.Inline,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==(i=e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)}))]})},u=e.definePlugin({name:"taxonomyManager",schema:{types:[s,a,l]}});exports.TreeView=e=>{let{document:i,documentId:o}=e;return t.jsx(n.Container,{width:1,style:{paddingTop:"1.25rem"},onResize:void 0,onResizeCapture:void 0,children:t.jsx(n.Box,{padding:4,onResize:void 0,onResizeCapture:void 0,children:t.jsxs(n.Stack,{space:2,children:[t.jsx(n.Text,{size:1,weight:"semibold",onResize:void 0,onResizeCapture:void 0,children:"Hierarchy Tree"}),t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"Concept hierarchy is determined by 'Broader' relationships assigned to each concept.."}),t.jsx(p,{document:i,documentId:o})]})})})},exports.taxonomyManager=u;//# sourceMappingURL=index.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("sanity"),t=require("react/jsx-runtime"),i=require("react-icons/ai"),n=require("@sanity/ui"),o=require("react-icons/ri"),r=require("react");var s=e.defineType({name:"skosConcept",title:"Concept",type:"document",icon:i.AiFillTags,initialValue:async(e,t)=>{var i;const{getClient:n}=t,o=n({apiVersion:"2021-03-25"});return{baseIri:null!=(i=await o.fetch("\n *[_type == 'skosConcept' && defined(baseIri)]| order(_createdAt desc)[0].baseIri\n "))?i:void 0,broader:[],related:[]}},groups:[{name:"label",title:"Labels",default:!0},{name:"relationship",title:"Relationships"},{name:"note",title:"Documentation"}],fields:[e.defineField({name:"prefLabel",title:"Preferred Label",group:"label",type:"string",description:"The preferred lexical label for this concept. This label is also used to unambiguously represent this concept via the concept IRI.",components:{input:function(i){var o;const r=e.useFormValue(["baseIri"]);return t.jsxs(n.Stack,{space:2,children:[i.renderDefault(i),t.jsx(n.Text,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:t.jsxs(t.Fragment,{children:[t.jsx("strong",{children:"Concept IRI: "}),r||"[base URI not defined] ",null==(o=i.value)?void 0:o.replaceAll(" ","")]})})]})}},validation:e=>e.required().custom(((e,t)=>{const{getClient:i}=t;return i({apiVersion:"2022-12-14"}).fetch('*[_type == "skosConcept" && prefLabel == "'.concat(e,'" && !(_id in path("drafts.**"))][0]._id')).then((e=>{var i;return!e||e===(null==(i=t.document)?void 0:i._id.replace("drafts.",""))||"Preferred Label must be unique."}))}))}),e.defineField({name:"baseIri",title:"Base IRI",type:"url",group:"label",validation:e=>e.required().error("Please supply a base IRI."),description:"The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies.",options:{collapsible:!0}}),e.defineField({name:"conceptIriBase",title:"Edit the base IRI",type:"baseIri",group:"label"}),e.defineField({name:"altLabel",title:"Alternate Label(s)",group:"label",type:"array",description:"Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),e.defineField({name:"hiddenLabel",title:"Hidden Label(s)",group:"label",type:"array",description:"Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.",of:[{type:"string"}],validation:e=>e.unique()}),e.defineField({name:"broader",title:"Broader Concept(s)",description:"Broader relationships create a hierarchy between concepts, for example to create category/subcategory, part/whole, or class/instance relationships.",group:"relationship",type:"array",of:[{type:"reference",to:{type:"skosConcept"},options:{filter:e=>{let{document:t}=e;return{filter:'!(_id in $broader || _id in $related || _id in path("drafts.**") || _id == $self)',params:{self:t._id.replace("drafts.",""),broader:t.broader.map((e=>{let{_ref:t}=e;return t})),related:t.related.map((e=>{let{_ref:t}=e;return t}))}}}}}]}),e.defineField({name:"related",title:"Related Concept(s)",description:'Associative links between concepts indicate that the two are inherently "related", but that one is not in any way more general than the other. Broader and Associated relationships are mutually exclusive.',group:"relationship",type:"array",of:[{type:"reference",to:[{type:"skosConcept"}]}]}),e.defineField({name:"scopeNote",title:"Scope Note",type:"text",description:"A brief statement on the intended meaning of this concept, especially as an indication of how the use of the concept is limited in indexing practice",rows:3,group:"note"}),e.defineField({name:"definition",title:"Definition",type:"text",description:"A complete explanation of the intended meaning of the concept",rows:3,group:"note"}),e.defineField({name:"example",title:"Examples",type:"text",description:"An example of the use of the concept.",rows:3,group:"note"}),e.defineField({name:"topConcept",title:"Top Concept",group:"relationship",type:"boolean",description:t.jsxs(t.Fragment,{children:['NOTE: Top Concepts are determined at the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, set Top Concept to "false."',t.jsx("br",{}),t.jsx("br",{}),"Description: Top concepts provide an efficient entry point to broader/narrower concept hierarchies and/or top level facets. By convention, resources can be a Top Concept, or have Broader relationships, but not both."]}),hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.topConcept)}}),e.defineField({name:"scheme",title:"Concept Scheme(s)",group:"relationship",type:"reference",hidden:e=>{let{document:t}=e;return!(null==t?void 0:t.scheme)},description:t.jsxs(t.Fragment,{children:["NOTE: Concept Scheme inclusion is are determined from the Concept Scheme for version 2 of this plugin. Please migrate this value accordingly. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",t.jsx("br",{}),t.jsx("br",{}),"Description: Concept schemes are used to group concepts into defined sets, such as thesauri, classification schemes, or facets."]}),to:[{type:"skosConceptScheme"}],options:{disableNew:!0}})],orderings:[{title:"Top Concepts",name:"topConcept",by:[{field:"topConcept",direction:"desc"},{field:"prefLabel",direction:"asc"}]},{title:"Preferred Label",name:"prefLabel",by:[{field:"prefLabel",direction:"asc"}]}],preview:{select:{title:"prefLabel",topConcept:"topConcept",broader:"broader.0.prefLabel",broaderPlusOne:"broader.0.broader.0.prefLabel",broaderPlusTwo:"broader.0.broader.0.broader.0.prefLabel"},prepare(e){let{title:t,topConcept:n,broader:o,broaderPlusOne:r,broaderPlusTwo:s}=e;const a=[r,o].filter(Boolean),l=a.length>0?"".concat(a.join(" ▷ ")," ▶︎ ").concat(t):"",d=s?"... ".concat(l):l;return{title:t,subtitle:n?"Top Concept":d,media:n?i.AiOutlineTag:i.AiFillTag}}}}),a=e.defineType({name:"skosConceptScheme",title:"Concept Scheme",type:"document",icon:o.RiNodeTree,fields:[e.defineField({name:"title",title:"Title",type:"string",description:"Taxonomy schemes group concepts into defined sets, such as thesauri, classification schemes, or facets. Concepts may belong on many (or no) concept schemes, and you may create as many (or few) concept schemes as you like"}),e.defineField({name:"description",title:"Description",type:"text",rows:5,description:"Describe the intended use of this scheme."}),e.defineField({name:"topConcepts",title:"Top Concepts",type:"array",validation:e=>e.unique(),of:[e.defineArrayMember({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}}),e.defineField({name:"concepts",title:"Concepts",type:"array",validation:e=>e.unique(),of:[e.defineArrayMember({type:"reference",to:[{type:"skosConcept"}]})],options:{sortable:!1}})],preview:{select:{title:"title"},prepare(e){let{title:t}=e;return{title:t,media:o.RiNodeTree}}}}),l=e.defineType({name:"baseIri",title:"Base IRI",type:"object",description:"NOTE: conceptIriBase.iriValue is deprecated in version 2 of this plugin. Please migrate this value to the baseIri field above. This field will be removed in future versions of this plugin. To hide it in the meantime, unset this value (delete it).",hidden:e=>{let{document:t}=e;var i;return!(null==(i=null==t?void 0:t.conceptIriBase)?void 0:i.iriValue)},fields:[e.defineField({name:"iriValue",title:"IRI Value",type:"url",description:"Description: The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies."})],options:{collapsible:!0,collapsed:!1}});const d=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return e>6?"":'"childConcepts": *[_id in coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]).concepts[]._ref && ^._id in broader[]._ref ]|order(prefLabel){\n "id": _id,\n "level": '.concat(e,",\n prefLabel,\n ").concat(d(e+1),"\n }")},c=e=>{let{concepts:i}=e;return t.jsx("ul",{style:{listStyle:"none"},children:i.map((e=>{var i;return t.jsxs("li",{style:{fontWeight:"normal",marginTop:".75rem"},children:[e.prefLabel,(null==(i=e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)}))})},p=i=>{let{document:o,documentId:s}=i;const a=e.useClient({apiVersion:"2021-10-21"}),[l,p]=r.useState([]),[u,h]=r.useState(!0),[f,m]=r.useState(!1),[b,y]=r.useState(!1);return r.useEffect((()=>{if(void 0===o.displayed._id)return;if(!(s===o.displayed._id.replace(/^drafts\./,"")))return void console.log("document not loaded yet.");(async()=>{h(!0),m(!1),y(!1);try{const e=await a.fetch('coalesce(*[_id == \'drafts.\' + $id][0], *[_id == $id][0]) {\n "topConcepts":topConcepts[]->|order(prefLabel){\n "id": _id,\n "level": 0,\n prefLabel,\n '.concat(d(),"\n },\n \"orphans\": *[\n // filter to concepts in this scheme only:\n _id in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref // filter to concepts in this scheme only\n // filter out concepts that reference a topConcept in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).topConcepts[]._ref]) < 1\n // filter out concepts that reference other concepts in this scheme as a broader term:\n && count((broader[]._ref) [@ in coalesce(*[_id == 'drafts.' + $id][0], *[_id == $id][0]).concepts[]._ref]) < 1\n ]|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n prefLabel,\n ").concat(d(),"\n }\n }"),{id:s});null==e.topConcepts&&e.orphans.length<1?m(!0):(h(!1),p(e))}catch(e){console.log("Error: ",e),y(!0)}h(!1)})()}),[s,o.displayed._id,o.displayed.concepts,o.displayed.topConcepts]),1==b?t.jsx("p",{children:"Sorry, could not get concepts."}):1==f?t.jsx("p",{children:"This scheme does not yet have any concepts assigned to it."}):u?t.jsxs(n.Flex,{align:"center",direction:"column",gap:5,height:"fill",justify:"center",style:{paddingTop:"1rem"},onResize:void 0,onResizeCapture:void 0,children:[t.jsx(n.Spinner,{muted:!0,onResize:void 0,onResizeCapture:void 0}),t.jsx(n.Text,{muted:!0,size:1,onResize:void 0,onResizeCapture:void 0,children:"Loading hierarchy…"})]}):t.jsxs("ul",{style:{listStyle:"none",paddingLeft:"0",marginTop:"1rem"},children:[l.topConcepts.map((e=>{var i;if(null==e?void 0:e.id)return t.jsxs("li",{style:{paddingTop:".5rem",fontWeight:"bold",marginTop:".75rem"},children:[t.jsxs(n.Inline,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"top concept"})]}),(null==(i=null==e?void 0:e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)})),l.orphans.map((e=>{var i;return t.jsxs("li",{style:{paddingTop:".5rem",fontWeight:"normal",marginTop:".75rem"},children:[t.jsxs(n.Inline,{space:2,onResize:void 0,onResizeCapture:void 0,children:[null==e?void 0:e.prefLabel,t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==(i=e.childConcepts)?void 0:i.length)>0&&t.jsx(c,{concepts:e.childConcepts})]},e.id)}))]})},u=e.definePlugin({name:"taxonomyManager",schema:{types:[s,a,l]}});exports.TreeView=e=>{let{document:i,documentId:o}=e;return t.jsx(n.Container,{width:1,style:{paddingTop:"1.25rem"},onResize:void 0,onResizeCapture:void 0,children:t.jsx(n.Box,{padding:4,onResize:void 0,onResizeCapture:void 0,children:t.jsxs(n.Stack,{space:2,children:[t.jsx(n.Text,{size:1,weight:"semibold",onResize:void 0,onResizeCapture:void 0,children:"Hierarchy Tree"}),t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"Concept hierarchy is determined by 'Broader' relationships assigned to each concept."}),t.jsx(p,{document:i,documentId:o})]})})})},exports.taxonomyManager=u;//# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-taxonomy-manager",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.1",
4
4
  "description": "Create and manage SKOS compliant taxonomies, thesauri, and classification schemes in Sanity Studio.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -79,47 +79,40 @@ const Hierarchy = ({document, documentId}: {document: any, documentId: any}) =>
79
79
  const [isLoading, setIsLoading] = useState(true)
80
80
  const [noConcept, setNoConcept] = useState(false)
81
81
  const [isError, setIsError] = useState(false)
82
- const [currentDoc, setCurrentDoc] = useState(documentId)
83
82
 
84
83
  useEffect(() => {
85
84
  if (document.displayed._id === undefined ) return
86
85
 
87
- const fetchConcepts = async () => {
86
+ const isReady = documentId === document.displayed._id.replace(/^drafts\./, '')
88
87
 
89
- console.log(concepts)
88
+ if (!isReady) {
89
+ console.log('document not loaded yet.')
90
+ return
91
+ }
90
92
 
91
- setCurrentDoc(documentId)
93
+ const fetchConcepts = async () => {
92
94
 
93
95
  // reset state variables for new fetch
94
96
  setIsLoading(true)
95
97
  setNoConcept(false)
96
98
  setIsError(false)
97
99
 
98
- // tree view has been loaded at least once, first wait to allow the content lake to update
99
- // documentID and document.displayed also don't align on first render
100
- concepts.length != 0 && currentDoc == documentId && await sleep(1000)
101
-
102
- await client.fetch(
103
- trunkBuilder(),
104
- {id: documentId}
105
- )
106
- .then(res => {
100
+ try {
101
+ const res = await client.fetch(trunkBuilder(), {id: documentId})
107
102
  if (res.topConcepts == null && res.orphans.length < 1) {
108
- setIsLoading(false)
109
103
  setNoConcept(true)
110
104
  } else {
111
105
  setIsLoading(false)
112
106
  setConcepts(res)
113
- }
114
- })
115
- .catch(error => {
107
+ }
108
+ } catch (error) {
116
109
  console.log('Error: ', error)
117
- setIsLoading(false)
118
110
  setIsError(true)
119
- })
111
+ }
112
+ setIsLoading(false)
120
113
  }
121
114
  fetchConcepts()
122
- }, [documentId, document.displayed.concepts, document.displayed.topConcepts])
115
+ }, [documentId, document.displayed._id, document.displayed.concepts, document.displayed.topConcepts])
123
116
 
124
117
  if (isError == true) {
125
118
  return <p>Sorry, could not get concepts.</p>
@@ -15,7 +15,7 @@ export const TreeView = ({document, documentId}: {document: any; documentId: any
15
15
  Hierarchy Tree
16
16
  </Text>
17
17
  <Text size={1} muted={true} onResize={undefined} onResizeCapture={undefined}>
18
- Concept hierarchy is determined by 'Broader' relationships assigned to each concept..
18
+ Concept hierarchy is determined by 'Broader' relationships assigned to each concept.
19
19
  </Text>
20
20
  <Hierarchy document={document} documentId={documentId} />
21
21
  </Stack>