@dxos/plugin-sheet 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b

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 (187) hide show
  1. package/dist/lib/browser/SheetContainer-QE32GHSA.mjs +370 -0
  2. package/dist/lib/browser/SheetContainer-QE32GHSA.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-5IP6N25X.mjs +18 -0
  4. package/dist/lib/browser/chunk-5IP6N25X.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-A374JPWV.mjs → chunk-EEN6LQ77.mjs} +157 -391
  6. package/dist/lib/browser/chunk-EEN6LQ77.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-FOO6NGBM.mjs +229 -0
  8. package/dist/lib/browser/chunk-FOO6NGBM.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-Q4XS4YWF.mjs → chunk-MW7L2R23.mjs} +2 -2
  10. package/dist/lib/browser/chunk-MW7L2R23.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-NA2WJTDQ.mjs +15 -0
  12. package/dist/lib/browser/chunk-NA2WJTDQ.mjs.map +7 -0
  13. package/dist/lib/browser/compute-graph-registry-5PSCPAJC.mjs +27 -0
  14. package/dist/lib/browser/compute-graph-registry-5PSCPAJC.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +80 -218
  16. package/dist/lib/browser/index.mjs.map +4 -4
  17. package/dist/lib/browser/intent-resolver-BIOB22P2.mjs +56 -0
  18. package/dist/lib/browser/intent-resolver-BIOB22P2.mjs.map +7 -0
  19. package/dist/lib/browser/markdown-ZXHPFU4B.mjs +26 -0
  20. package/dist/lib/browser/markdown-ZXHPFU4B.mjs.map +7 -0
  21. package/dist/lib/browser/meta.json +1 -1
  22. package/dist/lib/browser/react-surface-DUNHY5TJ.mjs +52 -0
  23. package/dist/lib/browser/react-surface-DUNHY5TJ.mjs.map +7 -0
  24. package/dist/lib/browser/thread-RSI66HGQ.mjs +17 -0
  25. package/dist/lib/browser/thread-RSI66HGQ.mjs.map +7 -0
  26. package/dist/lib/browser/types/index.mjs +2 -2
  27. package/dist/lib/node/SheetContainer-63QTAGQQ.cjs +364 -0
  28. package/dist/lib/node/SheetContainer-63QTAGQQ.cjs.map +7 -0
  29. package/dist/lib/node/{chunk-TQOJ7DG2.cjs → chunk-6BZ2TACG.cjs} +6 -6
  30. package/dist/lib/node/chunk-6BZ2TACG.cjs.map +7 -0
  31. package/dist/lib/node/{meta.cjs → chunk-6G6HHJOM.cjs} +12 -8
  32. package/dist/lib/node/chunk-6G6HHJOM.cjs.map +7 -0
  33. package/dist/lib/node/{chunk-2ZVZI2KJ.cjs → chunk-AUC5XQEN.cjs} +12 -9
  34. package/dist/lib/node/chunk-AUC5XQEN.cjs.map +7 -0
  35. package/dist/lib/node/{chunk-FDEQ2PGJ.cjs → chunk-K4VMHOR2.cjs} +198 -428
  36. package/dist/lib/node/chunk-K4VMHOR2.cjs.map +7 -0
  37. package/dist/lib/node/chunk-P4KSGZSS.cjs +251 -0
  38. package/dist/lib/node/chunk-P4KSGZSS.cjs.map +7 -0
  39. package/dist/lib/node/compute-graph-registry-IKYGA5ZC.cjs +53 -0
  40. package/dist/lib/node/compute-graph-registry-IKYGA5ZC.cjs.map +7 -0
  41. package/dist/lib/node/index.cjs +81 -215
  42. package/dist/lib/node/index.cjs.map +4 -4
  43. package/dist/lib/node/intent-resolver-EN5ZMUUF.cjs +69 -0
  44. package/dist/lib/node/intent-resolver-EN5ZMUUF.cjs.map +7 -0
  45. package/dist/lib/node/markdown-7NVPUDWI.cjs +40 -0
  46. package/dist/lib/node/markdown-7NVPUDWI.cjs.map +7 -0
  47. package/dist/lib/node/meta.json +1 -1
  48. package/dist/lib/node/react-surface-YGALRCRL.cjs +70 -0
  49. package/dist/lib/node/react-surface-YGALRCRL.cjs.map +7 -0
  50. package/dist/lib/node/thread-7KJVRDUE.cjs +32 -0
  51. package/dist/lib/node/thread-7KJVRDUE.cjs.map +7 -0
  52. package/dist/lib/node/types/index.cjs +30 -30
  53. package/dist/lib/node/types/index.cjs.map +1 -1
  54. package/dist/lib/node-esm/SheetContainer-EUR47CP5.mjs +371 -0
  55. package/dist/lib/node-esm/SheetContainer-EUR47CP5.mjs.map +7 -0
  56. package/dist/lib/node-esm/chunk-6C53G45G.mjs +16 -0
  57. package/dist/lib/node-esm/chunk-6C53G45G.mjs.map +7 -0
  58. package/dist/lib/node-esm/chunk-JFUKD7N4.mjs +20 -0
  59. package/dist/lib/node-esm/chunk-JFUKD7N4.mjs.map +7 -0
  60. package/dist/lib/node-esm/{chunk-NYYIDVR7.mjs → chunk-LGQMXD6K.mjs} +2 -2
  61. package/dist/lib/node-esm/chunk-LGQMXD6K.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-PTOI45NK.mjs +231 -0
  63. package/dist/lib/node-esm/chunk-PTOI45NK.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-L5PQHVTX.mjs → chunk-RFGJUSHF.mjs} +157 -391
  65. package/dist/lib/node-esm/chunk-RFGJUSHF.mjs.map +7 -0
  66. package/dist/lib/node-esm/compute-graph-registry-ZSLEFSS5.mjs +28 -0
  67. package/dist/lib/node-esm/compute-graph-registry-ZSLEFSS5.mjs.map +7 -0
  68. package/dist/lib/node-esm/index.mjs +80 -218
  69. package/dist/lib/node-esm/index.mjs.map +4 -4
  70. package/dist/lib/node-esm/intent-resolver-6BCKQA5A.mjs +57 -0
  71. package/dist/lib/node-esm/intent-resolver-6BCKQA5A.mjs.map +7 -0
  72. package/dist/lib/node-esm/markdown-LRZEUBKN.mjs +27 -0
  73. package/dist/lib/node-esm/markdown-LRZEUBKN.mjs.map +7 -0
  74. package/dist/lib/node-esm/meta.json +1 -1
  75. package/dist/lib/node-esm/react-surface-AAT56ZBA.mjs +53 -0
  76. package/dist/lib/node-esm/react-surface-AAT56ZBA.mjs.map +7 -0
  77. package/dist/lib/node-esm/thread-U3RIKVMB.mjs +18 -0
  78. package/dist/lib/node-esm/thread-U3RIKVMB.mjs.map +7 -0
  79. package/dist/lib/node-esm/types/index.mjs +2 -2
  80. package/dist/types/src/SheetPlugin.d.ts +1 -3
  81. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/capabilities.d.ts +5 -0
  83. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  84. package/dist/types/src/capabilities/compute-graph-registry.d.ts +4 -0
  85. package/dist/types/src/capabilities/compute-graph-registry.d.ts.map +1 -0
  86. package/dist/types/src/capabilities/index.d.ts +12 -0
  87. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  88. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  89. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  90. package/dist/types/src/capabilities/markdown.d.ts +6 -0
  91. package/dist/types/src/capabilities/markdown.d.ts.map +1 -0
  92. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  93. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  94. package/dist/types/src/capabilities/thread.d.ts +6 -0
  95. package/dist/types/src/capabilities/thread.d.ts.map +1 -0
  96. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  97. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts.map +1 -1
  98. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts +7 -0
  100. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts.map +1 -0
  101. package/dist/types/src/components/SheetToolbar/SheetToolbar.stories.d.ts +6 -0
  102. package/dist/types/src/components/SheetToolbar/SheetToolbar.stories.d.ts.map +1 -0
  103. package/dist/types/src/components/SheetToolbar/align.d.ts +28 -0
  104. package/dist/types/src/components/SheetToolbar/align.d.ts.map +1 -0
  105. package/dist/types/src/components/SheetToolbar/comment.d.ts +23 -0
  106. package/dist/types/src/components/SheetToolbar/comment.d.ts.map +1 -0
  107. package/dist/types/src/components/SheetToolbar/index.d.ts +2 -0
  108. package/dist/types/src/components/SheetToolbar/index.d.ts.map +1 -0
  109. package/dist/types/src/components/SheetToolbar/style.d.ts +26 -0
  110. package/dist/types/src/components/SheetToolbar/style.d.ts.map +1 -0
  111. package/dist/types/src/components/SheetToolbar/useToolbarAction.d.ts +8 -0
  112. package/dist/types/src/components/SheetToolbar/useToolbarAction.d.ts.map +1 -0
  113. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts +6 -0
  114. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts.map +1 -0
  115. package/dist/types/src/components/index.d.ts +1 -2
  116. package/dist/types/src/components/index.d.ts.map +1 -1
  117. package/dist/types/src/index.d.ts +2 -3
  118. package/dist/types/src/index.d.ts.map +1 -1
  119. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -1
  120. package/dist/types/src/meta.d.ts +2 -2
  121. package/dist/types/src/meta.d.ts.map +1 -1
  122. package/dist/types/src/testing/testing.d.ts +1 -1
  123. package/dist/types/src/testing/testing.d.ts.map +1 -1
  124. package/dist/types/src/types/sheet-range-types.d.ts +1 -1
  125. package/dist/types/src/types/sheet-range-types.d.ts.map +1 -1
  126. package/dist/types/src/types/types.d.ts +0 -11
  127. package/dist/types/src/types/types.d.ts.map +1 -1
  128. package/dist/types/tsconfig.tsbuildinfo +1 -1
  129. package/package.json +45 -52
  130. package/src/SheetPlugin.tsx +55 -103
  131. package/src/capabilities/capabilities.ts +14 -0
  132. package/src/capabilities/compute-graph-registry.ts +25 -0
  133. package/src/capabilities/index.ts +13 -0
  134. package/src/capabilities/intent-resolver.ts +38 -0
  135. package/src/capabilities/markdown.ts +22 -0
  136. package/src/capabilities/react-surface.tsx +37 -0
  137. package/src/capabilities/thread.ts +14 -0
  138. package/src/components/GridSheet/GridSheet.stories.tsx +2 -0
  139. package/src/components/GridSheet/util.ts +1 -1
  140. package/src/components/SheetContainer/SheetContainer.stories.tsx +28 -22
  141. package/src/components/SheetContainer/SheetContainer.tsx +3 -8
  142. package/src/components/{Toolbar/Toolbar.stories.tsx → SheetToolbar/SheetToolbar.stories.tsx} +4 -8
  143. package/src/components/SheetToolbar/SheetToolbar.tsx +48 -0
  144. package/src/components/SheetToolbar/align.ts +68 -0
  145. package/src/components/SheetToolbar/comment.ts +56 -0
  146. package/src/components/{Toolbar → SheetToolbar}/index.ts +1 -1
  147. package/src/components/SheetToolbar/style.ts +72 -0
  148. package/src/components/SheetToolbar/useToolbarAction.ts +87 -0
  149. package/src/components/SheetToolbar/useToolbarState.ts +17 -0
  150. package/src/components/index.ts +0 -1
  151. package/src/index.ts +2 -5
  152. package/src/integrations/thread-ranges.ts +21 -10
  153. package/src/meta.ts +3 -2
  154. package/src/types/sheet-range-types.ts +1 -1
  155. package/src/types/types.ts +0 -26
  156. package/dist/lib/browser/SheetContainer-S4NCLUYL.mjs +0 -290
  157. package/dist/lib/browser/SheetContainer-S4NCLUYL.mjs.map +0 -7
  158. package/dist/lib/browser/chunk-A374JPWV.mjs.map +0 -7
  159. package/dist/lib/browser/chunk-Q4XS4YWF.mjs.map +0 -7
  160. package/dist/lib/browser/chunk-RABELMEQ.mjs +0 -15
  161. package/dist/lib/browser/chunk-RABELMEQ.mjs.map +0 -7
  162. package/dist/lib/browser/meta.mjs +0 -9
  163. package/dist/lib/browser/meta.mjs.map +0 -7
  164. package/dist/lib/node/SheetContainer-TP4GYXZB.cjs +0 -296
  165. package/dist/lib/node/SheetContainer-TP4GYXZB.cjs.map +0 -7
  166. package/dist/lib/node/chunk-2ZVZI2KJ.cjs.map +0 -7
  167. package/dist/lib/node/chunk-FDEQ2PGJ.cjs.map +0 -7
  168. package/dist/lib/node/chunk-TQOJ7DG2.cjs.map +0 -7
  169. package/dist/lib/node/meta.cjs.map +0 -7
  170. package/dist/lib/node-esm/SheetContainer-YB3JBVPZ.mjs +0 -291
  171. package/dist/lib/node-esm/SheetContainer-YB3JBVPZ.mjs.map +0 -7
  172. package/dist/lib/node-esm/chunk-BM2Q3FFC.mjs +0 -17
  173. package/dist/lib/node-esm/chunk-BM2Q3FFC.mjs.map +0 -7
  174. package/dist/lib/node-esm/chunk-L5PQHVTX.mjs.map +0 -7
  175. package/dist/lib/node-esm/chunk-NYYIDVR7.mjs.map +0 -7
  176. package/dist/lib/node-esm/meta.mjs +0 -10
  177. package/dist/lib/node-esm/meta.mjs.map +0 -7
  178. package/dist/types/src/components/SheetObjectSettings.d.ts +0 -7
  179. package/dist/types/src/components/SheetObjectSettings.d.ts.map +0 -1
  180. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -48
  181. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  182. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +0 -7
  183. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +0 -1
  184. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  185. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  186. package/src/components/SheetObjectSettings.tsx +0 -38
  187. package/src/components/Toolbar/Toolbar.tsx +0 -344
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useEffect, useMemo } from 'react';
6
+
7
+ import { RefArray } from '@dxos/live-object';
8
+ import { createMenuAction } from '@dxos/react-ui-menu';
9
+
10
+ import { SHEET_PLUGIN } from '../../meta';
11
+ import { commentKey, type CommentKey, type CommentValue, rangeToIndex } from '../../types';
12
+ import { useSheetContext } from '../SheetContext';
13
+
14
+ export type CommentAction = { key: CommentKey; value: CommentValue; cellContent?: string };
15
+
16
+ export type CommentState = { commentEnabled: 'comment' | 'no cursor' | 'selection overlaps existing comment' };
17
+
18
+ export const useCommentState = (state: Partial<CommentState>) => {
19
+ const { cursorFallbackRange, model } = useSheetContext();
20
+
21
+ // TODO(thure): Can this O(n) call be memoized?
22
+ const overlapsCommentAnchor = useMemo(
23
+ () =>
24
+ RefArray.allResolvedTargets(model.sheet.threads ?? [])
25
+ .filter((thread) => thread.status !== 'resolved')
26
+ .some((thread) => {
27
+ if (!cursorFallbackRange) {
28
+ return false;
29
+ }
30
+ return rangeToIndex(model.sheet, cursorFallbackRange) === thread.anchor;
31
+ }),
32
+ [cursorFallbackRange, model.sheet],
33
+ );
34
+
35
+ useEffect(() => {
36
+ state.commentEnabled = !cursorFallbackRange
37
+ ? 'no cursor'
38
+ : overlapsCommentAnchor
39
+ ? 'selection overlaps existing comment'
40
+ : 'comment';
41
+ }, [overlapsCommentAnchor, cursorFallbackRange]);
42
+ };
43
+
44
+ const createCommentAction = (state: Partial<CommentState>) =>
45
+ createMenuAction<Pick<CommentAction, 'key'>>('comment', {
46
+ key: commentKey,
47
+ testId: 'editor.toolbar.comment',
48
+ icon: 'ph--chat-text--regular',
49
+ label: [`${state.commentEnabled} label`, { ns: SHEET_PLUGIN }],
50
+ disabled: state.commentEnabled !== 'comment',
51
+ });
52
+
53
+ export const createComment = (state: Partial<CommentState>) => ({
54
+ nodes: [createCommentAction(state)],
55
+ edges: [{ source: 'root', target: 'comment' }],
56
+ });
@@ -2,4 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './Toolbar';
5
+ export * from './SheetToolbar';
@@ -0,0 +1,72 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useEffect } from 'react';
6
+
7
+ import { inRange } from '@dxos/compute';
8
+ import { createMenuAction, createMenuItemGroup, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
9
+
10
+ import { SHEET_PLUGIN } from '../../meta';
11
+ import { rangeFromIndex, type StyleKey, type StyleValue } from '../../types';
12
+ import { useSheetContext } from '../SheetContext';
13
+
14
+ export type StyleState = Partial<Record<StyleValue, boolean>>;
15
+
16
+ export type StyleAction = { key: StyleKey; value: StyleValue };
17
+
18
+ const styles: Record<StyleValue, string> = {
19
+ highlight: 'ph--highlighter--regular',
20
+ softwrap: 'ph--paragraph--regular',
21
+ };
22
+
23
+ export const useStyleState = (state: StyleState) => {
24
+ const { cursorFallbackRange, model } = useSheetContext();
25
+
26
+ useEffect(() => {
27
+ state.highlight = false;
28
+ state.softwrap = false;
29
+ if (cursorFallbackRange && model.sheet.ranges) {
30
+ model.sheet.ranges
31
+ .filter(
32
+ ({ range, key }) => key === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),
33
+ )
34
+ .forEach(({ value }) => {
35
+ state[value as StyleValue] = true;
36
+ });
37
+ }
38
+ }, [cursorFallbackRange, model.sheet]);
39
+ };
40
+
41
+ const createStyleGroup = (state: StyleState) => {
42
+ return createMenuItemGroup('style', {
43
+ variant: 'toggleGroup',
44
+ selectCardinality: 'multiple',
45
+ value: Object.keys(styles)
46
+ .filter((key) => !!state[key as StyleValue])
47
+ .map((styleValue) => `style--${styleValue}`),
48
+ } as ToolbarMenuActionGroupProperties);
49
+ };
50
+
51
+ const createStyleActions = (state: StyleState) =>
52
+ Object.entries(styles).map(([styleValue, icon]) => {
53
+ return createMenuAction<StyleAction>(`style--${styleValue}`, {
54
+ key: 'style',
55
+ value: styleValue as StyleValue,
56
+ icon,
57
+ label: [`range value ${styleValue} label`, { ns: SHEET_PLUGIN }],
58
+ checked: !!state[styleValue as StyleValue],
59
+ });
60
+ });
61
+
62
+ export const createStyle = (state: StyleState) => {
63
+ const styleGroupAction = createStyleGroup(state);
64
+ const styleActions = createStyleActions(state);
65
+ return {
66
+ nodes: [styleGroupAction, ...styleActions],
67
+ edges: [
68
+ { source: 'root', target: 'style' },
69
+ ...styleActions.map(({ id }) => ({ source: styleGroupAction.id, target: id })),
70
+ ],
71
+ };
72
+ };
@@ -0,0 +1,87 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useCallback } from 'react';
6
+
7
+ import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { inRange } from '@dxos/compute';
9
+ import { ThreadAction } from '@dxos/plugin-thread/types';
10
+ import type { MenuAction, MenuActionHandler } from '@dxos/react-ui-menu';
11
+
12
+ import { type AlignAction } from './align';
13
+ import { type CommentAction } from './comment';
14
+ import { type StyleAction } from './style';
15
+ import { type ToolbarState } from './useToolbarState';
16
+ import { completeCellRangeToThreadCursor } from '../../integrations';
17
+ import { alignKey, rangeFromIndex, rangeToIndex } from '../../types';
18
+ import { useSheetContext } from '../SheetContext';
19
+
20
+ export type ToolbarAction = StyleAction | AlignAction | CommentAction;
21
+
22
+ export const useToolbarAction = (state: ToolbarState) => {
23
+ const { model, cursorFallbackRange, cursor } = useSheetContext();
24
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
25
+
26
+ // TODO(Zan): Externalize the toolbar action handler. E.g., Toolbar/keys should both fire events.
27
+ return useCallback(
28
+ (action: MenuAction<ToolbarAction>) => {
29
+ const { key, value } = action.properties;
30
+ if (cursorFallbackRange) {
31
+ const index =
32
+ model.sheet.ranges?.findIndex(
33
+ (range) => range.key === key && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),
34
+ ) ?? -1;
35
+ const nextRangeEntity = {
36
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
37
+ key,
38
+ value,
39
+ };
40
+ switch (key) {
41
+ case 'alignment':
42
+ if (index < 0) {
43
+ model.sheet.ranges?.push(nextRangeEntity);
44
+ state[alignKey] = value;
45
+ } else if (model.sheet.ranges![index].value === value) {
46
+ model.sheet.ranges?.splice(index, 1);
47
+ state[alignKey] = undefined;
48
+ } else {
49
+ model.sheet.ranges?.splice(index, 1, nextRangeEntity);
50
+ state[alignKey] = value;
51
+ }
52
+ break;
53
+ case 'style':
54
+ if (
55
+ model.sheet.ranges
56
+ .filter(
57
+ ({ range, key: rangeKey }) =>
58
+ rangeKey === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),
59
+ )
60
+ .some(({ value: rangeValue }) => rangeValue === value)
61
+ ) {
62
+ // this value should be unset
63
+ if (index >= 0) {
64
+ model.sheet.ranges?.splice(index, 1);
65
+ }
66
+ state[value] = false;
67
+ } else {
68
+ model.sheet.ranges?.push(nextRangeEntity);
69
+ state[value] = true;
70
+ }
71
+ break;
72
+ case 'comment': {
73
+ const cellContent = model.getCellText(cursorFallbackRange.from);
74
+ void dispatch(
75
+ createIntent(ThreadAction.Create, {
76
+ cursor: completeCellRangeToThreadCursor(cursorFallbackRange),
77
+ name: cellContent,
78
+ subject: model.sheet,
79
+ }),
80
+ );
81
+ }
82
+ }
83
+ }
84
+ },
85
+ [model.sheet, cursorFallbackRange, cursor, dispatch],
86
+ ) as MenuActionHandler;
87
+ };
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useMemo } from 'react';
6
+
7
+ import { create } from '@dxos/live-object';
8
+
9
+ import { type AlignState } from './align';
10
+ import { type CommentState } from './comment';
11
+ import { type StyleState } from './style';
12
+
13
+ export type ToolbarState = Partial<StyleState & AlignState & CommentState>;
14
+
15
+ export const useToolbarState = (initialState: ToolbarState = {}) => {
16
+ return useMemo(() => create<ToolbarState>(initialState), []);
17
+ };
@@ -8,6 +8,5 @@ export * from './ComputeGraph';
8
8
  export * from './GridSheet';
