sanity-plugin-graph-view 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var jsxRuntime = require("react/jsx-runtime"), BezierEasing = require("bezier-easing"), deepEqual = require("deep-equal"), polished = require("polished"), react = require("react"), reactForceGraph = require("react-force-graph"), sanity = require("sanity"), router = require("sanity/router"), uuid = require("uuid"), color = require("@sanity/color"), ui = require("@sanity/ui"),
|
|
2
|
+
var jsxRuntime = require("react/jsx-runtime"), BezierEasing = require("bezier-easing"), deepEqual = require("deep-equal"), polished = require("polished"), react = require("react"), reactForceGraph = require("react-force-graph"), sanity = require("sanity"), router = require("sanity/router"), uuid = require("uuid"), color = require("@sanity/color"), ui = require("@sanity/ui"), styledComponents = require("styled-components");
|
|
3
3
|
function _interopDefaultCompat(e) {
|
|
4
4
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
5
5
|
}
|
|
6
|
-
var BezierEasing__default = /* @__PURE__ */ _interopDefaultCompat(BezierEasing), deepEqual__default = /* @__PURE__ */ _interopDefaultCompat(deepEqual)
|
|
7
|
-
const GraphRoot =
|
|
6
|
+
var BezierEasing__default = /* @__PURE__ */ _interopDefaultCompat(BezierEasing), deepEqual__default = /* @__PURE__ */ _interopDefaultCompat(deepEqual);
|
|
7
|
+
const GraphRoot = styledComponents.styled.div`
|
|
8
8
|
font-family: ${({ theme }) => theme.fonts.text.family};
|
|
9
9
|
position: absolute;
|
|
10
10
|
top: 0;
|
|
@@ -12,11 +12,11 @@ const GraphRoot = styled__default.default.div`
|
|
|
12
12
|
width: 100%;
|
|
13
13
|
height: 100%;
|
|
14
14
|
background: ${color.black.hex};
|
|
15
|
-
`, GraphWrapper =
|
|
15
|
+
`, GraphWrapper = styledComponents.styled.div`
|
|
16
16
|
position: relative;
|
|
17
17
|
width: 100%;
|
|
18
18
|
height: 100%;
|
|
19
|
-
`, HoverNode =
|
|
19
|
+
`, HoverNode = styledComponents.styled.div`
|
|
20
20
|
font-family: ${({ theme }) => theme.fonts.text.family};
|
|
21
21
|
display: none;
|
|
22
22
|
position: absolute;
|
|
@@ -31,7 +31,7 @@ const GraphRoot = styled__default.default.div`
|
|
|
31
31
|
&:empty {
|
|
32
32
|
display: none;
|
|
33
33
|
}
|
|
34
|
-
`, Legend =
|
|
34
|
+
`, Legend = styledComponents.styled.div`
|
|
35
35
|
color: #ccc;
|
|
36
36
|
position: absolute;
|
|
37
37
|
top: ${({ theme }) => theme.space[4]}px;
|
|
@@ -40,32 +40,15 @@ const GraphRoot = styled__default.default.div`
|
|
|
40
40
|
& > div {
|
|
41
41
|
margin: 5px 0;
|
|
42
42
|
}
|
|
43
|
-
`, LegendRow =
|
|
43
|
+
`, LegendRow = styledComponents.styled.div`
|
|
44
44
|
display: flex;
|
|
45
|
-
`, LegendBadge =
|
|
45
|
+
`, LegendBadge = styledComponents.styled.div`
|
|
46
46
|
width: 1.25em;
|
|
47
47
|
height: 1.25em;
|
|
48
48
|
background: currentColor;
|
|
49
49
|
border-radius: 50%;
|
|
50
50
|
margin-right: ${({ theme }) => theme.space[2]}px;
|
|
51
51
|
`;
|
|
52
|
-
function useListen(query, params, options, onUpdate, dependencies, client) {
|
|
53
|
-
react.useEffect(() => {
|
|
54
|
-
const subscription = client.listen(query, params, options).subscribe((update) => {
|
|
55
|
-
onUpdate(update);
|
|
56
|
-
});
|
|
57
|
-
return () => {
|
|
58
|
-
subscription.unsubscribe();
|
|
59
|
-
};
|
|
60
|
-
}, [...dependencies, client]);
|
|
61
|
-
}
|
|
62
|
-
function useFetchDocuments(query, onFetch, dependencies, client) {
|
|
63
|
-
react.useEffect(() => {
|
|
64
|
-
client.fetch(query).then((result) => {
|
|
65
|
-
onFetch(result);
|
|
66
|
-
});
|
|
67
|
-
}, [...dependencies, client]);
|
|
68
|
-
}
|
|
69
52
|
function sizeOf(value) {
|
|
70
53
|
return value === null ? 0 : typeof value == "object" ? Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0) : Array.isArray(value) ? Object.entries(value).reduce((total, v) => total + sizeOf(v), 0) : typeof value == "string" ? value.length : 1;
|
|
71
54
|
}
|
|
@@ -191,11 +174,15 @@ const users = new Users();
|
|
|
191
174
|
function GraphView({
|
|
192
175
|
tool: { options: props }
|
|
193
176
|
}) {
|
|
194
|
-
const query = props.query || DEFAULT_QUERY, apiVersion = props.apiVersion ?? "2022-09-01", userColorManager = sanity.useUserColorManager(), [maxSize, setMaxSize] = react.useState(0), [hoverNode, setHoverNode] = react.useState(null), [documents, setDocuments] = react.useState([]), [docTypes, setDocTypes] = react.useState({}), [graph, setGraph] = react.useState(() => new GraphData()), router$1 = router.useRouter(), client = sanity.useClient({ apiVersion })
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
177
|
+
const query = props.query || DEFAULT_QUERY, apiVersion = props.apiVersion ?? "2022-09-01", userColorManager = sanity.useUserColorManager(), [maxSize, setMaxSize] = react.useState(0), [hoverNode, setHoverNode] = react.useState(null), [documents, setDocuments] = react.useState([]), [docTypes, setDocTypes] = react.useState({}), [graph, setGraph] = react.useState(() => new GraphData()), router$1 = router.useRouter(), client = sanity.useClient({ apiVersion });
|
|
178
|
+
react.useEffect(() => {
|
|
179
|
+
client.fetch(query).then((_docs) => {
|
|
180
|
+
const docs = deduplicateDrafts(_docs);
|
|
181
|
+
setMaxSize(Math.max(...docs.map(sizeOf))), setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setGraph(new GraphData(docs));
|
|
182
|
+
});
|
|
183
|
+
}, [client, query]), react.useEffect(() => {
|
|
184
|
+
const subscription = client.listen(query, {}, {}).subscribe(async (update) => {
|
|
185
|
+
if (update.type !== "mutation") return;
|
|
199
186
|
const doc = update.result;
|
|
200
187
|
if (doc) {
|
|
201
188
|
doc._id = stripDraftId(doc._id);
|
|
@@ -223,10 +210,11 @@ function GraphView({
|
|
|
223
210
|
(l) => l.source.id !== docId && l.target.id !== docId
|
|
224
211
|
), newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId), setGraph(newGraph);
|
|
225
212
|
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
213
|
+
});
|
|
214
|
+
return () => {
|
|
215
|
+
subscription.unsubscribe();
|
|
216
|
+
};
|
|
217
|
+
}, [client, query, documents, graph]), react.useEffect(() => {
|
|
230
218
|
const interval = setInterval(() => graph.reapSessions(), 1e3);
|
|
231
219
|
return () => clearInterval(interval);
|
|
232
220
|
}, [graph]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphView.cjs","sources":["../../src/tool/GraphViewStyle.tsx","../../src/tool/hooks.ts","../../src/tool/utils.ts","../../src/tool/GraphView.tsx"],"sourcesContent":["import {type PropsWithChildren} from 'react'\nimport styled from 'styled-components'\n\nimport type {Theme} from '@sanity/ui'\n\nimport {black} from '@sanity/color'\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 {useEffect} from 'react'\n\nimport type {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\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 }, [...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 void client.fetch(query).then((result) => {\n onFetch(result)\n })\n }, [...dependencies, client])\n}\n","export 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 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 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 BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport type {SanityDocument, SanityClient} from '@sanity/client'\n\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\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 (!doc._id.startsWith('drafts.')) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (doc._id.startsWith('drafts.')) {\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: any, docNode: any) {\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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: any) => {\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: any) => {\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: any) => 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: any) => 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 // @ts-expect-error - TODO: fix this\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: any) => l.source.id !== docId && l.target.id !== docId,\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n: any) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client],\n )\n // @ts-expect-error - TODO: fix this\n useFetchDocuments(query, fetchCallback, [], client)\n // @ts-expect-error - TODO: fix this\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 <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: any) => n.doc && n.doc._id === session?.doc?._id,\n )\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 -\n 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 try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur),\n )\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 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: Record<string, {fill: string; border: string}> = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType: any) {\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"],"names":["styled","black","useEffect","BezierEasing","uuidv4","useUserColorManager","useState","router","useRouter","useClient","useCallback","deepEqual","useTheme","jsx","jsxs","ForceGraph2D","rgba","gray","white","COLOR_HUES","hues"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GraphView.cjs","sources":["../../src/tool/GraphViewStyle.tsx","../../src/tool/utils.ts","../../src/tool/GraphView.tsx"],"sourcesContent":["import {type PropsWithChildren} from 'react'\nimport {styled} from 'styled-components'\n\nimport type {Theme} from '@sanity/ui'\n\nimport {black} from '@sanity/color'\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: React.FC<\n PropsWithChildren<{className?: string; style?: React.CSSProperties}>\n> = 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","export 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 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 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 BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {useClient, useUserColorManager, type SanityClient, type SanityDocument} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\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 // oxlint-disable-next-line restrict-template-expressions,no-base-to-string\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 (!doc._id.startsWith('drafts.')) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (doc._id.startsWith('drafts.')) {\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: any, docNode: any) {\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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 useEffect(() => {\n void client.fetch(query).then((_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 }, [client, query])\n\n useEffect(() => {\n const subscription = client.listen(query, {}, {}).subscribe(async (update) => {\n if (update.type !== 'mutation') return\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: any) => 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: any) => 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: any) => l.source.id !== docId && l.target.id !== docId,\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n: any) => n.id !== docId)\n setGraph(newGraph)\n }\n })\n return () => {\n subscription.unsubscribe()\n }\n }, [client, query, documents, graph])\n\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 <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: any) => n.doc && n.doc._id === session?.doc?._id,\n )\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 -\n 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 try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur),\n )\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 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: Record<string, {fill: string; border: string}> = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType: any) {\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"],"names":["styled","black","BezierEasing","uuidv4","useUserColorManager","useState","router","useRouter","useClient","useEffect","deepEqual","useTheme","jsx","jsxs","ForceGraph2D","rgba","gray","white","COLOR_HUES","hues"],"mappings":"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"}
|
|
@@ -2,14 +2,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import BezierEasing from "bezier-easing";
|
|
3
3
|
import deepEqual from "deep-equal";
|
|
4
4
|
import { rgba } from "polished";
|
|
5
|
-
import {
|
|
5
|
+
import { useState, useEffect } from "react";
|
|
6
6
|
import { ForceGraph2D } from "react-force-graph";
|
|
7
7
|
import { useUserColorManager, useClient } from "sanity";
|
|
8
8
|
import { useRouter } from "sanity/router";
|
|
9
9
|
import { v4 } from "uuid";
|
|
10
10
|
import { black, gray, white, hues, COLOR_HUES } from "@sanity/color";
|
|
11
11
|
import { useTheme } from "@sanity/ui";
|
|
12
|
-
import styled from "styled-components";
|
|
12
|
+
import { styled } from "styled-components";
|
|
13
13
|
const GraphRoot = styled.div`
|
|
14
14
|
font-family: ${({ theme }) => theme.fonts.text.family};
|
|
15
15
|
position: absolute;
|
|
@@ -55,23 +55,6 @@ const GraphRoot = styled.div`
|
|
|
55
55
|
border-radius: 50%;
|
|
56
56
|
margin-right: ${({ theme }) => theme.space[2]}px;
|
|
57
57
|
`;
|
|
58
|
-
function useListen(query, params, options, onUpdate, dependencies, client) {
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
const subscription = client.listen(query, params, options).subscribe((update) => {
|
|
61
|
-
onUpdate(update);
|
|
62
|
-
});
|
|
63
|
-
return () => {
|
|
64
|
-
subscription.unsubscribe();
|
|
65
|
-
};
|
|
66
|
-
}, [...dependencies, client]);
|
|
67
|
-
}
|
|
68
|
-
function useFetchDocuments(query, onFetch, dependencies, client) {
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
client.fetch(query).then((result) => {
|
|
71
|
-
onFetch(result);
|
|
72
|
-
});
|
|
73
|
-
}, [...dependencies, client]);
|
|
74
|
-
}
|
|
75
58
|
function sizeOf(value) {
|
|
76
59
|
return value === null ? 0 : typeof value == "object" ? Object.entries(value).reduce((total, [k, v]) => total + sizeOf(k) + sizeOf(v), 0) : Array.isArray(value) ? Object.entries(value).reduce((total, v) => total + sizeOf(v), 0) : typeof value == "string" ? value.length : 1;
|
|
77
60
|
}
|
|
@@ -197,11 +180,15 @@ const users = new Users();
|
|
|
197
180
|
function GraphView({
|
|
198
181
|
tool: { options: props }
|
|
199
182
|
}) {
|
|
200
|
-
const query = props.query || DEFAULT_QUERY, apiVersion = props.apiVersion ?? "2022-09-01", userColorManager = useUserColorManager(), [maxSize, setMaxSize] = useState(0), [hoverNode, setHoverNode] = useState(null), [documents, setDocuments] = useState([]), [docTypes, setDocTypes] = useState({}), [graph, setGraph] = useState(() => new GraphData()), router = useRouter(), client = useClient({ apiVersion })
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
183
|
+
const query = props.query || DEFAULT_QUERY, apiVersion = props.apiVersion ?? "2022-09-01", userColorManager = useUserColorManager(), [maxSize, setMaxSize] = useState(0), [hoverNode, setHoverNode] = useState(null), [documents, setDocuments] = useState([]), [docTypes, setDocTypes] = useState({}), [graph, setGraph] = useState(() => new GraphData()), router = useRouter(), client = useClient({ apiVersion });
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
client.fetch(query).then((_docs) => {
|
|
186
|
+
const docs = deduplicateDrafts(_docs);
|
|
187
|
+
setMaxSize(Math.max(...docs.map(sizeOf))), setDocuments(docs), setDocTypes(getDocTypeCounts(docs)), setGraph(new GraphData(docs));
|
|
188
|
+
});
|
|
189
|
+
}, [client, query]), useEffect(() => {
|
|
190
|
+
const subscription = client.listen(query, {}, {}).subscribe(async (update) => {
|
|
191
|
+
if (update.type !== "mutation") return;
|
|
205
192
|
const doc = update.result;
|
|
206
193
|
if (doc) {
|
|
207
194
|
doc._id = stripDraftId(doc._id);
|
|
@@ -229,10 +216,11 @@ function GraphView({
|
|
|
229
216
|
(l) => l.source.id !== docId && l.target.id !== docId
|
|
230
217
|
), newGraph.data.nodes = newGraph.data.nodes.filter((n) => n.id !== docId), setGraph(newGraph);
|
|
231
218
|
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
219
|
+
});
|
|
220
|
+
return () => {
|
|
221
|
+
subscription.unsubscribe();
|
|
222
|
+
};
|
|
223
|
+
}, [client, query, documents, graph]), useEffect(() => {
|
|
236
224
|
const interval = setInterval(() => graph.reapSessions(), 1e3);
|
|
237
225
|
return () => clearInterval(interval);
|
|
238
226
|
}, [graph]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphView.js","sources":["../../src/tool/GraphViewStyle.tsx","../../src/tool/hooks.ts","../../src/tool/utils.ts","../../src/tool/GraphView.tsx"],"sourcesContent":["import {type PropsWithChildren} from 'react'\nimport styled from 'styled-components'\n\nimport type {Theme} from '@sanity/ui'\n\nimport {black} from '@sanity/color'\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 {useEffect} from 'react'\n\nimport type {ListenEvent, ListenOptions, SanityClient} from '@sanity/client'\n\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 }, [...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 void client.fetch(query).then((result) => {\n onFetch(result)\n })\n }, [...dependencies, client])\n}\n","export 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 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 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 BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useCallback, useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {useClient, useUserColorManager} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport type {SanityDocument, SanityClient} from '@sanity/client'\n\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {useFetchDocuments, useListen} from './hooks'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\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 (!doc._id.startsWith('drafts.')) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (doc._id.startsWith('drafts.')) {\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: any, docNode: any) {\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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: any) => {\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: any) => {\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: any) => 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: any) => 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 // @ts-expect-error - TODO: fix this\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: any) => l.source.id !== docId && l.target.id !== docId,\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n: any) => n.id !== docId)\n setGraph(newGraph)\n }\n },\n [documents, graph, client],\n )\n // @ts-expect-error - TODO: fix this\n useFetchDocuments(query, fetchCallback, [], client)\n // @ts-expect-error - TODO: fix this\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 <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: any) => n.doc && n.doc._id === session?.doc?._id,\n )\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 -\n 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 try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur),\n )\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 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: Record<string, {fill: string; border: string}> = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType: any) {\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"],"names":["uuidv4"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GraphView.js","sources":["../../src/tool/GraphViewStyle.tsx","../../src/tool/utils.ts","../../src/tool/GraphView.tsx"],"sourcesContent":["import {type PropsWithChildren} from 'react'\nimport {styled} from 'styled-components'\n\nimport type {Theme} from '@sanity/ui'\n\nimport {black} from '@sanity/color'\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: React.FC<\n PropsWithChildren<{className?: string; style?: React.CSSProperties}>\n> = 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","export 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 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 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 BezierEasing from 'bezier-easing'\nimport deepEqual from 'deep-equal'\nimport {rgba} from 'polished'\nimport {useEffect, useState} from 'react'\nimport {ForceGraph2D} from 'react-force-graph'\nimport {useClient, useUserColorManager, type SanityClient, type SanityDocument} from 'sanity'\nimport {useRouter} from 'sanity/router'\nimport {v4 as uuidv4} from 'uuid'\n\nimport {black, COLOR_HUES, gray, white, hues} from '@sanity/color'\nimport {useTheme} from '@sanity/ui'\n\nimport {GraphRoot, GraphWrapper, HoverNode, Legend, LegendBadge, LegendRow} from './GraphViewStyle'\nimport {sortBy, loadImage, sizeOf, truncate} from './utils'\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 // oxlint-disable-next-line restrict-template-expressions,no-base-to-string\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 (!doc._id.startsWith('drafts.')) {\n deduped[doc._id] = doc\n }\n }\n for (const doc of docs) {\n if (doc._id.startsWith('drafts.')) {\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: any, docNode: any) {\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 default function GraphView({\n tool: {options: props},\n}: {\n tool: {options: GraphViewConfig}\n}): React.JSX.Element {\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 useEffect(() => {\n void client.fetch(query).then((_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 }, [client, query])\n\n useEffect(() => {\n const subscription = client.listen(query, {}, {}).subscribe(async (update) => {\n if (update.type !== 'mutation') return\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: any) => 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: any) => 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: any) => l.source.id !== docId && l.target.id !== docId,\n )\n newGraph.data.nodes = newGraph.data.nodes.filter((n: any) => n.id !== docId)\n setGraph(newGraph)\n }\n })\n return () => {\n subscription.unsubscribe()\n }\n }, [client, query, documents, graph])\n\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 <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: any) => n.doc && n.doc._id === session?.doc?._id,\n )\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 -\n 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 try {\n const dur = 700\n const f = softEasing(\n Math.max(0, (dur - (Date.now() - session.startTime)) / dur),\n )\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 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: Record<string, {fill: string; border: string}> = {}\nlet typeColorNum = 0\n\nfunction getDocTypeColor(docType: any) {\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"],"names":["uuidv4"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-graph-view",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/plugins/tree/main/plugins/sanity-plugin-graph-view#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -28,33 +28,30 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@sanity/
|
|
32
|
-
"@sanity/
|
|
33
|
-
"@sanity/ui": "^1.0.0",
|
|
31
|
+
"@sanity/color": "^3.0.6",
|
|
32
|
+
"@sanity/ui": "^3.1.11",
|
|
34
33
|
"bezier-easing": "^2.1.0",
|
|
35
|
-
"deep-equal": "^2.
|
|
36
|
-
"polished": "^4.
|
|
34
|
+
"deep-equal": "^2.2.3",
|
|
35
|
+
"polished": "^4.3.1",
|
|
37
36
|
"react-force-graph": "1.41.14",
|
|
38
|
-
"
|
|
39
|
-
"uuid": "^8.3.2"
|
|
37
|
+
"uuid": "^13.0.0"
|
|
40
38
|
},
|
|
41
39
|
"devDependencies": {
|
|
42
40
|
"@sanity/pkg-utils": "^9.1.1",
|
|
43
|
-
"@types/deep-equal": "^1.0.
|
|
41
|
+
"@types/deep-equal": "^1.0.4",
|
|
44
42
|
"@types/react": "^19.2.6",
|
|
45
|
-
"@types/uuid": "^8",
|
|
46
|
-
"@types/styled-components": "^5.1.25",
|
|
47
43
|
"@typescript/native-preview": "7.0.0-dev.20251122.1",
|
|
48
44
|
"react": "^19.2.0",
|
|
49
|
-
"rimraf": "^3.0.2",
|
|
50
45
|
"sanity": "^4.17.0",
|
|
46
|
+
"styled-components": "^6.1.19",
|
|
51
47
|
"typescript": "5.9.3",
|
|
52
48
|
"@repo/package.config": "0.0.0",
|
|
53
49
|
"@repo/tsconfig": "0.0.0"
|
|
54
50
|
},
|
|
55
51
|
"peerDependencies": {
|
|
56
52
|
"react": "^18.3 || ^19",
|
|
57
|
-
"sanity": "^4"
|
|
53
|
+
"sanity": "^4",
|
|
54
|
+
"styled-components": "^6.1"
|
|
58
55
|
},
|
|
59
56
|
"engines": {
|
|
60
57
|
"node": ">=20.19 <22 || >=22.12"
|