@beyondwork/docx-react-component 1.0.30 → 1.0.31

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.
@@ -34,138 +34,188 @@ const CELL_COLORS = [
34
34
  ] as const;
35
35
 
36
36
  export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
37
+ const tableContext = props.tableContext;
38
+ const tableSizeLabel = tableContext ? `${tableContext.rowCount} x ${tableContext.columnCount}` : null;
39
+ const selectionLabel = tableContext
40
+ ? tableContext.selectedCellCount > 1
41
+ ? `${tableContext.selectedCellCount} cells`
42
+ : `R${tableContext.currentCell.rowIndex + 1} C${tableContext.currentCell.columnIndex + 1}`
43
+ : null;
44
+
37
45
  return (
38
46
  <div
39
47
  data-testid="table-context-toolbar"
40
- className="flex flex-wrap items-center gap-2 rounded-xl border border-border bg-canvas px-3 py-2 shadow-sm"
48
+ className="flex max-w-[min(30rem,calc(100vw-1.5rem))] flex-wrap items-start gap-1.5 rounded-lg border border-border bg-canvas px-2.5 py-1.5 shadow-sm"
41
49
  >
42
- <span className="text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
50
+ <span className="text-[9px] font-semibold uppercase tracking-[0.12em] text-tertiary">
43
51
  Table
44
52
  </span>
53
+ {tableSizeLabel ? <ToolbarBadge>{tableSizeLabel}</ToolbarBadge> : null}
54
+ {selectionLabel ? <ToolbarBadge>{selectionLabel}</ToolbarBadge> : null}
55
+ {tableContext?.currentCell.isHeader ? <ToolbarBadge tone="accent">Header row</ToolbarBadge> : null}
56
+
57
+ <ToolbarSection label="Style">
58
+ <select
59
+ aria-label="Table style"
60
+ className="h-7 min-w-[9rem] rounded-md border border-border bg-canvas px-2 text-[11px] text-primary disabled:opacity-40"
61
+ disabled={
62
+ props.disabled ||
63
+ props.tableStyles.length === 0 ||
64
+ !props.onSetTableStyle ||
65
+ !tableContext?.operations.setTableStyle.enabled
66
+ }
67
+ onMouseDown={preserveEditorSelectionMouseDown}
68
+ onChange={(event) => props.onSetTableStyle?.(event.target.value)}
69
+ value={tableContext?.currentStyleId ?? ""}
70
+ title={tableContext?.operations.setTableStyle.reason}
71
+ >
72
+ <option value="" disabled>Table style</option>
73
+ {props.tableStyles.map((style) => (
74
+ <option key={style.styleId} value={style.styleId}>
75
+ {style.displayName}
76
+ </option>
77
+ ))}
78
+ </select>
79
+ </ToolbarSection>
80
+
81
+ <ToolbarSection label="Rows">
82
+ <ToolbarButton
83
+ ariaLabel="Add row above"
84
+ capability={tableContext?.operations.addRowBefore}
85
+ disabled={props.disabled}
86
+ onClick={props.onAddRowBefore}
87
+ >
88
+ Above
89
+ </ToolbarButton>
90
+ <ToolbarButton
91
+ ariaLabel="Add row below"
92
+ capability={tableContext?.operations.addRowAfter}
93
+ disabled={props.disabled}
94
+ onClick={props.onAddRowAfter}
95
+ >
96
+ Below
97
+ </ToolbarButton>
98
+ <ToolbarButton
99
+ ariaLabel="Delete row"
100
+ capability={tableContext?.operations.deleteRow}
101
+ disabled={props.disabled}
102
+ onClick={props.onDeleteRow}
103
+ >
104
+ Delete row
105
+ </ToolbarButton>
106
+ </ToolbarSection>
45
107
 
46
- {props.tableContext?.currentCell.isHeader ? (
47
- <span className="rounded bg-accent-soft px-2 py-1 text-[10px] font-medium text-accent">
48
- Header row
49
- </span>
50
- ) : null}
108
+ <ToolbarSection label="Columns">
109
+ <ToolbarButton
110
+ ariaLabel="Add column left"
111
+ capability={tableContext?.operations.addColumnBefore}
112
+ disabled={props.disabled}
113
+ onClick={props.onAddColumnBefore}
114
+ >
115
+ Left
116
+ </ToolbarButton>
117
+ <ToolbarButton
118
+ ariaLabel="Add column right"
119
+ capability={tableContext?.operations.addColumnAfter}
120
+ disabled={props.disabled}
121
+ onClick={props.onAddColumnAfter}
122
+ >
123
+ Right
124
+ </ToolbarButton>
125
+ <ToolbarButton
126
+ ariaLabel="Delete column"
127
+ capability={tableContext?.operations.deleteColumn}
128
+ disabled={props.disabled}
129
+ onClick={props.onDeleteColumn}
130
+ >
131
+ Delete column
132
+ </ToolbarButton>
133
+ </ToolbarSection>
51
134
 
52
- <select
53
- aria-label="Table style"
54
- className="h-8 rounded-md border border-border bg-canvas px-2 text-xs text-primary disabled:opacity-40"
55
- disabled={
56
- props.disabled ||
57
- props.tableStyles.length === 0 ||
58
- !props.onSetTableStyle ||
59
- !props.tableContext?.operations.setTableStyle.enabled
60
- }
61
- onMouseDown={preserveEditorSelectionMouseDown}
62
- onChange={(event) => props.onSetTableStyle?.(event.target.value)}
63
- value={props.tableContext?.currentStyleId ?? ""}
64
- title={props.tableContext?.operations.setTableStyle.reason}
65
- >
66
- <option value="" disabled>Table style</option>
67
- {props.tableStyles.map((style) => (
68
- <option key={style.styleId} value={style.styleId}>
69
- {style.displayName}
70
- </option>
71
- ))}
72
- </select>
135
+ <ToolbarSection label="Cells">
136
+ <ToolbarButton
137
+ ariaLabel="Merge cells"
138
+ capability={tableContext?.operations.mergeCells}
139
+ disabled={props.disabled}
140
+ onClick={props.onMergeCells}
141
+ >
142
+ Merge
143
+ </ToolbarButton>
144
+ <ToolbarButton
145
+ ariaLabel="Split cell"
146
+ capability={tableContext?.operations.splitCell}
147
+ disabled={props.disabled}
148
+ onClick={props.onSplitCell}
149
+ >
150
+ Split
151
+ </ToolbarButton>
152
+ </ToolbarSection>
73
153
 
74
- <ToolbarButton
75
- ariaLabel="Add row above"
76
- capability={props.tableContext?.operations.addRowBefore}
77
- disabled={props.disabled}
78
- onClick={props.onAddRowBefore}
79
- >
80
- Row above
81
- </ToolbarButton>
82
- <ToolbarButton
83
- ariaLabel="Add row below"
84
- capability={props.tableContext?.operations.addRowAfter}
85
- disabled={props.disabled}
86
- onClick={props.onAddRowAfter}
87
- >
88
- Row below
89
- </ToolbarButton>
90
- <ToolbarButton
91
- ariaLabel="Delete row"
92
- capability={props.tableContext?.operations.deleteRow}
93
- disabled={props.disabled}
94
- onClick={props.onDeleteRow}
95
- >
96
- Delete row
97
- </ToolbarButton>
98
- <ToolbarButton
99
- ariaLabel="Add column left"
100
- capability={props.tableContext?.operations.addColumnBefore}
101
- disabled={props.disabled}
102
- onClick={props.onAddColumnBefore}
103
- >
104
- Column left
105
- </ToolbarButton>
106
- <ToolbarButton
107
- ariaLabel="Add column right"
108
- capability={props.tableContext?.operations.addColumnAfter}
109
- disabled={props.disabled}
110
- onClick={props.onAddColumnAfter}
111
- >
112
- Column right
113
- </ToolbarButton>
114
- <ToolbarButton
115
- ariaLabel="Delete column"
116
- capability={props.tableContext?.operations.deleteColumn}
117
- disabled={props.disabled}
118
- onClick={props.onDeleteColumn}
119
- >
120
- Delete column
121
- </ToolbarButton>
122
- <ToolbarButton
123
- ariaLabel="Merge cells"
124
- capability={props.tableContext?.operations.mergeCells}
125
- disabled={props.disabled}
126
- onClick={props.onMergeCells}
127
- >
128
- Merge
129
- </ToolbarButton>
130
- <ToolbarButton
131
- ariaLabel="Split cell"
132
- capability={props.tableContext?.operations.splitCell}
133
- disabled={props.disabled}
134
- onClick={props.onSplitCell}
135
- >
136
- Split
137
- </ToolbarButton>
154
+ <ToolbarSection label="Fill">
155
+ <div className="flex items-center gap-1">
156
+ {CELL_COLORS.map((color) => (
157
+ <button
158
+ key={color}
159
+ type="button"
160
+ aria-label={`Set cell fill ${color}`}
161
+ disabled={
162
+ props.disabled ||
163
+ !props.onSetCellBackground ||
164
+ !tableContext?.operations.setCellBackground.enabled
165
+ }
166
+ onMouseDown={preserveEditorSelectionMouseDown}
167
+ onClick={() => props.onSetCellBackground?.(color)}
168
+ className="h-5 w-5 rounded border border-border disabled:opacity-40"
169
+ style={{ backgroundColor: color }}
170
+ title={tableContext?.operations.setCellBackground.reason}
171
+ />
172
+ ))}
173
+ </div>
174
+ </ToolbarSection>
138
175
 
139
- <div className="flex items-center gap-1">
140
- <span className="text-[11px] text-secondary">Fill</span>
141
- {CELL_COLORS.map((color) => (
142
- <button
143
- key={color}
144
- type="button"
145
- aria-label={`Set cell fill ${color}`}
146
- disabled={
147
- props.disabled ||
148
- !props.onSetCellBackground ||
149
- !props.tableContext?.operations.setCellBackground.enabled
150
- }
151
- onMouseDown={preserveEditorSelectionMouseDown}
152
- onClick={() => props.onSetCellBackground?.(color)}
153
- className="h-6 w-6 rounded border border-border disabled:opacity-40"
154
- style={{ backgroundColor: color }}
155
- title={props.tableContext?.operations.setCellBackground.reason}
156
- />
157
- ))}
158
- </div>
176
+ <ToolbarSection label="Table">
177
+ <ToolbarButton
178
+ ariaLabel="Delete table"
179
+ capability={tableContext?.operations.deleteTable}
180
+ danger
181
+ disabled={props.disabled}
182
+ onClick={props.onDeleteTable}
183
+ >
184
+ Delete table
185
+ </ToolbarButton>
186
+ </ToolbarSection>
187
+ </div>
188
+ );
189
+ }
190
+
191
+ function ToolbarBadge(props: {
192
+ children: React.ReactNode;
193
+ tone?: "neutral" | "accent";
194
+ }) {
195
+ return (
196
+ <span
197
+ className={[
198
+ "rounded-full px-1.5 py-0.5 text-[9px] font-medium uppercase tracking-[0.08em]",
199
+ props.tone === "accent"
200
+ ? "bg-accent-soft text-accent"
201
+ : "bg-surface text-secondary",
202
+ ].join(" ")}
203
+ >
204
+ {props.children}
205
+ </span>
206
+ );
207
+ }
159
208
 
