@dxos/react-ui-menu 0.8.2-staging.7ac8446 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/lib/browser/index.mjs +298 -262
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +299 -264
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +298 -262
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/ActionLabel.d.ts +8 -4
  11. package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
  12. package/dist/types/src/components/DropdownMenu.d.ts +4 -4
  13. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
  14. package/dist/types/src/components/MenuContext.d.ts +4 -13
  15. package/dist/types/src/components/MenuContext.d.ts.map +1 -1
  16. package/dist/types/src/{testing → components}/Menus.stories.d.ts +1 -1
  17. package/dist/types/src/components/Menus.stories.d.ts.map +1 -0
  18. package/dist/types/src/components/ToolbarMenu.d.ts +5 -1
  19. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  20. package/dist/types/src/hooks/index.d.ts +2 -0
  21. package/dist/types/src/hooks/index.d.ts.map +1 -0
  22. package/dist/types/src/hooks/useMenuActions.d.ts +13 -0
  23. package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -0
  24. package/dist/types/src/index.d.ts +2 -1
  25. package/dist/types/src/index.d.ts.map +1 -1
  26. package/dist/types/src/testing/generator.d.ts +27 -0
  27. package/dist/types/src/testing/generator.d.ts.map +1 -0
  28. package/dist/types/src/testing/index.d.ts +1 -25
  29. package/dist/types/src/testing/index.d.ts.map +1 -1
  30. package/dist/types/src/{defs.d.ts → types.d.ts} +3 -5
  31. package/dist/types/src/types.d.ts.map +1 -0
  32. package/dist/types/src/util.d.ts +2 -2
  33. package/dist/types/src/util.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +20 -13
  36. package/src/components/ActionLabel.tsx +11 -8
  37. package/src/components/DropdownMenu.tsx +7 -23
  38. package/src/components/MenuContext.tsx +11 -26
  39. package/src/{testing → components}/Menus.stories.tsx +13 -13
  40. package/src/components/ToolbarMenu.tsx +29 -32
  41. package/src/hooks/index.ts +5 -0
  42. package/src/hooks/useMenuActions.ts +69 -0
  43. package/src/index.ts +3 -1
  44. package/src/testing/generator.ts +116 -0
  45. package/src/testing/index.ts +1 -107
  46. package/src/{defs.ts → types.ts} +2 -5
  47. package/src/util.ts +7 -7
  48. package/dist/types/src/defs.d.ts.map +0 -1
  49. package/dist/types/src/hooks.d.ts +0 -17
  50. package/dist/types/src/hooks.d.ts.map +0 -1
  51. package/dist/types/src/testing/Menus.stories.d.ts.map +0 -1
  52. package/src/hooks.ts +0 -72