9
9
  export * from './RangeList';
10
10
  export * from './SheetContext';
11
- export * from './SheetObjectSettings';
12
11
 
13
12
  export const SheetContainer = lazy(() => import('./SheetContainer'));
package/src/index.ts CHANGED
@@ -2,9 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { SheetPlugin } from './SheetPlugin';
6
-
7
- export * from './types';
5
+ export { SheetCapabilities } from './capabilities';
6
+ export * from './meta';
8
7
  export * from './SheetPlugin';
9
-
10
- export default SheetPlugin;
@@ -8,14 +8,15 @@ import {
8
8
  createIntent,
9
9
  createResolver,
10
10
  LayoutAction,
11
- useIntentDispatcher,
12
11
  useIntentResolver,
12
+ useIntentDispatcher,
13
13
  } from '@dxos/app-framework';
14
14
  import { debounce } from '@dxos/async';
15
15
  import { type CellAddress, type CompleteCellRange, inRange } from '@dxos/compute';
16
+ import { S } from '@dxos/echo-schema';
16
17
  import { ThreadAction } from '@dxos/plugin-thread/types';
17
18
  import { fullyQualifiedId } from '@dxos/react-client/echo';
18
- import { type DxGridElement, type DxGridPosition } from '@dxos/react-ui-grid';
19
+ import { type DxGridElement, type DxGridPosition, type GridContentProps } from '@dxos/react-ui-grid';
19
20
 
