sanity-plugin-graph-view 1.0.7 → 2.0.0
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/LICENSE +1 -1
- package/README.md +70 -40
- package/lib/index.esm.js +2 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/src/index.d.ts +9 -0
- package/package.json +79 -34
- package/sanity.json +2 -6
- package/src/index.ts +1 -0
- package/src/plugin.tsx +30 -0
- package/src/tool/GraphView.tsx +489 -0
- package/{lib/tool/GraphToolIcon.js → src/tool/GraphViewIcon.tsx} +9 -28
- package/src/tool/GraphViewStyle.tsx +63 -0
- package/src/tool/hooks.ts +36 -0
- package/src/tool/utils.ts +51 -0
- package/v2-incompatible.js +11 -0
- package/.editorconfig +0 -16
- package/.eslintignore +0 -1
- package/.eslintrc.js +0 -18
- package/.prettierrc +0 -6
- package/assets/head-silhouette.jpg +0 -0
- package/assets/sanity-logo.png +0 -0
- package/assets/screengrab.gif +0 -0
- package/babel.config.js +0 -13
- package/config.dist.json +0 -1
- package/lib/tool/GraphTool.css +0 -56
- package/lib/tool/GraphTool.js +0 -980
- package/lib/tool/GraphView.css +0 -56
- package/lib/tool/GraphView.js +0 -724
- package/lib/tool/GraphViewIcon.js +0 -32
- package/lib/tool/hooks.js +0 -33
- package/lib/tool/index.js +0 -21
- package/lib/tool/utils.js +0 -76
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -5,79 +5,109 @@
|
|
|
5
5
|
<p><img src="assets/screengrab.gif" width="540" alt="Screengrab of the Graph tool" /></p>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
|
+
> This is a **Sanity Studio v3** plugin.
|
|
9
|
+
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-graph-view/tree/studio-v2).
|
|
10
|
+
|
|
8
11
|
Wonder how a visualization of your dataset will look? How many authors do you have? How many items have they worked on? And are currently working on! Edits and changes are shown in real-time!
|
|
9
12
|
|
|
10
13
|
**Explore your data with this plugin, seek out strange corners and data types, boldly go where you could not before!**
|
|
11
14
|
|
|
12
|
-
## Installation
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm install --save sanity-plugin-graph-view
|
|
19
|
+
```
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
or
|
|
15
22
|
|
|
16
|
-
```json
|
|
17
|
-
"resolutions": {
|
|
18
|
-
"**/three": "0.119.1"
|
|
19
|
-
}
|
|
20
23
|
```
|
|
24
|
+
yarn add sanity-plugin-graph-view
|
|
25
|
+
```
|
|
26
|
+
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
# In your Sanity Studio repository:
|
|
24
|
-
sanity install graph-view
|
|
28
|
+
## Usage
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
Add it as a plugin in sanity.config.ts (or .js):
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { contentGraphView } from "sanity-plugin-graph-view";
|
|
34
|
+
|
|
35
|
+
export default defineConfigConfig({
|
|
36
|
+
// ...
|
|
37
|
+
plugins: [
|
|
38
|
+
contentGraphView({}),
|
|
39
|
+
]
|
|
40
|
+
})
|
|
28
41
|
```
|
|
29
42
|
|
|
43
|
+
This will add a /graph-your-content tools to the Sanity Studio, configured with this default query:
|
|
44
|
+
```
|
|
45
|
+
*[
|
|
46
|
+
!(_id in path("_.*")) &&
|
|
47
|
+
!(_type match "system.*") &&
|
|
48
|
+
!(_type match "sanity.*")
|
|
49
|
+
]
|
|
50
|
+
````
|
|
51
|
+
|
|
30
52
|
## Configuration
|
|
31
53
|
|
|
32
|
-
|
|
54
|
+
You can control which documents appear in the graph by providing a query:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import { contentGraphView } from "sanity-plugin-graph-view";
|
|
33
58
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
export default defineConfig({
|
|
60
|
+
// ...
|
|
61
|
+
plugins: [
|
|
62
|
+
contentGraphView({
|
|
63
|
+
"query": "*[_type in ['a', 'b']]",
|
|
64
|
+
apiVersion: "2022-09-01" // optional, default shown
|
|
65
|
+
}),
|
|
66
|
+
]
|
|
67
|
+
})
|
|
38
68
|
```
|
|
39
69
|
|
|
40
|
-
For references to turn into graph edges, the entire document must be fetched,
|
|
70
|
+
For references to turn into graph edges, the entire document must be fetched,
|
|
71
|
+
but you can also selectively filter what references will be included. For example:
|
|
41
72
|
|
|
42
|
-
```
|
|
43
|
-
{
|
|
73
|
+
```js
|
|
74
|
+
contentGraphView({
|
|
44
75
|
"query": "*[_type in ['a', 'b']]{ 'refs': [author, publisher] }"
|
|
45
|
-
}
|
|
76
|
+
})
|
|
46
77
|
```
|
|
47
78
|
|
|
48
79
|
By default, the plugin uses `doc.title || doc.name || doc._id` as the node label.
|
|
49
80
|
|
|
50
81
|
If you want to use another property, compute a `title` property in your query, e.g.:
|
|
51
82
|
|
|
52
|
-
|
|
53
|
-
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
contentGraphView({
|
|
54
86
|
"query": "*[_type in ['a', 'b']] { ..., \"title\": select(_type == 'a' => 'Title A', _type == 'b' => 'Title B') }"
|
|
55
|
-
}
|
|
87
|
+
})
|
|
56
88
|
```
|
|
57
89
|
|
|
58
|
-
##
|
|
90
|
+
## Get help in the Sanity Community
|
|
59
91
|
|
|
60
|
-
|
|
92
|
+
[](https://slack.sanity.io/)
|
|
61
93
|
|
|
62
|
-
|
|
63
|
-
git clone git@github.com:sanity-io/sanity-plugin-graph-view.git
|
|
64
|
-
cd sanity-plugin-graph-view
|
|
65
|
-
yarn
|
|
66
|
-
yarn link
|
|
94
|
+
Join [Sanity’s developer community](https://slack.sanity.io) or ping us [on twitter](https://twitter.com/sanity_io).
|
|
67
95
|
|
|
68
|
-
|
|
69
|
-
yarn link sanity-plugin-graph-view
|
|
96
|
+
## License
|
|
70
97
|
|
|
71
|
-
|
|
72
|
-
yarn lint
|
|
73
|
-
```
|
|
98
|
+
MIT-licensed. See LICENSE.
|
|
74
99
|
|
|
75
|
-
##
|
|
100
|
+
## Develop & test
|
|
76
101
|
|
|
77
|
-
|
|
102
|
+
This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
|
|
103
|
+
with default configuration for build & watch scripts.
|
|
78
104
|
|
|
79
|
-
|
|
105
|
+
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
106
|
+
on how to run this plugin with hotreload in the studio.
|
|
80
107
|
|
|
81
|
-
|
|
108
|
+
### Release new version
|
|
109
|
+
|
|
110
|
+
Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-graph-view/actions/workflows/main.yml).
|
|
111
|
+
Make sure to select the v3 branch and check "Release new version".
|
|
82
112
|
|
|
83
|
-
|
|
113
|
+
Semantic release will only release on configured branches, so it is safe to run release on any branch.
|
package/lib/index.esm.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t,e,n,r,o,i;function s(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function a(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?s(Object(n),!0).forEach((function(e){c(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function c(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){return e||(e=t.slice(0)),Object.freeze(Object.defineProperties(t,{raw:{value:Object.freeze(e)}}))}import{jsx as d,jsxs as h}from"react/jsx-runtime";import{rgba as u}from"polished";import f from"deep-equal";import{useEffect as p,useState as m,useCallback as y}from"react";import{ForceGraph2D as g}from"react-force-graph";import{v4 as b}from"uuid";import v from"bezier-easing";import{useUserColorManager as x,useClient as _,definePlugin as w}from"sanity";import{useRouter as k,route as O}from"sanity/router";import j from"styled-components";import{black as M,gray as P,white as I,hues as S,COLOR_HUES as q}from"@sanity/color";import{useTheme as A}from"@sanity/ui";function D(t){return null===t?0:"object"==typeof t?Object.entries(t).reduce(((t,e)=>{let[n,r]=e;return t+D(n)+D(r)}),0):Array.isArray(t)?Object.entries(t).reduce(((t,e)=>t+D(e)),0):"string"==typeof t?t.length:1}const T=j.div(t||(t=l(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),(t=>{let{theme:e}=t;return e.fonts.text.family}),M.hex),C=j.div(e||(e=l(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),V=j.div(n||(n=l(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),(t=>{let{theme:e}=t;return e.fonts.text.family}),(t=>{let{theme:e}=t;return e.space[0]}),(t=>{let{theme:e}=t;return e.radius[2]}),(t=>{let{theme:e}=t;return e.space[2]})),W=j.div(r||(r=l(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),(t=>{let{theme:e}=t;return e.space[4]}),(t=>{let{theme:e}=t;return e.space[4]})),z=j.div(o||(o=l(["\n display: flex;\n"]))),B=j.div(i||(i=l(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),(t=>{let{theme:e}=t;return e.space[2]})),N=v(0,.9,1,1),E=v(.25,.1,0,1);function H(t){return(t.substring(0,1).toUpperCase()+t.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function R(t){const e={};for(const n of t)e[n._type]=(e[n._type]||0)+1;return e}function U(t){return"".concat(t.title||t.name||t._id).trim()}function F(t,e){return 5+D(t)/e*100}function G(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==t)if("object"==typeof t)for(const[n,r]of Object.entries(t))"_ref"===n&&"string"==typeof r&&r.length>0&&e.push(L(r)),G(r,e);else if(Array.isArray(t))for(const n of t)G(n,e);return e}function L(t){return t.replace(/^drafts\./,"")}class Z{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class ${constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const e={};for(const n of t)e[n._id]=n;this.data={nodes:t.map((t=>Object.assign({id:t._id,type:"document",doc:t}))),links:t.flatMap((t=>G(t).map((e=>({source:t._id,target:e}))))).filter((t=>e[t.source]&&e[t.target]))}}setSession(t,e){let n=this.sessions.find((n=>{var r;return n.user.id===t.id&&(null==(r=n.doc)?void 0:r._id)===e.doc._id}));n||(n=new Z,n.id=b(),n.user=t,n.startTime=Date.now(),n.doc=e.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let t=0;t<this.sessions.length;t++){const e=this.sessions[t];Date.now()-e.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,t),...this.sessions.slice(t+1)],t--)}}clone(){const t=new $;return Object.assign(t,this),t.data={nodes:[...this.data.nodes],links:[...this.data.links]},t}}const J=new class{constructor(){this._users=[]}async getById(t,e){let n=this._users.find((e=>e._id===t));var r,o,i;return n||(n=await e.users.getById(t),this._users.push(n),n.image=await(r=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",o=40,i=40,new Promise((t=>{const e=new Image(o,i);e.onload=()=>{t(e)},e.onerror=e=>{console.log("Image error",e),t(null)},e.src=r})))),n}};function K(t){var e;const n=t.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',r=null!=(e=t.apiVersion)?e:"2022-09-01",o=x(),[i,s]=m(0),[a,c]=m(null),[l,b]=m([]),[v,w]=m({}),[O,j]=m((()=>new $)),S=k(),q=_({apiVersion:r}),Z=y((t=>{const e=function(t){const e={};for(const n of t)/^drafts\./.test(n._id)||(e[n._id]=n);for(const n of t)if(/^drafts\./.test(n._id)){const t=L(n._id);e[t]=Object.assign(n,{_id:t})}return Object.values(e)}(t);s(Math.max(...e.map(D))),b(e),w(R(e)),j(new $(e))}),[]),K=y((async t=>{const e=t.result;if(e){e._id=L(e._id);const n={};for(const t of l)n[t._id]=t;let r;const o=[...l],i=l.findIndex((t=>t._id===e._id));i>=0?(r=o[i],o[i]=e):o.push(e),b(o),w(R(o)),s(Math.max(...o.map(D)));const a=O.clone(),c=G(r||{}).filter((t=>t===e._id||null!==n[t])),d=G(e).filter((t=>t===e._id||null!==n[t]));let h,u=!f(c,d);u&&(a.data.links=a.data.links.filter((t=>t.source.id!==e._id)).concat(d.map((t=>({source:e._id,target:t})))));const p=O.data.nodes.findIndex((t=>t.doc&&t.doc._id===e._id));p>=0?(h=O.data.nodes[p],h.doc=e):(h={id:e._id,type:"document",doc:e},a.data.nodes.push(h),u=!0),u&&j(a);const m=await J.getById(t.identity,q);O.setSession(m,h)}else if("disappear"===t.transition){const e=L(t.documentId),n=l.filter((t=>t._id!==e));b(n),w(R(n)),s(Math.max(...n.map(D)));const r=O.clone();r.data.links=r.data.links.filter((t=>t.source.id!==e&&t.target.id!==e)),r.data.nodes=r.data.nodes.filter((t=>t.id!==e)),j(r)}}),[l,O,q]);!function(t,e,n,r){p((()=>{r.fetch(t).then((t=>{e(t)}))}),[...n,r])}(n,Z,[],q),function(t,e,n,r,o,i){p((()=>{const o=i.listen(t,e,n).subscribe((t=>{r(t)}));return()=>{o.unsubscribe()}}),[...o,i])}(n,{},{},K,[l,O],q),p((()=>{const t=setInterval((()=>O.reapSessions()),1e3);return()=>clearInterval(t)}),[O]);const Q=A().sanity;return d(C,{theme:Q,children:h(T,{theme:Q,children:[d(W,{theme:Q,children:(X=v,(tt=Object.keys(X),et=t=>X[t]||0,tt.sort(((t,e)=>{const n=et(t),r=et(e);return n<r?-1:n>r?1:0}))).reverse().slice(0,10)).map((t=>h(z,{className:"legend__row",style:{color:Y(t).fill},children:[d(B,{theme:Q}),d("div",{children:H(t)})]},t)))}),a&&d(V,{theme:Q,children:U(a.doc)}),d(g,{graphData:O.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:t=>c(t),onNodeClick:t=>{S.navigateIntent("edit",{id:t.doc._id,documentType:t.doc._type})},linkColor:()=>u(P[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:t=>F(t.doc,i),onRenderFramePost:(t,e)=>{for(const n of O.sessions){const r=O.data.nodes.find((t=>{var e;return t.doc&&t.doc._id===(null==(e=null==n?void 0:n.doc)?void 0:e._id)}));if(r){const s=1e4,a=n.angle,c=Math.sqrt(F(r.doc,i)),l=n.user.image,d=o.get(n.user.displayName).tints[400].hex,h=c*e+40,f=l?l.width:0,p=l?l.height:0,m=r.x+Math.sin(a)*h/e,y=r.y+Math.cos(a)*h/e;t.save();try{if(t.globalAlpha=N(1-Math.min(s,Date.now()-n.lastActive)/s),t.font="bold ".concat(Math.round(12/e),"px sans-serif"),t.beginPath(),t.strokeStyle=u(I.hex,1),t.lineWidth=2/e,t.moveTo(r.x+Math.sin(a)*(h-f/2)/e,r.y+Math.cos(a)*(h-p/2)/e),t.lineTo(r.x+Math.sin(a)*c,r.y+Math.cos(a)*c),t.stroke(),t.beginPath(),t.strokeStyle=u(I.hex,1),t.lineWidth=2/e,t.arc(r.x,r.y,c,0,2*Math.PI,!1),t.stroke(),l){t.save();try{const r=700,o=E(Math.max(0,(r-(Date.now()-n.startTime))/r));o>0&&(t.beginPath(),t.fillStyle=u(d,o),t.arc(m,y,(f/2+10)/e,0,2*Math.PI,!1),t.fill()),t.beginPath(),t.fillStyle=u(I.hex,1),t.arc(m,y,f/e/2,0,2*Math.PI,!1),t.clip(),t.drawImage(l,m-f/e/2,y-p/e/2,f/e,p/e),t.strokeStyle=M.hex,t.lineWidth=6/e,t.stroke(),t.strokeStyle=d,t.lineWidth=4/e,t.stroke()}finally{t.restore()}}t.beginPath(),t.strokeStyle=u(M.hex,1),t.lineWidth=.5/e,t.arc(m,y,f/e/2,0,2*Math.PI,!1),t.stroke();const o=a>=Math.PI/2&&a<1.5*Math.PI,i=o?y-(p/2+5)/e:y+(p/2+5)/e;t.fillStyle=u(I.hex,1),t.textAlign="center",t.textBaseline=o?"bottom":"top",t.fillText(n.user.displayName,m,i)}finally{t.restore()}}}},nodeCanvasObject:(t,e,n)=>{if("document"===t.type){const s=Y(t.doc._type),c=Math.sqrt(F(t.doc,i)),l=Math.min(100,10/n);if(e.beginPath(),e.fillStyle=null!==a&&t.doc._id===a.doc._id?u(P[500].hex,.8):s.fill,e.strokeStyle=s.border,e.lineWidth=.5,e.arc(t.x,t.y,c,0,2*Math.PI,!1),e.stroke(),e.fill(),c*n>10){e.font="".concat(l,"px sans-serif");const i=2*c+30/n;for(let s=50;s>=5;s/=1.2){const a=(r=U(t.doc),o=Math.round(s),r.length>o?"".concat(r.substring(0,o),"…"):r);if(e.measureText(a).width<i){e.textAlign="center",e.textBaseline="top",e.strokeStyle=u(M.hex,.5),e.lineWidth=2/n,e.strokeText(a,t.x,t.y+c+5/n),e.fillText(a,t.x,t.y+c+5/n);break}}}}var r,o},linkCanvasObject:(t,e,n)=>{e.beginPath(),e.strokeStyle=u(P[500].hex,.125),e.lineWidth=2/n,e.moveTo(t.source.x,t.source.y),e.lineTo(t.target.x,t.target.y),e.stroke()}})]})});var X,tt,et}const Q={};let X=0;function Y(t){if(Q[t])return Q[t];const e=q[X%q.length];return X+=1,Q[t]={fill:S[e][400].hex,border:u(M.hex,.5)},Q[t]}const tt=()=>d("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:d("path",{fill:"currentColor",d:"M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z"})}),et=w((function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:e=>[...e,{name:"graph-your-content",title:"Graph",icon:tt,component:function(){return d(K,a({},t))},router:O.create("/:selectedDocumentId")}]}}));export{et as contentGraphView};
|
|
2
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/tool/utils.ts","../src/tool/GraphViewStyle.tsx","../src/tool/GraphView.tsx","../src/tool/hooks.ts","../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport function sizeOf(value: any): number {\n if (value === null) {\n return 0\n }\n\n if (typeof value === 'object') {\n return Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0)\n }\n\n if (Array.isArray(value)) {\n return Object.entries(value).reduce((total, v) => total + sizeOf(v), 0)\n }\n\n if (typeof value === 'string') {\n return value.length\n }\n\n return 1\n}\n\nexport function loadImage(url: string, w: number, h: number): Promise<HTMLImageElement | null> {\n return new Promise((resolve) => {\n const img = new Image(w, h)\n img.onload = () => {\n resolve(img)\n }\n img.onerror = (event) => {\n // eslint-disable-next-line no-console\n console.log('Image error', event)\n resolve(null)\n }\n img.src = url\n })\n}\n\nexport function sortBy<T>(array: T[], f: (t: T) => number): T[] {\n return array.sort((a, b) => {\n const va = f(a)\n const vb = f(b)\n // eslint-disable-next-line no-nested-ternary\n return va < vb ? -1 : va > vb ? 1 : 0\n })\n}\n\nexport function truncate(s: string, limit: number): string {\n if (s.length > limit) {\n return `${s.substring(0, limit)}…`\n }\n return s\n}\n","import styled from 'styled-components'\nimport {Theme} from '@sanity/ui'\nimport {black} from '@sanity/color'\nimport React, {PropsWithChildren} from 'react'\n\ntype Style = PropsWithChildren<{theme: SanityTheme}>\ntype SanityTheme = Theme['sanity']\n\nexport const GraphRoot: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ${black.hex};\n`\n\nexport const GraphWrapper: React.FC<Style> = styled.div`\n position: relative;\n width: 100%;\n height: 100%;\n` as React.FC<Style>\n\nexport const HoverNode: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n display: none;\n position: absolute;\n bottom: ${({theme}: Style) => theme.space[0]}px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ${({theme}: Style) => theme.radius[2]}px;\n padding: ${({theme}: Style) => theme.space[2]}px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n`\n\nexport const Legend: React.FC<Style> = styled.div`\n color: #ccc;\n position: absolute;\n top: ${({theme}: Style) => theme.space[4]}px;\n left: ${({theme}: Style) => theme.space[4]}px;\n\n & > div {\n margin: 5px 0;\n }\n`\n\nexport const LegendRow = styled.div`\n display: flex;\n`\n\nexport const LegendBadge: React.FC<Style> = styled.div`\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ${({theme}: Style) => theme.space[2]}px;\n`\n","import {rgba} from 'polished'\nimport deepEqual from 'deep-equal'\nimport React, {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {v4 as uuidv4} from 'uuid'\nimport BezierEasing from 'bezier-easing'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\nimport {SanityDocument, SanityClient} from '@sanity/client'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useTheme} from '@sanity/ui'\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\n\nconst DEFAULT_QUERY = `\n *[\n !(_id in path(\"_.*\")) &&\n !(_type match \"system.*\") &&\n !(_type match \"sanity.*\")\n ]\n`\n\nconst fadeEasing = BezierEasing(0, 0.9, 1, 1)\nconst softEasing = BezierEasing(0.25, 0.1, 0.0, 1.0)\nconst idleTimeout = 10000\nconst imageSize = 40\n\nfunction getTopDocTypes(counts: Record<string, number>) {\n return sortBy(Object.keys(counts), (docType) => counts[docType] || 0)\n .reverse()\n .slice(0, 10)\n}\n\nfunction formatDocType(docType: string) {\n return (docType.substring(0, 1).toUpperCase() + docType.substring(1))\n .replace(/\\./g, ' ')\n .replace(/[A-Z]/g, ' $&')\n .trim()\n}\n\nfunction getDocTypeCounts(docs: SanityDocument[]) {\n const types: Record<string, number> = {}\n for (const doc of docs) {\n types[doc._type] = (types[doc._type] || 0) + 1\n }\n return types\n}\n\nfunction labelFor(doc: SanityDocument) {\n return `${doc.title || doc.name || doc._id}`.trim()\n}\n\nfunction valueFor(doc: any, maxSize: number) {\n return 5 + 100 * (sizeOf(doc) / maxSize)\n}\n\nfunction findRefs(obj: any, dest: any[] = []) {\n if (obj !== null) {\n if (typeof obj === 'object') {\n for (const [k, v] of Object.entries(obj)) {\n if (k === '_ref' && typeof v === 'string' && v.length > 0) {\n dest.push(stripDraftId(v))\n }\n findRefs(v, dest)\n }\n } else if (Array.isArray(obj)) {\n for (const v of obj) {\n findRefs(v, dest)\n }\n }\n }\n return dest\n}\n\nfunction stripDraftId(id: string) {\n return id.replace(/^drafts\\./, '')\n}\n\nfunction deduplicateDrafts(docs: SanityDocument[]) {\n const deduped: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n if (!/^drafts\\./.test(doc._id)) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (/^drafts\\./.test(doc._id)) {\n const id = stripDraftId(doc._id)\n deduped[id] = Object.assign(doc, {_id: id})\n }\n }\n return Object.values(deduped)\n}\n\nclass Users {\n _users: any[] = []\n\n async getById(id: string, client: SanityClient) {\n let user = this._users.find((u) => u._id === id)\n if (!user) {\n user = await client.users.getById(id)\n this._users.push(user)\n user.image = await loadImage(\n user.imageUrl ||\n 'https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg',\n imageSize,\n imageSize\n )\n }\n return user\n }\n}\n\nclass Session {\n id: string | null = null\n user: any = null\n doc: SanityDocument | null = null\n lastActive: number = 0\n startTime: number = 0\n angle: number = 0\n}\n\nclass GraphData {\n sessions: Session[] = []\n data: any\n\n constructor(docs: SanityDocument[] = []) {\n const docsById: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n docsById[doc._id] = doc\n }\n\n this.data = {\n nodes: docs.map((d) => Object.assign({id: d._id, type: 'document', doc: d})),\n links: docs\n .flatMap((doc) => findRefs(doc).map((ref) => ({source: doc._id, target: ref})))\n .filter((link) => docsById[link.source] && docsById[link.target]),\n }\n }\n\n setSession(user, docNode) {\n let session = this.sessions.find((s) => s.user.id === user.id && s.doc?._id === docNode.doc._id)\n if (!session) {\n session = new Session()\n session.id = uuidv4()\n session.user = user\n session.startTime = Date.now()\n session.doc = docNode.doc\n session.angle = Math.random() * 2 * Math.PI\n this.sessions.push(session)\n }\n session.lastActive = Date.now()\n }\n\n reapSessions() {\n for (let i = 0; i < this.sessions.length; i++) {\n const session = this.sessions[i]\n if (Date.now() - session.lastActive > idleTimeout) {\n this.sessions = [...this.sessions.slice(0, i), ...this.sessions.slice(i + 1)]\n i--\n }\n }\n }\n\n clone() {\n const copy = new GraphData()\n Object.assign(copy, this)\n copy.data = {\n nodes: [...this.data.nodes],\n links: [...this.data.links],\n }\n return copy\n }\n}\n\nconst users = new Users()\n\ninterface GraphViewConfig {\n query?: string\n /** default: '2022-09-01' */\n apiVersion?: string\n}\n\nexport function GraphView(props: GraphViewConfig) {\n const query = props.query || DEFAULT_QUERY\n const apiVersion = props.apiVersion ?? '2022-09-01'\n\n const userColorManager = useUserColorManager()\n const [maxSize, setMaxSize] = useState(0)\n const [hoverNode, setHoverNode] = useState<any>(null)\n const [documents, setDocuments] = useState<SanityDocument[]>([])\n const [docTypes, setDocTypes] = useState<Record<string, number>>({})\n const [graph, setGraph] = useState(() => new GraphData())\n const router = useRouter()\n const client = useClient({apiVersion})\n\n const fetchCallback = useCallback((_docs) => {\n const docs = deduplicateDrafts(_docs)\n setMaxSize(Math.max(...docs.map(sizeOf)))\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setGraph(new GraphData(docs))\n }, [])\n\n const listenCallback = useCallback(\n async (update) => {\n const doc = update.result\n if (doc) {\n doc._id = stripDraftId(doc._id)\n\n const docsById: Record<string, SanityDocument> = {}\n for (const d of documents) {\n docsById[d._id] = d\n }\n\n let oldDoc\n const docs = [...documents]\n const idx = documents.findIndex((d) => d._id === doc._id)\n if (idx >= 0) {\n oldDoc = docs[idx]\n docs[idx] = doc\n } else {\n docs.push(doc)\n }\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n\n const oldRefs = findRefs(oldDoc || {}).filter(\n (id) => id === doc._id || docsById[id] !== null\n )\n const newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null)\n\n let graphChanged = !deepEqual(oldRefs, newRefs)\n if (graphChanged) {\n newGraph.data.links = newGraph.data.links\n .filter((l) => l.source.id !== doc._id)\n .concat(newRefs.map((ref) => ({source: doc._id, target: ref})))\n }\n\n let docNode\n const nodeIdx = graph.data.nodes.findIndex((n) => n.doc && n.doc._id === doc._id)\n if (nodeIdx >= 0) {\n docNode = graph.data.nodes[nodeIdx]\n docNode.doc = doc\n } else {\n docNode = {id: doc._id, type: 'document', doc: doc}\n newGraph.data.nodes.push(docNode)\n graphChanged = true\n }\n if (graphChanged) {\n setGraph(newGraph)\n }\n\n const user = await users.getById(update.identity, client)\n graph.setSession(user, docNode)\n } else if (update.transition === 'disappear') {\n const docId = stripDraftId(update.documentId)\n\n const docs = documents.filter((d) => d._id !== docId)\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n newGraph.data.links = newGraph.data.links.filter(\n (l) => l.source.id !== docId && l.target.id !== docId\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client]\n )\n useFetchDocuments(query, fetchCallback, [], client)\n useListen(query, {}, {}, listenCallback, [documents, graph], client)\n useEffect(() => {\n const interval = setInterval(() => graph.reapSessions(), 1000)\n return () => clearInterval(interval)\n }, [graph])\n\n const theme = useTheme().sanity\n\n return (\n <GraphWrapper theme={theme}>\n <GraphRoot theme={theme}>\n <Legend theme={theme}>\n {getTopDocTypes(docTypes).map((docType) => (\n <LegendRow\n className={'legend__row'}\n key={docType}\n style={{color: getDocTypeColor(docType).fill}}\n >\n <LegendBadge theme={theme} />\n <div>{formatDocType(docType)}</div>\n </LegendRow>\n ))}\n </Legend>\n {hoverNode && <HoverNode theme={theme}>{labelFor(hoverNode.doc)}</HoverNode>}\n\n <ForceGraph2D\n graphData={graph.data}\n nodeAutoColorBy=\"group\"\n enableNodeDrag={false}\n onNodeHover={(node: any) => setHoverNode(node)}\n onNodeClick={(node: any) => {\n router.navigateIntent('edit', {id: node.doc._id, documentType: node.doc._type})\n }}\n linkColor={() => rgba(gray[500].hex, 0.25)}\n nodeLabel={() => ''}\n nodeRelSize={1}\n nodeVal={(node: any) => valueFor(node.doc, maxSize)}\n onRenderFramePost={(ctx, globalScale) => {\n for (const session of graph.sessions) {\n const node = graph.data.nodes.find((n) => n.doc && n.doc._id === session?.doc?._id)\n if (node) {\n const idleFactorRange = idleTimeout\n const angle = session.angle\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const image = session.user.image\n const userColor = userColorManager.get(session.user.displayName).tints[400].hex\n const distance = radius * globalScale + 40\n const imgW = image ? image.width : 0\n const imgH = image ? image.height : 0\n const x = node.x + (Math.sin(angle) * distance) / globalScale\n const y = node.y + (Math.cos(angle) * distance) / globalScale\n\n ctx.save()\n try {\n ctx.globalAlpha = fadeEasing(\n 1 - Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange\n )\n ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(\n node.x + (Math.sin(angle) * (distance - imgW / 2)) / globalScale,\n node.y + (Math.cos(angle) * (distance - imgH / 2)) / globalScale\n )\n ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius)\n ctx.stroke()\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n if (image) {\n ctx.save()\n // eslint-disable-next-line max-depth\n try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur)\n )\n // eslint-disable-next-line max-depth\n if (f > 0) {\n ctx.beginPath()\n ctx.fillStyle = rgba(userColor, f)\n ctx.arc(x, y, (imgW / 2 + 10) / globalScale, 0, 2 * Math.PI, false)\n ctx.fill()\n }\n\n ctx.beginPath()\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.clip()\n\n ctx.drawImage(\n image,\n x - imgW / globalScale / 2,\n y - imgH / globalScale / 2,\n imgW / globalScale,\n imgH / globalScale\n )\n\n ctx.strokeStyle = black.hex\n ctx.lineWidth = 6 / globalScale\n ctx.stroke()\n\n ctx.strokeStyle = userColor\n ctx.lineWidth = 4 / globalScale\n ctx.stroke()\n } finally {\n ctx.restore()\n }\n }\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(black.hex, 1)\n ctx.lineWidth = 0.5 / globalScale\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n const above = angle >= Math.PI / 2 && angle < Math.PI * 1.5\n const textY = above\n ? y - (imgH / 2 + 5) / globalScale\n : y + (imgH / 2 + 5) / globalScale\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = above ? 'bottom' : 'top'\n ctx.fillText(session.user.displayName, x, textY)\n } finally {\n ctx.restore()\n }\n }\n }\n }}\n nodeCanvasObject={(node: any, ctx, globalScale) => {\n // eslint-disable-next-line default-case\n switch (node.type) {\n case 'document': {\n const nodeColor = getDocTypeColor(node.doc._type)\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const fontSize = Math.min(100, 10.0 / globalScale)\n\n ctx.beginPath()\n ctx.fillStyle =\n hoverNode !== null && node.doc._id === hoverNode.doc._id\n ? rgba(gray[500].hex, 0.8)\n : nodeColor.fill\n ctx.strokeStyle = nodeColor.border\n ctx.lineWidth = 0.5\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n ctx.fill()\n\n if (radius * globalScale > 10) {\n ctx.font = `${fontSize}px sans-serif`\n const w = radius * 2 + 30 / globalScale\n for (let len = 50; len >= 5; len /= 1.2) {\n const label = truncate(labelFor(node.doc), Math.round(len))\n const textMetrics = ctx.measureText(label)\n if (textMetrics.width < w) {\n // ctx.fillStyle = rgba(color.white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n\n ctx.strokeStyle = rgba(black.hex, 0.5)\n ctx.lineWidth = 2 / globalScale\n ctx.strokeText(label, node.x, node.y + radius + 5 / globalScale)\n\n ctx.fillText(label, node.x, node.y + radius + 5 / globalScale)\n break\n }\n }\n }\n }\n }\n }}\n linkCanvasObject={(link: any, ctx, globalScale) => {\n ctx.beginPath()\n ctx.strokeStyle = rgba(gray[500].hex, 0.125)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(link.source.x, link.source.y)\n ctx.lineTo(link.target.x, link.target.y)\n ctx.stroke()\n }}\n />\n </GraphRoot>\n </GraphWrapper>\n )\n}\n\nconst colorCache = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType) {\n if (colorCache[docType]) {\n return colorCache[docType]\n }\n\n const hue = COLOR_HUES[typeColorNum % COLOR_HUES.length]\n\n typeColorNum += 1\n\n colorCache[docType] = {\n fill: hues[hue][400].hex,\n border: rgba(black.hex, 0.5),\n }\n\n return colorCache[docType]\n}\n","import {useEffect} from 'react'\nimport {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\n// eslint-disable-next-line max-params\nexport function useListen(\n query: string,\n params: {[key: string]: any},\n options: ListenOptions,\n onUpdate: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient\n): void {\n useEffect(() => {\n const subscription = client.listen(query, params, options).subscribe((update) => {\n onUpdate(update)\n })\n return () => {\n subscription.unsubscribe()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\n}\n\nexport function useFetchDocuments(\n query: string,\n onFetch: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient\n): void {\n useEffect(() => {\n client.fetch(query).then((result) => {\n onFetch(result)\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\n}\n","import React from 'react'\n\n/**\n * Couple of things to note:\n * - width and height is set to 1em\n * - fill is `currentColor` - this will ensure that the icon looks uniform and\n * that the hover/active state works. You can of course render anything you\n * would like here, but for plugins that are to be used in more than one\n * studio, we suggest these rules are followed\n **/\nexport const GraphViewIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 46.063 46.063\">\n <path\n fill=\"currentColor\"\n d=\"M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z\"\n />\n </svg>\n)\n","import {GraphView} from './tool/GraphView'\nimport {GraphViewIcon} from './tool/GraphViewIcon'\nimport {definePlugin} from 'sanity'\nimport {route} from 'sanity/router'\nimport React from 'react'\n\nexport interface GraphViewConfig {\n query?: string\n}\n\nexport const contentGraphView = definePlugin<GraphViewConfig>((config: GraphViewConfig = {}) => {\n return {\n name: '@sanity/content-graph-view',\n\n tools: (prev) => {\n return [\n ...prev,\n {\n name: 'graph-your-content',\n title: 'Graph',\n icon: GraphViewIcon,\n component: function component() {\n return <GraphView {...config} />\n },\n router: route.create('/:selectedDocumentId'),\n },\n ]\n },\n }\n})\n"],"names":["sizeOf","value","Object","entries","reduce","total","k","v","_ref","Array","isArray","length","GraphRoot","styled","div","_templateObject","_taggedTemplateLiteral","_ref2","theme","fonts","text","family","black","hex","GraphWrapper","_templateObject2","HoverNode","_templateObject3","_ref3","_ref4","space","_ref5","radius","_ref6","Legend","_templateObject4","_ref7","_ref8","LegendRow","_templateObject5","LegendBadge","_templateObject6","_ref9","fadeEasing","BezierEasing","softEasing","formatDocType","docType","substring","toUpperCase","replace","trim","getDocTypeCounts","docs","types","doc","_type","labelFor","title","name","_id","valueFor","maxSize","findRefs","obj","dest","push","stripDraftId","id","Session","constructor","this","user","lastActive","startTime","angle","GraphData","sessions","docsById","data","nodes","map","d","assign","type","links","flatMap","ref","source","target","filter","link","setSession","docNode","session","find","s","_a","uuidv4","Date","now","Math","random","PI","reapSessions","i","slice","clone","copy","users","_users","async","client","u","url","w","h","getById","image","imageUrl","Promise","resolve","img","Image","onload","onerror","event","console","log","src","GraphView","props","query","apiVersion","userColorManager","useUserColorManager","setMaxSize","useState","hoverNode","setHoverNode","documents","setDocuments","docTypes","setDocTypes","graph","setGraph","router","useRouter","useClient","fetchCallback","useCallback","_docs","deduped","test","values","deduplicateDrafts","max","listenCallback","update","result","oldDoc","idx","findIndex","newGraph","oldRefs","newRefs","graphChanged","deepEqual","l","concat","nodeIdx","n","identity","transition","docId","documentId","onFetch","dependencies","useEffect","fetch","then","useFetchDocuments","params","options","onUpdate","subscription","listen","subscribe","unsubscribe","useListen","interval","setInterval","clearInterval","useTheme","sanity","jsx","children","jsxs","counts","array","keys","f","sort","a","b","va","vb","reverse","className","style","color","getDocTypeColor","fill","ForceGraph2D","graphData","nodeAutoColorBy","enableNodeDrag","onNodeHover","node","onNodeClick","navigateIntent","documentType","linkColor","rgba","gray","nodeLabel","nodeRelSize","nodeVal","onRenderFramePost","ctx","globalScale","idleFactorRange","sqrt","userColor","get","displayName","tints","distance","imgW","width","imgH","height","x","sin","y","cos","save","globalAlpha","min","font","round","beginPath","strokeStyle","white","lineWidth","moveTo","lineTo","stroke","arc","dur","fillStyle","clip","drawImage","restore","above","textY","textAlign","textBaseline","fillText","nodeCanvasObject","nodeColor","fontSize","border","len","label","limit","measureText","strokeText","linkCanvasObject","colorCache","typeColorNum","hue","COLOR_HUES","hues","GraphViewIcon","xmlns","viewBox","contentGraphView","definePlugin","config","arguments","undefined","tools","prev","icon","component","_objectSpread","route","create"],"mappings":"62CACO,SAASA,EAAOC,GACrB,OAAc,OAAVA,EACK,EAGY,iBAAVA,EACFC,OAAOC,QAAQF,GAAOG,QAAO,CAACC,WAAQC,EAAGC,GAACC,EAAA,OAAMH,EAAQL,EAAOM,GAAKN,EAAOO,KAAI,GAGpFE,MAAMC,QAAQT,GACTC,OAAOC,QAAQF,GAAOG,QAAO,CAACC,EAAOE,IAAMF,EAAQL,EAAOO,IAAI,GAGlD,iBAAVN,EACFA,EAAMU,OAGR,CACT,CCXO,MAAMC,EAA6BC,EAAOC,IAChCC,IAAAA,EAAAC,EAAA,CAAA,oBAAA,mGAAA,UAAAC,IAAA,IAACC,MAACA,GAAkBD,EAAA,OAAAC,EAAMC,MAAMC,KAAKC,MAAA,GAMtCC,EAAMC,KAGTC,EAAgCX,EAAOC,IAAAW,IAAAA,EAAAT,EAAA,CAAA,iEAMvCU,EAA6Bb,EAAOC,IAChCa,IAAAA,EAAAX,EAAA,CAAA,oBAAA,yDAAA,kHAAA,mBAAA,sEAAAY,IAAA,IAACV,MAACA,GAAkBU,EAAA,OAAAV,EAAMC,MAAMC,KAAKC,MAAA,IAG1CQ,IAAA,IAACX,MAACA,GAAKW,EAAA,OAAaX,EAAMY,MAAM,EAAA,IAIzBC,IAAA,IAACb,MAACA,GAAKa,EAAA,OAAab,EAAMc,OAAO,EAAA,IACvCC,IAAA,IAACf,MAACA,GAAKe,EAAA,OAAaf,EAAMY,MAAM,EAAA,IAQhCI,EAA0BrB,EAAOC,IAGrCqB,IAAAA,EAAAnB,EAAA,CAAA,mDAAA,gBAAA,oDAAAoB,IAAA,IAAClB,MAACA,GAAKkB,EAAA,OAAalB,EAAMY,MAAM,EAAA,IAC/BO,IAAA,IAACnB,MAACA,GAAKmB,EAAA,OAAanB,EAAMY,MAAM,EAAA,IAO7BQ,EAAYzB,EAAOC,IAAAyB,IAAAA,EAAAvB,EAAA,CAAA,2BAInBwB,EAA+B3B,EAAOC,IAKjC2B,IAAAA,EAAAzB,EAAA,CAAA,8GAAA,YAAA0B,IAAA,IAACxB,MAACA,GAAKwB,EAAA,OAAaxB,EAAMY,MAAM,EAAA,ICtC5Ca,EAAaC,EAAa,EAAG,GAAK,EAAG,GACrCC,EAAaD,EAAa,IAAM,GAAK,EAAK,GAUhD,SAASE,EAAcC,GACrB,OAAQA,EAAQC,UAAU,EAAG,GAAGC,cAAgBF,EAAQC,UAAU,IAC/DE,QAAQ,MAAO,KACfA,QAAQ,SAAU,OAClBC,MACL,CAEA,SAASC,EAAiBC,GACxB,MAAMC,EAAgC,CAAA,EACtC,IAAA,MAAWC,KAAOF,EAChBC,EAAMC,EAAIC,QAAUF,EAAMC,EAAIC,QAAU,GAAK,EAExC,OAAAF,CACT,CAEA,SAASG,EAASF,GAChB,MAAO,GAAGA,OAAAA,EAAIG,OAASH,EAAII,MAAQJ,EAAIK,KAAMT,MAC/C,CAEA,SAASU,EAASN,EAAUO,GAC1B,OAAO,EAAW9D,EAAOuD,GAAOO,EAArB,GACb,CAEA,SAASC,EAASC,GAA4B,IAAlBC,yDAAc,GACxC,GAAY,OAARD,EACE,GAAe,iBAARA,EACT,IAAA,MAAY1D,EAAGC,KAAML,OAAOC,QAAQ6D,GACxB,SAAN1D,GAA6B,iBAANC,GAAkBA,EAAEI,OAAS,GACjDsD,EAAAC,KAAKC,EAAa5D,IAEzBwD,EAASxD,EAAG0D,QAEL,GAAAxD,MAAMC,QAAQsD,GACvB,IAAA,MAAWzD,KAAKyD,EACdD,EAASxD,EAAG0D,GAIX,OAAAA,CACT,CAEA,SAASE,EAAaC,GACb,OAAAA,EAAGlB,QAAQ,YAAa,GACjC,CAqCA,MAAMmB,EAANC,cACsBC,KAAAH,GAAA,KACRG,KAAAC,KAAA,KACiBD,KAAAhB,IAAA,KACRgB,KAAAE,WAAA,EACDF,KAAAG,UAAA,EACJH,KAAAI,MAAA,CAAA,EAGlB,MAAMC,EAIJN,cAAyC,IAA7BjB,yDAAyB,GAHrCkB,KAAAM,SAAsB,GAIpB,MAAMC,EAA2C,CAAA,EACjD,IAAA,MAAWvB,KAAOF,EAChByB,EAASvB,EAAIK,KAAOL,EAGtBgB,KAAKQ,KAAO,CACVC,MAAO3B,EAAK4B,KAAKC,GAAMhF,OAAOiF,OAAO,CAACf,GAAIc,EAAEtB,IAAKwB,KAAM,WAAY7B,IAAK2B,MACxEG,MAAOhC,EACJiC,SAAS/B,GAAQQ,EAASR,GAAK0B,KAAKM,IAAS,CAACC,OAAQjC,EAAIK,IAAK6B,OAAQF,QACvEG,QAAQC,GAASb,EAASa,EAAKH,SAAWV,EAASa,EAAKF,UAE/D,CAEAG,WAAWpB,EAAMqB,GACf,IAAIC,EAAUvB,KAAKM,SAASkB,MAAMC,IA9ItC,IAAAC,EA8I8C,OAAAD,EAAAxB,KAAKJ,KAAOI,EAAKJ,KAAM,OAAA6B,IAAE1C,UAAF,EAAA0C,EAAOrC,OAAQiC,EAAQtC,IAAIK,GAAA,IACvFkC,IACHA,EAAU,IAAIzB,EACdyB,EAAQ1B,GAAK8B,IACbJ,EAAQtB,KAAOA,EACPsB,EAAApB,UAAYyB,KAAKC,MACzBN,EAAQvC,IAAMsC,EAAQtC,IACtBuC,EAAQnB,MAAwB,EAAhB0B,KAAKC,SAAeD,KAAKE,GACpChC,KAAAM,SAASX,KAAK4B,IAEbA,EAAArB,WAAa0B,KAAKC,KAC5B,CAEAI,eACE,IAAA,IAASC,EAAI,EAAGA,EAAIlC,KAAKM,SAASlE,OAAQ8F,IAAK,CACvC,MAAAX,EAAUvB,KAAKM,SAAS4B,GAC1BN,KAAKC,MAAQN,EAAQrB,WArIX,MAsIZF,KAAKM,SAAW,IAAIN,KAAKM,SAAS6B,MAAM,EAAGD,MAAOlC,KAAKM,SAAS6B,MAAMD,EAAI,IAC1EA,IAEJ,CACF,CAEAE,QACQ,MAAAC,EAAO,IAAIhC,EAMV,OALA1E,OAAAiF,OAAOyB,EAAMrC,MACpBqC,EAAK7B,KAAO,CACVC,MAAO,IAAIT,KAAKQ,KAAKC,OACrBK,MAAO,IAAId,KAAKQ,KAAKM,QAEhBuB,CACT,EAGF,MAAMC,EAAQ,IAjFd,MAAAvC,cACEC,KAAAuC,OAAgB,EAAC,CAEjBC,cAAc3C,EAAY4C,GACpB,IAAAxC,EAAOD,KAAKuC,OAAOf,MAAMkB,GAAMA,EAAErD,MAAQQ,IF9EjC,IAAU8C,EAAaC,EAAWC,EEyFvC,OAVF5C,IACHA,QAAawC,EAAOH,MAAMQ,QAAQjD,GAC7BG,KAAAuC,OAAO5C,KAAKM,GACjBA,EAAK8C,YFlFeJ,EEmFlB1C,EAAK+C,UACH,uGFpF6BJ,EEKrB,GFLgCC,EEKhC,GFJT,IAAII,SAASC,IAClB,MAAMC,EAAM,IAAIC,MAAMR,EAAGC,GACzBM,EAAIE,OAAS,KACXH,EAAQC,EAAG,EAETA,EAAAG,QAAWC,IAELC,QAAAC,IAAI,cAAeF,GAC3BL,EAAQ,KAAI,EAEdC,EAAIO,IAAMf,CAAA,ME8EH1C,CACT,GAyEK,SAAS0D,EAAUC,GAxL1B,IAAAlC,EAyLQ,MAAAmC,EAAQD,EAAMC,OA1KA,+GA2KdC,EAAa,OAAApC,EAAMkC,EAAAE,YAAcpC,EAAA,aAEjCqC,EAAmBC,KAClBzE,EAAS0E,GAAcC,EAAS,IAChCC,EAAWC,GAAgBF,EAAc,OACzCG,EAAWC,GAAgBJ,EAA2B,KACtDK,EAAUC,GAAeN,EAAiC,CAAE,IAC5DO,EAAOC,GAAYR,GAAS,IAAM,IAAI7D,IACvCsE,EAASC,IACTnC,EAASoC,EAAU,CAACf,eAEpBgB,EAAgBC,GAAaC,IAC3B,MAAAlG,EAvHV,SAA2BA,GACzB,MAAMmG,EAA0C,CAAA,EAChD,IAAA,MAAWjG,KAAOF,EACX,YAAYoG,KAAKlG,EAAIK,OACxB4F,EAAQjG,EAAIK,KAAOL,GAGvB,IAAA,MAAWA,KAAOF,EAChB,GAAI,YAAYoG,KAAKlG,EAAIK,KAAM,CACvB,MAAAQ,EAAKD,EAAaZ,EAAIK,KAC5B4F,EAAQpF,GAAMlE,OAAOiF,OAAO5B,EAAK,CAACK,IAAKQ,GACzC,CAEK,OAAAlE,OAAOwJ,OAAOF,EACvB,CAyGiBG,CAAkBJ,GAC/Bf,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAChC6I,EAAaxF,GACD0F,EAAA3F,EAAiBC,IACpB4F,EAAA,IAAIrE,EAAUvB,GAAK,GAC3B,IAEGwG,EAAiBP,GACrBvC,UACE,MAAMxD,EAAMuG,EAAOC,OACnB,GAAIxG,EAAK,CACHA,EAAAK,IAAMO,EAAaZ,EAAIK,KAE3B,MAAMkB,EAA2C,CAAA,EACjD,IAAA,MAAWI,KAAK0D,EACd9D,EAASI,EAAEtB,KAAOsB,EAGhB,IAAA8E,EACE,MAAA3G,EAAO,IAAIuF,GACXqB,EAAMrB,EAAUsB,WAAWhF,GAAMA,EAAEtB,MAAQL,EAAIK,MACjDqG,GAAO,GACTD,EAAS3G,EAAK4G,GACd5G,EAAK4G,GAAO1G,GAEZF,EAAKa,KAAKX,GAEZsF,EAAaxF,GACD0F,EAAA3F,EAAiBC,IAC7BmF,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAE1B,MAAAmK,EAAWnB,EAAMrC,QAEjByD,EAAUrG,EAASiG,GAAU,CAAA,GAAItE,QACpCtB,GAAOA,IAAOb,EAAIK,KAAwB,OAAjBkB,EAASV,KAE/BiG,EAAUtG,EAASR,GAAKmC,QAAQtB,GAAOA,IAAOb,EAAIK,KAAwB,OAAjBkB,EAASV,KAExE,IAOIyB,EAPAyE,GAAgBC,EAAUH,EAASC,GACnCC,IACOH,EAAApF,KAAKM,MAAQ8E,EAASpF,KAAKM,MACjCK,QAAQ8E,GAAMA,EAAEhF,OAAOpB,KAAOb,EAAIK,MAClC6G,OAAOJ,EAAQpF,KAAKM,IAAS,CAACC,OAAQjC,EAAIK,IAAK6B,OAAQF,QAI5D,MAAMmF,EAAU1B,EAAMjE,KAAKC,MAAMkF,WAAWS,GAAMA,EAAEpH,KAAOoH,EAAEpH,IAAIK,MAAQL,EAAIK,MACzE8G,GAAW,GACH7E,EAAAmD,EAAMjE,KAAKC,MAAM0F,GAC3B7E,EAAQtC,IAAMA,IAEdsC,EAAU,CAACzB,GAAIb,EAAIK,IAAKwB,KAAM,WAAY7B,OACjC4G,EAAApF,KAAKC,MAAMd,KAAK2B,GACVyE,GAAA,GAEbA,GACFrB,EAASkB,GAGX,MAAM3F,QAAaqC,EAAMQ,QAAQyC,EAAOc,SAAU5D,GAC5CgC,EAAApD,WAAWpB,EAAMqB,EAAO,MAChC,GAAiC,cAAtBiE,EAAOe,WAA4B,CACtC,MAAAC,EAAQ3G,EAAa2F,EAAOiB,YAE5B1H,EAAOuF,EAAUlD,QAAQR,GAAMA,EAAEtB,MAAQkH,IAC/CjC,EAAaxF,GACD0F,EAAA3F,EAAiBC,IAC7BmF,EAAWnC,KAAKuD,OAAOvG,EAAK4B,IAAIjF,KAE1B,MAAAmK,EAAWnB,EAAMrC,QACvBwD,EAASpF,KAAKM,MAAQ8E,EAASpF,KAAKM,MAAMK,QACvC8E,GAAMA,EAAEhF,OAAOpB,KAAO0G,GAASN,EAAE/E,OAAOrB,KAAO0G,IAEzCX,EAAApF,KAAKC,MAAQmF,EAASpF,KAAKC,MAAMU,QAAQiF,GAAMA,EAAEvG,KAAO0G,IACjE7B,EAASkB,EACX,IAEF,CAACvB,EAAWI,EAAOhC,KC5PhB,SACLoB,EACA4C,EACAC,EACAjE,GAEAkE,GAAU,KACRlE,EAAOmE,MAAM/C,GAAOgD,MAAMrB,IACxBiB,EAAQjB,EAAM,GACf,GAEA,IAAIkB,EAAcjE,GACvB,CDkPEqE,CAAkBjD,EAAOiB,EAAe,GAAIrC,GCjRvC,SACLoB,EACAkD,EACAC,EACAC,EACAP,EACAjE,GAEAkE,GAAU,KACF,MAAAO,EAAezE,EAAO0E,OAAOtD,EAAOkD,EAAQC,GAASI,WAAW7B,IACpE0B,EAAS1B,EAAM,IAEjB,MAAO,KACL2B,EAAaG,aAAY,CAC3B,GAEC,IAAIX,EAAcjE,GACvB,CDiQY6E,CAAAzD,EAAO,CAAC,EAAG,GAAIyB,EAAgB,CAACjB,EAAWI,GAAQhC,GAC7DkE,GAAU,KACR,MAAMY,EAAWC,aAAY,IAAM/C,EAAMxC,gBAAgB,KAClD,MAAA,IAAMwF,cAAcF,EAAQ,GAClC,CAAC9C,IAEE,MAAA9H,EAAQ+K,IAAWC,OAEzB,OACGC,EAAA3K,EAAA,CAAaN,QACZkL,SAACC,EAAAzL,EAAA,CAAUM,QACTkL,SAAA,CAACD,EAAAjK,EAAA,CAAOhB,QACLkL,UAtQaE,EAsQExD,GF9PAyD,GEPVrM,OAAOsM,KAAKF,GFOUG,GEPA1J,GAAYuJ,EAAOvJ,IAAY,EFQ5DwJ,GAAMG,MAAK,CAACC,EAAGC,KACd,MAAAC,EAAKJ,GAAEE,GACPG,EAAKL,GAAEG,GAEb,OAAOC,EAAKC,GAAU,EAAAD,EAAKC,EAAK,EAAI,CAAA,KEXnCC,UACArG,MAAM,EAAG,KAmQsBzB,KAAKlC,GAC5BsJ,EAAA/J,EAAA,CACC0K,UAAW,cAEXC,MAAO,CAACC,MAAOC,EAAgBpK,GAASqK,MAExChB,SAAA,CAACD,EAAA3J,EAAA,CAAYtB,UACZiL,EAAA,MAAA,CAAKC,WAAcrJ,OAJfA,OAQV2F,GAAcyD,EAAAzK,EAAA,CAAUR,QAAekL,SAAA3I,EAASiF,EAAUnF,OAE1D4I,EAAAkB,EAAA,CACCC,UAAWtE,EAAMjE,KACjBwI,gBAAgB,QAChBC,gBAAgB,EAChBC,YAAcC,GAAc/E,EAAa+E,GACzCC,YAAcD,IACLxE,EAAA0E,eAAe,OAAQ,CAACxJ,GAAIsJ,EAAKnK,IAAIK,IAAKiK,aAAcH,EAAKnK,IAAIC,OAAM,EAEhFsK,UAAW,IAAMC,EAAKC,EAAK,KAAKzM,IAAK,KACrC0M,UAAW,IAAM,GACjBC,YAAa,EACbC,QAAUT,GAAc7J,EAAS6J,EAAKnK,IAAKO,GAC3CsK,kBAAmB,CAACC,EAAKC,KACZ,IAAA,MAAAxI,KAAWkD,EAAMnE,SAAU,CACpC,MAAM6I,EAAO1E,EAAMjE,KAAKC,MAAMe,MAAM4E,IA7TlD1E,IAAAA,EA6T0D,OAAA0E,EAAApH,KAAOoH,EAAEpH,IAAIK,OAAQ,OAAAqC,EAAS,MAATH,OAAS,EAAAA,EAAAvC,YAAT0C,EAAcrC,IAAA,IAC/E,GAAI8J,EAAM,CACR,MAAMa,EAtSF,IAuSE5J,EAAQmB,EAAQnB,MAChB3C,EAASqE,KAAKmI,KAAK3K,EAAS6J,EAAKnK,IAAKO,IACtCwD,EAAQxB,EAAQtB,KAAK8C,MACrBmH,EAAYnG,EAAiBoG,IAAI5I,EAAQtB,KAAKmK,aAAaC,MAAM,KAAKrN,IACtEsN,EAAW7M,EAASsM,EAAc,GAClCQ,EAAOxH,EAAQA,EAAMyH,MAAQ,EAC7BC,EAAO1H,EAAQA,EAAM2H,OAAS,EAC9BC,EAAIxB,EAAKwB,EAAK7I,KAAK8I,IAAIxK,GAASkK,EAAYP,EAC5Cc,EAAI1B,EAAK0B,EAAK/I,KAAKgJ,IAAI1K,GAASkK,EAAYP,EAElDD,EAAIiB,OACA,IAsBF,GArBAjB,EAAIkB,YAAc5M,EAChB,EAAI0D,KAAKmJ,IAAIjB,EAAiBpI,KAAKC,MAAQN,EAAQrB,YAAc8J,GAEnEF,EAAIoB,KAAO,QAAAhF,OAAQpE,KAAKqJ,MAAM,GAAKpB,GAAW,iBAE9CD,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAK8B,EAAMtO,IAAK,GAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAA0B,OACFrC,EAAKwB,EAAK7I,KAAK8I,IAAIxK,IAAUkK,EAAWC,EAAO,GAAMR,EACrDZ,EAAK0B,EAAK/I,KAAKgJ,IAAI1K,IAAUkK,EAAWG,EAAO,GAAMV,GAEvDD,EAAI2B,OAAOtC,EAAKwB,EAAI7I,KAAK8I,IAAIxK,GAAS3C,EAAQ0L,EAAK0B,EAAI/I,KAAKgJ,IAAI1K,GAAS3C,GACzEqM,EAAI4B,SAEJ5B,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAK8B,EAAMtO,IAAK,GAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAA6B,IAAIxC,EAAKwB,EAAGxB,EAAK0B,EAAGpN,EAAQ,EAAG,EAAIqE,KAAKE,IAAI,GAChD8H,EAAI4B,SAEA3I,EAAO,CACT+G,EAAIiB,OAEA,IACF,MAAMa,EAAM,IACN1D,EAAI5J,EACRwD,KAAKuD,IAAI,GAAIuG,GAAOhK,KAAKC,MAAQN,EAAQpB,YAAcyL,IAGrD1D,EAAI,IACN4B,EAAIsB,YACAtB,EAAA+B,UAAYrC,EAAKU,EAAWhC,GAC5B4B,EAAA6B,IAAIhB,EAAGE,GAAIN,EAAO,EAAI,IAAMR,EAAa,EAAG,EAAIjI,KAAKE,IAAI,GAC7D8H,EAAIjB,QAGNiB,EAAIsB,YACJtB,EAAI+B,UAAYrC,EAAK8B,EAAMtO,IAAK,GAC5B8M,EAAA6B,IAAIhB,EAAGE,EAAGN,EAAOR,EAAc,EAAG,EAAG,EAAIjI,KAAKE,IAAI,GACtD8H,EAAIgC,OAEAhC,EAAAiC,UACFhJ,EACA4H,EAAIJ,EAAOR,EAAc,EACzBc,EAAIJ,EAAOV,EAAc,EACzBQ,EAAOR,EACPU,EAAOV,GAGTD,EAAIuB,YAActO,EAAMC,IACxB8M,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI4B,SAEJ5B,EAAIuB,YAAcnB,EAClBJ,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI4B,QAGN,CAFE,QACA5B,EAAIkC,SACN,CACF,CAEAlC,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAKzM,EAAMC,IAAK,GAClC8M,EAAIyB,UAAY,GAAMxB,EAClBD,EAAA6B,IAAIhB,EAAGE,EAAGN,EAAOR,EAAc,EAAG,EAAG,EAAIjI,KAAKE,IAAI,GACtD8H,EAAI4B,SAEJ,MAAMO,EAAQ7L,GAAS0B,KAAKE,GAAK,GAAK5B,EAAkB,IAAV0B,KAAKE,GAC7CkK,EAAQD,EACVpB,GAAKJ,EAAO,EAAI,GAAKV,EACrBc,GAAKJ,EAAO,EAAI,GAAKV,EACzBD,EAAI+B,UAAYrC,EAAK8B,EAAMtO,IAAK,GAChC8M,EAAIqC,UAAY,SACZrC,EAAAsC,aAAeH,EAAQ,SAAW,MACtCnC,EAAIuC,SAAS9K,EAAQtB,KAAKmK,YAAaO,EAAGuB,EAG5C,CAFE,QACApC,EAAIkC,SACN,CACF,CACF,GAEFM,iBAAkB,CAACnD,EAAWW,EAAKC,KAEjC,GACO,aADCZ,EAAKtI,KACM,CACf,MAAM0L,EAAY3D,EAAgBO,EAAKnK,IAAIC,OACrCxB,EAASqE,KAAKmI,KAAK3K,EAAS6J,EAAKnK,IAAKO,IACtCiN,EAAW1K,KAAKmJ,IAAI,IAAK,GAAOlB,GAalC,GAXJD,EAAIsB,YACJtB,EAAI+B,UACY,OAAd1H,GAAsBgF,EAAKnK,IAAIK,MAAQ8E,EAAUnF,IAAIK,IACjDmK,EAAKC,EAAK,KAAKzM,IAAK,IACpBuP,EAAU1D,KAChBiB,EAAIuB,YAAckB,EAAUE,OAC5B3C,EAAIyB,UAAY,GACZzB,EAAA6B,IAAIxC,EAAKwB,EAAGxB,EAAK0B,EAAGpN,EAAQ,EAAG,EAAIqE,KAAKE,IAAI,GAChD8H,EAAI4B,SACJ5B,EAAIjB,OAEApL,EAASsM,EAAc,GAAI,CAC7BD,EAAIoB,KAAUsB,GAAAA,OAAAA,EAAA,iBACR,MAAA5J,EAAa,EAATnF,EAAa,GAAKsM,EAC5B,IAAA,IAAS2C,EAAM,GAAIA,GAAO,EAAGA,GAAO,IAAK,CACjC,MAAAC,GFxYDlL,EEwYkBvC,EAASiK,EAAKnK,KFxYrB4N,EEwY2B9K,KAAKqJ,MAAMuB,GFvYpEjL,EAAErF,OAASwQ,EACb,GAAA1G,OAAUzE,EAAEhD,UAAU,EAAGmO,GAAK,KAEzBnL,GEsYe,GADgBqI,EAAI+C,YAAYF,GACpBnC,MAAQ5H,EAAG,CAEzBkH,EAAIqC,UAAY,SAChBrC,EAAIsC,aAAe,MAEnBtC,EAAIuB,YAAc7B,EAAKzM,EAAMC,IAAK,IAClC8M,EAAIyB,UAAY,EAAIxB,EAChBD,EAAAgD,WAAWH,EAAOxD,EAAKwB,EAAGxB,EAAK0B,EAAIpN,EAAS,EAAIsM,GAEhDD,EAAAuC,SAASM,EAAOxD,EAAKwB,EAAGxB,EAAK0B,EAAIpN,EAAS,EAAIsM,GAClD,KACF,CACF,CACF,CACF,CFxZE,IAAStI,EAAWmL,CEwZtB,EAGJG,iBAAkB,CAAC3L,EAAW0I,EAAKC,KACjCD,EAAIsB,YACJtB,EAAIuB,YAAc7B,EAAKC,EAAK,KAAKzM,IAAK,MACtC8M,EAAIyB,UAAY,EAAIxB,EACpBD,EAAI0B,OAAOpK,EAAKH,OAAO0J,EAAGvJ,EAAKH,OAAO4J,GACtCf,EAAI2B,OAAOrK,EAAKF,OAAOyJ,EAAGvJ,EAAKF,OAAO2J,GACtCf,EAAI4B,QAAO,SAlbvB,IAAwB3D,EFQEC,GAAYE,EEgbtC,CAEA,MAAM8E,EAAa,CAAA,EACnB,IAAIC,EAAe,EAEnB,SAASrE,EAAgBpK,GACvB,GAAIwO,EAAWxO,GACb,OAAOwO,EAAWxO,GAGd,MAAA0O,EAAMC,EAAWF,EAAeE,EAAW/Q,QASjD,OAPgB6Q,GAAA,EAEhBD,EAAWxO,GAAW,CACpBqK,KAAMuE,EAAKF,GAAK,KAAKlQ,IACrByP,OAAQjD,EAAKzM,EAAMC,IAAK,KAGnBgQ,EAAWxO,EACpB,CE9da,MAAA6O,GAAgB,IAC1BzF,EAAA,MAAA,CAAI0F,MAAM,6BAA6B9C,MAAM,MAAME,OAAO,MAAM6C,QAAQ,oBACvE1F,SAACD,EAAA,OAAA,CACCiB,KAAK,eACLlI,EAAE,g0BCJK6M,GAAmBC,GAA8B,WAAkC,IAAjCC,EAA0BC,UAAAvR,OAAA,QAAAwR,IAAAD,UAAA,GAAAA,UAAA,GAAA,GAChF,MAAA,CACLvO,KAAM,6BAENyO,MAAQC,GACC,IACFA,EACH,CACE1O,KAAM,qBACND,MAAO,QACP4O,KAAMV,GACNW,UAAW,WACT,OAAQpG,EAAAjE,EAAAsK,EAAA,CAAA,EAAcP,GACxB,EACA/I,OAAQuJ,EAAMC,OAAO,0BAK/B"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e,t,n,r,s,i;function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){l(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}Object.defineProperty(exports,"__esModule",{value:!0});var d=require("react/jsx-runtime"),u=require("polished"),h=require("deep-equal"),f=require("react"),p=require("react-force-graph"),g=require("uuid"),y=require("bezier-easing"),b=require("sanity"),m=require("sanity/router"),x=require("styled-components"),v=require("@sanity/color"),_=require("@sanity/ui");function w(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var j=w(h),k=w(y),O=w(x);function q(e){return null===e?0:"object"==typeof e?Object.entries(e).reduce(((e,t)=>{let[n,r]=t;return e+q(n)+q(r)}),0):Array.isArray(e)?Object.entries(e).reduce(((e,t)=>e+q(t)),0):"string"==typeof e?e.length:1}const M=O.default.div(e||(e=c(["\n font-family: ",";\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ",";\n"])),(e=>{let{theme:t}=e;return t.fonts.text.family}),v.black.hex),P=O.default.div(t||(t=c(["\n position: relative;\n width: 100%;\n height: 100%;\n"]))),S=O.default.div(n||(n=c(["\n font-family: ",";\n display: none;\n position: absolute;\n bottom: ","px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ","px;\n padding: ","px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n"])),(e=>{let{theme:t}=e;return t.fonts.text.family}),(e=>{let{theme:t}=e;return t.space[0]}),(e=>{let{theme:t}=e;return t.radius[2]}),(e=>{let{theme:t}=e;return t.space[2]})),I=O.default.div(r||(r=c(["\n color: #ccc;\n position: absolute;\n top: ","px;\n left: ","px;\n\n & > div {\n margin: 5px 0;\n }\n"])),(e=>{let{theme:t}=e;return t.space[4]}),(e=>{let{theme:t}=e;return t.space[4]})),C=O.default.div(s||(s=c(["\n display: flex;\n"]))),A=O.default.div(i||(i=c(["\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ","px;\n"])),(e=>{let{theme:t}=e;return t.space[2]})),D=k.default(0,.9,1,1),T=k.default(.25,.1,0,1);function V(e){return(e.substring(0,1).toUpperCase()+e.substring(1)).replace(/\./g," ").replace(/[A-Z]/g," $&").trim()}function W(e){const t={};for(const n of e)t[n._type]=(t[n._type]||0)+1;return t}function z(e){return"".concat(e.title||e.name||e._id).trim()}function B(e,t){return 5+q(e)/t*100}function E(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(null!==e)if("object"==typeof e)for(const[n,r]of Object.entries(e))"_ref"===n&&"string"==typeof r&&r.length>0&&t.push(N(r)),E(r,t);else if(Array.isArray(e))for(const n of e)E(n,t);return t}function N(e){return e.replace(/^drafts\./,"")}class R{constructor(){this.id=null,this.user=null,this.doc=null,this.lastActive=0,this.startTime=0,this.angle=0}}class U{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.sessions=[];const t={};for(const n of e)t[n._id]=n;this.data={nodes:e.map((e=>Object.assign({id:e._id,type:"document",doc:e}))),links:e.flatMap((e=>E(e).map((t=>({source:e._id,target:t}))))).filter((e=>t[e.source]&&t[e.target]))}}setSession(e,t){let n=this.sessions.find((n=>{var r;return n.user.id===e.id&&(null==(r=n.doc)?void 0:r._id)===t.doc._id}));n||(n=new R,n.id=g.v4(),n.user=e,n.startTime=Date.now(),n.doc=t.doc,n.angle=2*Math.random()*Math.PI,this.sessions.push(n)),n.lastActive=Date.now()}reapSessions(){for(let e=0;e<this.sessions.length;e++){const t=this.sessions[e];Date.now()-t.lastActive>1e4&&(this.sessions=[...this.sessions.slice(0,e),...this.sessions.slice(e+1)],e--)}}clone(){const e=new U;return Object.assign(e,this),e.data={nodes:[...this.data.nodes],links:[...this.data.links]},e}}const H=new class{constructor(){this._users=[]}async getById(e,t){let n=this._users.find((t=>t._id===e));var r,s,i;return n||(n=await t.users.getById(e),this._users.push(n),n.image=await(r=n.imageUrl||"https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg",s=40,i=40,new Promise((e=>{const t=new Image(s,i);t.onload=()=>{e(t)},t.onerror=t=>{console.log("Image error",t),e(null)},t.src=r})))),n}};function G(e){var t;const n=e.query||'\n *[\n !(_id in path("_.*")) &&\n !(_type match "system.*") &&\n !(_type match "sanity.*")\n ]\n',r=null!=(t=e.apiVersion)?t:"2022-09-01",s=b.useUserColorManager(),[i,o]=f.useState(0),[a,l]=f.useState(null),[c,h]=f.useState([]),[g,y]=f.useState({}),[x,w]=f.useState((()=>new U)),k=m.useRouter(),O=b.useClient({apiVersion:r}),R=f.useCallback((e=>{const t=function(e){const t={};for(const n of e)/^drafts\./.test(n._id)||(t[n._id]=n);for(const n of e)if(/^drafts\./.test(n._id)){const e=N(n._id);t[e]=Object.assign(n,{_id:e})}return Object.values(t)}(e);o(Math.max(...t.map(q))),h(t),y(W(t)),w(new U(t))}),[]),G=f.useCallback((async e=>{const t=e.result;if(t){t._id=N(t._id);const n={};for(const e of c)n[e._id]=e;let r;const s=[...c],i=c.findIndex((e=>e._id===t._id));i>=0?(r=s[i],s[i]=t):s.push(t),h(s),y(W(s)),o(Math.max(...s.map(q)));const a=x.clone(),l=E(r||{}).filter((e=>e===t._id||null!==n[e])),d=E(t).filter((e=>e===t._id||null!==n[e]));let u,f=!j.default(l,d);f&&(a.data.links=a.data.links.filter((e=>e.source.id!==t._id)).concat(d.map((e=>({source:t._id,target:e})))));const p=x.data.nodes.findIndex((e=>e.doc&&e.doc._id===t._id));p>=0?(u=x.data.nodes[p],u.doc=t):(u={id:t._id,type:"document",doc:t},a.data.nodes.push(u),f=!0),f&&w(a);const g=await H.getById(e.identity,O);x.setSession(g,u)}else if("disappear"===e.transition){const t=N(e.documentId),n=c.filter((e=>e._id!==t));h(n),y(W(n)),o(Math.max(...n.map(q)));const r=x.clone();r.data.links=r.data.links.filter((e=>e.source.id!==t&&e.target.id!==t)),r.data.nodes=r.data.nodes.filter((e=>e.id!==t)),w(r)}}),[c,x,O]);!function(e,t,n,r){f.useEffect((()=>{r.fetch(e).then((e=>{t(e)}))}),[...n,r])}(n,R,[],O),function(e,t,n,r,s,i){f.useEffect((()=>{const s=i.listen(e,t,n).subscribe((e=>{r(e)}));return()=>{s.unsubscribe()}}),[...s,i])}(n,{},{},G,[c,x],O),f.useEffect((()=>{const e=setInterval((()=>x.reapSessions()),1e3);return()=>clearInterval(e)}),[x]);const L=_.useTheme().sanity;return d.jsx(P,{theme:L,children:d.jsxs(M,{theme:L,children:[d.jsx(I,{theme:L,children:(F=g,($=Object.keys(F),J=e=>F[e]||0,$.sort(((e,t)=>{const n=J(e),r=J(t);return n<r?-1:n>r?1:0}))).reverse().slice(0,10)).map((e=>d.jsxs(C,{className:"legend__row",style:{color:Z(e).fill},children:[d.jsx(A,{theme:L}),d.jsx("div",{children:V(e)})]},e)))}),a&&d.jsx(S,{theme:L,children:z(a.doc)}),d.jsx(p.ForceGraph2D,{graphData:x.data,nodeAutoColorBy:"group",enableNodeDrag:!1,onNodeHover:e=>l(e),onNodeClick:e=>{k.navigateIntent("edit",{id:e.doc._id,documentType:e.doc._type})},linkColor:()=>u.rgba(v.gray[500].hex,.25),nodeLabel:()=>"",nodeRelSize:1,nodeVal:e=>B(e.doc,i),onRenderFramePost:(e,t)=>{for(const n of x.sessions){const r=x.data.nodes.find((e=>{var t;return e.doc&&e.doc._id===(null==(t=null==n?void 0:n.doc)?void 0:t._id)}));if(r){const o=1e4,a=n.angle,l=Math.sqrt(B(r.doc,i)),c=n.user.image,d=s.get(n.user.displayName).tints[400].hex,h=l*t+40,f=c?c.width:0,p=c?c.height:0,g=r.x+Math.sin(a)*h/t,y=r.y+Math.cos(a)*h/t;e.save();try{if(e.globalAlpha=D(1-Math.min(o,Date.now()-n.lastActive)/o),e.font="bold ".concat(Math.round(12/t),"px sans-serif"),e.beginPath(),e.strokeStyle=u.rgba(v.white.hex,1),e.lineWidth=2/t,e.moveTo(r.x+Math.sin(a)*(h-f/2)/t,r.y+Math.cos(a)*(h-p/2)/t),e.lineTo(r.x+Math.sin(a)*l,r.y+Math.cos(a)*l),e.stroke(),e.beginPath(),e.strokeStyle=u.rgba(v.white.hex,1),e.lineWidth=2/t,e.arc(r.x,r.y,l,0,2*Math.PI,!1),e.stroke(),c){e.save();try{const r=700,s=T(Math.max(0,(r-(Date.now()-n.startTime))/r));s>0&&(e.beginPath(),e.fillStyle=u.rgba(d,s),e.arc(g,y,(f/2+10)/t,0,2*Math.PI,!1),e.fill()),e.beginPath(),e.fillStyle=u.rgba(v.white.hex,1),e.arc(g,y,f/t/2,0,2*Math.PI,!1),e.clip(),e.drawImage(c,g-f/t/2,y-p/t/2,f/t,p/t),e.strokeStyle=v.black.hex,e.lineWidth=6/t,e.stroke(),e.strokeStyle=d,e.lineWidth=4/t,e.stroke()}finally{e.restore()}}e.beginPath(),e.strokeStyle=u.rgba(v.black.hex,1),e.lineWidth=.5/t,e.arc(g,y,f/t/2,0,2*Math.PI,!1),e.stroke();const s=a>=Math.PI/2&&a<1.5*Math.PI,i=s?y-(p/2+5)/t:y+(p/2+5)/t;e.fillStyle=u.rgba(v.white.hex,1),e.textAlign="center",e.textBaseline=s?"bottom":"top",e.fillText(n.user.displayName,g,i)}finally{e.restore()}}}},nodeCanvasObject:(e,t,n)=>{if("document"===e.type){const o=Z(e.doc._type),l=Math.sqrt(B(e.doc,i)),c=Math.min(100,10/n);if(t.beginPath(),t.fillStyle=null!==a&&e.doc._id===a.doc._id?u.rgba(v.gray[500].hex,.8):o.fill,t.strokeStyle=o.border,t.lineWidth=.5,t.arc(e.x,e.y,l,0,2*Math.PI,!1),t.stroke(),t.fill(),l*n>10){t.font="".concat(c,"px sans-serif");const i=2*l+30/n;for(let o=50;o>=5;o/=1.2){const a=(r=z(e.doc),s=Math.round(o),r.length>s?"".concat(r.substring(0,s),"…"):r);if(t.measureText(a).width<i){t.textAlign="center",t.textBaseline="top",t.strokeStyle=u.rgba(v.black.hex,.5),t.lineWidth=2/n,t.strokeText(a,e.x,e.y+l+5/n),t.fillText(a,e.x,e.y+l+5/n);break}}}}var r,s},linkCanvasObject:(e,t,n)=>{t.beginPath(),t.strokeStyle=u.rgba(v.gray[500].hex,.125),t.lineWidth=2/n,t.moveTo(e.source.x,e.source.y),t.lineTo(e.target.x,e.target.y),t.stroke()}})]})});var F,$,J}const L={};let F=0;function Z(e){if(L[e])return L[e];const t=v.COLOR_HUES[F%v.COLOR_HUES.length];return F+=1,L[e]={fill:v.hues[t][400].hex,border:u.rgba(v.black.hex,.5)},L[e]}const $=()=>d.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 46.063 46.063",children:d.jsx("path",{fill:"currentColor",d:"M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z"})}),J=b.definePlugin((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{name:"@sanity/content-graph-view",tools:t=>[...t,{name:"graph-your-content",title:"Graph",icon:$,component:function(){return d.jsx(G,a({},e))},router:m.route.create("/:selectedDocumentId")}]}}));exports.contentGraphView=J;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/tool/utils.ts","../src/tool/GraphViewStyle.tsx","../src/tool/GraphView.tsx","../src/tool/hooks.ts","../src/tool/GraphViewIcon.tsx","../src/plugin.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\nexport function sizeOf(value: any): number {\n if (value === null) {\n return 0\n }\n\n if (typeof value === 'object') {\n return Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0)\n }\n\n if (Array.isArray(value)) {\n return Object.entries(value).reduce((total, v) => total + sizeOf(v), 0)\n }\n\n if (typeof value === 'string') {\n return value.length\n }\n\n return 1\n}\n\nexport function loadImage(url: string, w: number, h: number): Promise<HTMLImageElement | null> {\n return new Promise((resolve) => {\n const img = new Image(w, h)\n img.onload = () => {\n resolve(img)\n }\n img.onerror = (event) => {\n // eslint-disable-next-line no-console\n console.log('Image error', event)\n resolve(null)\n }\n img.src = url\n })\n}\n\nexport function sortBy<T>(array: T[], f: (t: T) => number): T[] {\n return array.sort((a, b) => {\n const va = f(a)\n const vb = f(b)\n // eslint-disable-next-line no-nested-ternary\n return va < vb ? -1 : va > vb ? 1 : 0\n })\n}\n\nexport function truncate(s: string, limit: number): string {\n if (s.length > limit) {\n return `${s.substring(0, limit)}…`\n }\n return s\n}\n","import styled from 'styled-components'\nimport {Theme} from '@sanity/ui'\nimport {black} from '@sanity/color'\nimport React, {PropsWithChildren} from 'react'\n\ntype Style = PropsWithChildren<{theme: SanityTheme}>\ntype SanityTheme = Theme['sanity']\n\nexport const GraphRoot: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ${black.hex};\n`\n\nexport const GraphWrapper: React.FC<Style> = styled.div`\n position: relative;\n width: 100%;\n height: 100%;\n` as React.FC<Style>\n\nexport const HoverNode: React.FC<Style> = styled.div`\n font-family: ${({theme}: Style) => theme.fonts.text.family};\n display: none;\n position: absolute;\n bottom: ${({theme}: Style) => theme.space[0]}px;\n left: 50%;\n transform: translate3d(-50%, 0, 0);\n background: var(--component-bg);\n border-radius: ${({theme}: Style) => theme.radius[2]}px;\n padding: ${({theme}: Style) => theme.space[2]}px;\n z-index: 1000;\n\n &:empty {\n display: none;\n }\n`\n\nexport const Legend: React.FC<Style> = styled.div`\n color: #ccc;\n position: absolute;\n top: ${({theme}: Style) => theme.space[4]}px;\n left: ${({theme}: Style) => theme.space[4]}px;\n\n & > div {\n margin: 5px 0;\n }\n`\n\nexport const LegendRow = styled.div`\n display: flex;\n`\n\nexport const LegendBadge: React.FC<Style> = styled.div`\n width: 1.25em;\n height: 1.25em;\n background: currentColor;\n border-radius: 50%;\n margin-right: ${({theme}: Style) => theme.space[2]}px;\n`\n","import {rgba} from 'polished'\nimport deepEqual from 'deep-equal'\nimport React, {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {v4 as uuidv4} from 'uuid'\nimport BezierEasing from 'bezier-easing'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\nimport {SanityDocument, SanityClient} from '@sanity/client'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useTheme} from '@sanity/ui'\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\n\nconst DEFAULT_QUERY = `\n *[\n !(_id in path(\"_.*\")) &&\n !(_type match \"system.*\") &&\n !(_type match \"sanity.*\")\n ]\n`\n\nconst fadeEasing = BezierEasing(0, 0.9, 1, 1)\nconst softEasing = BezierEasing(0.25, 0.1, 0.0, 1.0)\nconst idleTimeout = 10000\nconst imageSize = 40\n\nfunction getTopDocTypes(counts: Record<string, number>) {\n return sortBy(Object.keys(counts), (docType) => counts[docType] || 0)\n .reverse()\n .slice(0, 10)\n}\n\nfunction formatDocType(docType: string) {\n return (docType.substring(0, 1).toUpperCase() + docType.substring(1))\n .replace(/\\./g, ' ')\n .replace(/[A-Z]/g, ' $&')\n .trim()\n}\n\nfunction getDocTypeCounts(docs: SanityDocument[]) {\n const types: Record<string, number> = {}\n for (const doc of docs) {\n types[doc._type] = (types[doc._type] || 0) + 1\n }\n return types\n}\n\nfunction labelFor(doc: SanityDocument) {\n return `${doc.title || doc.name || doc._id}`.trim()\n}\n\nfunction valueFor(doc: any, maxSize: number) {\n return 5 + 100 * (sizeOf(doc) / maxSize)\n}\n\nfunction findRefs(obj: any, dest: any[] = []) {\n if (obj !== null) {\n if (typeof obj === 'object') {\n for (const [k, v] of Object.entries(obj)) {\n if (k === '_ref' && typeof v === 'string' && v.length > 0) {\n dest.push(stripDraftId(v))\n }\n findRefs(v, dest)\n }\n } else if (Array.isArray(obj)) {\n for (const v of obj) {\n findRefs(v, dest)\n }\n }\n }\n return dest\n}\n\nfunction stripDraftId(id: string) {\n return id.replace(/^drafts\\./, '')\n}\n\nfunction deduplicateDrafts(docs: SanityDocument[]) {\n const deduped: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n if (!/^drafts\\./.test(doc._id)) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (/^drafts\\./.test(doc._id)) {\n const id = stripDraftId(doc._id)\n deduped[id] = Object.assign(doc, {_id: id})\n }\n }\n return Object.values(deduped)\n}\n\nclass Users {\n _users: any[] = []\n\n async getById(id: string, client: SanityClient) {\n let user = this._users.find((u) => u._id === id)\n if (!user) {\n user = await client.users.getById(id)\n this._users.push(user)\n user.image = await loadImage(\n user.imageUrl ||\n 'https://raw.githubusercontent.com/sanity-io/sanity-plugin-graph-view/main/assets/head-silhouette.jpg',\n imageSize,\n imageSize\n )\n }\n return user\n }\n}\n\nclass Session {\n id: string | null = null\n user: any = null\n doc: SanityDocument | null = null\n lastActive: number = 0\n startTime: number = 0\n angle: number = 0\n}\n\nclass GraphData {\n sessions: Session[] = []\n data: any\n\n constructor(docs: SanityDocument[] = []) {\n const docsById: Record<string, SanityDocument> = {}\n for (const doc of docs) {\n docsById[doc._id] = doc\n }\n\n this.data = {\n nodes: docs.map((d) => Object.assign({id: d._id, type: 'document', doc: d})),\n links: docs\n .flatMap((doc) => findRefs(doc).map((ref) => ({source: doc._id, target: ref})))\n .filter((link) => docsById[link.source] && docsById[link.target]),\n }\n }\n\n setSession(user, docNode) {\n let session = this.sessions.find((s) => s.user.id === user.id && s.doc?._id === docNode.doc._id)\n if (!session) {\n session = new Session()\n session.id = uuidv4()\n session.user = user\n session.startTime = Date.now()\n session.doc = docNode.doc\n session.angle = Math.random() * 2 * Math.PI\n this.sessions.push(session)\n }\n session.lastActive = Date.now()\n }\n\n reapSessions() {\n for (let i = 0; i < this.sessions.length; i++) {\n const session = this.sessions[i]\n if (Date.now() - session.lastActive > idleTimeout) {\n this.sessions = [...this.sessions.slice(0, i), ...this.sessions.slice(i + 1)]\n i--\n }\n }\n }\n\n clone() {\n const copy = new GraphData()\n Object.assign(copy, this)\n copy.data = {\n nodes: [...this.data.nodes],\n links: [...this.data.links],\n }\n return copy\n }\n}\n\nconst users = new Users()\n\ninterface GraphViewConfig {\n query?: string\n /** default: '2022-09-01' */\n apiVersion?: string\n}\n\nexport function GraphView(props: GraphViewConfig) {\n const query = props.query || DEFAULT_QUERY\n const apiVersion = props.apiVersion ?? '2022-09-01'\n\n const userColorManager = useUserColorManager()\n const [maxSize, setMaxSize] = useState(0)\n const [hoverNode, setHoverNode] = useState<any>(null)\n const [documents, setDocuments] = useState<SanityDocument[]>([])\n const [docTypes, setDocTypes] = useState<Record<string, number>>({})\n const [graph, setGraph] = useState(() => new GraphData())\n const router = useRouter()\n const client = useClient({apiVersion})\n\n const fetchCallback = useCallback((_docs) => {\n const docs = deduplicateDrafts(_docs)\n setMaxSize(Math.max(...docs.map(sizeOf)))\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setGraph(new GraphData(docs))\n }, [])\n\n const listenCallback = useCallback(\n async (update) => {\n const doc = update.result\n if (doc) {\n doc._id = stripDraftId(doc._id)\n\n const docsById: Record<string, SanityDocument> = {}\n for (const d of documents) {\n docsById[d._id] = d\n }\n\n let oldDoc\n const docs = [...documents]\n const idx = documents.findIndex((d) => d._id === doc._id)\n if (idx >= 0) {\n oldDoc = docs[idx]\n docs[idx] = doc\n } else {\n docs.push(doc)\n }\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n\n const oldRefs = findRefs(oldDoc || {}).filter(\n (id) => id === doc._id || docsById[id] !== null\n )\n const newRefs = findRefs(doc).filter((id) => id === doc._id || docsById[id] !== null)\n\n let graphChanged = !deepEqual(oldRefs, newRefs)\n if (graphChanged) {\n newGraph.data.links = newGraph.data.links\n .filter((l) => l.source.id !== doc._id)\n .concat(newRefs.map((ref) => ({source: doc._id, target: ref})))\n }\n\n let docNode\n const nodeIdx = graph.data.nodes.findIndex((n) => n.doc && n.doc._id === doc._id)\n if (nodeIdx >= 0) {\n docNode = graph.data.nodes[nodeIdx]\n docNode.doc = doc\n } else {\n docNode = {id: doc._id, type: 'document', doc: doc}\n newGraph.data.nodes.push(docNode)\n graphChanged = true\n }\n if (graphChanged) {\n setGraph(newGraph)\n }\n\n const user = await users.getById(update.identity, client)\n graph.setSession(user, docNode)\n } else if (update.transition === 'disappear') {\n const docId = stripDraftId(update.documentId)\n\n const docs = documents.filter((d) => d._id !== docId)\n setDocuments(docs)\n setDocTypes(getDocTypeCounts(docs))\n setMaxSize(Math.max(...docs.map(sizeOf)))\n\n const newGraph = graph.clone()\n newGraph.data.links = newGraph.data.links.filter(\n (l) => l.source.id !== docId && l.target.id !== docId\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client]\n )\n useFetchDocuments(query, fetchCallback, [], client)\n useListen(query, {}, {}, listenCallback, [documents, graph], client)\n useEffect(() => {\n const interval = setInterval(() => graph.reapSessions(), 1000)\n return () => clearInterval(interval)\n }, [graph])\n\n const theme = useTheme().sanity\n\n return (\n <GraphWrapper theme={theme}>\n <GraphRoot theme={theme}>\n <Legend theme={theme}>\n {getTopDocTypes(docTypes).map((docType) => (\n <LegendRow\n className={'legend__row'}\n key={docType}\n style={{color: getDocTypeColor(docType).fill}}\n >\n <LegendBadge theme={theme} />\n <div>{formatDocType(docType)}</div>\n </LegendRow>\n ))}\n </Legend>\n {hoverNode && <HoverNode theme={theme}>{labelFor(hoverNode.doc)}</HoverNode>}\n\n <ForceGraph2D\n graphData={graph.data}\n nodeAutoColorBy=\"group\"\n enableNodeDrag={false}\n onNodeHover={(node: any) => setHoverNode(node)}\n onNodeClick={(node: any) => {\n router.navigateIntent('edit', {id: node.doc._id, documentType: node.doc._type})\n }}\n linkColor={() => rgba(gray[500].hex, 0.25)}\n nodeLabel={() => ''}\n nodeRelSize={1}\n nodeVal={(node: any) => valueFor(node.doc, maxSize)}\n onRenderFramePost={(ctx, globalScale) => {\n for (const session of graph.sessions) {\n const node = graph.data.nodes.find((n) => n.doc && n.doc._id === session?.doc?._id)\n if (node) {\n const idleFactorRange = idleTimeout\n const angle = session.angle\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const image = session.user.image\n const userColor = userColorManager.get(session.user.displayName).tints[400].hex\n const distance = radius * globalScale + 40\n const imgW = image ? image.width : 0\n const imgH = image ? image.height : 0\n const x = node.x + (Math.sin(angle) * distance) / globalScale\n const y = node.y + (Math.cos(angle) * distance) / globalScale\n\n ctx.save()\n try {\n ctx.globalAlpha = fadeEasing(\n 1 - Math.min(idleFactorRange, Date.now() - session.lastActive) / idleFactorRange\n )\n ctx.font = `bold ${Math.round(12 / globalScale)}px sans-serif`\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(\n node.x + (Math.sin(angle) * (distance - imgW / 2)) / globalScale,\n node.y + (Math.cos(angle) * (distance - imgH / 2)) / globalScale\n )\n ctx.lineTo(node.x + Math.sin(angle) * radius, node.y + Math.cos(angle) * radius)\n ctx.stroke()\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(white.hex, 1.0)\n ctx.lineWidth = 2 / globalScale\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n if (image) {\n ctx.save()\n // eslint-disable-next-line max-depth\n try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur)\n )\n // eslint-disable-next-line max-depth\n if (f > 0) {\n ctx.beginPath()\n ctx.fillStyle = rgba(userColor, f)\n ctx.arc(x, y, (imgW / 2 + 10) / globalScale, 0, 2 * Math.PI, false)\n ctx.fill()\n }\n\n ctx.beginPath()\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.clip()\n\n ctx.drawImage(\n image,\n x - imgW / globalScale / 2,\n y - imgH / globalScale / 2,\n imgW / globalScale,\n imgH / globalScale\n )\n\n ctx.strokeStyle = black.hex\n ctx.lineWidth = 6 / globalScale\n ctx.stroke()\n\n ctx.strokeStyle = userColor\n ctx.lineWidth = 4 / globalScale\n ctx.stroke()\n } finally {\n ctx.restore()\n }\n }\n\n ctx.beginPath()\n ctx.strokeStyle = rgba(black.hex, 1)\n ctx.lineWidth = 0.5 / globalScale\n ctx.arc(x, y, imgW / globalScale / 2, 0, 2 * Math.PI, false)\n ctx.stroke()\n\n const above = angle >= Math.PI / 2 && angle < Math.PI * 1.5\n const textY = above\n ? y - (imgH / 2 + 5) / globalScale\n : y + (imgH / 2 + 5) / globalScale\n ctx.fillStyle = rgba(white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = above ? 'bottom' : 'top'\n ctx.fillText(session.user.displayName, x, textY)\n } finally {\n ctx.restore()\n }\n }\n }\n }}\n nodeCanvasObject={(node: any, ctx, globalScale) => {\n // eslint-disable-next-line default-case\n switch (node.type) {\n case 'document': {\n const nodeColor = getDocTypeColor(node.doc._type)\n const radius = Math.sqrt(valueFor(node.doc, maxSize))\n const fontSize = Math.min(100, 10.0 / globalScale)\n\n ctx.beginPath()\n ctx.fillStyle =\n hoverNode !== null && node.doc._id === hoverNode.doc._id\n ? rgba(gray[500].hex, 0.8)\n : nodeColor.fill\n ctx.strokeStyle = nodeColor.border\n ctx.lineWidth = 0.5\n ctx.arc(node.x, node.y, radius, 0, 2 * Math.PI, false)\n ctx.stroke()\n ctx.fill()\n\n if (radius * globalScale > 10) {\n ctx.font = `${fontSize}px sans-serif`\n const w = radius * 2 + 30 / globalScale\n for (let len = 50; len >= 5; len /= 1.2) {\n const label = truncate(labelFor(node.doc), Math.round(len))\n const textMetrics = ctx.measureText(label)\n if (textMetrics.width < w) {\n // ctx.fillStyle = rgba(color.white.hex, 1.0)\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n\n ctx.strokeStyle = rgba(black.hex, 0.5)\n ctx.lineWidth = 2 / globalScale\n ctx.strokeText(label, node.x, node.y + radius + 5 / globalScale)\n\n ctx.fillText(label, node.x, node.y + radius + 5 / globalScale)\n break\n }\n }\n }\n }\n }\n }}\n linkCanvasObject={(link: any, ctx, globalScale) => {\n ctx.beginPath()\n ctx.strokeStyle = rgba(gray[500].hex, 0.125)\n ctx.lineWidth = 2 / globalScale\n ctx.moveTo(link.source.x, link.source.y)\n ctx.lineTo(link.target.x, link.target.y)\n ctx.stroke()\n }}\n />\n </GraphRoot>\n </GraphWrapper>\n )\n}\n\nconst colorCache = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType) {\n if (colorCache[docType]) {\n return colorCache[docType]\n }\n\n const hue = COLOR_HUES[typeColorNum % COLOR_HUES.length]\n\n typeColorNum += 1\n\n colorCache[docType] = {\n fill: hues[hue][400].hex,\n border: rgba(black.hex, 0.5),\n }\n\n return colorCache[docType]\n}\n","import {useEffect} from 'react'\nimport {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\n// eslint-disable-next-line max-params\nexport function useListen(\n query: string,\n params: {[key: string]: any},\n options: ListenOptions,\n onUpdate: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient\n): void {\n useEffect(() => {\n const subscription = client.listen(query, params, options).subscribe((update) => {\n onUpdate(update)\n })\n return () => {\n subscription.unsubscribe()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\n}\n\nexport function useFetchDocuments(\n query: string,\n onFetch: (event: ListenEvent<any>) => void,\n dependencies: unknown[],\n client: SanityClient\n): void {\n useEffect(() => {\n client.fetch(query).then((result) => {\n onFetch(result)\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...dependencies, client])\n}\n","import React from 'react'\n\n/**\n * Couple of things to note:\n * - width and height is set to 1em\n * - fill is `currentColor` - this will ensure that the icon looks uniform and\n * that the hover/active state works. You can of course render anything you\n * would like here, but for plugins that are to be used in more than one\n * studio, we suggest these rules are followed\n **/\nexport const GraphViewIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 46.063 46.063\">\n <path\n fill=\"currentColor\"\n d=\"M18.022 38.676v2.813h-1.21q-4.864 0-6.525-1.447-1.64-1.445-1.64-5.762v-4.666q0-2.95-1.055-4.083-1.055-1.13-3.828-1.13h-1.19v-2.794h1.19q2.793 0 3.828-1.114 1.055-1.132 1.055-4.043V11.76q0-4.316 1.64-5.742 1.66-1.446 6.524-1.446h1.212v2.793h-1.328q-2.754 0-3.594.86-.84.86-.84 3.613v4.844q0 3.066-.898 4.453-.88 1.387-3.028 1.875 2.168.527 3.047 1.914.88 1.387.88 4.434v4.843q0 2.754.84 3.614.84.86 3.594.86h1.328zM28.04 38.676h1.368q2.735 0 3.555-.84.84-.84.84-3.633V29.36q0-3.047.88-4.434.878-1.387 3.046-1.914-2.17-.488-3.048-1.875-.88-1.387-.88-4.453V11.84q0-2.773-.84-3.613-.82-.86-3.554-.86H28.04V4.574h1.232q4.863 0 6.484 1.446 1.64 1.426 1.64 5.742v4.687q0 2.91 1.055 4.042 1.056 1.114 3.83 1.114h1.21V24.4h-1.21q-2.774 0-3.83 1.13-1.053 1.134-1.053 4.084v4.667q0 4.318-1.64 5.763-1.622 1.446-6.485 1.446h-1.23v-2.814z\"\n />\n </svg>\n)\n","import {GraphView} from './tool/GraphView'\nimport {GraphViewIcon} from './tool/GraphViewIcon'\nimport {definePlugin} from 'sanity'\nimport {route} from 'sanity/router'\nimport React from 'react'\n\nexport interface GraphViewConfig {\n query?: string\n}\n\nexport const contentGraphView = definePlugin<GraphViewConfig>((config: GraphViewConfig = {}) => {\n return {\n name: '@sanity/content-graph-view',\n\n tools: (prev) => {\n return [\n ...prev,\n {\n name: 'graph-your-content',\n title: 'Graph',\n icon: GraphViewIcon,\n component: function component() {\n return <GraphView {...config} />\n },\n router: route.create('/:selectedDocumentId'),\n },\n ]\n },\n }\n})\n"],"names":["sizeOf","value","Object","entries","reduce","total","k","v","_ref","Array","isArray","length","GraphRoot","styled","div","_templateObject","_taggedTemplateLiteral","_ref2","theme","fonts","text","family","black","hex","GraphWrapper","_templateObject2","HoverNode","_templateObject3","_ref3","_ref4","space","_ref5","radius","_ref6","Legend","_templateObject4","_ref7","_ref8","LegendRow","_templateObject5","LegendBadge","_templateObject6","_ref9","fadeEasing","BezierEasing","softEasing","formatDocType","docType","substring","toUpperCase","replace","trim","getDocTypeCounts","docs","types","doc","_type","labelFor","title","name","_id","valueFor","maxSize","findRefs","obj","dest","push","stripDraftId","id","Session","constructor","this","user","lastActive","startTime","angle","GraphData","sessions","docsById","data","nodes","map","d","assign","type","links","flatMap","ref","source","target","filter","link","setSession","docNode","session","find","s","_a","uuidv4","Date","now","Math","random","PI","reapSessions","i","slice","clone","copy","users","_users","async","client","u","url","w","h","getById","image","imageUrl","Promise","resolve","img","Image","onload","onerror","event","console","log","src","GraphView","props","query","apiVersion","userColorManager","useUserColorManager","setMaxSize","useState","hoverNode","setHoverNode","documents","setDocuments","docTypes","setDocTypes","graph","setGraph","router","useRouter","useClient","fetchCallback","useCallback","_docs","deduped","test","values","deduplicateDrafts","max","listenCallback","update","result","oldDoc","idx","findIndex","newGraph","oldRefs","newRefs","graphChanged","deepEqual","l","concat","nodeIdx","n","identity","transition","docId","documentId","onFetch","dependencies","useEffect","fetch","then","useFetchDocuments","params","options","onUpdate","subscription","listen","subscribe","unsubscribe","useListen","interval","setInterval","clearInterval","useTheme","sanity","jsx","children","jsxs","counts","array","keys","f","sort","a","b","va","vb","reverse","className","style","color","getDocTypeColor","fill","ForceGraph2D","graphData","nodeAutoColorBy","enableNodeDrag","onNodeHover","node","onNodeClick","navigateIntent","documentType","linkColor","rgba","gray","nodeLabel","nodeRelSize","nodeVal","onRenderFramePost","ctx","globalScale","idleFactorRange","sqrt","userColor","get","displayName","tints","distance","imgW","width","imgH","height","x","sin","y","cos","save","globalAlpha","min","font","round","beginPath","strokeStyle","white","lineWidth","moveTo","lineTo","stroke","arc","dur","fillStyle","clip","drawImage","restore","above","textY","textAlign","textBaseline","fillText","nodeCanvasObject","nodeColor","fontSize","border","len","label","limit","measureText","strokeText","linkCanvasObject","colorCache","typeColorNum","hue","COLOR_HUES","hues","GraphViewIcon","xmlns","viewBox","contentGraphView","definePlugin","config","arguments","undefined","tools","prev","icon","component","_objectSpread","route","create"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,49 +1,94 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-graph-view",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"homepage": "https://github.com/sanity-io/sanity-plugin-graph-view#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/sanity-io/sanity-plugin-graph-view/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git@github.com:sanity-io/sanity-plugin-graph-view.git"
|
|
12
|
+
},
|
|
6
13
|
"license": "MIT",
|
|
14
|
+
"author": "Sanity.io <hello@sanity.io>",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./lib/src/index.d.ts",
|
|
18
|
+
"source": "./src/index.ts",
|
|
19
|
+
"import": "./lib/index.esm.js",
|
|
20
|
+
"require": "./lib/index.js",
|
|
21
|
+
"default": "./lib/index.esm.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"main": "./lib/index.js",
|
|
26
|
+
"module": "./lib/index.esm.js",
|
|
27
|
+
"source": "./src/index.ts",
|
|
28
|
+
"types": "./lib/src/index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"src",
|
|
31
|
+
"lib",
|
|
32
|
+
"v2-incompatible.js",
|
|
33
|
+
"sanity.json"
|
|
34
|
+
],
|
|
7
35
|
"scripts": {
|
|
8
|
-
"
|
|
36
|
+
"prebuild": "npm run clean && plugin-kit verify-package --silent && pkg-utils",
|
|
37
|
+
"build": "pkg-utils build --strict",
|
|
38
|
+
"clean": "rimraf lib",
|
|
39
|
+
"compile": "tsc --noEmit",
|
|
40
|
+
"format": "prettier src -w",
|
|
41
|
+
"link-watch": "plugin-kit link-watch",
|
|
9
42
|
"lint": "eslint .",
|
|
10
|
-
"
|
|
43
|
+
"lint:fix": "eslint . --fix",
|
|
44
|
+
"prepare": "husky install",
|
|
45
|
+
"prepublishOnly": "npm run build",
|
|
46
|
+
"watch": "pkg-utils watch"
|
|
11
47
|
},
|
|
12
48
|
"dependencies": {
|
|
13
|
-
"@sanity/
|
|
49
|
+
"@sanity/client": "^3.3.0",
|
|
50
|
+
"@sanity/color": "^2.1.10",
|
|
51
|
+
"@sanity/incompatible-plugin": "^1.0.4",
|
|
52
|
+
"@sanity/ui": "1.0.0-beta.32",
|
|
14
53
|
"bezier-easing": "^2.1.0",
|
|
15
|
-
"deep-equal": "^2.0.
|
|
16
|
-
"polished": "^4.
|
|
17
|
-
"react-force-graph": "^1.
|
|
18
|
-
"
|
|
54
|
+
"deep-equal": "^2.0.5",
|
|
55
|
+
"polished": "^4.2.2",
|
|
56
|
+
"react-force-graph": "^1.41.12",
|
|
57
|
+
"styled-components": "^5.3.5",
|
|
58
|
+
"uuid": "^8.3.2"
|
|
19
59
|
},
|
|
20
60
|
"devDependencies": {
|
|
21
|
-
"@
|
|
22
|
-
"@
|
|
23
|
-
"@
|
|
24
|
-
"@
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
30
|
-
"eslint": "^
|
|
31
|
-
"eslint
|
|
32
|
-
"eslint-
|
|
33
|
-
"eslint-
|
|
34
|
-
"eslint-plugin-
|
|
35
|
-
"
|
|
36
|
-
"react": "^
|
|
61
|
+
"@commitlint/cli": "^17.2.0",
|
|
62
|
+
"@commitlint/config-conventional": "^17.2.0",
|
|
63
|
+
"@sanity/pkg-utils": "^1.17.2",
|
|
64
|
+
"@sanity/plugin-kit": "^2.1.6",
|
|
65
|
+
"@sanity/semantic-release-preset": "^2.0.2",
|
|
66
|
+
"@types/deep-equal": "^1.0.1",
|
|
67
|
+
"@types/react": "^18",
|
|
68
|
+
"@types/styled-components": "^5.1.25",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
|
70
|
+
"@typescript-eslint/parser": "^5.42.0",
|
|
71
|
+
"eslint": "^8.26.0",
|
|
72
|
+
"eslint-config-prettier": "^8.5.0",
|
|
73
|
+
"eslint-config-sanity": "^6.0.0",
|
|
74
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
75
|
+
"eslint-plugin-react": "^7.31.10",
|
|
76
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
77
|
+
"husky": "^8.0.1",
|
|
78
|
+
"lint-staged": "^13.0.3",
|
|
79
|
+
"prettier": "^2.7.1",
|
|
80
|
+
"prettier-plugin-packagejson": "^2.3.0",
|
|
81
|
+
"react": "^18",
|
|
82
|
+
"rimraf": "^3.0.2",
|
|
83
|
+
"sanity": "3.0.0-rc.2",
|
|
84
|
+
"typescript": "^4.8.4"
|
|
37
85
|
},
|
|
38
86
|
"peerDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
},
|
|
42
|
-
"resolutions": {
|
|
43
|
-
"**/three": "0.119.1"
|
|
87
|
+
"react": "^18",
|
|
88
|
+
"sanity": "dev-preview || 3.0.0-rc.2"
|
|
44
89
|
},
|
|
45
|
-
"
|
|
46
|
-
"
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=14"
|
|
47
92
|
},
|
|
48
|
-
"
|
|
93
|
+
"sanityExchangeUrl": "https://www.sanity.io/plugins/graph-view"
|
|
49
94
|
}
|
package/sanity.json
CHANGED
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {contentGraphView, type GraphViewConfig} from './plugin'
|
package/src/plugin.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {GraphView} from './tool/GraphView'
|
|
2
|
+
import {GraphViewIcon} from './tool/GraphViewIcon'
|
|
3
|
+
import {definePlugin} from 'sanity'
|
|
4
|
+
import {route} from 'sanity/router'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
|
|
7
|
+
export interface GraphViewConfig {
|
|
8
|
+
query?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const contentGraphView = definePlugin<GraphViewConfig>((config: GraphViewConfig = {}) => {
|
|
12
|
+
return {
|
|
13
|
+
name: '@sanity/content-graph-view',
|
|
14
|
+
|
|
15
|
+
tools: (prev) => {
|
|
16
|
+
return [
|
|
17
|
+
...prev,
|
|
18
|
+
{
|
|
19
|
+
name: 'graph-your-content',
|
|
20
|
+
title: 'Graph',
|
|
21
|
+
icon: GraphViewIcon,
|
|
22
|
+
component: function component() {
|
|
23
|
+
return <GraphView {...config} />
|
|
24
|
+
},
|
|
25
|
+
router: route.create('/:selectedDocumentId'),
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
})
|