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 +6 -6
- package/lib/index.esm.js +1 -1
- package/lib/index.js +1 -1
- package/lib/src/index.d.ts +12 -2
- package/package.json +1 -1
- package/src/components/Hierarchy.tsx +30 -39
- package/src/components/PrefLabel.tsx +2 -1
- package/src/components/TreeView.tsx +3 -2
- package/src/types.ts +25 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
+
**Scope Notes, Definition, and Examples**
|
|
117
117
|
Standard optional SKOS documentation fields are included by default.
|
|
118
118
|
|
|
119
|
-
|
|
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
|
-
##
|
|
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
|
package/lib/src/index.d.ts
CHANGED
|
@@ -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:
|
|
10
|
-
documentId:
|
|
19
|
+
document: DocumentVersionsCollection
|
|
20
|
+
documentId: string
|
|
11
21
|
}) => JSX.Element
|
|
12
22
|
|
|
13
23
|
export {}
|
package/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
82
|
+
const isReady = documentId === document.displayed._id.replace(/^drafts\./, '')
|
|
88
83
|
|
|
89
|
-
|
|
84
|
+
if (!isReady) {
|
|
85
|
+
console.log('document not loaded yet.')
|
|
86
|
+
return
|
|
87
|
+
}
|
|
90
88
|
|
|
91
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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:
|
|
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
|
|
144
|
+
{concept?.childConcepts && concept.childConcepts.length > 0 && <ChildConcepts concepts={concept.childConcepts} />}
|
|
156
145
|
</li>
|
|
157
146
|
)
|
|
158
147
|
})}
|
|
159
|
-
{concepts.orphans.map((concept:
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
+
}
|