20
21
  import { useSheetContext } from '../components';
21
22
  import { SHEET_PLUGIN } from '../meta';
@@ -41,19 +42,29 @@ export const useUpdateFocusedCellOnThreadSelection = (grid: DxGridElement | null
41
42
  const { model, setActiveRefs } = useSheetContext();
42
43
  const scrollIntoViewResolver = useMemo(
43
44
  () =>
44
- createResolver(
45
- LayoutAction.ScrollIntoView,
46
- ({ cursor, ref }) => {
45
+ createResolver({
46
+ intent: LayoutAction.ScrollIntoView,
47
+ position: 'hoist',
48
+ filter: (
49
+ data,
50
+ ): data is {
51
+ part: 'current';
52
+ subject: string;
53
+ options: { cursor: string; ref: GridContentProps['activeRefs'] };
54
+ } => {
55
+ if (!S.is(LayoutAction.ScrollIntoView.fields.input)(data)) {
56
+ return false;
57
+ }
58
+
59
+ return data.subject === fullyQualifiedId(model.sheet) && !!data.options?.cursor;
60
+ },
61
+ resolve: ({ options: { cursor, ref } }) => {
47
62
  setActiveRefs(ref);
48
63
  // TODO(Zan): Everywhere we refer to the cursor in a thread context should change to `anchor`.
49
64
  const range = parseThreadAnchorAsCellRange(cursor!);
50
65
  range && grid?.setFocus({ ...range.to, plane: 'grid' }, true);
51
66
  },
52
- {
53
- disposition: 'hoist',
54
- filter: (data) => data.id === fullyQualifiedId(model.sheet) && !!data.cursor,
55
- },
56
- ),
67
+ }),
57
68
  [model.sheet, setActiveRefs],
58
69
  );
59
70
 
package/src/meta.ts CHANGED
@@ -6,10 +6,11 @@ import { type PluginMeta } from '@dxos/app-framework';
6
6
 
7
7
  export const SHEET_PLUGIN = 'dxos.org/plugin/sheet';
8
8
 
9
- export default {
9
+ export const meta = {
10
10
  id: SHEET_PLUGIN,
11
11
  name: 'Sheet',
12
- description: 'A simple spreadsheet plugin.',
12
+ description: `Sheets in Composer are simple spreadsheets which allow you to leverage custom functions inside of cell grids. Leverage more than 400 pre-built formulas like Sum, Average, Count, Max, Min along with many others. You can also deploy your own custom functions using the Scripts plugin. `,
13
13
  icon: 'ph--grid-nine--regular',
14
14
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-sheet',
15
+ screenshots: ['https://dxos.network/plugin-details-sheet-dark.png'],
15
16
  } satisfies PluginMeta;
@@ -8,7 +8,7 @@ import { type SheetType } from '../types';
8
8
 
9
9
  export const alignKey = 'alignment';
10
10
  export type AlignKey = typeof alignKey;
11
- export type AlignValue = 'start' | 'center' | 'end' | 'unset';
11
+ export type AlignValue = 'start' | 'center' | 'end';
12
12
 
13
13
  export const commentKey = 'comment';
14
14
  export type CommentKey = typeof commentKey;
@@ -2,38 +2,12 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import type {
6
- IntentResolverProvides,
7
- MetadataRecordsProvides,
8
- SurfaceProvides,
9
- TranslationsProvides,
10
- } from '@dxos/app-framework';
11
5
  import { S } from '@dxos/echo-schema';
12
- import { type MarkdownExtensionProvides } from '@dxos/plugin-markdown';
13
- import { type SchemaProvides } from '@dxos/plugin-space';
14
6
 
15
7
  import { type CellValue, RowColumnMeta, SheetType } from './schema';
16
8
  import { SHEET_PLUGIN } from '../meta';
17
9
  import { SheetModel } from '../model';
18
10
 
19
- // TODO(Zan): Move this to the plugin-space plugin or another common location
20
- // when we implement comments in sheets.
21
- // This is currently duplicated in a few places.
22
- type ThreadProvides<T> = {
23
- thread: {
24
- predicate: (obj: any) => obj is T;
25
- createSort: (obj: T) => (anchorA: string | undefined, anchorB: string | undefined) => number;
26
- };
27
- };
28
-
29
- export type SheetPluginProvides = SurfaceProvides &
30
- IntentResolverProvides &
31
- MarkdownExtensionProvides &
32
- MetadataRecordsProvides &
33
- TranslationsProvides &
34
- SchemaProvides &
35
- ThreadProvides<SheetType>;
36
-
37
11
  export type SheetSize = {
38
12
  rows: number;
39
13
  columns: number;
@@ -1,290 +0,0 @@
1
- import {
2
- GridSheet,
3
- SheetProvider,
4
- completeCellRangeToThreadCursor,
5
- useComputeGraph,
6
- useSheetContext
7
- } from "./chunk-A374JPWV.mjs";
8
- import {
9
- alignKey,
10
- mapFormulaIndicesToRefs,
11
- rangeFromIndex,
12
- rangeToIndex,
13
- styleKey
14
- } from "./chunk-Q4XS4YWF.mjs";
15
- import {
16
- SHEET_PLUGIN
17
- } from "./chunk-RABELMEQ.mjs";
18
-
19
- // packages/plugins/plugin-sheet/src/components/SheetContainer/SheetContainer.tsx
20
- import React3 from "react";
21
- import { StackItem } from "@dxos/react-ui-stack";
22
-
23
- // packages/plugins/plugin-sheet/src/components/FunctionEditor/FunctionEditor.tsx
24
- import React from "react";
25
- import { addressToA1Notation, isFormula, rangeToA1Notation } from "@dxos/compute";
26
- import { Icon } from "@dxos/react-ui";
27
- var FunctionEditor = () => {
28
- const { model, cursor, range } = useSheetContext();
29
- let value;
30
- let formula = false;
31
- if (cursor) {
32
- value = model.getCellValue(cursor);
33
- if (isFormula(value)) {
34
- value = model.graph.mapFunctionBindingFromId(mapFormulaIndicesToRefs(model.sheet, value));
35
- formula = true;
36
- } else if (value != null) {
37
- value = String(value);
38
- }
39
- }
40
- return /* @__PURE__ */ React.createElement("div", {
41
- className: "flex shrink-0 justify-between items-center px-4 py-1 text-sm attention-surface border-bs !border-separator"
42
- }, /* @__PURE__ */ React.createElement("div", {
43
- className: "flex gap-4 items-center"
44
- }, /* @__PURE__ */ React.createElement("div", {
45
- className: "flex w-16 items-center font-mono"
46
- }, range && rangeToA1Notation(range) || cursor && addressToA1Notation(cursor)), /* @__PURE__ */ React.createElement("div", {
47
- className: "flex gap-2 items-center"
48
- }, /* @__PURE__ */ React.createElement(Icon, {
49
- icon: "ph--function--regular",
50
- classNames: [
51
- "text-greenText",
52
- formula ? "visible" : "invisible"
53
- ]
54
- }), /* @__PURE__ */ React.createElement("span", {
55
- className: "font-mono"
56
- }, value))));
57
- };
58
-
59
- // packages/plugins/plugin-sheet/src/components/Toolbar/Toolbar.tsx
60
- import { createContext } from "@radix-ui/react-context";
61
- import React2, { useCallback } from "react";
62
- import { createIntent, useIntentDispatcher } from "@dxos/app-framework";
63
- import { inRange } from "@dxos/compute";
64
- import { RefArray } from "@dxos/live-object";
65
- import { ThreadAction } from "@dxos/plugin-thread/types";
66
- import { Icon as Icon2, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
67
- import { useAttention } from "@dxos/react-ui-attention";
68
- var buttonStyles = "min-bs-0 p-2";
69
- var tooltipProps = {
70
- side: "bottom"
71
- };
72
- var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
73
- role: "separator",
74
- className: "grow"
75
- });
76
- var ToolbarItem = ({ itemType, icon, children, ...props }) => {
77
- const Invoker = itemType === "toggleGroupItem" ? NaturalToolbar.ToggleGroupItem : itemType === "toggle" ? NaturalToolbar.Toggle : NaturalToolbar.Button;
78
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
79
- asChild: true
80
- }, /* @__PURE__ */ React2.createElement(Invoker, {
81
- variant: "ghost",
82
- ...props,
83
- classNames: buttonStyles
84
- }, /* @__PURE__ */ React2.createElement(Icon2, {
85
- icon,
86
- size: 5
87
- }), /* @__PURE__ */ React2.createElement("span", {
88
- className: "sr-only"
89
- }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
90
- };
91
- var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
92
- var ToolbarRoot = ({ children, role, classNames }) => {
93
- const { id, model, cursorFallbackRange, cursor } = useSheetContext();
94
- const { hasAttention } = useAttention(id);
95
- const { dispatchPromise: dispatch } = useIntentDispatcher();
96
- const handleAction = useCallback((action) => {
97
- switch (action.key) {
98
- case "alignment":
99
- if (cursorFallbackRange) {
100
- const index = model.sheet.ranges?.findIndex((range) => range.key === action.key && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from)) ?? -1;
101
- const nextRangeEntity = {
102
- range: rangeToIndex(model.sheet, cursorFallbackRange),
103
- key: action.key,
104
- value: action.value
105
- };
106
- if (index < 0) {
107
- model.sheet.ranges?.push(nextRangeEntity);
108
- } else if (model.sheet.ranges[index].value === action.value) {
109
- model.sheet.ranges?.splice(index, 1);
110
- } else {
111
- model.sheet.ranges?.splice(index, 1, nextRangeEntity);
112
- }
113
- }
114
- break;
115
- case "style":
116
- if (action.unset) {
117
- const index = model.sheet.ranges?.findIndex((range) => range.key === action.key && cursorFallbackRange && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from));
118
- if (index >= 0) {
119
- model.sheet.ranges?.splice(index, 1);
120
- }
121
- } else if (cursorFallbackRange) {
122
- model.sheet.ranges?.push({
123
- range: rangeToIndex(model.sheet, cursorFallbackRange),
124
- key: action.key,
125
- value: action.value
126
- });
127
- }
128
- break;
129
- case "comment": {
130
- if (cursorFallbackRange) {
131
- void dispatch(createIntent(ThreadAction.Create, {
132
- cursor: completeCellRangeToThreadCursor(cursorFallbackRange),
133
- name: action.cellContent,
134
- subject: model.sheet
135
- }));
136
- }
137
- }
138
- }
139
- }, [
140
- model.sheet,
141
- cursorFallbackRange,
142
- cursor,
143
- dispatch
144
- ]);
145
- return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
146
- onAction: handleAction
147
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Root, {
148
- classNames: [
149
- "pli-0.5 attention-surface",
150
- !hasAttention && "opacity-20",
151
- classNames
152
- ]
153
- }, children));
154
- };
155
- var alignmentOptions = [
156
- {
157
- value: "start",
158
- icon: "ph--text-align-left--regular"
159
- },
160
- {
161
- value: "center",
162
- icon: "ph--text-align-center--regular"
163
- },
164
- {
165
- value: "end",
166
- icon: "ph--text-align-right--regular"
167
- }
168
- ];
169
- var Alignment = () => {
170
- const { cursor, model } = useSheetContext();
171
- const { onAction } = useToolbarContext("Alignment");
172
- const { t } = useTranslation(SHEET_PLUGIN);
173
- const value = cursor ? model.sheet.ranges?.findLast(({ range, key }) => key === alignKey && inRange(rangeFromIndex(model.sheet, range), cursor))?.value : void 0;
174
- return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
175
- type: "single",
176
- value: (
177
- // TODO(thure): providing `undefined` leaves the last item active which was active rather than showing none.
178
- value ?? "never"
179
- ),
180
- onValueChange: (value2) => onAction?.({
181
- key: alignKey,
182
- value: value2
183
- })
184
- }, alignmentOptions.map(({ value: value2, icon }) => /* @__PURE__ */ React2.createElement(ToolbarItem, {
185
- itemType: "toggleGroupItem",
186
- key: value2,
187
- value: value2,
188
- icon,
189
- "data-testid": `grid.toolbar.${alignKey}.${value2}`
190
- }, t("toolbar action label", {
191
- key: t(`range key ${alignKey} label`),
192
- value: t(`range value ${value2} label`)
193
- }))));
194
- };
195
- var styleOptions = [
196
- {
197
- value: "highlight",
198
- icon: "ph--highlighter--regular"
199
- },
200
- {
201
- value: "softwrap",
202
- icon: "ph--paragraph--regular"
203
- }
204
- ];
205
- var Styles = () => {
206
- const { cursorFallbackRange, model } = useSheetContext();
207
- const { onAction } = useToolbarContext("Styles");
208
- const { t } = useTranslation(SHEET_PLUGIN);
209
- const activeValues = cursorFallbackRange ? model.sheet.ranges?.filter(({ range, key }) => key === "style" && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from)).reduce((acc, { value }) => {
210
- acc.add(value);
211
- return acc;
212
- }, /* @__PURE__ */ new Set()) : void 0;
213
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, styleOptions.map(({ value, icon }) => /* @__PURE__ */ React2.createElement(ToolbarItem, {
214
- itemType: "toggle",
215
- key: value,
216
- pressed: activeValues?.has(value),
217
- onPressedChange: (nextPressed) => {
218
- onAction?.({
219
- key: "style",
220
- value,
221
- unset: !nextPressed
222
- });
223
- },
224
- icon
225
- }, t("toolbar action label", {
226
- key: t(`range key ${styleKey} label`),
227
- value: t(`range value ${value} label`)
228
- }))));
229
- };
230
- var Actions = () => {
231
- const { onAction } = useToolbarContext("Actions");
232
- const { cursorFallbackRange, cursor, model } = useSheetContext();
233
- const { t } = useTranslation(SHEET_PLUGIN);
234
- const overlapsCommentAnchor = RefArray.allResolvedTargets(model.sheet.threads ?? []).filter((thread) => thread.status !== "resolved").some((thread) => {
235
- if (!cursorFallbackRange) {
236
- return false;
237
- }
238
- return rangeToIndex(model.sheet, cursorFallbackRange) === thread.anchor;
239
- });
240
- const tooltipLabelKey = !cursor ? "no cursor label" : overlapsCommentAnchor ? "selection overlaps existing comment label" : "comment label";
241
- return /* @__PURE__ */ React2.createElement(ToolbarItem, {
242
- itemType: "button",
243
- value: "comment",
244
- icon: "ph--chat-text--regular",
245
- "data-testid": "editor.toolbar.comment",
246
- onClick: () => {
247
- if (!cursorFallbackRange) {
248
- return;
249
- }
250
- return onAction?.({
251
- key: "comment",
252
- value: rangeToIndex(model.sheet, cursorFallbackRange),
253
- cellContent: model.getCellText(cursorFallbackRange.from)
254
- });
255
- },
256
- disabled: !cursorFallbackRange || overlapsCommentAnchor
257
- }, t(tooltipLabelKey));
258
- };
259
- var Toolbar = {
260
- Root: ToolbarRoot,
261
- Separator: ToolbarSeparator,
262
- Alignment,
263
- Styles,
264
- Actions
265
- };
266
-
267
- // packages/plugins/plugin-sheet/src/components/SheetContainer/SheetContainer.tsx
268
- var SheetContainer = ({ space, sheet, role, ignoreAttention }) => {
269
- const graph = useComputeGraph(space);
270
- return graph ? /* @__PURE__ */ React3.createElement(SheetProvider, {
271
- sheet,
272
- graph,
273
- ignoreAttention
274
- }, /* @__PURE__ */ React3.createElement(StackItem.Content, {
275
- toolbar: true,
276
- statusbar: true,
277
- ...role === "section" && {
278
- classNames: "aspect-video"
279
- }
280
- }, /* @__PURE__ */ React3.createElement(Toolbar.Root, {
281
- role
282
- }, /* @__PURE__ */ React3.createElement(Toolbar.Styles, null), /* @__PURE__ */ React3.createElement(Toolbar.Alignment, null), /* @__PURE__ */ React3.createElement(Toolbar.Separator, null), /* @__PURE__ */ React3.createElement(Toolbar.Actions, null)), /* @__PURE__ */ React3.createElement(GridSheet, null), /* @__PURE__ */ React3.createElement(FunctionEditor, null))) : null;
283
- };
284
-
285
- // packages/plugins/plugin-sheet/src/components/SheetContainer/index.ts
286
- var SheetContainer_default = SheetContainer;
287
- export {
288
- SheetContainer_default as default
289
- };
290
- //# sourceMappingURL=SheetContainer-S4NCLUYL.mjs.map