160
- <ToolbarButton
161
- ariaLabel="Delete table"
162
- capability={props.tableContext?.operations.deleteTable}
163
- danger
164
- disabled={props.disabled}
165
- onClick={props.onDeleteTable}
166
- >
167
- Delete table
168
- </ToolbarButton>
209
+ function ToolbarSection(props: {
210
+ label: string;
211
+ children: React.ReactNode;
212
+ }) {
213
+ return (
214
+ <div className="flex flex-wrap items-center gap-1 rounded-md bg-surface/60 px-1.5 py-1 ring-1 ring-border/35">
215
+ <span className="text-[9px] font-semibold uppercase tracking-[0.08em] text-tertiary">
216
+ {props.label}
217
+ </span>
218
+ <div className="flex flex-wrap items-center gap-1">{props.children}</div>
169
219
  </div>
170
220
  );
171
221
  }
@@ -188,7 +238,7 @@ function ToolbarButton(props: {
188
238
  onMouseDown={preserveEditorSelectionMouseDown}
189
239
  onClick={props.onClick}
190
240
  title={title}
191
- className={`inline-flex h-8 items-center rounded-md px-2 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
241
+ className={`inline-flex h-7 items-center rounded-md px-2 text-[11px] font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
192
242
  props.danger
193
243
  ? "text-danger hover:bg-danger/10"
194
244
  : "text-primary hover:bg-surface"
@@ -34,7 +34,7 @@ export function createPMStateFromSnapshot(
34
34
  selection: SelectionSnapshot,
35
35
  plugins: Plugin[],
36
36
  mediaPreviews: Record<string, MediaPreviewDescriptor> = {},
37
- showUnsupportedObjectPreviews = true,
37
+ showUnsupportedObjectPreviews = false,
38
38
  ): PMStateResult {
39
39
  const doc = buildPMDoc(surface, mediaPreviews, showUnsupportedObjectPreviews);
40
40
  const positionMap = buildPositionMap(surface);
@@ -30,7 +30,7 @@ export function createSurfaceDocumentBuildKey(input: {
30
30
  : getSurfaceIdentity(input.surface),
31
31
  activeStory: input.activeStory,
32
32
  mediaPreviewKey: input.mediaPreviewKey,
33
- showUnsupportedObjectPreviews: input.showUnsupportedObjectPreviews ?? true,
33
+ showUnsupportedObjectPreviews: input.showUnsupportedObjectPreviews ?? false,
34
34
  });
35
35
  }
36
36
 
@@ -46,7 +46,6 @@ export function createSurfaceDecorationKey(input: {
46
46
  workflowMetadataSignature?: string;
47
47
  activeWorkflowWorkItemId?: string | null;
48
48
  activeWorkflowScopeIds?: readonly string[];
49
- suggestionsEnabled?: boolean;
50
49
  }): string {
51
50
  return JSON.stringify({
52
51
  markupDisplay: input.markupDisplay,
@@ -60,6 +59,5 @@ export function createSurfaceDecorationKey(input: {
60
59
  workflowMetadataSignature: input.workflowMetadataSignature ?? null,
61
60
  activeWorkflowWorkItemId: input.activeWorkflowWorkItemId ?? null,
62
61
  activeWorkflowScopeIds: input.activeWorkflowScopeIds ?? [],
63
- suggestionsEnabled: input.suggestionsEnabled ?? false,
64
62
  });
65
63
  }
@@ -20,6 +20,7 @@ export type ReviewRailTab = "comments" | "changes";
20
20
 
21
21
  export interface TwReviewRailProps {
22
22
  activeTab: ReviewRailTab;
23
+ variant?: "docked" | "drawer";
23
24
  currentUserId?: string;
24
25
  comments: CommentSidebarSnapshot;
25
26
  trackedChanges: TrackedChangesSnapshot;
@@ -46,10 +47,17 @@ const focusRingClass =
46
47
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
47
48
 
48
49
  export function TwReviewRail(props: TwReviewRailProps) {
50
+ const variant = props.variant ?? "docked";
49
51
  return (
50
52
  <aside
51
53
  aria-label="Review rail"
52
- className="flex w-[336px] shrink-0 flex-col border-l border-border/60 bg-[var(--color-sidebar-tint)]"
54
+ data-wre-drawer={variant === "drawer" ? "true" : "false"}
55
+ className={[
56
+ "flex flex-col border-l border-border/60 bg-[var(--color-sidebar-tint)]",
57
+ variant === "drawer"
58
+ ? "h-full w-[min(336px,calc(100vw-1rem))] max-w-full shrink-0 shadow-[0_18px_40px_-22px_var(--color-shadow-strong)]"
59
+ : "w-[336px] shrink-0",
60
+ ].join(" ")}
53
61
  >
54
62
  <Tabs.Root
55
63
  value={props.activeTab}
@@ -0,0 +1,47 @@
1
+ import type { WordReviewEditorChromePreset } from "../../api/public-types";
2
+
3
+ export interface ToolbarLayoutModelInput {
4
+ compactMode: boolean;
5
+ preset: WordReviewEditorChromePreset;
6
+ hasActiveListContext: boolean;
7
+ }
8
+
9
+ export interface ToolbarLayoutModel {
10
+ showStyleSelectorsInRow: boolean;
11
+ showListActionsInRow: boolean;
12
+ showSpacingActionsInRow: boolean;
13
+ showInsertActionsInRow: boolean;
14
+ showUpdateActionsInRow: boolean;
15
+ showCompactOverflow: boolean;
16
+ showListContinuationInRow: boolean;
17
+ }
18
+
19
+ export function resolveToolbarLayoutModel(
20
+ input: ToolbarLayoutModelInput,
21
+ ): ToolbarLayoutModel {
22
+ if (!input.compactMode) {
23
+ const showListActions = input.preset === "simple" || input.preset === "advanced";
24
+ return {
25
+ showStyleSelectorsInRow: input.preset === "advanced",
26
+ showListActionsInRow: showListActions,
27
+ showSpacingActionsInRow: true,
28
+ showInsertActionsInRow: input.preset === "simple" || input.preset === "advanced",
29
+ showUpdateActionsInRow: input.preset === "advanced",
30
+ showCompactOverflow: false,
31
+ showListContinuationInRow: showListActions && input.hasActiveListContext,
32
+ };
33
+ }
34
+
35
+ const compactOverflowHasSecondaryActions =
36
+ input.preset === "simple" || input.preset === "advanced";
37
+
38
+ return {
39
+ showStyleSelectorsInRow: false,
40
+ showListActionsInRow: false,
41
+ showSpacingActionsInRow: false,
42
+ showInsertActionsInRow: false,
43
+ showUpdateActionsInRow: false,
44
+ showCompactOverflow: compactOverflowHasSecondaryActions,
45
+ showListContinuationInRow: false,
46
+ };
47
+ }