sanity-plugin-taxonomy-manager 2.0.0 → 2.0.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/README.md CHANGED
@@ -107,23 +107,23 @@ export const structure = (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&&r.topConcepts.map((e=>{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==e?void 0:e.childConcepts)&&e.childConcepts.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,(null==(t=r.topConcept)?void 0:t.length)>0&&s(u,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==e?void 0:e.childConcepts)&&e.childConcepts.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&&l.topConcepts.map((e=>{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==e?void 0:e.childConcepts)&&e.childConcepts.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,(null==(i=l.topConcept)?void 0:i.length)>0&&t.jsx(n.Text,{size:1,muted:!0,onResize:void 0,onResizeCapture:void 0,children:"orphan"})]}),(null==e?void 0:e.childConcepts)&&e.childConcepts.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,4 +1,14 @@
1
+ /// <reference types="react" />
2
+
1
3
  import {Plugin as Plugin_2} from 'sanity'
4
+ import {SanityDocument} from '@sanity/client'
5
+
6
+ declare interface DocumentVersionsCollection {
7
+ displayed: SanityDocument
8
+ published: SanityDocument
9
+ draft: SanityDocument
10
+ historical: SanityDocument
11
+ }
2
12
 
3
13
  export declare const taxonomyManager: Plugin_2<void>
4
14
 
@@ -6,8 +16,8 @@ export declare const TreeView: ({
6
16
  document,
7
17
  documentId,
8
18
  }: {
9
- document: any
10
- documentId: any
19
+ document: DocumentVersionsCollection
20
+ documentId: string
11
21
  }) => JSX.Element
12
22
 
13
23
  export {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-taxonomy-manager",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Create and manage SKOS compliant taxonomies, thesauri, and classification schemes in Sanity Studio.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -5,6 +5,7 @@
5
5
  import {Flex, Spinner, Text, Inline} from '@sanity/ui'
6
6
  import {useEffect, useState} from 'react'
7
7
  import {useClient} from 'sanity'
8
+ import { TopConceptTerms, ChildConceptTerms, DocumentVersionsCollection } from '../types'
8
9
 
9
10
  // CSS module import plagued by an inscrutable Rollup error. To address in future work.
10
11
 
@@ -50,28 +51,23 @@ const branchBuilder = (level = 1): string | void => {
50
51
  }
51
52
  }
52
53
 
53
- // setTimeout function
54
- const sleep = (ms: number) => {
55
- console.log('waiting')
56
- return new Promise((resolve) => setTimeout(resolve, ms))
57
- }
58
- const ChildConcepts = ({concepts}: {concepts: any}) => {
54
+ const ChildConcepts = ({concepts}: {concepts: ChildConceptTerms[]}): JSX.Element => {
59
55
  return (
60
- <ul style={{listStyle: 'none'}}>
56
+ <ul style={{ listStyle: 'none' }}>
61
57
  {concepts.map((concept: any) => {
62
58
  return (
63
59
  <li key={concept.id}
64
- style={{fontWeight: 'normal', marginTop: '.75rem'}}>
60
+ style={{ fontWeight: 'normal', marginTop: '.75rem' }}>
65
61
  {concept.prefLabel}
66
62
  {concept.childConcepts?.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
67
63
  </li>
68
- )
64
+ );
69
65
  })}
70
66
  </ul>
71
- )
67
+ );
72
68
  }
73
69
 
74
- const Hierarchy = ({document, documentId}: {document: any, documentId: any}) => {
70
+ const Hierarchy = ({document, documentId}: {document: DocumentVersionsCollection, documentId: string}) => {
75
71
 
76
72
  const client = useClient({apiVersion: '2021-10-21'})
77
73
 
@@ -79,47 +75,40 @@ const Hierarchy = ({document, documentId}: {document: any, documentId: any}) =>
79
75
  const [isLoading, setIsLoading] = useState(true)
80
76
  const [noConcept, setNoConcept] = useState(false)
81
77
  const [isError, setIsError] = useState(false)
82
- const [currentDoc, setCurrentDoc] = useState(documentId)
83
78
 
84
79
  useEffect(() => {
85
80
  if (document.displayed._id === undefined ) return
86
81
 
87
- const fetchConcepts = async () => {
82
+ const isReady = documentId === document.displayed._id.replace(/^drafts\./, '')
88
83
 
89
- console.log(concepts)
84
+ if (!isReady) {
85
+ console.log('document not loaded yet.')
86
+ return
87
+ }
90
88
 
91
- setCurrentDoc(documentId)
89
+ const fetchConcepts = async () => {
92
90
 
93
91
  // reset state variables for new fetch
94
92
  setIsLoading(true)
95
93
  setNoConcept(false)
96
94
  setIsError(false)
97
95
 
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 => {
96
+ try {
97
+ const res = await client.fetch(trunkBuilder(), {id: documentId})
107
98
  if (res.topConcepts == null && res.orphans.length < 1) {
108
- setIsLoading(false)
109
99
  setNoConcept(true)
110
100
  } else {
111
101
  setIsLoading(false)
112
102
  setConcepts(res)
113
- }
114
- })
115
- .catch(error => {
103
+ }
104
+ } catch (error) {
116
105
  console.log('Error: ', error)
117
- setIsLoading(false)
118
106
  setIsError(true)
119
- })
107
+ }
108
+ setIsLoading(false)
120
109
  }
121
110
  fetchConcepts()
122
- }, [documentId, document.displayed.concepts, document.displayed.topConcepts])
111
+ }, [documentId, document.displayed._id, document.displayed.concepts, document.displayed.topConcepts])
123
112
 
124
113
  if (isError == true) {
125
114
  return <p>Sorry, could not get concepts.</p>
@@ -141,7 +130,7 @@ const Hierarchy = ({document, documentId}: {document: any, documentId: any}) =>
141
130
  </Flex>
142
131
  ) : (
143
132
  <ul style={{listStyle: 'none', paddingLeft: '0', marginTop: '1rem'}}>
144
- {concepts.topConcepts.map((concept: any) => {
133
+ {concepts.topConcepts && concepts.topConcepts.map((concept: TopConceptTerms) => {
145
134
  if (concept?.id)
146
135
  return (
147
136
  <li key={concept.id}
@@ -152,21 +141,23 @@ const Hierarchy = ({document, documentId}: {document: any, documentId: any}) =>
152
141
  top concept
153
142
  </Text>
154
143
  </Inline>
155
- {concept?.childConcepts?.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
144
+ {concept?.childConcepts && concept.childConcepts.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
156
145
  </li>
157
146
  )
158
147
  })}
159
- {concepts.orphans.map((concept: any) => {
148
+ {concepts.orphans.map((concept: ChildConceptTerms) => {
160
149
  return (
161
150
  <li key={concept.id}
162
151
  style={{paddingTop: '.5rem', fontWeight: 'normal', marginTop: '.75rem'}}>
163
152
  <Inline space={2} onResize={undefined} onResizeCapture={undefined}>
164
- {concept?.prefLabel}
165
- <Text size={1} muted={true} onResize={undefined} onResizeCapture={undefined}>
166
- orphan
167
- </Text>
153
+ {concept?.prefLabel}
154
+ {concepts.topConcept?.length > 0 &&
155
+ <Text size={1} muted={true} onResize={undefined} onResizeCapture={undefined}>
156
+ orphan
157
+ </Text>
158
+ }
168
159
  </Inline>
169
- {concept.childConcepts?.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
160
+ {concept?.childConcepts && concept.childConcepts.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
170
161
  </li>
171
162
  )
172
163
  })}
@@ -5,8 +5,9 @@
5
5
 
6
6
  import {Stack, Text} from '@sanity/ui'
7
7
  import { useFormValue } from "sanity"
8
+ import { PrefLabelValue } from '../types'
8
9
 
9
- export function PrefLabel(props: any) {
10
+ export function PrefLabel(props: PrefLabelValue) {
10
11
  const baseIri = useFormValue(['baseIri'])
11
12
  return (
12
13
  <Stack space={2}>
@@ -1,7 +1,8 @@
1
1
  import {Container, Stack, Box, Text} from '@sanity/ui'
2
2
  import Hierarchy from './Hierarchy'
3
+ import { DocumentVersionsCollection } from '../types'
3
4
 
4
- export const TreeView = ({document, documentId}: {document: any; documentId: any}) => {
5
+ export const TreeView = ({document, documentId}: {document: DocumentVersionsCollection, documentId: string}) => {
5
6
  return (
6
7
  <Container
7
8
  width={1}
@@ -15,7 +16,7 @@ export const TreeView = ({document, documentId}: {document: any; documentId: any
15
16
  Hierarchy Tree
16
17
  </Text>
17
18
  <Text size={1} muted={true} onResize={undefined} onResizeCapture={undefined}>
18
- Concept hierarchy is determined by 'Broader' relationships assigned to each concept..
19
+ Concept hierarchy is determined by 'Broader' relationships assigned to each concept.
19
20
  </Text>
20
21
  <Hierarchy document={document} documentId={documentId} />
21
22
  </Stack>
package/src/types.ts ADDED
@@ -0,0 +1,25 @@
1
+ import {SanityDocument} from '@sanity/client'
2
+
3
+ export interface TopConceptTerms {
4
+ prefLabel: string;
5
+ id: string;
6
+ childConcepts?: ChildConceptTerms[];
7
+ }
8
+
9
+ export interface ChildConceptTerms {
10
+ prefLabel: string;
11
+ id: string;
12
+ childConcepts?: ChildConceptTerms[];
13
+ }
14
+
15
+ export interface DocumentVersionsCollection {
16
+ displayed: SanityDocument
17
+ published: SanityDocument
18
+ draft: SanityDocument
19
+ historical: SanityDocument
20
+ }
21
+
22
+ export interface PrefLabelValue {
23
+ value: string;
24
+ renderDefault: (props: PrefLabelValue) => React.ReactElement
25
+ }