@beyondwork/docx-react-component 1.0.24-rc → 1.0.26-rc
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 +1 -1
- package/src/core/selection/review-anchors.ts +1 -6
- package/src/io/export/serialize-comments.ts +10 -4
- package/src/runtime/document-runtime.ts +2 -2
- package/src/runtime/session-capabilities.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +30 -6
- package/src/ui-tailwind/theme/editor-theme.css +18 -0
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.
|
|
4
|
+
"version": "1.0.26rc",
|
|
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": [
|
|
@@ -104,7 +104,6 @@ export function rangeStaysWithinSingleParagraph(
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export function canCreateDocxCommentAnchor(
|
|
107
|
-
content: unknown,
|
|
108
107
|
anchor: ReviewAnchor,
|
|
109
108
|
): boolean {
|
|
110
109
|
if (anchor.kind !== "range") {
|
|
@@ -112,11 +111,7 @@ export function canCreateDocxCommentAnchor(
|
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
const normalized = normalizeRange(anchor.range);
|
|
115
|
-
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return rangeStaysWithinSingleParagraph(content, normalized);
|
|
114
|
+
return normalized.from !== normalized.to;
|
|
120
115
|
}
|
|
121
116
|
|
|
122
117
|
function readSurfaceBlocks(
|
|
@@ -261,19 +261,25 @@ export function serializeCommentAnchorsIntoDocumentXml(
|
|
|
261
261
|
continue;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
const
|
|
264
|
+
const startParagraph = paragraphs.find(
|
|
265
265
|
(candidate) =>
|
|
266
266
|
anchor.range.from >= candidate.start &&
|
|
267
|
+
anchor.range.from <= candidate.end,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const endParagraph = paragraphs.find(
|
|
271
|
+
(candidate) =>
|
|
272
|
+
anchor.range.to >= candidate.start &&
|
|
267
273
|
anchor.range.to <= candidate.end,
|
|
268
274
|
);
|
|
269
275
|
|
|
270
|
-
if (!
|
|
276
|
+
if (!startParagraph || !endParagraph) {
|
|
271
277
|
skippedCommentIds.push(thread.commentId);
|
|
272
278
|
continue;
|
|
273
279
|
}
|
|
274
280
|
|
|
275
|
-
const startIndex =
|
|
276
|
-
const endIndex =
|
|
281
|
+
const startIndex = startParagraph.boundaries.get(anchor.range.from);
|
|
282
|
+
const endIndex = endParagraph.boundaries.get(anchor.range.to);
|
|
277
283
|
|
|
278
284
|
if (startIndex === undefined || endIndex === undefined) {
|
|
279
285
|
skippedCommentIds.push(thread.commentId);
|
|
@@ -1160,9 +1160,9 @@ export function createDocumentRuntime(
|
|
|
1160
1160
|
const selection = params.anchor
|
|
1161
1161
|
? createSelectionFromPublicAnchor(params.anchor)
|
|
1162
1162
|
: state.selection;
|
|
1163
|
-
if (!canCreateDocxCommentAnchor(
|
|
1163
|
+
if (!canCreateDocxCommentAnchor(anchor)) {
|
|
1164
1164
|
const message =
|
|
1165
|
-
"DOCX comments must use a non-empty range
|
|
1165
|
+
"DOCX comments must use a non-empty range.";
|
|
1166
1166
|
emitError({
|
|
1167
1167
|
errorId: createSessionId("comment-anchor", clock()),
|
|
1168
1168
|
code: "validation_failed",
|
|
@@ -110,7 +110,7 @@ export function deriveCapabilities(
|
|
|
110
110
|
activeStory.kind === "main" &&
|
|
111
111
|
!snapshot.selection.isCollapsed &&
|
|
112
112
|
Boolean(snapshot.surface) &&
|
|
113
|
-
canCreateDocxCommentAnchor(
|
|
113
|
+
canCreateDocxCommentAnchor(toRuntimeAnchor(snapshot.selection.activeRange));
|
|
114
114
|
const canExport = isReady && !exportBlocked && !hasFatalError;
|
|
115
115
|
|
|
116
116
|
// Revision capabilities
|
|
@@ -21,7 +21,22 @@ type RailDecorationSpec = {
|
|
|
21
21
|
attrs: Record<string, string>;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
function
|
|
24
|
+
function isSelectionZoneScope(scope: WorkflowScope): boolean {
|
|
25
|
+
return (
|
|
26
|
+
scope.scopeId.startsWith("scope-lab-") ||
|
|
27
|
+
scope.scopeId.startsWith("metadata-lab-") ||
|
|
28
|
+
scope.workItemId?.startsWith("scope-lab-") === true ||
|
|
29
|
+
scope.workItemId?.startsWith("metadata-lab-") === true ||
|
|
30
|
+
scope.label?.toLowerCase().includes("scope lab") === true ||
|
|
31
|
+
scope.label?.toLowerCase().includes("metadata lab") === true
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getWorkflowInlineClass(
|
|
36
|
+
scope: WorkflowScope,
|
|
37
|
+
isActiveWorkItem: boolean,
|
|
38
|
+
isSelectionZone: boolean,
|
|
39
|
+
): string {
|
|
25
40
|
const base =
|
|
26
41
|
scope.mode === "edit"
|
|
27
42
|
? "wre-workflow-inline wre-workflow-inline-edit"
|
|
@@ -30,10 +45,15 @@ function getWorkflowInlineClass(scope: WorkflowScope, isActiveWorkItem: boolean)
|
|
|
30
45
|
: scope.mode === "comment"
|
|
31
46
|
? "wre-workflow-inline wre-workflow-inline-comment"
|
|
32
47
|
: "wre-workflow-inline wre-workflow-inline-view";
|
|
33
|
-
|
|
48
|
+
const withZone = isSelectionZone ? `${base} wre-workflow-inline-zone` : base;
|
|
49
|
+
return isActiveWorkItem ? `${withZone} wre-workflow-inline-active` : withZone;
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
function getWorkflowRailClass(
|
|
52
|
+
function getWorkflowRailClass(
|
|
53
|
+
scope: WorkflowScope,
|
|
54
|
+
isActiveWorkItem: boolean,
|
|
55
|
+
isSelectionZone: boolean,
|
|
56
|
+
): string {
|
|
37
57
|
const base =
|
|
38
58
|
scope.mode === "edit"
|
|
39
59
|
? "wre-workflow-rail wre-workflow-rail-edit"
|
|
@@ -42,7 +62,8 @@ function getWorkflowRailClass(scope: WorkflowScope, isActiveWorkItem: boolean):
|
|
|
42
62
|
: scope.mode === "comment"
|
|
43
63
|
? "wre-workflow-rail wre-workflow-rail-comment"
|
|
44
64
|
: "wre-workflow-rail wre-workflow-rail-view";
|
|
45
|
-
|
|
65
|
+
const withZone = isSelectionZone ? `${base} wre-workflow-rail-selection-zone` : base;
|
|
66
|
+
return isActiveWorkItem ? `${withZone} wre-workflow-rail-active` : withZone;
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
function getWorkflowCandidateInlineClass(): string {
|
|
@@ -311,6 +332,7 @@ export function buildDecorations(
|
|
|
311
332
|
if (!storyTargetsEqual(scopeStoryTarget, activeStory)) continue;
|
|
312
333
|
const pmRange = buildAnchorPmRange(scope.anchor, positionMap);
|
|
313
334
|
if (!pmRange) continue;
|
|
335
|
+
const isSelectionZone = isSelectionZoneScope(scope);
|
|
314
336
|
const isActiveWorkItem =
|
|
315
337
|
Boolean(activeWorkflowWorkItemId) &&
|
|
316
338
|
(
|
|
@@ -321,21 +343,23 @@ export function buildDecorations(
|
|
|
321
343
|
if (pmRange.allowInline && pmRange.from < pmRange.to) {
|
|
322
344
|
decorations.push(
|
|
323
345
|
Decoration.inline(pmRange.from, pmRange.to, {
|
|
324
|
-
class: getWorkflowInlineClass(scope, isActiveWorkItem),
|
|
346
|
+
class: getWorkflowInlineClass(scope, isActiveWorkItem, isSelectionZone),
|
|
325
347
|
"data-workflow-scope-id": scope.scopeId,
|
|
326
348
|
"data-workflow-scope-mode": scope.mode,
|
|
327
349
|
"data-workflow-active": isActiveWorkItem ? "true" : "false",
|
|
350
|
+
...(isSelectionZone ? { "data-workflow-zone": "selection" } : {}),
|
|
328
351
|
}),
|
|
329
352
|
);
|
|
330
353
|
}
|
|
331
354
|
|
|
332
355
|
pushRailDecorations(decorations, doc, pmRange.from, pmRange.to, {
|
|
333
356
|
railKind: "scope",
|
|
334
|
-
className: getWorkflowRailClass(scope, isActiveWorkItem),
|
|
357
|
+
className: getWorkflowRailClass(scope, isActiveWorkItem, isSelectionZone),
|
|
335
358
|
attrs: {
|
|
336
359
|
"data-workflow-scope-id": scope.scopeId,
|
|
337
360
|
"data-workflow-scope-mode": scope.mode,
|
|
338
361
|
"data-workflow-active": isActiveWorkItem ? "true" : "false",
|
|
362
|
+
...(isSelectionZone ? { "data-workflow-zone": "selection" } : {}),
|
|
339
363
|
},
|
|
340
364
|
}, railRangeCache);
|
|
341
365
|
}
|
|
@@ -261,6 +261,8 @@
|
|
|
261
261
|
.prosemirror-surface .ProseMirror .wre-workflow-inline {
|
|
262
262
|
border-radius: 0.25rem;
|
|
263
263
|
box-shadow: inset 0 0 0 1px transparent;
|
|
264
|
+
-webkit-box-decoration-break: clone;
|
|
265
|
+
box-decoration-break: clone;
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
.prosemirror-surface .ProseMirror .wre-workflow-inline-edit {
|
|
@@ -333,6 +335,13 @@
|
|
|
333
335
|
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--wre-workflow-rail-color, var(--color-border-strong)) 28%, transparent);
|
|
334
336
|
}
|
|
335
337
|
|
|
338
|
+
.prosemirror-surface .ProseMirror .wre-workflow-inline-zone {
|
|
339
|
+
border-radius: 0.35rem;
|
|
340
|
+
box-shadow:
|
|
341
|
+
inset 0 0 0 1px color-mix(in srgb, var(--wre-workflow-rail-color, var(--color-accent)) 18%, transparent),
|
|
342
|
+
inset 0 0 0 999px color-mix(in srgb, var(--wre-workflow-rail-color, var(--color-accent)) 8%, transparent);
|
|
343
|
+
}
|
|
344
|
+
|
|
336
345
|
.prosemirror-surface .ProseMirror .wre-workflow-rail-edit {
|
|
337
346
|
--wre-workflow-rail-color: var(--color-accent);
|
|
338
347
|
background: color-mix(in srgb, var(--color-accent) 7%, transparent);
|
|
@@ -394,6 +403,15 @@
|
|
|
394
403
|
);
|
|
395
404
|
}
|
|
396
405
|
|
|
406
|
+
.prosemirror-surface .ProseMirror .wre-workflow-rail-selection-zone {
|
|
407
|
+
background: transparent;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.prosemirror-surface .ProseMirror .wre-workflow-rail-selection-zone::before {
|
|
411
|
+
width: 0.25rem;
|
|
412
|
+
opacity: 0.95;
|
|
413
|
+
}
|
|
414
|
+
|
|
397
415
|
.prosemirror-surface:focus-visible {
|
|
398
416
|
outline: none;
|
|
399
417
|
}
|