@dxos/app-graph 0.8.3 → 0.8.4-main.1da679c

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.
@@ -5,38 +5,38 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Registry, RegistryContext, Rx, useRxValue } from '@effect-rx/rx-react';
8
- import { Pause, Play, Plus, Timer } from '@phosphor-icons/react';
8
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
9
9
  import { Option, pipe } from 'effect';
10
10
  import React, { type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';
11
11
 
12
12
  import {
13
- live,
14
- isSpace,
13
+ Expando,
14
+ Filter,
15
+ type Live,
15
16
  Query,
16
17
  type QueryResult,
17
18
  type Space,
18
19
  SpaceState,
19
- Expando,
20
- type Live,
21
- Filter,
20
+ isSpace,
21
+ live,
22
22
  } from '@dxos/client/echo';
23
23
  import { Obj, Type } from '@dxos/echo';
24
24
  import { faker } from '@dxos/random';
25
25
  import { type Client, useClient } from '@dxos/react-client';
26
26
  import { withClientProvider } from '@dxos/react-client/testing';
27
- import { Button, Input, Select } from '@dxos/react-ui';
27
+ import { Icon, IconButton, Input, Select } from '@dxos/react-ui';
28
28
  import { Path, Tree } from '@dxos/react-ui-list';
29
- import { Tabs } from '@dxos/react-ui-tabs';
30
29
  import { getSize, mx } from '@dxos/react-ui-theme';
31
30
  import { withTheme } from '@dxos/storybook-utils';
32
31
  import { byPosition, isNonNullable, safeParseInt } from '@dxos/util';
33
32
 
34
- import { JsonTree } from './Tree';
35
33
  import { type ExpandableGraph, ROOT_ID } from '../graph';
36
34
  import { GraphBuilder, createExtension, rxFromObservable, rxFromSignal } from '../graph-builder';
37
35
  import { type Node } from '../node';
38
36
  import { rxFromQuery } from '../testing';
39
37
 
38
+ import { JsonTree } from './Tree';
39
+
40
40
  const DEFAULT_PERIOD = 500;
41
41
 
42
42
  enum Action {
@@ -205,7 +205,11 @@ const Controls = ({ children }: PropsWithChildren) => {
205
205
  return (
206
206
  <>
207
207
  <div className='flex shrink-0 p-2 space-x-2'>
208
- <Button onClick={() => setGenerating((generating) => !generating)}>{generating ? <Pause /> : <Play />}</Button>
208
+ <IconButton
209
+ icon={generating ? 'ph--pause--regular' : 'ph--play--regular'}
210
+ label={generating ? 'Pause' : 'Play'}
211
+ onClick={() => setGenerating((generating) => !generating)}
212
+ />
209
213
  <div className='relative' title='mutation period'>
210
214
  <Input.Root>
211
215
  <Input.TextInput
@@ -217,11 +221,9 @@ const Controls = ({ children }: PropsWithChildren) => {
217
221
  onChange={({ target: { value } }) => setActionInterval(value)}
218
222
  />
219
223
  </Input.Root>
220
- <Timer className={mx('absolute inline-end-1 block-start-1 mt-[6px]', getSize(3))} />
224
+ <Icon icon='ph--timer--regular' classNames={mx('absolute inline-end-1 block-start-1 mt-[6px]', getSize(3))} />
221
225
  </div>
222
- <Button onClick={() => action && runAction(client, action)}>
223
- <Plus />
224
- </Button>
226
+ <IconButton icon='ph--plus--regular' label='Add' onClick={() => action && runAction(client, action)} />
225
227
  <Select.Root value={action?.toString()} onValueChange={(action) => setAction(action as unknown as Action)}>
226
228
  <Select.TriggerButton placeholder='Select value' />
227
229
  <Select.Portal>
@@ -245,10 +247,9 @@ const Controls = ({ children }: PropsWithChildren) => {
245
247
  );
246
248
  };
247
249
 
248
- export default {
250
+ const meta = {
249
251
  title: 'sdk/app-graph/EchoGraph',
250
252
  decorators: [
251
- withTheme,
252
253
  withClientProvider({
253
254
  createIdentity: true,
254
255
  onIdentityCreated: async ({ client }) => {
@@ -256,10 +257,15 @@ export default {
256
257
  await client.spaces.create();
257
258
  },
258
259
  }),
260
+ withTheme,
259
261
  ],
260
- };
262
+ } satisfies Meta<typeof Registry>;
263
+
264
+ export default meta;
261
265
 
262
- export const JsonView = {
266
+ type Story = StoryObj<typeof meta>;
267
+
268
+ export const JsonView: Story = {
263
269
  render: () => {
264
270
  const client = useClient();
265
271
  const registry = useContext(RegistryContext);
@@ -275,7 +281,7 @@ export const JsonView = {
275
281
  },
276
282
  };
277
283
 
278
- export const TreeView = {
284
+ export const TreeView: Story = {
279
285
  render: () => {
280
286
  const client = useClient();
281
287
  const registry = useContext(RegistryContext);
@@ -372,122 +378,3 @@ export const TreeView = {
372
378
  );
373
379
  },
374
380
  };
375
-
376
- // TODO(wittjosiah): Remove.
377
- export const TabTreeView = {
378
- render: () => {
379
- const client = useClient();
380
- const registry = useContext(RegistryContext);
381
- const graph = useMemo(() => createGraph(client, registry), [client, registry]);
382
- const state = useMemo(() => new Map<string, Live<{ open: boolean; current: boolean }>>(), []);
383
-
384
- const useItems = useCallback(
385
- (node?: Node, options?: { disposition?: string; sort?: boolean }) => {
386
- const connections = useRxValue(graph.connections(node?.id ?? ROOT_ID));
387
- return options?.sort ? connections.toSorted((a, b) => byPosition(a.properties, b.properties)) : connections;
388
- },
389
- [graph],
390
- );
391
-
392
- const getProps = useCallback(
393
- (node: Node, path: string[]) => {
394
- const children = graph
395
- .getConnections(node.id, 'outbound')
396
- .map((n) => {
397
- // Break cycles.
398
- const nextPath = [...path, node.id];
399
- return nextPath.includes(n.id) ? undefined : (n as Node);
400
- })
401
- .filter(isNonNullable) as Node[];
402
- const parentOf =
403
- children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
404
- return {
405
- id: node.id,
406
- label: node.id,
407
- icon: node.type === 'dxos.org/type/Space' ? 'ph--planet--regular' : 'ph--placeholder--regular',
408
- parentOf,
409
- };
410
- },
411
- [graph],
412
- );
413
-
414
- const isOpen = useCallback(
415
- (_path: string[]) => {
416
- const path = Path.create(..._path);
417
- const object = state.get(path) ?? live({ open: true, current: false });
418
- if (!state.has(path)) {
419
- state.set(path, object);
420
- }
421
-
422
- return object.open;
423
- },
424
- [state],
425
- );
426
-
427
- const isCurrent = useCallback(
428
- (_path: string[]) => {
429
- const path = Path.create(..._path);
430
- const object = state.get(path) ?? live({ open: false, current: false });
431
- if (!state.has(path)) {
432
- state.set(path, object);
433
- }
434
-
435
- return object.current;
436
- },
437
- [state],
438
- );
439
-
440
- const onOpenChange = useCallback(
441
- ({ path: _path, open }: { path: string[]; open: boolean }) => {
442
- const path = Path.create(..._path);
443
- const object = state.get(path);
444
- object!.open = open;
445
- },
446
- [state],
447
- );
448
-
449
- const onSelect = useCallback(
450
- ({ path: _path, current }: { path: string[]; current: boolean }) => {
451
- const path = Path.create(..._path);
452
- const object = state.get(path);
453
- object!.current = current;
454
- },
455
- [state],
456
- );
457
-
458
- const spaces = useItems(graph.root);
459
-
460
- return (
461
- <>
462
- <Controls />
463
- <Tabs.Root defaultValue={spaces[0].id}>
464
- <Tabs.Tablist>
465
- {spaces.map((space) => {
466
- return (
467
- <Tabs.Tab key={space.id} value={space.id}>
468
- {space.id}
469
- </Tabs.Tab>
470
- );
471
- })}
472
- </Tabs.Tablist>
473
- {spaces.map((space) => {
474
- return (
475
- <Tabs.Tabpanel key={space.id} value={space.id}>
476
- <Tree
477
- id={space.id}
478
- root={space}
479
- useItems={useItems}
480
- getProps={getProps}
481
- isOpen={isOpen}
482
- isCurrent={isCurrent}
483
- onOpenChange={onOpenChange}
484
- onSelect={onSelect}
485
- />
486
- </Tabs.Tabpanel>
487
- );
488
- })}
489
- </Tabs.Root>
490
- </>
491
- );
492
- },
493
- };