@dxos/plugin-explorer 0.6.8-main.046e6cf

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.
Files changed (124) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/dist/lib/browser/ExplorerArticle-IPAJQMAX.mjs +27 -0
  4. package/dist/lib/browser/ExplorerArticle-IPAJQMAX.mjs.map +7 -0
  5. package/dist/lib/browser/ExplorerMain-3KFXOEYO.mjs +33 -0
  6. package/dist/lib/browser/ExplorerMain-3KFXOEYO.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-7YEM64IQ.mjs +473 -0
  8. package/dist/lib/browser/chunk-7YEM64IQ.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-JIDPF2GF.mjs +27 -0
  10. package/dist/lib/browser/chunk-JIDPF2GF.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-TL6ADY3P.mjs +21 -0
  12. package/dist/lib/browser/chunk-TL6ADY3P.mjs.map +7 -0
  13. package/dist/lib/browser/index.mjs +38151 -0
  14. package/dist/lib/browser/index.mjs.map +7 -0
  15. package/dist/lib/browser/meta.json +1 -0
  16. package/dist/lib/browser/meta.mjs +9 -0
  17. package/dist/lib/browser/meta.mjs.map +7 -0
  18. package/dist/lib/browser/types/index.mjs +10 -0
  19. package/dist/lib/browser/types/index.mjs.map +7 -0
  20. package/dist/lib/node/ExplorerArticle-PYOGBY3Z.cjs +53 -0
  21. package/dist/lib/node/ExplorerArticle-PYOGBY3Z.cjs.map +7 -0
  22. package/dist/lib/node/ExplorerMain-HGCLO5O4.cjs +59 -0
  23. package/dist/lib/node/ExplorerMain-HGCLO5O4.cjs.map +7 -0
  24. package/dist/lib/node/chunk-2GOPBQBC.cjs +494 -0
  25. package/dist/lib/node/chunk-2GOPBQBC.cjs.map +7 -0
  26. package/dist/lib/node/chunk-HYXFS3AG.cjs +45 -0
  27. package/dist/lib/node/chunk-HYXFS3AG.cjs.map +7 -0
  28. package/dist/lib/node/chunk-UJZYAOD2.cjs +54 -0
  29. package/dist/lib/node/chunk-UJZYAOD2.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +38158 -0
  31. package/dist/lib/node/index.cjs.map +7 -0
  32. package/dist/lib/node/meta.cjs +30 -0
  33. package/dist/lib/node/meta.cjs.map +7 -0
  34. package/dist/lib/node/meta.json +1 -0
  35. package/dist/lib/node/types/index.cjs +32 -0
  36. package/dist/lib/node/types/index.cjs.map +7 -0
  37. package/dist/types/src/ExplorerPlugin.d.ts +4 -0
  38. package/dist/types/src/ExplorerPlugin.d.ts.map +1 -0
  39. package/dist/types/src/components/Chart/Chart.d.ts +10 -0
  40. package/dist/types/src/components/Chart/Chart.d.ts.map +1 -0
  41. package/dist/types/src/components/Chart/Chart.stories.d.ts +11 -0
  42. package/dist/types/src/components/Chart/Chart.stories.d.ts.map +1 -0
  43. package/dist/types/src/components/Chart/index.d.ts +2 -0
  44. package/dist/types/src/components/Chart/index.d.ts.map +1 -0
  45. package/dist/types/src/components/ExplorerArticle.d.ts +7 -0
  46. package/dist/types/src/components/ExplorerArticle.d.ts.map +1 -0
  47. package/dist/types/src/components/ExplorerMain.d.ts +7 -0
  48. package/dist/types/src/components/ExplorerMain.d.ts.map +1 -0
  49. package/dist/types/src/components/Globe/Globe.d.ts +12 -0
  50. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -0
  51. package/dist/types/src/components/Globe/Globe.stories.d.ts +12 -0
  52. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -0
  53. package/dist/types/src/components/Globe/index.d.ts +2 -0
  54. package/dist/types/src/components/Globe/index.d.ts.map +1 -0
  55. package/dist/types/src/components/Graph/Graph.d.ts +8 -0
  56. package/dist/types/src/components/Graph/Graph.d.ts.map +1 -0
  57. package/dist/types/src/components/Graph/Graph.stories.d.ts +14 -0
  58. package/dist/types/src/components/Graph/Graph.stories.d.ts.map +1 -0
  59. package/dist/types/src/components/Graph/graph-model.d.ts +33 -0
  60. package/dist/types/src/components/Graph/graph-model.d.ts.map +1 -0
  61. package/dist/types/src/components/Graph/index.d.ts +3 -0
  62. package/dist/types/src/components/Graph/index.d.ts.map +1 -0
  63. package/dist/types/src/components/Tree/Tree.d.ts +27 -0
  64. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -0
  65. package/dist/types/src/components/Tree/Tree.stories.d.ts +29 -0
  66. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -0
  67. package/dist/types/src/components/Tree/index.d.ts +2 -0
  68. package/dist/types/src/components/Tree/index.d.ts.map +1 -0
  69. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts +5 -0
  70. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts.map +1 -0
  71. package/dist/types/src/components/Tree/layout/RadialTree.d.ts +4 -0
  72. package/dist/types/src/components/Tree/layout/RadialTree.d.ts.map +1 -0
  73. package/dist/types/src/components/Tree/layout/TidyTree.d.ts +4 -0
  74. package/dist/types/src/components/Tree/layout/TidyTree.d.ts.map +1 -0
  75. package/dist/types/src/components/Tree/layout/index.d.ts +5 -0
  76. package/dist/types/src/components/Tree/layout/index.d.ts.map +1 -0
  77. package/dist/types/src/components/Tree/types.d.ts +8 -0
  78. package/dist/types/src/components/Tree/types.d.ts.map +1 -0
  79. package/dist/types/src/components/index.d.ts +12 -0
  80. package/dist/types/src/components/index.d.ts.map +1 -0
  81. package/dist/types/src/components/plot.d.ts +12 -0
  82. package/dist/types/src/components/plot.d.ts.map +1 -0
  83. package/dist/types/src/index.d.ts +5 -0
  84. package/dist/types/src/index.d.ts.map +1 -0
  85. package/dist/types/src/meta.d.ts +15 -0
  86. package/dist/types/src/meta.d.ts.map +1 -0
  87. package/dist/types/src/translations.d.ts +12 -0
  88. package/dist/types/src/translations.d.ts.map +1 -0
  89. package/dist/types/src/types/index.d.ts +3 -0
  90. package/dist/types/src/types/index.d.ts.map +1 -0
  91. package/dist/types/src/types/types.d.ts +7 -0
  92. package/dist/types/src/types/types.d.ts.map +1 -0
  93. package/dist/types/src/types/view.d.ts +14 -0
  94. package/dist/types/src/types/view.d.ts.map +1 -0
  95. package/package.json +98 -0
  96. package/src/ExplorerPlugin.tsx +103 -0
  97. package/src/components/Chart/Chart.stories.tsx +46 -0
  98. package/src/components/Chart/Chart.tsx +54 -0
  99. package/src/components/Chart/index.ts +5 -0
  100. package/src/components/ExplorerArticle.tsx +28 -0
  101. package/src/components/ExplorerMain.tsx +34 -0
  102. package/src/components/Globe/Globe.stories.tsx +115 -0
  103. package/src/components/Globe/Globe.tsx +65 -0
  104. package/src/components/Globe/index.ts +5 -0
  105. package/src/components/Graph/Graph.stories.tsx +59 -0
  106. package/src/components/Graph/Graph.tsx +151 -0
  107. package/src/components/Graph/graph-model.ts +146 -0
  108. package/src/components/Graph/index.ts +7 -0
  109. package/src/components/Tree/Tree.stories.tsx +97 -0
  110. package/src/components/Tree/Tree.tsx +109 -0
  111. package/src/components/Tree/index.ts +5 -0
  112. package/src/components/Tree/layout/HierarchicalEdgeBundling.ts +164 -0
  113. package/src/components/Tree/layout/RadialTree.ts +96 -0
  114. package/src/components/Tree/layout/TidyTree.ts +102 -0
  115. package/src/components/Tree/layout/index.ts +9 -0
  116. package/src/components/Tree/types.ts +39 -0
  117. package/src/components/index.ts +14 -0
  118. package/src/components/plot.ts +15 -0
  119. package/src/index.ts +12 -0
  120. package/src/meta.tsx +19 -0
  121. package/src/translations.ts +18 -0
  122. package/src/types/index.ts +6 -0
  123. package/src/types/types.ts +27 -0
  124. package/src/types/view.ts +11 -0
