@beyondwork/docx-react-component 1.0.106 → 1.0.108
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 +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +2 -1
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +38 -0
- package/src/api/v3/ai/attach.ts +7 -2
- package/src/api/v3/ai/replacement.ts +101 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +1 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +149 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +13 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +22 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +118 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +240 -97
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +591 -20
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +181 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +21 -4
- package/src/runtime/layout/paginated-layout-engine.ts +139 -15
- package/src/runtime/layout/project-block-fragments.ts +265 -7
- package/src/runtime/layout/public-facet.ts +78 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +3 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +168 -10
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +0 -1
- package/src/ui/ui-controller-factory.ts +7 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { createPortal } from "react-dom";
|
|
3
2
|
|
|
4
3
|
import * as Popover from "@radix-ui/react-popover";
|
|
5
4
|
import * as Select from "@radix-ui/react-select";
|
|
@@ -71,7 +70,10 @@ import {
|
|
|
71
70
|
} from "../../ui/headless/scoped-chrome-policy";
|
|
72
71
|
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
73
72
|
import { TwDisplayModeSelector } from "../chrome/tw-display-mode-selector";
|
|
74
|
-
import {
|
|
73
|
+
import {
|
|
74
|
+
TwRoleActionRegion,
|
|
75
|
+
type MarkupDisplayMode,
|
|
76
|
+
} from "./tw-role-action-region";
|
|
75
77
|
import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
|
|
76
78
|
|
|
77
79
|
export interface TwToolbarProps {
|
|
@@ -86,9 +88,9 @@ export interface TwToolbarProps {
|
|
|
86
88
|
compactMode?: boolean;
|
|
87
89
|
/**
|
|
88
90
|
* True when the runtime has an active editable text/paragraph target
|
|
89
|
-
* (focused caret or range selection).
|
|
90
|
-
*
|
|
91
|
-
*
|
|
91
|
+
* (focused caret or range selection). Selection-scoped controls combine
|
|
92
|
+
* this with edit capability so top chrome does not advertise document
|
|
93
|
+
* commands when focus is on chrome or an object frame.
|
|
92
94
|
*/
|
|
93
95
|
hasEditableSelectionTarget?: boolean;
|
|
94
96
|
workspaceMode: WorkspaceMode;
|
|
@@ -132,7 +134,7 @@ export interface TwToolbarProps {
|
|
|
132
134
|
onToggleSidebar?: () => void;
|
|
133
135
|
onZoomChange?: (level: ZoomLevel) => void;
|
|
134
136
|
onShowTrackedChangesChange: (show: boolean) => void;
|
|
135
|
-
/** Top-toolbar
|
|
137
|
+
/** Top-toolbar control for changing redline/comment display. */
|
|
136
138
|
onMarkupDisplayChange?: (mode: MarkupDisplayMode) => void;
|
|
137
139
|
onRestartNumbering?: () => void;
|
|
138
140
|
onContinueNumbering?: () => void;
|
|
@@ -141,13 +143,13 @@ export interface TwToolbarProps {
|
|
|
141
143
|
|
|
142
144
|
// ───── R1: role-scoped inline action region (spec §6.4) ──────────────
|
|
143
145
|
/**
|
|
144
|
-
* Active editor role.
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* retained here only so the formatting cluster can defer items owned
|
|
148
|
-
* by the role-region via `isChromeItemOwnedByRoleRegion`.
|
|
146
|
+
* Active editor role. Shipping product chrome keeps role switching in
|
|
147
|
+
* the main toolbar's Edit / Review / Workflow toggle; extra
|
|
148
|
+
* context-band rows are not mounted by default.
|
|
149
149
|
*/
|
|
150
150
|
role?: EditorRole;
|
|
151
|
+
/** Called when the main toolbar role toggle changes product mode. */
|
|
152
|
+
onEditorRoleChange?: (role: EditorRole) => void;
|
|
151
153
|
/** Review-queue snapshot — consumed only by the view-cluster change-counter chip. */
|
|
152
154
|
reviewQueue?: ReviewQueueSnapshot;
|
|
153
155
|
/** Markup display mode for the review role. */
|
|
@@ -159,8 +161,7 @@ export interface TwToolbarProps {
|
|
|
159
161
|
// Workflow + review role: scope posture
|
|
160
162
|
onMarkScopePosture?: (posture: ScopeRailPosture) => void;
|
|
161
163
|
// Review role — inline prev/next + accept/reject stay on the toolbar for
|
|
162
|
-
// the view-cluster mount (queue counter chip)
|
|
163
|
-
// role-region to <TwContextBand> and are threaded there, not here.
|
|
164
|
+
// the view-cluster mount (queue counter chip).
|
|
164
165
|
onReviewPrev?: () => void;
|
|
165
166
|
onReviewNext?: () => void;
|
|
166
167
|
onReviewAccept?: () => void;
|
|
@@ -228,6 +229,31 @@ export function getSupportedZoomPresets(): ReadonlyArray<number> {
|
|
|
228
229
|
const focusRingClass =
|
|
229
230
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
|
|
230
231
|
|
|
232
|
+
function ToolbarTextButton(props: {
|
|
233
|
+
ariaLabel: string;
|
|
234
|
+
children: React.ReactNode;
|
|
235
|
+
disabled?: boolean;
|
|
236
|
+
disabledReason?: string;
|
|
237
|
+
onClick?: () => void;
|
|
238
|
+
}): React.JSX.Element {
|
|
239
|
+
const disabledReason =
|
|
240
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined;
|
|
241
|
+
return (
|
|
242
|
+
<button
|
|
243
|
+
type="button"
|
|
244
|
+
aria-label={props.ariaLabel}
|
|
245
|
+
data-disabled-reason={disabledReason}
|
|
246
|
+
disabled={props.disabled}
|
|
247
|
+
title={disabledReason ? `Not available: ${disabledReason}` : undefined}
|
|
248
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
249
|
+
onClick={props.onClick}
|
|
250
|
+
className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
251
|
+
>
|
|
252
|
+
{props.children}
|
|
253
|
+
</button>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
231
257
|
const FONT_FAMILIES = ["Arial", "Times New Roman", "Calibri", "Cambria", "Georgia", "Verdana"];
|
|
232
258
|
const FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36];
|
|
233
259
|
const TEXT_COLORS = ["#1f1f1f", "#5c5852", "#1660a8", "#50684d", "#9b4f49", "#7b5f32"];
|
|
@@ -238,80 +264,19 @@ const HIGHLIGHT_COLORS = [
|
|
|
238
264
|
{ value: "#e6e0d4", label: "Stone" },
|
|
239
265
|
{ value: null, label: "None" },
|
|
240
266
|
] as const;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
open: props.open,
|
|
255
|
-
});
|
|
256
|
-
const body = props.anchorRef.current?.ownerDocument?.body;
|
|
257
|
-
if (!props.open || !body) {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
return createPortal(
|
|
261
|
-
<div className={props.className} style={style}>
|
|
262
|
-
{props.children}
|
|
263
|
-
</div>,
|
|
264
|
-
body,
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function useToolbarPortalPosition(input: {
|
|
269
|
-
align: "start" | "end";
|
|
270
|
-
anchorRef: React.RefObject<HTMLButtonElement | null>;
|
|
271
|
-
menuWidthPx: number;
|
|
272
|
-
open: boolean;
|
|
273
|
-
}): React.CSSProperties {
|
|
274
|
-
const [style, setStyle] = React.useState<React.CSSProperties>({
|
|
275
|
-
left: 8,
|
|
276
|
-
position: "fixed",
|
|
277
|
-
top: 8,
|
|
278
|
-
zIndex: 50,
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
React.useLayoutEffect(() => {
|
|
282
|
-
if (!input.open) return;
|
|
283
|
-
const anchor = input.anchorRef.current;
|
|
284
|
-
const ownerWindow = anchor?.ownerDocument?.defaultView;
|
|
285
|
-
if (!anchor || !ownerWindow) return;
|
|
286
|
-
|
|
287
|
-
const update = () => {
|
|
288
|
-
const rect = anchor.getBoundingClientRect();
|
|
289
|
-
const viewportWidth = ownerWindow.innerWidth || input.menuWidthPx + 16;
|
|
290
|
-
const rawLeft =
|
|
291
|
-
input.align === "end" ? rect.right - input.menuWidthPx : rect.left;
|
|
292
|
-
const left = Math.min(
|
|
293
|
-
Math.max(8, rawLeft),
|
|
294
|
-
Math.max(8, viewportWidth - input.menuWidthPx - 8),
|
|
295
|
-
);
|
|
296
|
-
setStyle({
|
|
297
|
-
left,
|
|
298
|
-
position: "fixed",
|
|
299
|
-
top: Math.max(8, rect.bottom + 8),
|
|
300
|
-
zIndex: 50,
|
|
301
|
-
});
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
update();
|
|
305
|
-
ownerWindow.addEventListener("resize", update);
|
|
306
|
-
ownerWindow.addEventListener("scroll", update, true);
|
|
307
|
-
return () => {
|
|
308
|
-
ownerWindow.removeEventListener("resize", update);
|
|
309
|
-
ownerWindow.removeEventListener("scroll", update, true);
|
|
310
|
-
};
|
|
311
|
-
}, [input.align, input.anchorRef, input.menuWidthPx, input.open]);
|
|
312
|
-
|
|
313
|
-
return style;
|
|
314
|
-
}
|
|
267
|
+
const ROLE_TOGGLE_ITEMS: ReadonlyArray<{
|
|
268
|
+
role: EditorRole;
|
|
269
|
+
label: string;
|
|
270
|
+
testId: string;
|
|
271
|
+
}> = [
|
|
272
|
+
{ role: "editor", label: "Edit", testId: "toolbar-role-toggle-edit" },
|
|
273
|
+
{ role: "review", label: "Review", testId: "toolbar-role-toggle-review" },
|
|
274
|
+
{
|
|
275
|
+
role: "workflow",
|
|
276
|
+
label: "Workflow",
|
|
277
|
+
testId: "toolbar-role-toggle-workflow",
|
|
278
|
+
},
|
|
279
|
+
];
|
|
315
280
|
|
|
316
281
|
export function TwToolbar(props: TwToolbarProps) {
|
|
317
282
|
const caps = props.capabilities;
|
|
@@ -330,9 +295,11 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
330
295
|
props.interactionPolicy && props.interactionPolicy.mode !== "edit"
|
|
331
296
|
? `Not available in ${props.interactionPolicy.mode} mode.`
|
|
332
297
|
: "Editing is not available in this document.";
|
|
333
|
-
const editDisabledReason =
|
|
298
|
+
const editDisabledReason =
|
|
299
|
+
selectionTargetDisabledReason ?? (!baseCanFormatText ? editModeDisabledReason : undefined);
|
|
334
300
|
const insertDisabledReason =
|
|
335
|
-
selectionTargetDisabledReason ??
|
|
301
|
+
selectionTargetDisabledReason ??
|
|
302
|
+
(!baseCanInsertStructural ? editModeDisabledReason : undefined);
|
|
336
303
|
const canEdit = baseCanFormatText && hasEditableSelectionTarget;
|
|
337
304
|
const canInsertStructural = baseCanInsertStructural && hasEditableSelectionTarget;
|
|
338
305
|
const canAddComment = props.interactionPolicy?.canAddComment ?? (caps ? caps.canAddComment : false);
|
|
@@ -367,10 +334,20 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
367
334
|
const showRightClusterComment =
|
|
368
335
|
isToolbarChromeItemVisible(scopedChromePolicy, "comment") &&
|
|
369
336
|
!isChromeItemOwnedByRoleRegion("comment", props.role);
|
|
337
|
+
const healthIssueCount = caps?.healthIssueCount ?? 0;
|
|
338
|
+
const hasHealthIssue =
|
|
339
|
+
healthIssueCount > 0 ||
|
|
340
|
+
props.compatibility?.blockExport === true ||
|
|
341
|
+
(props.compatibility?.featureEntries.some(
|
|
342
|
+
(entry) => entry.featureClass === "unsupported-fatal",
|
|
343
|
+
) ??
|
|
344
|
+
false) ||
|
|
345
|
+
(props.warnings?.length ?? 0) > 0 ||
|
|
346
|
+
(props.blockedReasons?.length ?? 0) > 0;
|
|
370
347
|
const showHealth =
|
|
371
348
|
showDiagnosticsChrome &&
|
|
372
349
|
isToolbarChromeItemVisible(scopedChromePolicy, "health") &&
|
|
373
|
-
|
|
350
|
+
hasHealthIssue;
|
|
374
351
|
const showListActions = isToolbarChromeItemVisible(scopedChromePolicy, "list-actions");
|
|
375
352
|
const showUpdateActions = isToolbarChromeItemVisible(scopedChromePolicy, "update-actions");
|
|
376
353
|
const showSidebarToggle =
|
|
@@ -429,6 +406,16 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
429
406
|
>
|
|
430
407
|
{/* Left cluster: undo/redo + formatting */}
|
|
431
408
|
<div className={`flex min-w-0 flex-1 items-center gap-0.5 ${isCompact ? "flex-wrap" : ""}`}>
|
|
409
|
+
{props.role ? (
|
|
410
|
+
<>
|
|
411
|
+
<ToolbarRoleToggle
|
|
412
|
+
role={props.role}
|
|
413
|
+
disabled={!props.onEditorRoleChange}
|
|
414
|
+
onRoleChange={props.onEditorRoleChange}
|
|
415
|
+
/>
|
|
416
|
+
<div className="mx-1 h-4 w-px bg-border" />
|
|
417
|
+
</>
|
|
418
|
+
) : null}
|
|
432
419
|
<TwToolbarIconButton
|
|
433
420
|
icon={Undo2}
|
|
434
421
|
label="Undo"
|
|
@@ -531,7 +518,6 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
531
518
|
disabled={!canEdit || !props.onSetTextColor}
|
|
532
519
|
disabledReason={editDisabledReason}
|
|
533
520
|
icon={<Baseline className="h-3.5 w-3.5" />}
|
|
534
|
-
activeValue={props.formattingState?.textColor?.toLowerCase()}
|
|
535
521
|
onSelect={(value) => {
|
|
536
522
|
if (value) {
|
|
537
523
|
props.onSetTextColor?.(value);
|
|
@@ -545,7 +531,6 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
545
531
|
disabled={!canEdit || !props.onSetHighlightColor}
|
|
546
532
|
disabledReason={editDisabledReason}
|
|
547
533
|
icon={<Highlighter className="h-3.5 w-3.5" />}
|
|
548
|
-
activeValue={props.formattingState?.highlightColor?.toLowerCase() ?? null}
|
|
549
534
|
onSelect={(value) => props.onSetHighlightColor?.(value)}
|
|
550
535
|
title="Highlight color"
|
|
551
536
|
/>
|
|
@@ -604,30 +589,22 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
604
589
|
) : null}
|
|
605
590
|
{showListContinuationInRow ? (
|
|
606
591
|
<>
|
|
607
|
-
<
|
|
608
|
-
|
|
609
|
-
aria-label="Restart numbering"
|
|
592
|
+
<ToolbarTextButton
|
|
593
|
+
ariaLabel="Restart numbering"
|
|
610
594
|
disabled={!canEdit || !props.onRestartNumbering}
|
|
611
|
-
|
|
612
|
-
data-disabled-reason={!canEdit && editDisabledReason ? editDisabledReason : undefined}
|
|
613
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
595
|
+
disabledReason={editDisabledReason}
|
|
614
596
|
onClick={props.onRestartNumbering}
|
|
615
|
-
className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
616
597
|
>
|
|
617
598
|
Restart
|
|
618
|
-
</
|
|
619
|
-
<
|
|
620
|
-
|
|
621
|
-
aria-label="Continue numbering"
|
|
599
|
+
</ToolbarTextButton>
|
|
600
|
+
<ToolbarTextButton
|
|
601
|
+
ariaLabel="Continue numbering"
|
|
622
602
|
disabled={!canEdit || !props.onContinueNumbering}
|
|
623
|
-
|
|
624
|
-
data-disabled-reason={!canEdit && editDisabledReason ? editDisabledReason : undefined}
|
|
625
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
603
|
+
disabledReason={editDisabledReason}
|
|
626
604
|
onClick={props.onContinueNumbering}
|
|
627
|
-
className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
628
605
|
>
|
|
629
606
|
Continue
|
|
630
|
-
</
|
|
607
|
+
</ToolbarTextButton>
|
|
631
608
|
</>
|
|
632
609
|
) : null}
|
|
633
610
|
{showInsertMenu && showInsertActionsInRow ? (
|
|
@@ -643,30 +620,22 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
643
620
|
) : null}
|
|
644
621
|
{showUpdateActions && showUpdateActionsInRow ? (
|
|
645
622
|
<>
|
|
646
|
-
<
|
|
647
|
-
|
|
648
|
-
aria-label="Refresh fields"
|
|
623
|
+
<ToolbarTextButton
|
|
624
|
+
ariaLabel="Refresh fields"
|
|
649
625
|
disabled={!canEdit || !props.onUpdateFields}
|
|
650
|
-
|
|
651
|
-
data-disabled-reason={!canEdit && editDisabledReason ? editDisabledReason : undefined}
|
|
652
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
626
|
+
disabledReason={editDisabledReason}
|
|
653
627
|
onClick={props.onUpdateFields}
|
|
654
|
-
className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
655
628
|
>
|
|
656
629
|
Fields
|
|
657
|
-
</
|
|
658
|
-
<
|
|
659
|
-
|
|
660
|
-
aria-label="Refresh table of contents"
|
|
630
|
+
</ToolbarTextButton>
|
|
631
|
+
<ToolbarTextButton
|
|
632
|
+
ariaLabel="Refresh table of contents"
|
|
661
633
|
disabled={!canEdit || !props.onUpdateTableOfContents}
|
|
662
|
-
|
|
663
|
-
data-disabled-reason={!canEdit && editDisabledReason ? editDisabledReason : undefined}
|
|
664
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
634
|
+
disabledReason={editDisabledReason}
|
|
665
635
|
onClick={props.onUpdateTableOfContents}
|
|
666
|
-
className={`inline-flex h-6 items-center rounded-md px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
667
636
|
>
|
|
668
637
|
TOC
|
|
669
|
-
</
|
|
638
|
+
</ToolbarTextButton>
|
|
670
639
|
</>
|
|
671
640
|
) : null}
|
|
672
641
|
{showCompactOverflow ? (
|
|
@@ -721,16 +690,28 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
721
690
|
) : null}
|
|
722
691
|
</div>
|
|
723
692
|
|
|
724
|
-
{
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
693
|
+
{props.role ? (
|
|
694
|
+
<TwRoleActionRegion
|
|
695
|
+
role={props.role}
|
|
696
|
+
policy={scopedChromePolicy}
|
|
697
|
+
compactMode={isCompact}
|
|
698
|
+
reviewQueue={props.reviewQueue}
|
|
699
|
+
markupDisplay={props.markupDisplay}
|
|
700
|
+
canAddComment={canAddComment}
|
|
701
|
+
showTrackedChanges={props.showTrackedChanges}
|
|
702
|
+
onAddComment={props.onAddComment}
|
|
703
|
+
onShowTrackedChangesChange={props.onShowTrackedChangesChange}
|
|
704
|
+
capabilities={caps}
|
|
705
|
+
onReviewSidebarTrackedChanges={props.onReviewSidebarTrackedChanges}
|
|
706
|
+
onReviewSidebarComments={props.onReviewSidebarComments}
|
|
707
|
+
onMarkScopePosture={props.onMarkScopePosture}
|
|
708
|
+
onReviewPrev={props.onReviewPrev}
|
|
709
|
+
onReviewNext={props.onReviewNext}
|
|
710
|
+
onReviewAccept={props.onReviewAccept}
|
|
711
|
+
onReviewReject={props.onReviewReject}
|
|
712
|
+
onReviewMarkupMode={props.onMarkupDisplayChange}
|
|
713
|
+
/>
|
|
714
|
+
) : null}
|
|
734
715
|
|
|
735
716
|
{/* Right cluster: comment, track changes, markup, view, export */}
|
|
736
717
|
<div className={`flex items-center gap-0.5 ${isCompact ? "ml-auto flex-wrap justify-end" : ""}`}>
|
|
@@ -1002,17 +983,17 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
1002
983
|
className={`relative inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface hover:text-primary outline-none ${focusRingClass}`}
|
|
1003
984
|
>
|
|
1004
985
|
<AlertCircle className="h-3.5 w-3.5" />
|
|
1005
|
-
{
|
|
986
|
+
{healthIssueCount > 0 ? (
|
|
1006
987
|
<span className="absolute -top-0.5 -right-0.5 flex h-3 min-w-[12px] items-center justify-center rounded-full bg-tertiary text-[8px] font-medium text-white">
|
|
1007
|
-
{
|
|
988
|
+
{healthIssueCount}
|
|
1008
989
|
</span>
|
|
1009
990
|
) : null}
|
|
1010
991
|
</button>
|
|
1011
992
|
</Tooltip.Trigger>
|
|
1012
993
|
<Tooltip.Portal>
|
|
1013
994
|
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
1014
|
-
{
|
|
1015
|
-
? `Document health — ${
|
|
995
|
+
{healthIssueCount > 0
|
|
996
|
+
? `Document health — ${healthIssueCount} issue${healthIssueCount !== 1 ? "s" : ""} (opens rail)`
|
|
1016
997
|
: "Document health — no issues"}
|
|
1017
998
|
</Tooltip.Content>
|
|
1018
999
|
</Tooltip.Portal>
|
|
@@ -1036,6 +1017,49 @@ export function TwToolbar(props: TwToolbarProps) {
|
|
|
1036
1017
|
);
|
|
1037
1018
|
}
|
|
1038
1019
|
|
|
1020
|
+
function ToolbarRoleToggle(props: {
|
|
1021
|
+
role: EditorRole;
|
|
1022
|
+
disabled?: boolean;
|
|
1023
|
+
onRoleChange?: (role: EditorRole) => void;
|
|
1024
|
+
}): React.JSX.Element {
|
|
1025
|
+
return (
|
|
1026
|
+
<ToggleGroup.Root
|
|
1027
|
+
type="single"
|
|
1028
|
+
value={props.role}
|
|
1029
|
+
aria-label="Editor mode"
|
|
1030
|
+
data-testid="toolbar-role-toggle"
|
|
1031
|
+
disabled={props.disabled}
|
|
1032
|
+
onValueChange={(value) => {
|
|
1033
|
+
if (!value || value === props.role) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
props.onRoleChange?.(value as EditorRole);
|
|
1037
|
+
}}
|
|
1038
|
+
className="mr-0.5 inline-flex h-7 shrink-0 items-center rounded-[var(--radius-md)] border border-[var(--color-border-subtle)] bg-[var(--color-bg-muted)] p-0.5"
|
|
1039
|
+
>
|
|
1040
|
+
{ROLE_TOGGLE_ITEMS.map((item) => (
|
|
1041
|
+
<ToggleGroup.Item
|
|
1042
|
+
key={item.role}
|
|
1043
|
+
value={item.role}
|
|
1044
|
+
aria-label={item.label}
|
|
1045
|
+
data-testid={item.testId}
|
|
1046
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1047
|
+
className={[
|
|
1048
|
+
"inline-flex h-6 items-center rounded-[var(--radius-sm)] px-2 text-[11px] font-semibold",
|
|
1049
|
+
"text-[var(--color-text-secondary)] transition-colors duration-[var(--motion-fast)]",
|
|
1050
|
+
"hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]",
|
|
1051
|
+
"data-[state=on]:bg-[var(--color-bg-canvas)] data-[state=on]:text-[var(--color-accent-primary)]",
|
|
1052
|
+
"disabled:cursor-not-allowed disabled:opacity-40",
|
|
1053
|
+
"focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
1054
|
+
].join(" ")}
|
|
1055
|
+
>
|
|
1056
|
+
{item.label}
|
|
1057
|
+
</ToggleGroup.Item>
|
|
1058
|
+
))}
|
|
1059
|
+
</ToggleGroup.Root>
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1039
1063
|
function ToolbarParagraphStyleSelect(props: {
|
|
1040
1064
|
styles: StyleCatalogSnapshot["paragraphs"];
|
|
1041
1065
|
value?: string;
|
|
@@ -1062,9 +1086,15 @@ function ToolbarParagraphStyleSelect(props: {
|
|
|
1062
1086
|
aria-label="Paragraph style"
|
|
1063
1087
|
aria-disabled={props.disabled || undefined}
|
|
1064
1088
|
data-disabled={props.disabled ? "" : undefined}
|
|
1065
|
-
data-disabled-reason={
|
|
1089
|
+
data-disabled-reason={
|
|
1090
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1091
|
+
}
|
|
1066
1092
|
data-mixed={isMixed ? "true" : undefined}
|
|
1067
|
-
title={
|
|
1093
|
+
title={
|
|
1094
|
+
props.disabled && props.disabledReason
|
|
1095
|
+
? `Not available: ${props.disabledReason}`
|
|
1096
|
+
: undefined
|
|
1097
|
+
}
|
|
1068
1098
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1069
1099
|
className={`inline-flex h-6 min-w-[7.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1070
1100
|
>
|
|
@@ -1129,9 +1159,15 @@ function ToolbarFontFamilySelect(props: {
|
|
|
1129
1159
|
aria-label="Font family"
|
|
1130
1160
|
aria-disabled={props.disabled || undefined}
|
|
1131
1161
|
data-disabled={props.disabled ? "" : undefined}
|
|
1132
|
-
data-disabled-reason={
|
|
1162
|
+
data-disabled-reason={
|
|
1163
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1164
|
+
}
|
|
1133
1165
|
data-mixed={isMixed ? "true" : undefined}
|
|
1134
|
-
title={
|
|
1166
|
+
title={
|
|
1167
|
+
props.disabled && props.disabledReason
|
|
1168
|
+
? `Not available: ${props.disabledReason}`
|
|
1169
|
+
: undefined
|
|
1170
|
+
}
|
|
1135
1171
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1136
1172
|
className={`inline-flex h-6 min-w-[6.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1137
1173
|
>
|
|
@@ -1198,9 +1234,15 @@ function ToolbarFontSizeSelect(props: {
|
|
|
1198
1234
|
aria-label="Font size"
|
|
1199
1235
|
aria-disabled={props.disabled || undefined}
|
|
1200
1236
|
data-disabled={props.disabled ? "" : undefined}
|
|
1201
|
-
data-disabled-reason={
|
|
1237
|
+
data-disabled-reason={
|
|
1238
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1239
|
+
}
|
|
1202
1240
|
data-mixed={isMixed ? "true" : undefined}
|
|
1203
|
-
title={
|
|
1241
|
+
title={
|
|
1242
|
+
props.disabled && props.disabledReason
|
|
1243
|
+
? `Not available: ${props.disabledReason}`
|
|
1244
|
+
: undefined
|
|
1245
|
+
}
|
|
1204
1246
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1205
1247
|
className={`inline-flex h-6 min-w-[3.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1206
1248
|
>
|
|
@@ -1273,7 +1315,6 @@ function ToolbarCompactOverflow(props: {
|
|
|
1273
1315
|
onUpdateTableOfContents?: () => void;
|
|
1274
1316
|
}) {
|
|
1275
1317
|
const [open, setOpen] = React.useState(false);
|
|
1276
|
-
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
1277
1318
|
|
|
1278
1319
|
async function handleImageChange(event: React.ChangeEvent<HTMLInputElement>): Promise<void> {
|
|
1279
1320
|
const file = event.target.files?.[0];
|
|
@@ -1293,28 +1334,19 @@ function ToolbarCompactOverflow(props: {
|
|
|
1293
1334
|
}
|
|
1294
1335
|
|
|
1295
1336
|
return (
|
|
1296
|
-
<
|
|
1337
|
+
<div className="relative">
|
|
1297
1338
|
<Tooltip.Root>
|
|
1298
1339
|
<Tooltip.Trigger asChild>
|
|
1299
|
-
<
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
setOpen((value) => !value);
|
|
1310
|
-
}}
|
|
1311
|
-
className={`inline-flex h-6 w-6 items-center justify-center rounded-md border border-border bg-canvas text-primary transition-colors hover:bg-surface outline-none ${
|
|
1312
|
-
open ? "text-accent ring-1 ring-accent/30 shadow-sm" : ""
|
|
1313
|
-
} ${focusRingClass}`}
|
|
1314
|
-
>
|
|
1315
|
-
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
1316
|
-
</button>
|
|
1317
|
-
</Popover.Anchor>
|
|
1340
|
+
<button
|
|
1341
|
+
type="button"
|
|
1342
|
+
aria-label="More document tools"
|
|
1343
|
+
aria-expanded={open}
|
|
1344
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1345
|
+
onClick={() => setOpen((value) => !value)}
|
|
1346
|
+
className={`inline-flex h-6 w-6 items-center justify-center rounded-md border border-border bg-canvas text-primary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
|
|
1347
|
+
>
|
|
1348
|
+
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
1349
|
+
</button>
|
|
1318
1350
|
</Tooltip.Trigger>
|
|
1319
1351
|
<Tooltip.Portal>
|
|
1320
1352
|
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
@@ -1322,12 +1354,8 @@ function ToolbarCompactOverflow(props: {
|
|
|
1322
1354
|
</Tooltip.Content>
|
|
1323
1355
|
</Tooltip.Portal>
|
|
1324
1356
|
</Tooltip.Root>
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
className="w-[min(20rem,calc(100vw-2rem))] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1328
|
-
menuWidthPx={320}
|
|
1329
|
-
open={open}
|
|
1330
|
-
>
|
|
1357
|
+
{open ? (
|
|
1358
|
+
<div className="absolute left-0 top-9 z-50 w-[min(20rem,calc(100vw-2rem))] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
1331
1359
|
<div className="space-y-3">
|
|
1332
1360
|
{props.showStyleSelectors ? (
|
|
1333
1361
|
<div className="space-y-2">
|
|
@@ -1487,14 +1515,15 @@ function ToolbarCompactOverflow(props: {
|
|
|
1487
1515
|
}}
|
|
1488
1516
|
/>
|
|
1489
1517
|
<label
|
|
1490
|
-
|
|
1518
|
+
aria-disabled={!props.canInsertStructural || !props.onInsertImage ? "true" : undefined}
|
|
1519
|
+
data-disabled-reason={
|
|
1491
1520
|
!props.canInsertStructural && props.insertDisabledReason
|
|
1492
|
-
?
|
|
1521
|
+
? props.insertDisabledReason
|
|
1493
1522
|
: undefined
|
|
1494
1523
|
}
|
|
1495
|
-
|
|
1524
|
+
title={
|
|
1496
1525
|
!props.canInsertStructural && props.insertDisabledReason
|
|
1497
|
-
? props.insertDisabledReason
|
|
1526
|
+
? `Not available: ${props.insertDisabledReason}`
|
|
1498
1527
|
: undefined
|
|
1499
1528
|
}
|
|
1500
1529
|
className={`flex h-7 cursor-pointer items-center gap-2 rounded-md px-2 text-left text-[11px] font-medium text-primary transition-colors hover:bg-surface ${
|
|
@@ -1558,8 +1587,9 @@ function ToolbarCompactOverflow(props: {
|
|
|
1558
1587
|
</div>
|
|
1559
1588
|
) : null}
|
|
1560
1589
|
</div>
|
|
1561
|
-
|
|
1562
|
-
|
|
1590
|
+
</div>
|
|
1591
|
+
) : null}
|
|
1592
|
+
</div>
|
|
1563
1593
|
);
|
|
1564
1594
|
}
|
|
1565
1595
|
|
|
@@ -1572,36 +1602,28 @@ function ToolbarFormattingOverflow(props: {
|
|
|
1572
1602
|
onToggleSubscript?: () => void;
|
|
1573
1603
|
}) {
|
|
1574
1604
|
const [open, setOpen] = React.useState(false);
|
|
1575
|
-
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
1576
1605
|
const disabledTitle =
|
|
1577
1606
|
props.disabled && props.disabledReason ? `Not available: ${props.disabledReason}` : undefined;
|
|
1578
1607
|
|
|
1579
1608
|
return (
|
|
1580
|
-
<
|
|
1609
|
+
<div className="relative">
|
|
1581
1610
|
<Tooltip.Root>
|
|
1582
1611
|
<Tooltip.Trigger asChild>
|
|
1583
|
-
<
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1599
|
-
open ? "bg-canvas text-accent ring-1 ring-accent/30 shadow-sm" : ""
|
|
1600
|
-
} ${focusRingClass}`}
|
|
1601
|
-
>
|
|
1602
|
-
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
1603
|
-
</button>
|
|
1604
|
-
</Popover.Anchor>
|
|
1612
|
+
<button
|
|
1613
|
+
type="button"
|
|
1614
|
+
aria-label="More text formatting"
|
|
1615
|
+
aria-expanded={open}
|
|
1616
|
+
data-disabled-reason={
|
|
1617
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1618
|
+
}
|
|
1619
|
+
disabled={props.disabled}
|
|
1620
|
+
title={disabledTitle}
|
|
1621
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1622
|
+
onClick={() => setOpen((value) => !value)}
|
|
1623
|
+
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1624
|
+
>
|
|
1625
|
+
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
1626
|
+
</button>
|
|
1605
1627
|
</Tooltip.Trigger>
|
|
1606
1628
|
<Tooltip.Portal>
|
|
1607
1629
|
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
@@ -1609,12 +1631,8 @@ function ToolbarFormattingOverflow(props: {
|
|
|
1609
1631
|
</Tooltip.Content>
|
|
1610
1632
|
</Tooltip.Portal>
|
|
1611
1633
|
</Tooltip.Root>
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
className="w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1615
|
-
menuWidthPx={220}
|
|
1616
|
-
open={open}
|
|
1617
|
-
>
|
|
1634
|
+
{open ? (
|
|
1635
|
+
<div className="absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
1618
1636
|
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
1619
1637
|
Text styling
|
|
1620
1638
|
</div>
|
|
@@ -1653,8 +1671,9 @@ function ToolbarFormattingOverflow(props: {
|
|
|
1653
1671
|
}}
|
|
1654
1672
|
/>
|
|
1655
1673
|
</div>
|
|
1656
|
-
|
|
1657
|
-
|
|
1674
|
+
</div>
|
|
1675
|
+
) : null}
|
|
1676
|
+
</div>
|
|
1658
1677
|
);
|
|
1659
1678
|
}
|
|
1660
1679
|
|
|
@@ -1664,42 +1683,32 @@ function ToolbarColorPopover(props: {
|
|
|
1664
1683
|
disabled: boolean;
|
|
1665
1684
|
disabledReason?: string;
|
|
1666
1685
|
icon: React.ReactNode;
|
|
1667
|
-
activeValue?: string | null;
|
|
1668
1686
|
title: string;
|
|
1669
1687
|
onSelect: (value: string | null) => void;
|
|
1670
1688
|
}) {
|
|
1671
1689
|
const [open, setOpen] = React.useState(false);
|
|
1672
|
-
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
1673
|
-
const activeValue = props.activeValue?.toLowerCase() ?? null;
|
|
1674
1690
|
const disabledTitle =
|
|
1675
1691
|
props.disabled && props.disabledReason ? `Not available: ${props.disabledReason}` : undefined;
|
|
1676
1692
|
|
|
1677
1693
|
return (
|
|
1678
|
-
<
|
|
1694
|
+
<div className="relative">
|
|
1679
1695
|
<Tooltip.Root>
|
|
1680
1696
|
<Tooltip.Trigger asChild>
|
|
1681
|
-
<
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1697
|
-
open ? "bg-canvas text-accent ring-1 ring-accent/30 shadow-sm" : ""
|
|
1698
|
-
} ${focusRingClass}`}
|
|
1699
|
-
>
|
|
1700
|
-
{props.icon}
|
|
1701
|
-
</button>
|
|
1702
|
-
</Popover.Anchor>
|
|
1697
|
+
<button
|
|
1698
|
+
type="button"
|
|
1699
|
+
aria-label={props.ariaLabel}
|
|
1700
|
+
aria-expanded={open}
|
|
1701
|
+
data-disabled-reason={
|
|
1702
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1703
|
+
}
|
|
1704
|
+
disabled={props.disabled}
|
|
1705
|
+
title={disabledTitle}
|
|
1706
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1707
|
+
onClick={() => setOpen((value) => !value)}
|
|
1708
|
+
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1709
|
+
>
|
|
1710
|
+
{props.icon}
|
|
1711
|
+
</button>
|
|
1703
1712
|
</Tooltip.Trigger>
|
|
1704
1713
|
<Tooltip.Portal>
|
|
1705
1714
|
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
@@ -1707,46 +1716,35 @@ function ToolbarColorPopover(props: {
|
|
|
1707
1716
|
</Tooltip.Content>
|
|
1708
1717
|
</Tooltip.Portal>
|
|
1709
1718
|
</Tooltip.Root>
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
className="w-[180px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1713
|
-
menuWidthPx={180}
|
|
1714
|
-
open={open}
|
|
1715
|
-
>
|
|
1719
|
+
{open ? (
|
|
1720
|
+
<div className="absolute left-0 top-9 z-50 w-[180px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
1716
1721
|
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
1717
1722
|
{props.title}
|
|
1718
1723
|
</div>
|
|
1719
1724
|
<div className="grid grid-cols-3 gap-1">
|
|
1720
|
-
{props.colors.map((color) =>
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
: "border-border"
|
|
1740
|
-
} ${color.value ? "" : "bg-surface"} ${focusRingClass}`}
|
|
1741
|
-
style={color.value ? { backgroundColor: color.value } : undefined}
|
|
1742
|
-
>
|
|
1743
|
-
{color.value ? <span className="sr-only">{color.label}</span> : "None"}
|
|
1744
|
-
</button>
|
|
1745
|
-
);
|
|
1746
|
-
})}
|
|
1725
|
+
{props.colors.map((color) => (
|
|
1726
|
+
<button
|
|
1727
|
+
key={`${props.ariaLabel}-${color.label}`}
|
|
1728
|
+
type="button"
|
|
1729
|
+
aria-label={`${props.title} ${color.label}`}
|
|
1730
|
+
disabled={props.disabled}
|
|
1731
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1732
|
+
onClick={() => {
|
|
1733
|
+
props.onSelect(color.value);
|
|
1734
|
+
setOpen(false);
|
|
1735
|
+
}}
|
|
1736
|
+
className={`inline-flex h-7 items-center justify-center rounded-md border border-border text-[10px] font-medium text-primary transition-transform hover:scale-[1.04] disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1737
|
+
color.value ? "" : "bg-surface"
|
|
1738
|
+
} ${focusRingClass}`}
|
|
1739
|
+
style={color.value ? { backgroundColor: color.value } : undefined}
|
|
1740
|
+
>
|
|
1741
|
+
{color.value ? <span className="sr-only">{color.label}</span> : "None"}
|
|
1742
|
+
</button>
|
|
1743
|
+
))}
|
|
1747
1744
|
</div>
|
|
1748
|
-
|
|
1749
|
-
|
|
1745
|
+
</div>
|
|
1746
|
+
) : null}
|
|
1747
|
+
</div>
|
|
1750
1748
|
);
|
|
1751
1749
|
}
|
|
1752
1750
|
|
|
@@ -1757,7 +1755,6 @@ function ToolbarAlignmentPopover(props: {
|
|
|
1757
1755
|
onSelect: (alignment: FormattingAlignment) => void;
|
|
1758
1756
|
}) {
|
|
1759
1757
|
const [open, setOpen] = React.useState(false);
|
|
1760
|
-
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
1761
1758
|
const disabledTitle =
|
|
1762
1759
|
props.disabled && props.disabledReason ? `Not available: ${props.disabledReason}` : undefined;
|
|
1763
1760
|
const alignments = [
|
|
@@ -1768,31 +1765,24 @@ function ToolbarAlignmentPopover(props: {
|
|
|
1768
1765
|
];
|
|
1769
1766
|
|
|
1770
1767
|
return (
|
|
1771
|
-
<
|
|
1768
|
+
<div className="relative">
|
|
1772
1769
|
<Tooltip.Root>
|
|
1773
1770
|
<Tooltip.Trigger asChild>
|
|
1774
|
-
<
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1790
|
-
open ? "bg-canvas text-accent ring-1 ring-accent/30 shadow-sm" : ""
|
|
1791
|
-
} ${focusRingClass}`}
|
|
1792
|
-
>
|
|
1793
|
-
{(alignments.find((entry) => entry.value === props.activeAlignment) ?? alignments[0])?.icon}
|
|
1794
|
-
</button>
|
|
1795
|
-
</Popover.Anchor>
|
|
1771
|
+
<button
|
|
1772
|
+
type="button"
|
|
1773
|
+
aria-label="Paragraph alignment"
|
|
1774
|
+
aria-expanded={open}
|
|
1775
|
+
data-disabled-reason={
|
|
1776
|
+
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
1777
|
+
}
|
|
1778
|
+
disabled={props.disabled}
|
|
1779
|
+
title={disabledTitle}
|
|
1780
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1781
|
+
onClick={() => setOpen((value) => !value)}
|
|
1782
|
+
className={`inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1783
|
+
>
|
|
1784
|
+
{(alignments.find((entry) => entry.value === props.activeAlignment) ?? alignments[0])?.icon}
|
|
1785
|
+
</button>
|
|
1796
1786
|
</Tooltip.Trigger>
|
|
1797
1787
|
<Tooltip.Portal>
|
|
1798
1788
|
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
@@ -1800,12 +1790,8 @@ function ToolbarAlignmentPopover(props: {
|
|
|
1800
1790
|
</Tooltip.Content>
|
|
1801
1791
|
</Tooltip.Portal>
|
|
1802
1792
|
</Tooltip.Root>
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
className="w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1806
|
-
menuWidthPx={220}
|
|
1807
|
-
open={open}
|
|
1808
|
-
>
|
|
1793
|
+
{open ? (
|
|
1794
|
+
<div className="absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
1809
1795
|
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
1810
1796
|
Paragraph alignment
|
|
1811
1797
|
</div>
|
|
@@ -1825,8 +1811,9 @@ function ToolbarAlignmentPopover(props: {
|
|
|
1825
1811
|
/>
|
|
1826
1812
|
))}
|
|
1827
1813
|
</div>
|
|
1828
|
-
|
|
1829
|
-
|
|
1814
|
+
</div>
|
|
1815
|
+
) : null}
|
|
1816
|
+
</div>
|
|
1830
1817
|
);
|
|
1831
1818
|
}
|
|
1832
1819
|
|
|
@@ -1850,9 +1837,11 @@ function ToolbarInsertMenu(props: {
|
|
|
1850
1837
|
onInsertImage?: (options: InsertImageOptions) => void;
|
|
1851
1838
|
}) {
|
|
1852
1839
|
const [open, setOpen] = React.useState(false);
|
|
1853
|
-
const
|
|
1840
|
+
const triggerUnavailable = props.disabled && props.disabledReason
|
|
1841
|
+
? props.disabledReason
|
|
1842
|
+
: undefined;
|
|
1854
1843
|
const disabledTitle =
|
|
1855
|
-
|
|
1844
|
+
triggerUnavailable ? `Not available: ${triggerUnavailable}` : undefined;
|
|
1856
1845
|
|
|
1857
1846
|
async function handleImageChange(event: React.ChangeEvent<HTMLInputElement>): Promise<void> {
|
|
1858
1847
|
const file = event.target.files?.[0];
|
|
@@ -1871,50 +1860,31 @@ function ToolbarInsertMenu(props: {
|
|
|
1871
1860
|
}
|
|
1872
1861
|
|
|
1873
1862
|
return (
|
|
1874
|
-
<
|
|
1875
|
-
<
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
disabled={props.disabled}
|
|
1886
|
-
title={disabledTitle}
|
|
1887
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1888
|
-
onClick={(event) => {
|
|
1889
|
-
event.preventDefault();
|
|
1890
|
-
setOpen((value) => !value);
|
|
1891
|
-
}}
|
|
1892
|
-
className={`inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1893
|
-
open ? "text-accent ring-1 ring-accent/30 shadow-sm" : ""
|
|
1894
|
-
} ${focusRingClass}`}
|
|
1895
|
-
>
|
|
1896
|
-
Insert
|
|
1897
|
-
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
1898
|
-
</button>
|
|
1899
|
-
</Popover.Anchor>
|
|
1900
|
-
</Tooltip.Trigger>
|
|
1901
|
-
<Tooltip.Portal>
|
|
1902
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
1903
|
-
Insert
|
|
1904
|
-
</Tooltip.Content>
|
|
1905
|
-
</Tooltip.Portal>
|
|
1906
|
-
</Tooltip.Root>
|
|
1907
|
-
<ToolbarPortalMenu
|
|
1908
|
-
anchorRef={triggerRef}
|
|
1909
|
-
className="w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1910
|
-
menuWidthPx={220}
|
|
1911
|
-
open={open}
|
|
1863
|
+
<div className="relative">
|
|
1864
|
+
<button
|
|
1865
|
+
type="button"
|
|
1866
|
+
aria-label="Insert"
|
|
1867
|
+
aria-disabled={triggerUnavailable ? "true" : undefined}
|
|
1868
|
+
aria-expanded={open}
|
|
1869
|
+
data-disabled-reason={triggerUnavailable}
|
|
1870
|
+
title={disabledTitle ?? "Insert"}
|
|
1871
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1872
|
+
onClick={() => setOpen((value) => !value)}
|
|
1873
|
+
className={`inline-flex h-6 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
|
|
1912
1874
|
>
|
|
1875
|
+
Insert
|
|
1876
|
+
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
1877
|
+
</button>
|
|
1878
|
+
{open ? (
|
|
1879
|
+
<div
|
|
1880
|
+
className="absolute left-0 top-9 z-[60] w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border"
|
|
1881
|
+
data-testid="toolbar-insert-menu"
|
|
1882
|
+
>
|
|
1913
1883
|
<div className="space-y-1">
|
|
1914
1884
|
<ToolbarMenuButton
|
|
1915
1885
|
ariaLabel="Insert page break"
|
|
1916
1886
|
disabled={props.disabled || !props.onInsertPageBreak}
|
|
1917
|
-
disabledReason={props.
|
|
1887
|
+
disabledReason={props.disabledReasons?.pageBreak ?? props.disabledReason}
|
|
1918
1888
|
icon={<Minus className="h-3.5 w-3.5" />}
|
|
1919
1889
|
label="Page break"
|
|
1920
1890
|
onClick={() => {
|
|
@@ -1925,7 +1895,7 @@ function ToolbarInsertMenu(props: {
|
|
|
1925
1895
|
<ToolbarMenuButton
|
|
1926
1896
|
ariaLabel="Insert table"
|
|
1927
1897
|
disabled={props.disabled || !props.onInsertTable}
|
|
1928
|
-
disabledReason={props.
|
|
1898
|
+
disabledReason={props.disabledReasons?.table ?? props.disabledReason}
|
|
1929
1899
|
icon={<Rows3 className="h-3.5 w-3.5" />}
|
|
1930
1900
|
label="Table"
|
|
1931
1901
|
onClick={() => {
|
|
@@ -1935,19 +1905,15 @@ function ToolbarInsertMenu(props: {
|
|
|
1935
1905
|
/>
|
|
1936
1906
|
<label
|
|
1937
1907
|
aria-disabled={props.disabled || !props.onInsertImage ? "true" : undefined}
|
|
1938
|
-
title={
|
|
1939
|
-
props.disabled
|
|
1940
|
-
? disabledTitle
|
|
1941
|
-
: !props.onInsertImage && props.disabledReasons?.image
|
|
1942
|
-
? `Not available: ${props.disabledReasons.image}`
|
|
1943
|
-
: undefined
|
|
1944
|
-
}
|
|
1945
1908
|
data-disabled-reason={
|
|
1946
|
-
props.disabled
|
|
1947
|
-
? props.disabledReason
|
|
1948
|
-
:
|
|
1949
|
-
|
|
1950
|
-
|
|
1909
|
+
props.disabled || !props.onInsertImage
|
|
1910
|
+
? props.disabledReasons?.image ?? props.disabledReason
|
|
1911
|
+
: undefined
|
|
1912
|
+
}
|
|
1913
|
+
title={
|
|
1914
|
+
props.disabled || !props.onInsertImage
|
|
1915
|
+
? `Not available: ${props.disabledReasons?.image ?? props.disabledReason ?? "Insert image is not available."}`
|
|
1916
|
+
: undefined
|
|
1951
1917
|
}
|
|
1952
1918
|
className={`flex h-7 cursor-pointer items-center gap-2 rounded-md px-2 text-[11px] font-medium text-primary transition-colors hover:bg-surface ${
|
|
1953
1919
|
props.disabled || !props.onInsertImage ? "pointer-events-none opacity-40" : ""
|
|
@@ -1969,7 +1935,7 @@ function ToolbarInsertMenu(props: {
|
|
|
1969
1935
|
<ToolbarMenuButton
|
|
1970
1936
|
ariaLabel="Insert next-page section break"
|
|
1971
1937
|
disabled={props.disabled || !props.onInsertSectionBreak}
|
|
1972
|
-
disabledReason={props.
|
|
1938
|
+
disabledReason={props.disabledReasons?.sectionBreak ?? props.disabledReason}
|
|
1973
1939
|
icon={<FileText className="h-3.5 w-3.5" />}
|
|
1974
1940
|
label="Next-page section break"
|
|
1975
1941
|
onClick={() => {
|
|
@@ -1978,8 +1944,9 @@ function ToolbarInsertMenu(props: {
|
|
|
1978
1944
|
}}
|
|
1979
1945
|
/>
|
|
1980
1946
|
</div>
|
|
1981
|
-
|
|
1982
|
-
|
|
1947
|
+
</div>
|
|
1948
|
+
) : null}
|
|
1949
|
+
</div>
|
|
1983
1950
|
);
|
|
1984
1951
|
}
|
|
1985
1952
|
|
|
@@ -2000,11 +1967,11 @@ function ToolbarPopoverActionButton(props: {
|
|
|
2000
1967
|
type="button"
|
|
2001
1968
|
aria-label={props.ariaLabel}
|
|
2002
1969
|
aria-pressed={props.active}
|
|
2003
|
-
disabled={props.disabled}
|
|
2004
|
-
title={titleAttr}
|
|
2005
1970
|
data-disabled-reason={
|
|
2006
1971
|
props.disabled && props.disabledReason ? props.disabledReason : undefined
|
|
2007
1972
|
}
|
|
1973
|
+
disabled={props.disabled}
|
|
1974
|
+
title={titleAttr}
|
|
2008
1975
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
2009
1976
|
onClick={props.onClick}
|
|
2010
1977
|
className={`inline-flex h-7 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
|