@@ -2,110 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { useEffect } from 'react';
6
-
7
- import { type Action, Graph, type NodeArg, ACTION_TYPE, ACTION_GROUP_TYPE, actionGroupSymbol } from '@dxos/app-graph';
8
- import { create } from '@dxos/live-object';
9
- import { faker } from '@dxos/random';
10
- import { type DeepWriteable } from '@dxos/util';
11
-
12
- import { type MenuAction, type MenuItemGroup, type MenuItem } from '../defs';
13
- import { type ActionGraphProps } from '../hooks';
14
-
15
- export type CreateActionsParams = Partial<{
16
- type?: typeof ACTION_TYPE | typeof ACTION_GROUP_TYPE;
17
- callback: () => void;
18
- count: number;
19
- }>;
20
-
21
- const icons = {
22
- regular: [
23
- 'ph--text-b--regular',
24
- 'ph--text-italic--regular',
25
- 'ph--text-h-five--regular',
26
- 'ph--chat-text--regular',
27
- 'ph--clipboard-text--regular',
28
- 'ph--link-simple--regular',
29
- ],
30
- fill: [
31
- 'ph--text-b--fill',
32
- 'ph--text-italic--fill',
33
- 'ph--text-h-five--fill',
34
- 'ph--chat-text--fill',
35
- 'ph--clipboard-text--fill',
36
- 'ph--link-simple--fill',
37
- ],
38
- };
39
-
40
- export const createActions = (params?: CreateActionsParams) => {
41
- // eslint-disable-next-line no-console
42
- const { callback = () => console.log('invoke'), count = 12, type = ACTION_TYPE } = params ?? {};
43
- return faker.helpers.multiple(
44
- () =>
45
- create({
46
- id: faker.string.uuid(),
47
- type,
48
- data: type === ACTION_GROUP_TYPE ? actionGroupSymbol : callback,
49
- properties: {
50
- label: faker.lorem.words(2),
51
- icon: faker.helpers.arrayElement(icons[faker.helpers.arrayElement(Object.keys(icons)) as keyof typeof icons]),
52
- disabled: faker.helpers.arrayElement([true, false]),
53
- ...(type === ACTION_GROUP_TYPE && { variant: 'dropdownMenu' }),
54
- },
55
- }),
56
- { count },
57
- );
58
- };
59
-
60
- export const createNestedActions = () => {
61
- const result: ActionGraphProps = { edges: [], nodes: [] };
62
- const actionGroups = createActions({ type: ACTION_GROUP_TYPE });
63
- actionGroups.forEach((group) => {
64
- const actions = createActions();
65
- result.nodes.push(group, ...actions);
66
- result.edges.push(
67
- { source: 'root', target: group.id },
68
- ...actions.map((action) => ({ source: group.id, target: action.id })),
69
- );
70
- });
71
- return result;
72
- };
73
-
74
- export const createNestedActionsResolver = (groupParams?: CreateActionsParams, params?: CreateActionsParams) => {
75
- const graph = new Graph();
76
- const actionGroups = createActions({ type: ACTION_GROUP_TYPE, ...groupParams });
77
- actionGroups.forEach((group) => {
78
- const actions = createActions(params);
79
- // TODO(thure): these methods exist on `graph` but are marked as `@internal`; how should consumers properly build
80
- // graph “islands” for the purposes of these components?
81
- // @ts-ignore
82
- graph._addNodes([group as NodeArg<any>, ...(actions as NodeArg<any>[])]);
83
- // @ts-ignore
84
- graph._addEdges([
85
- { source: 'root', target: group.id },
86
- ...actions.map((action) => ({ source: group.id, target: action.id })),
87
- ]);
88
- void graph.expand(group);
89
- });
90
- const resolveGroupItems = (groupNode?: MenuItemGroup) =>
91
- (graph.actions(groupNode ?? graph.root) || null) as MenuItem[] | null;
92
- return { resolveGroupItems };
93
- };
94
-
95
- export const mutateActionsOnInterval = (actions: Action[]) => {
96
- let cursor = 0;
97
- return setInterval(() => {
98
- const action = actions[cursor] as DeepWriteable<Action>;
99
- action.properties.icon = faker.helpers.arrayElement(
100
- action.properties.icon.endsWith('regular') ? icons.fill : icons.regular,
101
- );
102
- action.properties.disabled = !action.properties.disabled;
103
- cursor = (cursor + actions.length + 1) % actions.length;
104
- }, 1_000);
105
- };
106
-
107
- export const useMutateActions = (actions: MenuAction[]) =>
108
- useEffect(() => {
109
- const interval = mutateActionsOnInterval(actions);
110
- return () => clearInterval(interval);
111
- }, []);
5
+ export * from './generator';
@@ -26,8 +26,6 @@ export type MenuItemGroupProperties = {
26
26
 
27
27
  export type MenuAction<P extends {} = {}> = Action<P & MenuActionProperties>;
28
28
 
29
- export type MenuActionHandler<A extends Node = MenuAction> = (action: A) => void;
30
-
31
29
  export const MenuSeparatorType = '@dxos/react-ui-toolbar/separator' as const;
32
30
 
33
31
  export type MenuSeparator = Node<never, Pick<ToolbarSeparatorProps, 'variant'>> & {
@@ -47,9 +45,8 @@ export type MenuItem = MenuSeparator | MenuAction | MenuItemGroup;
47
45
 
48
46
  export type MenuItemsResolver = (group?: MenuItemGroup) => MenuItem[] | null;
49
47
 
50
- export type MenuContextValue<A extends Node = MenuAction> = {
51
- resolveGroupItems: MenuItemsResolver;
48
+ export type MenuContextValue = {
49
+ useGroupItems: MenuItemsResolver;
52
50
  iconSize: IconButtonProps['size'];
53
- onAction: MenuActionHandler<A>;
54
51
  attendableId?: string;
55
52
  };
package/src/util.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  type MenuItemGroup,
12
12
  type MenuItemGroupProperties,
13
13
  type MenuSeparator,
14
- } from './defs';
14
+ } from './types';
15
15
 
16
16
  export const getShortcut = (action: ActionLike) => {
17
17
  return typeof action.properties?.keyBinding === 'string'
@@ -21,16 +21,16 @@ export const getShortcut = (action: ActionLike) => {
21
21
 
22
22
  export const fallbackIcon = 'ph--placeholder--regular';
23
23
 
24
- // TODO(thure,wittjosiah): This is probably a smell; consider the whether react-ui-menu should use action nodes if the
25
- // callback in `data` is never used. See https://github.com/dxos/dxos/pull/8506#discussion_r1925855118
26
- const noop = () => {};
27
-
28
- export const createMenuAction = <P extends {} = {}>(id: string, properties: P & MenuActionProperties) =>
24
+ export const createMenuAction = <P extends {} = {}>(
25
+ id: string,
26
+ invoke: () => void,
27
+ properties: P & MenuActionProperties,
28
+ ) =>
29
29
  ({
30
30
  id,
31
31
  type: ACTION_TYPE,
32
32
  properties,
33
- data: noop,
33
+ data: invoke,
34
34
  }) satisfies MenuAction;
35
35
 
36
36
  export const createMenuItemGroup = <
@@ -1 +0,0 @@
1
- {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AAC9F,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEnH,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,MAAM,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC;AAE7E,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;AAEjF,eAAO,MAAM,iBAAiB,EAAG,kCAA2C,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,CAAC,CAAC,GAAG;IAChF,IAAI,EAAE,OAAO,iBAAiB,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,WAAW,SAAU,IAAI,KAAG,IAAI,IAAI,aAAgD,CAAC;AAElG,MAAM,MAAM,2BAA2B,GAAG;IAAE,iBAAiB,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACzF,MAAM,MAAM,6BAA6B,GAAG;IAAE,iBAAiB,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE/F,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAEhG,eAAO,MAAM,WAAW,SAAU,IAAI,KAAG,IAAI,IAAI,aAAgD,CAAC;AAElG,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,UAAU,GAAG,aAAa,CAAC;AAElE,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,EAAE,aAAa,KAAK,QAAQ,EAAE,GAAG,IAAI,CAAC;AAE7E,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,IAAI;IAC1D,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC"}
@@ -1,17 +0,0 @@
1
- import { type NodeArg } from '@dxos/app-graph';
2
- import { type MenuItem, type MenuItemGroup } from './defs';
3
- type Edge = {
4
- source: string;
5
- target: string;
6
- };
7
- export type ActionGraphNodes = NodeArg<any>[];
8
- export type ActionGraphEdges = Edge[];
9
- export type ActionGraphProps = {
10
- nodes: ActionGraphNodes;
11
- edges: ActionGraphEdges;
12
- };
13
- export declare const useMenuActions: (actionCreator: () => ActionGraphProps) => {
14
- resolveGroupItems: (sourceNode?: MenuItemGroup) => MenuItem[] | null;
15
- };
16
- export {};
17
- //# sourceMappingURL=hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/hooks.ts"],"names":[],"mappings":"AAOA,OAAO,EAAS,KAAK,OAAO,EAAa,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE3D,KAAK,IAAI,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA6B/C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,MAAM,MAAM,gBAAgB,GAAG,IAAI,EAAE,CAAC;AACtC,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAEpF,eAAO,MAAM,cAAc,kBAAmB,MAAM,gBAAgB;qCAgBnD,aAAa;CAW7B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Menus.stories.d.ts","sourceRoot":"","sources":["../../../../src/testing/Menus.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AACrB,OAAO,KAAsB,MAAM,OAAO,CAAC;;;;;;;;;;;;;AAc3C,wBAKE;AAOF,eAAO,MAAM,YAAY;;CAcxB,CAAC;AAEF,eAAO,MAAM,OAAO;;CAQnB,CAAC;AAEF,eAAO,MAAM,qBAAqB;;CASjC,CAAC"}
package/src/hooks.ts DELETED
@@ -1,72 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { useSignalEffect } from '@preact/signals-react';
6
- import { useCallback, useState } from 'react';
7
-
8
- import { Graph, type NodeArg, type Node } from '@dxos/app-graph';
9
-
10
- import { type MenuItem, type MenuItemGroup } from './defs';
11
-
12
- type Edge = { source: string; target: string };
13
-
14
- const edgesArrayToRecord = (edges: { source: string; target: string }[]): Record<string, string[]> => {
15
- return Object.fromEntries(
16
- Object.entries(
17
- edges.reduce((acc: Record<string, { inbound: string[]; outbound: string[] }>, { source, target }) => {
18
- if (!acc[source]) {
19
- acc[source] = { inbound: [], outbound: [] };
20
- }
21
- if (!acc[target]) {
22
- acc[target] = { inbound: [], outbound: [] };
23
- }
24
-
25
- const sourceEdges = acc[source];
26
- if (!sourceEdges.outbound.includes(target)) {
27
- sourceEdges.outbound.push(target);
28
- }
29
-
30
- const targetEdges = acc[target];
31
- if (!targetEdges.inbound.includes(source)) {
32
- targetEdges.inbound.push(source);
33
- }
34
-
35
- return acc;
36
- }, {}),
37
- ).map(([id, { outbound }]): [string, string[]] => [id, outbound]),
38
- );
39
- };
40
-
41
- export type ActionGraphNodes = NodeArg<any>[];
42
- export type ActionGraphEdges = Edge[];
43
- export type ActionGraphProps = { nodes: ActionGraphNodes; edges: ActionGraphEdges };
44
-
45
- export const useMenuActions = (actionCreator: () => ActionGraphProps) => {
46
- const initialMenuGraphProps = actionCreator();
47
-
48
- const [graph] = useState(
49
- new Graph({ nodes: initialMenuGraphProps.nodes as Node[], edges: edgesArrayToRecord(initialMenuGraphProps.edges) }),
50
- );
51
-
52
- useSignalEffect(() => {
53
- const menuGraphProps = actionCreator();
54
- // @ts-ignore
55
- graph._addNodes(menuGraphProps.nodes);
56
- // @ts-ignore
57
- graph._addEdges(menuGraphProps.edges);
58
- });
59
-
60
- const resolveGroupItems = useCallback(
61
- (sourceNode: MenuItemGroup = graph.root as MenuItemGroup) => {
62
- if (graph) {
63
- return (graph.nodes(sourceNode, { filter: (n): n is any => true }) || null) as MenuItem[] | null;
64
- } else {
65
- return null;
66
- }
67
- },
68
- [graph],
69
- );
70
-
71
- return { resolveGroupItems };
72
- };