@@ -0,0 +1,14 @@
1
+ import { S } from '@dxos/echo-schema';
2
+ declare const ViewType_base: import("@dxos/echo-schema").AbstractTypedObject<{
3
+ type: string;
4
+ name?: string | undefined;
5
+ } & {
6
+ id: string;
7
+ }, S.Struct.Encoded<{
8
+ name: S.optional<typeof S.String>;
9
+ type: typeof S.String;
10
+ }>>;
11
+ export declare class ViewType extends ViewType_base {
12
+ }
13
+ export {};
14
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../../src/types/view.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;;;;;;;;;;AAGnD,qBAAa,QAAS,SAAQ,aAG5B;CAAG"}
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@dxos/plugin-explorer",
3
+ "version": "0.6.8-main.046e6cf",
4
+ "description": "Braneframe data visualization plugin",
5
+ "homepage": "https://dxos.org",
6
+ "bugs": "https://github.com/dxos/dxos/issues",
7
+ "license": "MIT",
8
+ "author": "DXOS.org",
9
+ "exports": {
10
+ ".": {
11
+ "browser": "./dist/lib/browser/index.mjs",
12
+ "node": {
13
+ "default": "./dist/lib/node/index.cjs"
14
+ },
15
+ "types": "./dist/types/src/index.d.ts"
16
+ },
17
+ "./meta": {
18
+ "browser": "./dist/lib/browser/meta.mjs",
19
+ "node": {
20
+ "default": "./dist/lib/node/meta.cjs"
21
+ },
22
+ "types": "./dist/types/src/meta.d.ts"
23
+ },
24
+ "./types": {
25
+ "browser": "./dist/lib/browser/types/index.mjs",
26
+ "node": {
27
+ "default": "./dist/lib/node/types/index.cjs"
28
+ },
29
+ "types": "./dist/types/src/types/index.d.ts"
30
+ }
31
+ },
32
+ "types": "dist/types/src/index.d.ts",
33
+ "typesVersions": {
34
+ "*": {
35
+ "meta": [
36
+ "dist/types/src/meta.d.ts"
37
+ ],
38
+ "types": [
39
+ "dist/types/src/types/index.d.ts"
40
+ ]
41
+ }
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "src"
46
+ ],
47
+ "dependencies": {
48
+ "@observablehq/plot": "^0.6.11",
49
+ "@preact/signals-core": "^1.6.0",
50
+ "d3": "^7.9.0",
51
+ "lodash.defaultsdeep": "^4.6.1",
52
+ "lodash.get": "^4.4.2",
53
+ "react-resize-detector": "^11.0.1",
54
+ "topojson-client": "^3.1.0",
55
+ "@dxos/async": "0.6.8-main.046e6cf",
56
+ "@dxos/app-framework": "0.6.8-main.046e6cf",
57
+ "@dxos/client": "0.6.8-main.046e6cf",
58
+ "@dxos/gem-core": "0.6.8-main.046e6cf",
59
+ "@dxos/gem-spore": "0.6.8-main.046e6cf",
60
+ "@dxos/echo-schema": "0.6.8-main.046e6cf",
61
+ "@dxos/log": "0.6.8-main.046e6cf",
62
+ "@dxos/plugin-client": "0.6.8-main.046e6cf",
63
+ "@dxos/plugin-graph": "0.6.8-main.046e6cf",
64
+ "@dxos/plugin-space": "0.6.8-main.046e6cf",
65
+ "@dxos/plugin-search": "0.6.8-main.046e6cf",
66
+ "@dxos/react-ui": "0.6.8-main.046e6cf",
67
+ "@dxos/react-client": "0.6.8-main.046e6cf"
68
+ },
69
+ "devDependencies": {
70
+ "@phosphor-icons/react": "^2.1.5",
71
+ "@types/d3": "^7.4.3",
72
+ "@types/d3-hierarchy": "^3.1.6",
73
+ "@types/lodash.defaultsdeep": "^4.6.6",
74
+ "@types/lodash.get": "^4.4.7",
75
+ "@types/react": "~18.2.0",
76
+ "@types/react-dom": "~18.2.0",
77
+ "@types/topojson-client": "^3.1.3",
78
+ "react": "~18.2.0",
79
+ "react-dom": "~18.2.0",
80
+ "vite": "^5.3.4",
81
+ "@dxos/echo-generator": "0.6.8-main.046e6cf",
82
+ "@dxos/plugin-outliner": "0.6.8-main.046e6cf",
83
+ "@dxos/random": "0.6.8-main.046e6cf",
84
+ "@dxos/react-ui": "0.6.8-main.046e6cf",
85
+ "@dxos/react-ui-theme": "0.6.8-main.046e6cf",
86
+ "@dxos/storybook-utils": "0.6.8-main.046e6cf"
87
+ },
88
+ "optionalDependencies": {
89
+ "@phosphor-icons/react": "^2.1.5",
90
+ "react": "^18.0.0",
91
+ "react-dom": "^18.0.0",
92
+ "@dxos/react-ui": "0.6.8-main.046e6cf",
93
+ "@dxos/react-ui-theme": "0.6.8-main.046e6cf"
94
+ },
95
+ "publishConfig": {
96
+ "access": "public"
97
+ }
98
+ }
@@ -0,0 +1,103 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Graph, type IconProps } from '@phosphor-icons/react';
6
+ import React from 'react';
7
+
8
+ import { NavigationAction, parseIntentPlugin, resolvePlugin, type PluginDefinition } from '@dxos/app-framework';
9
+ import { parseClientPlugin } from '@dxos/plugin-client';
10
+ import { type ActionGroup, createExtension, isActionGroup } from '@dxos/plugin-graph';
11
+ import { SpaceAction } from '@dxos/plugin-space';
12
+ import { create } from '@dxos/react-client/echo';
13
+
14
+ import { ExplorerArticle, ExplorerMain } from './components';
15
+ import meta, { EXPLORER_PLUGIN } from './meta';
16
+ import translations from './translations';
17
+ import { ViewType } from './types';
18
+ import { ExplorerAction, type ExplorerPluginProvides } from './types';
19
+
20
+ export const ExplorerPlugin = (): PluginDefinition<ExplorerPluginProvides> => {
21
+ return {
22
+ meta,
23
+ provides: {
24
+ metadata: {
25
+ records: {
26
+ [ViewType.typename]: {
27
+ placeholder: ['object title placeholder', { ns: EXPLORER_PLUGIN }],
28
+ icon: (props: IconProps) => <Graph {...props} />,
29
+ iconSymbol: 'ph--graph--regular',
30
+ },
31
+ },
32
+ },
33
+ translations,
34
+ echo: {
35
+ schema: [ViewType],
36
+ },
37
+ graph: {
38
+ builder: (plugins) => {
39
+ const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
40
+ const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
41
+ if (!client || !dispatch) {
42
+ return [];
43
+ }
44
+
45
+ return createExtension({
46
+ id: ExplorerAction.CREATE,
47
+ filter: (node): node is ActionGroup => isActionGroup(node) && node.id.startsWith(SpaceAction.ADD_OBJECT),
48
+ actions: ({ node }) => {
49
+ const id = node.id.split('/').at(-1);
50
+ const [spaceId, objectId] = id?.split(':') ?? [];
51
+ const space = client.spaces.get().find((space) => space.id === spaceId);
52
+ const object = objectId && space?.db.getObjectById(objectId);
53
+ const target = objectId ? object : space;
54
+ if (!target) {
55
+ return;
56
+ }
57
+
58
+ return [
59
+ {
60
+ id: `${EXPLORER_PLUGIN}/create/${node.id}`,
61
+ data: async () => {
62
+ await dispatch([
63
+ { plugin: EXPLORER_PLUGIN, action: ExplorerAction.CREATE },
64
+ { action: SpaceAction.ADD_OBJECT, data: { target } },
65
+ { action: NavigationAction.OPEN },
66
+ ]);
67
+ },
68
+ properties: {
69
+ label: ['create object label', { ns: EXPLORER_PLUGIN }],
70
+ icon: (props: IconProps) => <Graph {...props} />,
71
+ iconSymbol: 'ph--graph--regular',
72
+ testId: 'explorerPlugin.createObject',
73
+ },
74
+ },
75
+ ];
76
+ },
77
+ });
78
+ },
79
+ },
80
+ surface: {
81
+ component: ({ data, role }) => {
82
+ switch (role) {
83
+ case 'main':
84
+ return data.active instanceof ViewType ? <ExplorerMain view={data.active} /> : null;
85
+ case 'article':
86
+ return data.object instanceof ViewType ? <ExplorerArticle view={data.object} /> : null;
87
+ default:
88
+ return null;
89
+ }
90
+ },
91
+ },
92
+ intent: {
93
+ resolver: (intent) => {
94
+ switch (intent.action) {
95
+ case ExplorerAction.CREATE: {
96
+ return { data: create(ViewType, { name: '', type: '' }) };
97
+ }
98
+ }
99
+ },
100
+ },
101
+ },
102
+ };
103
+ };
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+
7
+ import * as Plot from '@observablehq/plot';
8
+ import React, { useEffect, useState } from 'react';
9
+
10
+ import { ClientRepeater } from '@dxos/react-client/testing';
11
+ import { withFullscreen, withTheme } from '@dxos/storybook-utils';
12
+
13
+ import { Chart } from './Chart';
14
+
15
+ // TODO(burdon): Generate data with geo lat/lng.
16
+ // TODO(burdon): How to provide geo service via agent?
17
+
18
+ export default {
19
+ title: 'plugin-explorer/Chart',
20
+ component: Plot,
21
+ decorators: [withTheme, withFullscreen()],
22
+ };
23
+
24
+ export const Default = () => <ClientRepeater component={DefaultStory} />;
25
+ const DefaultStory = () => {
26
+ const [data, setData] = useState<{ cities: any }>();
27
+ useEffect(() => {
28
+ setTimeout(async () => {
29
+ const cities = await (await fetch('/cities.json')).json();
30
+ setData({
31
+ cities,
32
+ });
33
+ });
34
+ }, []);
35
+
36
+ if (!data) {
37
+ return null;
38
+ }
39
+
40
+ const cities = data.cities.features.map((feature: any) => ({
41
+ x: feature.geometry.coordinates[0],
42
+ y: feature.geometry.coordinates[1],
43
+ }));
44
+
45
+ return <Chart items={cities} accessor={(obj) => ({ x: obj.x, y: obj.y })} options={{ stroke: 'blue' }} />;
46
+ };
@@ -0,0 +1,54 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import * as Plot from '@observablehq/plot';
6
+ import { type DotOptions } from '@observablehq/plot';
7
+ import React, { useEffect } from 'react';
8
+ import { useResizeDetector } from 'react-resize-detector';
9
+
10
+ import { type Accessor, createAdapter, type Point } from '../plot';
11
+
12
+ const defaultOptions: DotOptions = {
13
+ r: 4,
14
+ stroke: 'gray',
15
+ fill: 'gray',
16
+ fillOpacity: 0.2,
17
+ };
18
+
19
+ export type CharProps = {
20
+ items?: any[];
21
+ accessor?: Accessor<Point>;
22
+ options?: DotOptions;
23
+ };
24
+
25
+ export const Chart = ({ items = [], accessor, options = defaultOptions }: CharProps) => {
26
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({ refreshRate: 200 });
27
+ useEffect(() => {
28
+ if (!width || !height) {
29
+ return;
30
+ }
31
+
32
+ const plot = Plot.plot({
33
+ grid: true,
34
+ width,
35
+ height,
36
+ style: {
37
+ background: 'transparent',
38
+ },
39
+ marks: [
40
+ Plot.frame(),
41
+ Plot.dot(items, {
42
+ x: createAdapter('x', accessor),
43
+ y: createAdapter('y', accessor),
44
+ ...options,
45
+ }),
46
+ ],
47
+ });
48
+
49
+ containerRef.current!.append(plot);
50
+ return () => plot?.remove();
51
+ }, [items, width, height]);
52
+
53
+ return <div ref={containerRef} className='grow' />;
54
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './Chart';
@@ -0,0 +1,28 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { useGlobalSearch } from '@dxos/plugin-search';
8
+ import { getSpace } from '@dxos/react-client/echo';
9
+
10
+ import { Graph } from './Graph';
11
+ import { type ViewType } from '../types';
12
+
13
+ const ExplorerArticle = ({ view }: { view: ViewType }) => {
14
+ const space = getSpace(view);
15
+ const { match } = useGlobalSearch();
16
+
17
+ if (!space) {
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <div role='none' className='row-span-2 overflow-auto'>
23
+ <Graph space={space} match={match} />
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export default ExplorerArticle;
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { useGlobalSearch } from '@dxos/plugin-search';
8
+ import { getSpace } from '@dxos/react-client/echo';
9
+ import { Main } from '@dxos/react-ui';
10
+ import {
11
+ baseSurface,
12
+ topbarBlockPaddingStart,
13
+ fixedInsetFlexLayout,
14
+ bottombarBlockPaddingEnd,
15
+ } from '@dxos/react-ui-theme';
16
+
17
+ import { Graph } from './Graph';
18
+ import { type ViewType } from '../types';
19
+
20
+ const ExplorerMain = ({ view }: { view: ViewType }) => {
21
+ const space = getSpace(view);
22
+ const { match } = useGlobalSearch();
23
+ if (!space) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <Main.Content classNames={[baseSurface, fixedInsetFlexLayout, topbarBlockPaddingStart, bottombarBlockPaddingEnd]}>
29
+ <Graph space={space} match={match} />
30
+ </Main.Content>
31
+ );
32
+ };
33
+
34
+ export default ExplorerMain;
@@ -0,0 +1,115 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+
7
+ import * as Plot from '@observablehq/plot';
8
+ import * as d3 from 'd3';
9
+ import React, { useEffect, useState } from 'react';
10
+ import { useResizeDetector } from 'react-resize-detector';
11
+ import * as topojson from 'topojson-client';
12
+
13
+ import { ClientRepeater } from '@dxos/react-client/testing';
14
+ import { withFullscreen, withTheme } from '@dxos/storybook-utils';
15
+
16
+ import { Globe } from './Globe';
17
+
18
+ // TODO(burdon): Generate data with geo lat/lng.
19
+ // TODO(burdon): How to provide geo service via agent?
20
+
21
+ export default {
22
+ title: 'plugin-explorer/Globe',
23
+ component: Plot,
24
+ decorators: [withTheme, withFullscreen()],
25
+ };
26
+
27
+ export const Default = () => <ClientRepeater component={DefaultStory} createSpace />;
28
+
29
+ const DefaultStory = () => {
30
+ const [data, setData] = useState<{ world: any; cities: any }>();
31
+ useEffect(() => {
32
+ setTimeout(async () => {
33
+ const world = await (await fetch('/countries-110m.json')).json();
34
+ const cities = await (await fetch('/cities.json')).json();
35
+ setData({
36
+ world,
37
+ cities,
38
+ });
39
+ });
40
+ }, []);
41
+
42
+ if (!data) {
43
+ return null;
44
+ }
45
+
46
+ const cities = data.cities.features.map((feature: any) => ({
47
+ lat: feature.geometry.coordinates[0],
48
+ lng: feature.geometry.coordinates[1],
49
+ }));
50
+
51
+ return <Globe items={cities} />;
52
+ };
53
+
54
+ export const Extended = () => <ClientRepeater component={ExtendedStory} createSpace />;
55
+ const ExtendedStory = () => {
56
+ const [data, setData] = useState<{ world: any; cities: any }>();
57
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({ refreshRate: 200 });
58
+ useEffect(() => {
59
+ setTimeout(async () => {
60
+ const world = await (await fetch('/countries-110m.json')).json();
61
+ const cities = await (await fetch('/cities.json')).json();
62
+ setData({
63
+ world,
64
+ cities,
65
+ });
66
+ });
67
+ }, []);
68
+
69
+ useEffect(() => {
70
+ if (!data || !width || !height) {
71
+ return;
72
+ }
73
+
74
+ const land = topojson.feature(data.world, data.world.objects.land);
75
+ const cities = data.cities.features.map((feature: any) => ({
76
+ lat: feature.geometry.coordinates[0],
77
+ lng: feature.geometry.coordinates[1],
78
+ }));
79
+
80
+ const city = cities[0];
81
+ const circle = d3.geoCircle().center([city.lat, city.lng]).radius(16)();
82
+
83
+ // https://observablehq.com/plot/marks/geo
84
+ // https://observablehq.com/@observablehq/plot-earthquake-globe?intent=fork
85
+ const plot = Plot.plot({
86
+ // https://observablehq.com/plot/features/projections
87
+ projection: { type: 'orthographic', rotate: [-city.lat + 30, -30] },
88
+ // projection: { type: 'equirectangular', rotate: [-140, -30] },
89
+ width,
90
+ height,
91
+ style: {
92
+ background: 'transparent',
93
+ },
94
+ marks: [
95
+ Plot.sphere({ fill: 'lightblue', fillOpacity: 0.5 }),
96
+ Plot.geo(land, { fill: 'green', fillOpacity: 0.3 }),
97
+ Plot.graticule(),
98
+ Plot.geo(circle, { stroke: 'black', fill: 'darkblue', fillOpacity: 0.1, strokeWidth: 2 }),
99
+ Plot.dot(cities, {
100
+ x: 'lat',
101
+ y: 'lng',
102
+ r: 6,
103
+ stroke: 'red',
104
+ fill: 'red',
105
+ fillOpacity: 0.2,
106
+ }),
107
+ ],
108
+ });
109
+
110
+ containerRef.current!.append(plot);
111
+ return () => plot?.remove();
112
+ }, [data, width, height]);
113
+
114
+ return <div ref={containerRef} className='grow p-8' />;
115
+ };
@@ -0,0 +1,65 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import type { DotOptions } from '@observablehq/plot';
6
+ import * as Plot from '@observablehq/plot';
7
+ import React, { useEffect } from 'react';
8
+ import { useResizeDetector } from 'react-resize-detector';
9
+ import * as topojson from 'topojson-client';
10
+
11
+ // @ts-ignore
12
+ import world from '../../../public/countries-110m.json?json';
13
+ import { type Accessor, createAdapter, type GeoLocation } from '../plot';
14
+
15
+ const defaultOptions: DotOptions = {
16
+ r: 4,
17
+ fill: '#003300',
18
+ };
19
+
20
+ export type GlobeProps = {
21
+ items?: any[];
22
+ accessor?: Accessor<GeoLocation>;
23
+ projection?: Plot.ProjectionName;
24
+ options?: DotOptions;
25
+ };
26
+
27
+ export const Globe = ({ items = [], accessor, projection = 'orthographic', options = defaultOptions }: GlobeProps) => {
28
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({ refreshRate: 200 });
29
+ const land = topojson.feature(world, world.objects.land);
30
+
31
+ useEffect(() => {
32
+ if (!width || !height) {
33
+ return;
34
+ }
35
+
36
+ // https://observablehq.com/plot/marks/geo
37
+ // https://observablehq.com/@observablehq/plot-earthquake-globe?intent=fork
38
+ const plot = Plot.plot({
39
+ // https://observablehq.com/plot/features/projections
40
+ projection: { type: projection, rotate: [-100, -20] },
41
+ // projection: { type: 'equirectangular', rotate: [-140, -30] },
42
+ width,
43
+ height,
44
+ style: {
45
+ background: 'transparent',
46
+ },
47
+ // TODO(burdon): Create simple wrapper for Plot with good defaults.
48
+ marks: [
49
+ Plot.sphere({ fill: 'lightblue', fillOpacity: 0.5 }),
50
+ Plot.geo(land, { fill: 'darkgreen', fillOpacity: 0.5 }),
51
+ Plot.graticule(),
52
+ Plot.dot(items, {
53
+ x: createAdapter('lat', accessor),
54
+ y: createAdapter('lng', accessor),
55
+ ...options,
56
+ }),
57
+ ],
58
+ });
59
+
60
+ containerRef.current!.append(plot);
61
+ return () => plot?.remove();
62
+ }, [items, width, height]);
63
+
64
+ return <div ref={containerRef} className='grow p-4' />;
65
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './Globe';
@@ -0,0 +1,59 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+
7
+ import React, { useEffect, useState } from 'react';
8
+
9
+ import { createSpaceObjectGenerator, TestSchemaType } from '@dxos/echo-generator';
10
+ import { create } from '@dxos/echo-schema';
11
+ import { faker } from '@dxos/random';
12
+ import { useClient } from '@dxos/react-client';
13
+ import { type Space } from '@dxos/react-client/echo';
14
+ import { ClientRepeater } from '@dxos/react-client/testing';
15
+ import { withFullscreen, withTheme } from '@dxos/storybook-utils';
16
+
17
+ import { Graph } from './Graph';
18
+ import { ViewType } from '../../types';
19
+
20
+ faker.seed(1);
21
+
22
+ const Story = () => {
23
+ const client = useClient();
24
+ const [space, setSpace] = useState<Space>();
25
+ const [view, setView] = useState<ViewType>();
26
+ useEffect(() => {
27
+ const space = client.spaces.default;
28
+ const generator = createSpaceObjectGenerator(space);
29
+ generator.addSchemas();
30
+ void generator
31
+ .createObjects({
32
+ [TestSchemaType.organization]: 20,
33
+ [TestSchemaType.contact]: 50,
34
+ })
35
+ .catch();
36
+
37
+ const view = space.db.add(create(ViewType, { name: '', type: '' }));
38
+ setSpace(space);
39
+ setView(view);
40
+ }, []);
41
+
42
+ if (!space || !view) {
43
+ return null;
44
+ }
45
+
46
+ return <Graph space={space} />;
47
+ };
48
+
49
+ export default {
50
+ title: 'plugin-explorer/Graph',
51
+ component: Graph,
52
+ render: () => <ClientRepeater component={Story} createSpace types={[ViewType]} />,
53
+ decorators: [withTheme, withFullscreen()],
54
+ parameters: {
55
+ layout: 'fullscreen',
56
+ },
57
+ };
58
+
59
+ export const Default = {};