@beyondwork/docx-react-component 1.0.89 → 1.0.90

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beyondwork/docx-react-component",
3
3
  "publisher": "beyondwork",
4
- "version": "1.0.89",
4
+ "version": "1.0.90",
5
5
  "description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
6
6
  "type": "module",
7
7
  "sideEffects": [
@@ -9,8 +9,8 @@
9
9
  * Review-role actions here collapse what used to live in
10
10
  * `TwReviewQueueBar` as a second strip — the review prev/next, counts,
11
11
  * active-item label, accept/reject, markup-mode, and batch operations.
12
- * Editor-role actions surface the scope posture menu. Workflow-role
13
- * actions surface work-item traversal + claim/skip/complete.
12
+ * Workflow-role actions surface the scope posture menu plus work-item
13
+ * traversal + claim/skip/complete.
14
14
  */
15
15
 
16
16
  import React, { useState } from "react";
@@ -18,14 +18,12 @@ import * as Popover from "@radix-ui/react-popover";
18
18
  import * as Toggle from "@radix-ui/react-toggle";
19
19
  import * as Tooltip from "@radix-ui/react-tooltip";
20
20
  import {
21
- BookmarkCheck,
22
21
  Check,
23
22
  CheckCheck,
24
23
  ChevronLeft,
25
24
  ChevronRight,
26
25
  CircleOff,
27
26
  FileDiff,
28
- Flag,
29
27
  Hand,
30
28
  MessageSquare,
31
29
  MessageSquareDot,
@@ -48,6 +46,7 @@ import type { ScopedChromePolicy } from "../../ui/headless/scoped-chrome-policy"
48
46
  import type { ToolbarChromeItemId } from "../../ui/headless/chrome-registry";
49
47
  import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
50
48
  import { ROLE_ACTION_SETS } from "../../ui/headless/role-action-sets";
49
+ import { TwDisplayModeSelector } from "../chrome/tw-display-mode-selector";
51
50
  import { TwScopePostureMenu } from "./tw-scope-posture-menu";
52
51
 
53
52
  /**
@@ -103,7 +102,7 @@ export interface TwRoleActionRegionProps {
103
102
  onReviewSidebarTrackedChanges?: () => void;
104
103
  onReviewSidebarComments?: () => void;
105
104
 
106
- // Workflow + review role: scope posture menu
105
+ // Workflow role: assign authorable scope posture.
107
106
  onMarkScopePosture?: (posture: ScopeRailPosture) => void;
108
107
 
109
108
  // Review role
@@ -366,10 +365,11 @@ function RoleActionButton(arg: RoleActionButtonProps): React.JSX.Element | null
366
365
  );
367
366
  case "review-markup-mode":
368
367
  return (
369
- <MarkupModeSelect
370
- mode={props.markupDisplay ?? "simple"}
368
+ <TwDisplayModeSelector
369
+ value={props.markupDisplay ?? "simple"}
371
370
  onChange={(mode) => props.onReviewMarkupMode?.(mode)}
372
371
  disabled={!props.onReviewMarkupMode}
372
+ data-testid="role-review-markup-mode"
373
373
  />
374
374
  );
375
375
  case "workflow-prev":
@@ -693,30 +693,4 @@ function ReviewActiveLabel({
693
693
  );
694
694
  }
695
695
 
696
- function MarkupModeSelect(arg: {
697
- mode: MarkupDisplayMode;
698
- onChange: (mode: MarkupDisplayMode) => void;
699
- disabled?: boolean;
700
- }): React.JSX.Element {
701
- const Icon = arg.mode === "clean" ? BookmarkCheck : arg.mode === "all" ? Flag : Rows3;
702
- return (
703
- <button
704
- type="button"
705
- aria-label={`Markup display: ${arg.mode}`}
706
- disabled={arg.disabled}
707
- onMouseDown={preserveEditorSelectionMouseDown}
708
- onClick={() => {
709
- const next: MarkupDisplayMode =
710
- arg.mode === "all" ? "clean" : arg.mode === "clean" ? "simple" : "all";
711
- arg.onChange(next);
712
- }}
713
- data-testid="role-review-markup-mode"
714
- className="inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40"
715
- >
716
- <Icon className="h-3.5 w-3.5" />
717
- <span className="capitalize">{arg.mode}</span>
718
- </button>
719
- );
720
- }
721
-
722
696
  export default TwRoleActionRegion;
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Scope posture menu — replaces the old "Mark section" button with a
3
- * topnav dropdown listing the seven `ScopeRailPosture` values so
4
- * editors can mark regions with an explicit workflow mode instead of a
5
- * single "marked" flag.
3
+ * topnav dropdown listing the authorable `ScopeRailPosture` values so
4
+ * workflow operators can mark regions with an explicit workflow mode
5
+ * instead of a single "marked" flag.
6
6
  *
7
7
  * Per runtime-rendering-and-chrome-phase.md §6.4, the menu lives inline
8
- * in the editor role's primary action region (not in the review queue
8
+ * in the workflow role's primary action region (not in the review queue
9
9
  * strip). Postures align 1:1 with the rail vocabulary so the rail
10
- * updates visually as soon as the user picks one.
10
+ * updates visually as soon as the user picks one. Runtime-only postures
11
+ * like preserve-only and blocked-import still render in overlays, but
12
+ * they are not choices users can assign from this product menu.
11
13
  */
12
14
 
13
15
  import React, { useState } from "react";
@@ -17,7 +19,6 @@ import {
17
19
  ChevronDown,
18
20
  Eye,
19
21
  Flag,
20
- Lock,
21
22
  MessageCircle,
22
23
  Pencil,
23
24
  Sparkles,
@@ -35,12 +36,12 @@ export interface TwScopePostureMenuProps {
35
36
  "data-testid"?: string;
36
37
  }
37
38
 
38
- interface PostureEntry {
39
+ export interface ScopePostureMenuEntry {
39
40
  posture: ScopeRailPosture;
40
41
  label: string;
41
42
  hint: string;
42
43
  icon: React.ComponentType<{ className?: string }>;
43
- tone: "accent" | "warning" | "comment" | "secondary" | "danger";
44
+ tone: "accent" | "warning" | "comment" | "secondary";
44
45
  }
45
46
 
46
47
  /**
@@ -49,7 +50,7 @@ interface PostureEntry {
49
50
  * glyphs via the `data-icon` attribute). Extract both into a single
50
51
  * source of truth in a follow-up.
51
52
  */
52
- const POSTURE_ENTRIES: readonly PostureEntry[] = [
53
+ export const SCOPE_POSTURE_MENU_ENTRIES: readonly ScopePostureMenuEntry[] = [
53
54
  {
54
55
  posture: "edit",
55
56
  label: "Edit scope",
@@ -85,22 +86,11 @@ const POSTURE_ENTRIES: readonly PostureEntry[] = [
85
86
  icon: Flag,
86
87
  tone: "warning",
87
88
  },
88
- {
89
- posture: "preserve-only",
90
- label: "Preserve only",
91
- hint: "Blocked — export-preserving only",
92
- icon: Lock,
93
- tone: "danger",
94
- },
95
- {
96
- posture: "blocked-import",
97
- label: "Blocked import",
98
- hint: "Blocked — imported region is locked",
99
- icon: Lock,
100
- tone: "danger",
101
- },
102
89
  ];
103
90
 
91
+ export const SCOPE_POSTURE_MENU_POSTURES: readonly ScopeRailPosture[] =
92
+ SCOPE_POSTURE_MENU_ENTRIES.map((entry) => entry.posture);
93
+
104
94
  export function TwScopePostureMenu(props: TwScopePostureMenuProps): React.JSX.Element {
105
95
  const [open, setOpen] = useState(false);
106
96
 
@@ -131,7 +121,7 @@ export function TwScopePostureMenu(props: TwScopePostureMenuProps): React.JSX.El
131
121
  <div className="mb-1 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
132
122
  Mark section with posture
133
123
  </div>
134
- {POSTURE_ENTRIES.map((entry) => (
124
+ {SCOPE_POSTURE_MENU_ENTRIES.map((entry) => (
135
125
  <Popover.Close key={entry.posture} asChild>
136
126
  <button
137
127
  type="button"
@@ -163,7 +153,7 @@ export function TwScopePostureMenu(props: TwScopePostureMenuProps): React.JSX.El
163
153
  );
164
154
  }
165
155
 
166
- function toneClass(tone: PostureEntry["tone"]): string {
156
+ function toneClass(tone: ScopePostureMenuEntry["tone"]): string {
167
157
  switch (tone) {
168
158
  case "accent":
169
159
  return "text-accent";
@@ -171,8 +161,6 @@ function toneClass(tone: PostureEntry["tone"]): string {
171
161
  return "text-warning";
172
162
  case "comment":
173
163
  return "text-comment";
174
- case "danger":
175
- return "text-danger";
176
164
  case "secondary":
177
165
  default:
178
166
  return "text-secondary";
@@ -69,6 +69,7 @@ import {
69
69
  type ScopedChromePolicy,
70
70
  } from "../../ui/headless/scoped-chrome-policy";
71
71
  import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
72
+ import { TwDisplayModeSelector } from "../chrome/tw-display-mode-selector";
72
73
  import { type MarkupDisplayMode } from "./tw-role-action-region";
73
74
  import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
74
75
 
@@ -123,6 +124,8 @@ export interface TwToolbarProps {
123
124
  onToggleSidebar?: () => void;
124
125
  onZoomChange?: (level: ZoomLevel) => void;
125
126
  onShowTrackedChangesChange: (show: boolean) => void;
127
+ /** Top-toolbar fallback for changing redline/comment display when review context band is not active. */
128
+ onMarkupDisplayChange?: (mode: MarkupDisplayMode) => void;
126
129
  onRestartNumbering?: () => void;
127
130
  onContinueNumbering?: () => void;
128
131
  onUpdateFields?: () => void;
@@ -262,6 +265,10 @@ export function TwToolbar(props: TwToolbarProps) {
262
265
  const showTrackedChangesToggle =
263
266
  isToolbarChromeItemVisible(scopedChromePolicy, "tracked-changes-toggle") &&
264
267
  !isChromeItemOwnedByRoleRegion("tracked-changes-toggle", props.role);
268
+ const showMarkupDisplaySelector =
269
+ props.markupDisplay !== undefined &&
270
+ props.onMarkupDisplayChange !== undefined &&
271
+ !isChromeItemOwnedByRoleRegion("review-markup-mode", props.role);
265
272
  const showRightClusterComment =
266
273
  isToolbarChromeItemVisible(scopedChromePolicy, "comment") &&
267
274
  !isChromeItemOwnedByRoleRegion("comment", props.role);
@@ -695,6 +702,17 @@ export function TwToolbar(props: TwToolbarProps) {
695
702
  </>
696
703
  ) : null}
697
704
 
705
+ {showMarkupDisplaySelector ? (
706
+ <>
707
+ <TwDisplayModeSelector
708
+ value={props.markupDisplay!}
709
+ onChange={(mode) => props.onMarkupDisplayChange?.(mode)}
710
+ data-testid="toolbar-display-mode-selector"
711
+ />
712
+ <div className="mx-1 h-4 w-px bg-border" />
713
+ </>
714
+ ) : null}
715
+
698
716
  {/* View mode toggle group: Canvas (clean, flowing) / Page (layout-sensitive) */}
699
717
  {isToolbarChromeItemVisible(scopedChromePolicy, "workspace-mode") ? (
700
718
  <ToggleGroup.Root
@@ -878,6 +878,10 @@ export function TwReviewWorkspace(inputProps: TwReviewWorkspaceProps) {
878
878
  dismissSelectionToolbar();
879
879
  props.onShowTrackedChangesChange(show);
880
880
  }}
881
+ onMarkupDisplayChange={(mode) => {
882
+ dismissSelectionToolbar();
883
+ props.onReviewMarkupModeChange?.(mode);
884
+ }}
881
885
  role={viewState.editorRole}
882
886
  reviewQueue={props.reviewQueue}
883
887
  markupDisplay={markupDisplay}