@beyondwork/docx-react-component 1.0.27 → 1.0.29
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/dist/canonical-document-BLEbzL2J.d.cts +844 -0
- package/dist/canonical-document-BLEbzL2J.d.ts +844 -0
- package/dist/chunk-2FJS5GZM.js +763 -0
- package/dist/chunk-2FJS5GZM.js.map +1 -0
- package/{src/core/commands/section-layout-commands.ts → dist/chunk-2OQBZS3F.js} +106 -340
- package/dist/chunk-2OQBZS3F.js.map +1 -0
- package/dist/chunk-2S7W4KFO.js +127 -0
- package/dist/chunk-2S7W4KFO.js.map +1 -0
- package/dist/chunk-2TG72QSW.js +3874 -0
- package/dist/chunk-2TG72QSW.js.map +1 -0
- package/{src/core/commands/table-structure-commands.ts → dist/chunk-36QNIZBO.js} +126 -315
- package/dist/chunk-36QNIZBO.js.map +1 -0
- package/dist/chunk-4AQOYAW4.js +3069 -0
- package/dist/chunk-4AQOYAW4.js.map +1 -0
- package/dist/chunk-4D5EWJ3P.js +77 -0
- package/dist/chunk-4D5EWJ3P.js.map +1 -0
- package/dist/chunk-5FN54NDH.js +2257 -0
- package/dist/chunk-5FN54NDH.js.map +1 -0
- package/dist/chunk-BOYGQYRQ.js +7306 -0
- package/dist/chunk-BOYGQYRQ.js.map +1 -0
- package/dist/chunk-CN3XMECL.js +212 -0
- package/dist/chunk-CN3XMECL.js.map +1 -0
- package/dist/chunk-EBI3BX6U.js +164 -0
- package/dist/chunk-EBI3BX6U.js.map +1 -0
- package/dist/chunk-EILUG3VB.js +1275 -0
- package/dist/chunk-EILUG3VB.js.map +1 -0
- package/dist/chunk-FUDY333O.js +70 -0
- package/dist/chunk-FUDY333O.js.map +1 -0
- package/dist/chunk-GBVOWFIK.js +1237 -0
- package/dist/chunk-GBVOWFIK.js.map +1 -0
- package/dist/chunk-H4TQ3H3Y.js +262 -0
- package/dist/chunk-H4TQ3H3Y.js.map +1 -0
- package/{src/core/commands/style-commands.ts → dist/chunk-JGB3IXZO.js} +40 -113
- package/dist/chunk-JGB3IXZO.js.map +1 -0
- package/dist/chunk-KD2QRQPY.js +4342 -0
- package/dist/chunk-KD2QRQPY.js.map +1 -0
- package/dist/chunk-KLMXQVYK.js +369 -0
- package/dist/chunk-KLMXQVYK.js.map +1 -0
- package/dist/chunk-KZUG5KFQ.js +214 -0
- package/dist/chunk-KZUG5KFQ.js.map +1 -0
- package/{src/core/state/text-transaction.ts → dist/chunk-QDAQ4CJU.js} +79 -236
- package/dist/chunk-QDAQ4CJU.js.map +1 -0
- package/{src/legal/bookmarks.ts → dist/chunk-RMH72RZI.js} +44 -130
- package/dist/chunk-RMH72RZI.js.map +1 -0
- package/dist/chunk-SWKWQZXM.js +117 -0
- package/dist/chunk-SWKWQZXM.js.map +1 -0
- package/{src/core/commands/formatting-commands.ts → dist/chunk-TJBP2K4T.js} +196 -536
- package/dist/chunk-TJBP2K4T.js.map +1 -0
- package/dist/chunk-TLCEAQDQ.js +542 -0
- package/dist/chunk-TLCEAQDQ.js.map +1 -0
- package/{src/core/commands/text-commands.ts → dist/chunk-UZXBISGO.js} +86 -142
- package/dist/chunk-UZXBISGO.js.map +1 -0
- package/dist/chunk-WGBAKP3Q.js +3220 -0
- package/dist/chunk-WGBAKP3Q.js.map +1 -0
- package/dist/compare/index.cjs +5475 -0
- package/dist/compare/index.cjs.map +1 -0
- package/dist/compare/index.d.cts +114 -0
- package/dist/compare/index.d.ts +114 -0
- package/dist/compare/index.js +731 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/core/commands/formatting-commands.cjs +828 -0
- package/dist/core/commands/formatting-commands.cjs.map +1 -0
- package/dist/core/commands/formatting-commands.d.cts +63 -0
- package/dist/core/commands/formatting-commands.d.ts +63 -0
- package/dist/core/commands/formatting-commands.js +37 -0
- package/dist/core/commands/formatting-commands.js.map +1 -0
- package/dist/core/commands/image-commands.cjs +2023 -0
- package/dist/core/commands/image-commands.cjs.map +1 -0
- package/dist/core/commands/image-commands.d.cts +58 -0
- package/dist/core/commands/image-commands.d.ts +58 -0
- package/dist/core/commands/image-commands.js +18 -0
- package/dist/core/commands/image-commands.js.map +1 -0
- package/dist/core/commands/section-layout-commands.cjs +477 -0
- package/dist/core/commands/section-layout-commands.cjs.map +1 -0
- package/dist/core/commands/section-layout-commands.d.cts +62 -0
- package/dist/core/commands/section-layout-commands.d.ts +62 -0
- package/dist/core/commands/section-layout-commands.js +21 -0
- package/dist/core/commands/section-layout-commands.js.map +1 -0
- package/dist/core/commands/style-commands.cjs +214 -0
- package/dist/core/commands/style-commands.cjs.map +1 -0
- package/dist/core/commands/style-commands.d.cts +13 -0
- package/dist/core/commands/style-commands.d.ts +13 -0
- package/dist/core/commands/style-commands.js +9 -0
- package/dist/core/commands/style-commands.js.map +1 -0
- package/dist/core/commands/table-structure-commands.cjs +1883 -0
- package/dist/core/commands/table-structure-commands.cjs.map +1 -0
- package/dist/core/commands/table-structure-commands.d.cts +59 -0
- package/dist/core/commands/table-structure-commands.d.ts +59 -0
- package/dist/core/commands/table-structure-commands.js +12 -0
- package/dist/core/commands/table-structure-commands.js.map +1 -0
- package/dist/core/commands/text-commands.cjs +2391 -0
- package/dist/core/commands/text-commands.cjs.map +1 -0
- package/dist/core/commands/text-commands.d.cts +24 -0
- package/dist/core/commands/text-commands.d.ts +24 -0
- package/dist/core/commands/text-commands.js +28 -0
- package/dist/core/commands/text-commands.js.map +1 -0
- package/dist/core/selection/mapping.cjs +200 -0
- package/dist/core/selection/mapping.cjs.map +1 -0
- package/dist/core/selection/mapping.d.cts +2 -0
- package/dist/core/selection/mapping.d.ts +2 -0
- package/dist/core/selection/mapping.js +31 -0
- package/dist/core/selection/mapping.js.map +1 -0
- package/dist/core/state/editor-state.cjs +2278 -0
- package/dist/core/state/editor-state.cjs.map +1 -0
- package/dist/core/state/editor-state.d.cts +2 -0
- package/dist/core/state/editor-state.d.ts +2 -0
- package/dist/core/state/editor-state.js +26 -0
- package/dist/core/state/editor-state.js.map +1 -0
- package/dist/index.cjs +38553 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +7856 -0
- package/dist/index.js.map +1 -0
- package/dist/io/docx-session.cjs +16236 -0
- package/dist/io/docx-session.cjs.map +1 -0
- package/dist/io/docx-session.d.cts +21 -0
- package/dist/io/docx-session.d.ts +21 -0
- package/dist/io/docx-session.js +18 -0
- package/dist/io/docx-session.js.map +1 -0
- package/dist/legal/index.cjs +3900 -0
- package/dist/legal/index.cjs.map +1 -0
- package/dist/legal/index.d.cts +86 -0
- package/dist/legal/index.d.ts +86 -0
- package/dist/legal/index.js +616 -0
- package/dist/legal/index.js.map +1 -0
- package/dist/public-types-7ZL_94cz.d.ts +1573 -0
- package/dist/public-types-CeMaDueh.d.cts +1573 -0
- package/dist/public-types.cjs +19 -0
- package/dist/public-types.cjs.map +1 -0
- package/dist/public-types.d.cts +2 -0
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.js +1 -0
- package/dist/public-types.js.map +1 -0
- package/dist/runtime/document-runtime.cjs +11140 -0
- package/dist/runtime/document-runtime.cjs.map +1 -0
- package/dist/runtime/document-runtime.d.cts +231 -0
- package/dist/runtime/document-runtime.d.ts +231 -0
- package/dist/runtime/document-runtime.js +21 -0
- package/dist/runtime/document-runtime.js.map +1 -0
- package/dist/structural-helpers-CilgOVhh.d.cts +10 -0
- package/dist/structural-helpers-q0Gd-eBN.d.ts +10 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.cjs +313 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +1 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +67 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +67 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.js +23 -0
- package/dist/ui-tailwind/editor-surface/search-plugin.js.map +1 -0
- package/dist/ui-tailwind/index.cjs +4833 -0
- package/dist/ui-tailwind/index.cjs.map +1 -0
- package/dist/ui-tailwind/index.d.cts +617 -0
- package/dist/ui-tailwind/index.d.ts +617 -0
- package/dist/ui-tailwind/index.js +575 -0
- package/dist/ui-tailwind/index.js.map +1 -0
- package/package.json +64 -54
- package/src/README.md +0 -85
- package/src/api/README.md +0 -26
- package/src/api/public-types.ts +0 -1418
- package/src/api/session-state.ts +0 -60
- package/src/compare/diff-engine.ts +0 -623
- package/src/compare/export-redlines.ts +0 -280
- package/src/compare/index.ts +0 -25
- package/src/compare/snapshot.ts +0 -97
- package/src/component-inventory.md +0 -99
- package/src/core/README.md +0 -10
- package/src/core/commands/README.md +0 -3
- package/src/core/commands/image-commands.ts +0 -373
- package/src/core/commands/index.ts +0 -1757
- package/src/core/commands/list-commands.ts +0 -565
- package/src/core/commands/paragraph-layout-commands.ts +0 -339
- package/src/core/commands/review-commands.ts +0 -108
- package/src/core/commands/structural-helpers.ts +0 -309
- package/src/core/schema/README.md +0 -3
- package/src/core/schema/text-schema.ts +0 -516
- package/src/core/search/search-text.ts +0 -357
- package/src/core/selection/README.md +0 -3
- package/src/core/selection/mapping.ts +0 -289
- package/src/core/selection/review-anchors.ts +0 -183
- package/src/core/state/README.md +0 -3
- package/src/core/state/editor-state.ts +0 -892
- package/src/formats/xlsx/io/parse-shared-strings.ts +0 -41
- package/src/formats/xlsx/io/parse-sheet.ts +0 -459
- package/src/formats/xlsx/io/parse-styles.ts +0 -59
- package/src/formats/xlsx/io/parse-workbook.ts +0 -75
- package/src/formats/xlsx/io/serialize-shared-strings.ts +0 -72
- package/src/formats/xlsx/io/serialize-sheet.ts +0 -333
- package/src/formats/xlsx/io/serialize-styles.ts +0 -98
- package/src/formats/xlsx/io/serialize-workbook.ts +0 -429
- package/src/formats/xlsx/io/xlsx-session.ts +0 -314
- package/src/formats/xlsx/model/cell.ts +0 -189
- package/src/formats/xlsx/model/sheet.ts +0 -326
- package/src/formats/xlsx/model/styles.ts +0 -118
- package/src/formats/xlsx/model/workbook.ts +0 -453
- package/src/formats/xlsx/runtime/cell-commands.ts +0 -567
- package/src/formats/xlsx/runtime/sheet-commands.ts +0 -206
- package/src/formats/xlsx/runtime/workbook-runtime.ts +0 -177
- package/src/formats/xlsx/runtime/workbook-transaction.ts +0 -822
- package/src/index.ts +0 -101
- package/src/io/README.md +0 -10
- package/src/io/docx-session.ts +0 -2882
- package/src/io/export/README.md +0 -3
- package/src/io/export/export-session.ts +0 -220
- package/src/io/export/minimal-docx.ts +0 -115
- package/src/io/export/reattach-preserved-parts.ts +0 -54
- package/src/io/export/serialize-comments.ts +0 -947
- package/src/io/export/serialize-footnotes.ts +0 -399
- package/src/io/export/serialize-headers-footers.ts +0 -372
- package/src/io/export/serialize-main-document.ts +0 -1376
- package/src/io/export/serialize-numbering.ts +0 -118
- package/src/io/export/serialize-revisions.ts +0 -389
- package/src/io/export/serialize-runtime-revisions.ts +0 -269
- package/src/io/export/serialize-tables.ts +0 -174
- package/src/io/export/split-review-boundaries.ts +0 -356
- package/src/io/normalize/README.md +0 -3
- package/src/io/normalize/normalize-text.ts +0 -639
- package/src/io/ooxml/README.md +0 -3
- package/src/io/ooxml/highlight-colors.ts +0 -39
- package/src/io/ooxml/numbering-sentinels.ts +0 -44
- package/src/io/ooxml/parse-comments.ts +0 -846
- package/src/io/ooxml/parse-complex-content.ts +0 -287
- package/src/io/ooxml/parse-fields.ts +0 -834
- package/src/io/ooxml/parse-footnotes.ts +0 -896
- package/src/io/ooxml/parse-headers-footers.ts +0 -1169
- package/src/io/ooxml/parse-inline-media.ts +0 -461
- package/src/io/ooxml/parse-main-document.ts +0 -2877
- package/src/io/ooxml/parse-numbering.ts +0 -432
- package/src/io/ooxml/parse-revisions.ts +0 -931
- package/src/io/ooxml/parse-settings.ts +0 -184
- package/src/io/ooxml/parse-shapes.ts +0 -296
- package/src/io/ooxml/parse-styles.ts +0 -463
- package/src/io/ooxml/parse-tables.ts +0 -618
- package/src/io/ooxml/parse-theme.ts +0 -346
- package/src/io/ooxml/part-manifest.ts +0 -136
- package/src/io/ooxml/revision-boundaries.ts +0 -351
- package/src/io/opc/README.md +0 -3
- package/src/io/opc/corrupt-package.ts +0 -166
- package/src/io/opc/docx-package.ts +0 -74
- package/src/io/opc/package-reader.ts +0 -325
- package/src/io/opc/package-writer.ts +0 -273
- package/src/io/source-package-provenance.ts +0 -241
- package/src/legal/cross-references.ts +0 -414
- package/src/legal/defined-terms.ts +0 -203
- package/src/legal/index.ts +0 -32
- package/src/legal/signature-blocks.ts +0 -259
- package/src/model/README.md +0 -3
- package/src/model/canonical-document.ts +0 -2632
- package/src/model/cds-1.0.0.ts +0 -212
- package/src/model/snapshot.ts +0 -649
- package/src/preservation/README.md +0 -3
- package/src/preservation/markup-compatibility.ts +0 -48
- package/src/preservation/opaque-fragment-store.ts +0 -89
- package/src/preservation/opaque-region.ts +0 -233
- package/src/preservation/package-preservation.ts +0 -113
- package/src/preservation/preserved-part-manifest.ts +0 -56
- package/src/preservation/relationship-retention.ts +0 -57
- package/src/preservation/store.ts +0 -185
- package/src/review/README.md +0 -16
- package/src/review/store/README.md +0 -3
- package/src/review/store/comment-anchors.ts +0 -70
- package/src/review/store/comment-remapping.ts +0 -154
- package/src/review/store/comment-store.ts +0 -331
- package/src/review/store/comment-thread.ts +0 -109
- package/src/review/store/revision-actions.ts +0 -394
- package/src/review/store/revision-store.ts +0 -312
- package/src/review/store/revision-types.ts +0 -171
- package/src/review/store/runtime-comment-store.ts +0 -43
- package/src/runtime/README.md +0 -3
- package/src/runtime/ai-action-policy.ts +0 -764
- package/src/runtime/collab-review-sync.ts +0 -254
- package/src/runtime/document-layout.ts +0 -332
- package/src/runtime/document-navigation.ts +0 -603
- package/src/runtime/document-runtime.ts +0 -3159
- package/src/runtime/document-search.ts +0 -145
- package/src/runtime/numbering-prefix.ts +0 -216
- package/src/runtime/page-layout-estimation.ts +0 -212
- package/src/runtime/read-only-diagnostics-runtime.ts +0 -241
- package/src/runtime/review-runtime.ts +0 -44
- package/src/runtime/revision-runtime.ts +0 -107
- package/src/runtime/session-capabilities.ts +0 -192
- package/src/runtime/story-context.ts +0 -164
- package/src/runtime/story-targeting.ts +0 -162
- package/src/runtime/surface-projection.ts +0 -1357
- package/src/runtime/table-commands.ts +0 -173
- package/src/runtime/table-schema.ts +0 -309
- package/src/runtime/view-state.ts +0 -477
- package/src/runtime/virtualized-rendering.ts +0 -258
- package/src/runtime/workflow-markup.ts +0 -353
- package/src/ui/README.md +0 -30
- package/src/ui/WordReviewEditor.tsx +0 -4097
- package/src/ui/browser-export.ts +0 -52
- package/src/ui/comments/README.md +0 -3
- package/src/ui/compatibility/README.md +0 -3
- package/src/ui/editor-command-bag.ts +0 -120
- package/src/ui/editor-runtime-boundary.ts +0 -1457
- package/src/ui/editor-shell-view.tsx +0 -142
- package/src/ui/editor-surface/README.md +0 -3
- package/src/ui/editor-surface-controller.tsx +0 -63
- package/src/ui/headless/comment-decoration-model.ts +0 -124
- package/src/ui/headless/preserve-editor-selection.ts +0 -5
- package/src/ui/headless/revision-decoration-model.ts +0 -128
- package/src/ui/headless/selection-helpers.ts +0 -54
- package/src/ui/headless/selection-toolbar-model.ts +0 -34
- package/src/ui/headless/use-editor-keyboard.ts +0 -103
- package/src/ui/review/README.md +0 -3
- package/src/ui/runtime-snapshot-selectors.ts +0 -197
- package/src/ui/shared/revision-filters.ts +0 -31
- package/src/ui/status/README.md +0 -3
- package/src/ui/theme/README.md +0 -3
- package/src/ui/toolbar/README.md +0 -3
- package/src/ui/workflow-surface-blocked-rails.ts +0 -94
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +0 -64
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +0 -129
- package/src/ui-tailwind/chrome/tw-layout-panel.tsx +0 -114
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +0 -34
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +0 -386
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +0 -186
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +0 -139
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +0 -128
- package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +0 -58
- package/src/ui-tailwind/chrome/use-before-unload.ts +0 -20
- package/src/ui-tailwind/editor-surface/perf-probe.ts +0 -179
- package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +0 -178
- package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +0 -31
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +0 -427
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +0 -123
- package/src/ui-tailwind/editor-surface/pm-schema.ts +0 -876
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +0 -504
- package/src/ui-tailwind/editor-surface/search-plugin.ts +0 -168
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +0 -61
- package/src/ui-tailwind/editor-surface/tw-caret.tsx +0 -12
- package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +0 -150
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +0 -129
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +0 -58
- package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +0 -151
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +0 -973
- package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +0 -111
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -436
- package/src/ui-tailwind/index.ts +0 -62
- package/src/ui-tailwind/page-chrome-model.ts +0 -27
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +0 -406
- package/src/ui-tailwind/review/tw-health-panel.tsx +0 -149
- package/src/ui-tailwind/review/tw-review-rail.tsx +0 -120
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +0 -164
- package/src/ui-tailwind/status/tw-status-bar.tsx +0 -61
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +0 -52
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +0 -1064
- package/src/ui-tailwind/tw-review-workspace.tsx +0 -1417
- package/src/validation/README.md +0 -3
- package/src/validation/compatibility-engine.ts +0 -634
- package/src/validation/compatibility-report.ts +0 -161
- package/src/validation/diagnostics.ts +0 -204
- package/src/validation/docx-comment-proof.ts +0 -707
- package/src/validation/import-diagnostics.ts +0 -128
- package/src/validation/low-priority-word-surfaces.ts +0 -373
- /package/{src → dist}/ui-tailwind/theme/editor-theme.css +0 -0
|
@@ -1,1064 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import * as Popover from "@radix-ui/react-popover";
|
|
4
|
-
import * as Select from "@radix-ui/react-select";
|
|
5
|
-
import * as Toggle from "@radix-ui/react-toggle";
|
|
6
|
-
import * as ToggleGroup from "@radix-ui/react-toggle-group";
|
|
7
|
-
import * as Tooltip from "@radix-ui/react-tooltip";
|
|
8
|
-
import {
|
|
9
|
-
AlignCenter,
|
|
10
|
-
AlignJustify,
|
|
11
|
-
AlignLeft,
|
|
12
|
-
AlignRight,
|
|
13
|
-
Baseline,
|
|
14
|
-
Bold,
|
|
15
|
-
ChevronDown,
|
|
16
|
-
Download,
|
|
17
|
-
Eye,
|
|
18
|
-
EyeOff,
|
|
19
|
-
FileText,
|
|
20
|
-
Highlighter,
|
|
21
|
-
ImagePlus,
|
|
22
|
-
Indent,
|
|
23
|
-
Italic,
|
|
24
|
-
MessageSquare,
|
|
25
|
-
Minus,
|
|
26
|
-
Monitor,
|
|
27
|
-
MoreHorizontal,
|
|
28
|
-
Outdent,
|
|
29
|
-
Plus,
|
|
30
|
-
Redo2,
|
|
31
|
-
Rows3,
|
|
32
|
-
Strikethrough,
|
|
33
|
-
Subscript,
|
|
34
|
-
Superscript,
|
|
35
|
-
ShieldAlert,
|
|
36
|
-
ShieldCheck,
|
|
37
|
-
Underline,
|
|
38
|
-
Undo2,
|
|
39
|
-
} from "lucide-react";
|
|
40
|
-
|
|
41
|
-
import type {
|
|
42
|
-
CompatibilityPanelSnapshot,
|
|
43
|
-
EditorStoryTarget,
|
|
44
|
-
EditorWarning,
|
|
45
|
-
FormattingStateSnapshot,
|
|
46
|
-
FormattingAlignment,
|
|
47
|
-
InsertImageOptions,
|
|
48
|
-
SectionBreakType,
|
|
49
|
-
StyleCatalogSnapshot,
|
|
50
|
-
WorkflowBlockedCommandReason,
|
|
51
|
-
WorkspaceMode,
|
|
52
|
-
ZoomLevel,
|
|
53
|
-
} from "../../api/public-types";
|
|
54
|
-
import type { SessionCapabilities } from "../../runtime/session-capabilities";
|
|
55
|
-
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
56
|
-
import { TwHealthPanel } from "../review/tw-health-panel";
|
|
57
|
-
import { TwToolbarIconButton } from "./tw-toolbar-icon-button";
|
|
58
|
-
|
|
59
|
-
export interface TwToolbarProps {
|
|
60
|
-
sourceLabel?: string;
|
|
61
|
-
capabilities?: SessionCapabilities;
|
|
62
|
-
compatibility?: CompatibilityPanelSnapshot;
|
|
63
|
-
warnings?: EditorWarning[];
|
|
64
|
-
blockedReasons?: WorkflowBlockedCommandReason[];
|
|
65
|
-
interactionPolicy?: ToolbarInteractionPolicy;
|
|
66
|
-
workspaceMode: WorkspaceMode;
|
|
67
|
-
zoomLevel?: ZoomLevel;
|
|
68
|
-
formattingState?: FormattingStateSnapshot;
|
|
69
|
-
styleCatalog?: StyleCatalogSnapshot;
|
|
70
|
-
/** Display toggle for tracked change decorations (not a runtime mutation toggle). */
|
|
71
|
-
showTrackedChanges: boolean;
|
|
72
|
-
/** Active story target — shows a breadcrumb when editing a secondary story. */
|
|
73
|
-
activeStory?: EditorStoryTarget;
|
|
74
|
-
/** Called when the user clicks the story breadcrumb to return to main body. */
|
|
75
|
-
onCloseStory?: () => void;
|
|
76
|
-
onUndo: () => void;
|
|
77
|
-
onRedo: () => void;
|
|
78
|
-
onSetParagraphStyle?: (styleId: string) => void;
|
|
79
|
-
onToggleBold?: () => void;
|
|
80
|
-
onToggleItalic?: () => void;
|
|
81
|
-
onToggleUnderline?: () => void;
|
|
82
|
-
onToggleStrikethrough?: () => void;
|
|
83
|
-
onToggleSuperscript?: () => void;
|
|
84
|
-
onToggleSubscript?: () => void;
|
|
85
|
-
onSetFontFamily?: (fontFamily: string) => void;
|
|
86
|
-
onSetFontSize?: (fontSize: number) => void;
|
|
87
|
-
onSetTextColor?: (color: string) => void;
|
|
88
|
-
onSetHighlightColor?: (color: string | null) => void;
|
|
89
|
-
onSetAlignment?: (alignment: FormattingAlignment) => void;
|
|
90
|
-
onOutdent?: () => void;
|
|
91
|
-
onIndent?: () => void;
|
|
92
|
-
onAddComment: () => void;
|
|
93
|
-
onInsertPageBreak?: () => void;
|
|
94
|
-
onInsertTable?: () => void;
|
|
95
|
-
onInsertSectionBreak?: (type: SectionBreakType) => void;
|
|
96
|
-
onInsertImage?: (options: InsertImageOptions) => void;
|
|
97
|
-
onExport: () => void;
|
|
98
|
-
onWorkspaceModeChange: (value: WorkspaceMode) => void;
|
|
99
|
-
onZoomChange?: (level: ZoomLevel) => void;
|
|
100
|
-
onShowTrackedChangesChange: (show: boolean) => void;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface ToolbarInteractionPolicy {
|
|
104
|
-
mode: "edit" | "suggest" | "comment" | "view" | "blocked";
|
|
105
|
-
canFormatText: boolean;
|
|
106
|
-
canInsertStructural: boolean;
|
|
107
|
-
canAddComment: boolean;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function getSupportedZoomPresets(): ReadonlyArray<number> {
|
|
111
|
-
return [75, 100, 125, 150];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const focusRingClass =
|
|
115
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
|
|
116
|
-
|
|
117
|
-
const FONT_FAMILIES = ["Arial", "Times New Roman", "Calibri", "Cambria", "Georgia", "Verdana"];
|
|
118
|
-
const FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36];
|
|
119
|
-
const TEXT_COLORS = ["#000000", "#434343", "#1660a8", "#1a7f37", "#cf222e", "#7a4f00"];
|
|
120
|
-
const HIGHLIGHT_COLORS = [
|
|
121
|
-
{ value: "#ffff00", label: "Yellow" },
|
|
122
|
-
{ value: "#00ff00", label: "Green" },
|
|
123
|
-
{ value: "#00ffff", label: "Cyan" },
|
|
124
|
-
{ value: "#ff69b4", label: "Pink" },
|
|
125
|
-
{ value: null, label: "None" },
|
|
126
|
-
] as const;
|
|
127
|
-
|
|
128
|
-
export function TwToolbar(props: TwToolbarProps) {
|
|
129
|
-
const caps = props.capabilities;
|
|
130
|
-
const workspaceMode = props.workspaceMode;
|
|
131
|
-
const isPageMode = workspaceMode === "page";
|
|
132
|
-
const paragraphStyles = props.styleCatalog?.paragraphs ?? [];
|
|
133
|
-
const zoomLevel = props.zoomLevel ?? 100;
|
|
134
|
-
const canEdit = props.interactionPolicy?.canFormatText ?? (caps ? caps.canEdit : false);
|
|
135
|
-
const canInsertStructural = props.interactionPolicy?.canInsertStructural ?? canEdit;
|
|
136
|
-
const canAddComment = props.interactionPolicy?.canAddComment ?? (caps ? caps.canAddComment : false);
|
|
137
|
-
const zoomLabel =
|
|
138
|
-
typeof zoomLevel === "number"
|
|
139
|
-
? `${zoomLevel}%`
|
|
140
|
-
: zoomLevel === "pageWidth"
|
|
141
|
-
? "Fit width"
|
|
142
|
-
: "Fit page";
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<header className="flex h-10 shrink-0 items-center gap-1 border-b border-border px-2">
|
|
146
|
-
{/* Left cluster: undo/redo + formatting */}
|
|
147
|
-
<div className="flex items-center gap-0.5">
|
|
148
|
-
<TwToolbarIconButton
|
|
149
|
-
icon={Undo2}
|
|
150
|
-
label="Undo"
|
|
151
|
-
disabled={caps ? !caps.canUndo : true}
|
|
152
|
-
onClick={props.onUndo}
|
|
153
|
-
/>
|
|
154
|
-
<TwToolbarIconButton
|
|
155
|
-
icon={Redo2}
|
|
156
|
-
label="Redo"
|
|
157
|
-
disabled={caps ? !caps.canRedo : true}
|
|
158
|
-
onClick={props.onRedo}
|
|
159
|
-
/>
|
|
160
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
161
|
-
|
|
162
|
-
<ToolbarParagraphStyleSelect
|
|
163
|
-
disabled={!canEdit || paragraphStyles.length === 0 || !props.onSetParagraphStyle}
|
|
164
|
-
styles={paragraphStyles}
|
|
165
|
-
value={props.formattingState?.paragraphStyleId}
|
|
166
|
-
onValueChange={props.onSetParagraphStyle}
|
|
167
|
-
/>
|
|
168
|
-
|
|
169
|
-
<ToolbarFontFamilySelect
|
|
170
|
-
disabled={!canEdit || !props.onSetFontFamily}
|
|
171
|
-
value={props.formattingState?.fontFamily}
|
|
172
|
-
onValueChange={props.onSetFontFamily}
|
|
173
|
-
/>
|
|
174
|
-
<ToolbarFontSizeSelect
|
|
175
|
-
disabled={!canEdit || !props.onSetFontSize}
|
|
176
|
-
value={props.formattingState?.fontSize}
|
|
177
|
-
onValueChange={props.onSetFontSize}
|
|
178
|
-
/>
|
|
179
|
-
|
|
180
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
181
|
-
|
|
182
|
-
<TwToolbarIconButton
|
|
183
|
-
icon={Bold}
|
|
184
|
-
label="Bold"
|
|
185
|
-
active={props.formattingState?.bold ?? false}
|
|
186
|
-
disabled={!canEdit}
|
|
187
|
-
onClick={props.onToggleBold}
|
|
188
|
-
/>
|
|
189
|
-
<TwToolbarIconButton
|
|
190
|
-
icon={Italic}
|
|
191
|
-
label="Italic"
|
|
192
|
-
active={props.formattingState?.italic ?? false}
|
|
193
|
-
disabled={!canEdit}
|
|
194
|
-
onClick={props.onToggleItalic}
|
|
195
|
-
/>
|
|
196
|
-
<TwToolbarIconButton
|
|
197
|
-
icon={Underline}
|
|
198
|
-
label="Underline"
|
|
199
|
-
active={props.formattingState?.underline ?? false}
|
|
200
|
-
disabled={!canEdit}
|
|
201
|
-
onClick={props.onToggleUnderline}
|
|
202
|
-
/>
|
|
203
|
-
<ToolbarFormattingOverflow
|
|
204
|
-
disabled={!canEdit}
|
|
205
|
-
formattingState={props.formattingState}
|
|
206
|
-
onToggleStrikethrough={props.onToggleStrikethrough}
|
|
207
|
-
onToggleSuperscript={props.onToggleSuperscript}
|
|
208
|
-
onToggleSubscript={props.onToggleSubscript}
|
|
209
|
-
/>
|
|
210
|
-
<ToolbarColorPopover
|
|
211
|
-
ariaLabel="Text color"
|
|
212
|
-
colors={TEXT_COLORS.map((value) => ({ value, label: value }))}
|
|
213
|
-
disabled={!canEdit || !props.onSetTextColor}
|
|
214
|
-
icon={<Baseline className="h-3.5 w-3.5" />}
|
|
215
|
-
onSelect={(value) => {
|
|
216
|
-
if (value) {
|
|
217
|
-
props.onSetTextColor?.(value);
|
|
218
|
-
}
|
|
219
|
-
}}
|
|
220
|
-
title="Text color"
|
|
221
|
-
/>
|
|
222
|
-
<ToolbarColorPopover
|
|
223
|
-
ariaLabel="Highlight color"
|
|
224
|
-
colors={HIGHLIGHT_COLORS.map((entry) => ({ value: entry.value, label: entry.label }))}
|
|
225
|
-
disabled={!canEdit || !props.onSetHighlightColor}
|
|
226
|
-
icon={<Highlighter className="h-3.5 w-3.5" />}
|
|
227
|
-
onSelect={(value) => props.onSetHighlightColor?.(value)}
|
|
228
|
-
title="Highlight color"
|
|
229
|
-
/>
|
|
230
|
-
<ToolbarAlignmentPopover
|
|
231
|
-
activeAlignment={props.formattingState?.alignment}
|
|
232
|
-
disabled={!canEdit || !props.onSetAlignment}
|
|
233
|
-
onSelect={(alignment) => props.onSetAlignment?.(alignment)}
|
|
234
|
-
/>
|
|
235
|
-
|
|
236
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
237
|
-
|
|
238
|
-
<TwToolbarIconButton
|
|
239
|
-
icon={Outdent}
|
|
240
|
-
label="Outdent"
|
|
241
|
-
disabled={!canEdit}
|
|
242
|
-
onClick={props.onOutdent}
|
|
243
|
-
/>
|
|
244
|
-
<TwToolbarIconButton
|
|
245
|
-
icon={Indent}
|
|
246
|
-
label="Indent"
|
|
247
|
-
disabled={!canEdit}
|
|
248
|
-
onClick={props.onIndent}
|
|
249
|
-
/>
|
|
250
|
-
<ToolbarInsertMenu
|
|
251
|
-
disabled={!canInsertStructural}
|
|
252
|
-
onInsertImage={props.onInsertImage}
|
|
253
|
-
onInsertPageBreak={props.onInsertPageBreak}
|
|
254
|
-
onInsertSectionBreak={props.onInsertSectionBreak}
|
|
255
|
-
onInsertTable={props.onInsertTable}
|
|
256
|
-
/>
|
|
257
|
-
|
|
258
|
-
{/* Story focus breadcrumb — visible when editing a secondary story */}
|
|
259
|
-
{props.activeStory && props.activeStory.kind !== "main" ? (
|
|
260
|
-
<>
|
|
261
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
262
|
-
<button
|
|
263
|
-
type="button"
|
|
264
|
-
onClick={props.onCloseStory}
|
|
265
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
266
|
-
className={`inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-accent hover:bg-accent-soft transition-colors outline-none ${focusRingClass}`}
|
|
267
|
-
aria-label={`Editing ${storyLabel(props.activeStory)} — click to return to main body`}
|
|
268
|
-
>
|
|
269
|
-
<span className="text-secondary">←</span>
|
|
270
|
-
{storyLabel(props.activeStory)}
|
|
271
|
-
</button>
|
|
272
|
-
</>
|
|
273
|
-
) : null}
|
|
274
|
-
</div>
|
|
275
|
-
|
|
276
|
-
{/* Center: document title */}
|
|
277
|
-
<div className="flex-1 text-center min-w-0">
|
|
278
|
-
<span className="text-sm font-medium truncate block">
|
|
279
|
-
{props.sourceLabel ?? "Untitled"}
|
|
280
|
-
</span>
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
{/* Right cluster: comment, track changes, markup, view, export */}
|
|
284
|
-
<div className="flex items-center gap-0.5">
|
|
285
|
-
<TwToolbarIconButton
|
|
286
|
-
icon={MessageSquare}
|
|
287
|
-
label="Add comment"
|
|
288
|
-
disabled={!canAddComment}
|
|
289
|
-
emphasis
|
|
290
|
-
onClick={props.onAddComment}
|
|
291
|
-
/>
|
|
292
|
-
|
|
293
|
-
<Tooltip.Root>
|
|
294
|
-
<Tooltip.Trigger asChild>
|
|
295
|
-
<Toggle.Root
|
|
296
|
-
pressed={props.showTrackedChanges}
|
|
297
|
-
onPressedChange={props.onShowTrackedChangesChange}
|
|
298
|
-
disabled={caps ? !caps.trackChangesSupported : false}
|
|
299
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
300
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none disabled:opacity-40 ${focusRingClass}`}
|
|
301
|
-
>
|
|
302
|
-
{props.showTrackedChanges ? <Eye className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />}
|
|
303
|
-
</Toggle.Root>
|
|
304
|
-
</Tooltip.Trigger>
|
|
305
|
-
<Tooltip.Portal>
|
|
306
|
-
<Tooltip.Content
|
|
307
|
-
className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50"
|
|
308
|
-
sideOffset={6}
|
|
309
|
-
>
|
|
310
|
-
{props.showTrackedChanges ? "Hide tracked changes" : "Show tracked changes"}
|
|
311
|
-
</Tooltip.Content>
|
|
312
|
-
</Tooltip.Portal>
|
|
313
|
-
</Tooltip.Root>
|
|
314
|
-
|
|
315
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
316
|
-
|
|
317
|
-
{/* View mode toggle group: Canvas (clean, flowing) / Page (layout-sensitive) */}
|
|
318
|
-
<ToggleGroup.Root
|
|
319
|
-
type="single"
|
|
320
|
-
value={workspaceMode}
|
|
321
|
-
onValueChange={(v: string) => {
|
|
322
|
-
if (v) props.onWorkspaceModeChange(v as WorkspaceMode);
|
|
323
|
-
}}
|
|
324
|
-
className="flex items-center gap-0.5"
|
|
325
|
-
>
|
|
326
|
-
<Tooltip.Root>
|
|
327
|
-
<Tooltip.Trigger asChild>
|
|
328
|
-
<ToggleGroup.Item
|
|
329
|
-
value="canvas"
|
|
330
|
-
aria-label="Canvas workspace"
|
|
331
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
332
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none ${focusRingClass}`}
|
|
333
|
-
>
|
|
334
|
-
<Monitor className="h-3.5 w-3.5" />
|
|
335
|
-
</ToggleGroup.Item>
|
|
336
|
-
</Tooltip.Trigger>
|
|
337
|
-
<Tooltip.Portal>
|
|
338
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
339
|
-
Canvas — clean flowing text
|
|
340
|
-
</Tooltip.Content>
|
|
341
|
-
</Tooltip.Portal>
|
|
342
|
-
</Tooltip.Root>
|
|
343
|
-
<Tooltip.Root>
|
|
344
|
-
<Tooltip.Trigger asChild>
|
|
345
|
-
<ToggleGroup.Item
|
|
346
|
-
value="page"
|
|
347
|
-
aria-label="Page workspace"
|
|
348
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
349
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none ${focusRingClass}`}
|
|
350
|
-
>
|
|
351
|
-
<FileText className="h-3.5 w-3.5" />
|
|
352
|
-
</ToggleGroup.Item>
|
|
353
|
-
</Tooltip.Trigger>
|
|
354
|
-
<Tooltip.Portal>
|
|
355
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
356
|
-
Page — layout-sensitive view
|
|
357
|
-
</Tooltip.Content>
|
|
358
|
-
</Tooltip.Portal>
|
|
359
|
-
</Tooltip.Root>
|
|
360
|
-
</ToggleGroup.Root>
|
|
361
|
-
|
|
362
|
-
{/* Zoom controls — visible in page mode */}
|
|
363
|
-
{isPageMode && props.onZoomChange ? (
|
|
364
|
-
<>
|
|
365
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
366
|
-
<div className="flex items-center gap-0.5">
|
|
367
|
-
<Tooltip.Root>
|
|
368
|
-
<Tooltip.Trigger asChild>
|
|
369
|
-
<button
|
|
370
|
-
type="button"
|
|
371
|
-
aria-label="Zoom out"
|
|
372
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
|
|
373
|
-
disabled={typeof zoomLevel === "number" && zoomLevel <= 50}
|
|
374
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
375
|
-
onClick={() => {
|
|
376
|
-
const current = typeof zoomLevel === "number" ? zoomLevel : 100;
|
|
377
|
-
props.onZoomChange!(Math.max(50, current - 10));
|
|
378
|
-
}}
|
|
379
|
-
>
|
|
380
|
-
<Minus className="h-3 w-3" />
|
|
381
|
-
</button>
|
|
382
|
-
</Tooltip.Trigger>
|
|
383
|
-
<Tooltip.Portal>
|
|
384
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
385
|
-
Zoom out
|
|
386
|
-
</Tooltip.Content>
|
|
387
|
-
</Tooltip.Portal>
|
|
388
|
-
</Tooltip.Root>
|
|
389
|
-
|
|
390
|
-
<Popover.Root>
|
|
391
|
-
<Tooltip.Root>
|
|
392
|
-
<Tooltip.Trigger asChild>
|
|
393
|
-
<Popover.Trigger asChild>
|
|
394
|
-
<button
|
|
395
|
-
type="button"
|
|
396
|
-
aria-label={`Zoom: ${zoomLabel}`}
|
|
397
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
398
|
-
className={`inline-flex h-7 items-center justify-center rounded-md px-1.5 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
|
|
399
|
-
>
|
|
400
|
-
{zoomLabel}
|
|
401
|
-
</button>
|
|
402
|
-
</Popover.Trigger>
|
|
403
|
-
</Tooltip.Trigger>
|
|
404
|
-
<Tooltip.Portal>
|
|
405
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
406
|
-
Zoom level
|
|
407
|
-
</Tooltip.Content>
|
|
408
|
-
</Tooltip.Portal>
|
|
409
|
-
</Tooltip.Root>
|
|
410
|
-
<Popover.Portal>
|
|
411
|
-
<Popover.Content
|
|
412
|
-
className="w-[140px] rounded-lg bg-canvas shadow-lg ring-1 ring-border p-1 z-50"
|
|
413
|
-
sideOffset={8}
|
|
414
|
-
align="center"
|
|
415
|
-
>
|
|
416
|
-
{getSupportedZoomPresets().map((preset) => {
|
|
417
|
-
const label = `${preset}%`;
|
|
418
|
-
return (
|
|
419
|
-
<Popover.Close key={preset} asChild>
|
|
420
|
-
<button
|
|
421
|
-
type="button"
|
|
422
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
423
|
-
className={`w-full rounded-md px-3 py-1.5 text-left text-xs transition-colors hover:bg-surface ${
|
|
424
|
-
zoomLevel === preset ? "font-semibold text-accent" : "text-primary"
|
|
425
|
-
}`}
|
|
426
|
-
onClick={() => props.onZoomChange!(preset)}
|
|
427
|
-
>
|
|
428
|
-
{label}
|
|
429
|
-
</button>
|
|
430
|
-
</Popover.Close>
|
|
431
|
-
);
|
|
432
|
-
})}
|
|
433
|
-
</Popover.Content>
|
|
434
|
-
</Popover.Portal>
|
|
435
|
-
</Popover.Root>
|
|
436
|
-
|
|
437
|
-
<Tooltip.Root>
|
|
438
|
-
<Tooltip.Trigger asChild>
|
|
439
|
-
<button
|
|
440
|
-
type="button"
|
|
441
|
-
aria-label="Zoom in"
|
|
442
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass}`}
|
|
443
|
-
disabled={typeof zoomLevel === "number" && zoomLevel >= 200}
|
|
444
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
445
|
-
onClick={() => {
|
|
446
|
-
const current = typeof zoomLevel === "number" ? zoomLevel : 100;
|
|
447
|
-
props.onZoomChange!(Math.min(200, current + 10));
|
|
448
|
-
}}
|
|
449
|
-
>
|
|
450
|
-
<Plus className="h-3 w-3" />
|
|
451
|
-
</button>
|
|
452
|
-
</Tooltip.Trigger>
|
|
453
|
-
<Tooltip.Portal>
|
|
454
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
455
|
-
Zoom in
|
|
456
|
-
</Tooltip.Content>
|
|
457
|
-
</Tooltip.Portal>
|
|
458
|
-
</Tooltip.Root>
|
|
459
|
-
</div>
|
|
460
|
-
</>
|
|
461
|
-
) : null}
|
|
462
|
-
|
|
463
|
-
{/* Health indicator */}
|
|
464
|
-
{props.compatibility && props.warnings ? (
|
|
465
|
-
<Popover.Root>
|
|
466
|
-
<Tooltip.Root>
|
|
467
|
-
<Tooltip.Trigger asChild>
|
|
468
|
-
<Popover.Trigger asChild>
|
|
469
|
-
<button
|
|
470
|
-
type="button"
|
|
471
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
472
|
-
className={`relative inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-surface hover:text-primary outline-none ${focusRingClass} ${
|
|
473
|
-
(caps?.healthIssueCount ?? 0) > 0 ? "text-secondary" : "text-secondary"
|
|
474
|
-
}`}
|
|
475
|
-
>
|
|
476
|
-
{(caps?.healthIssueCount ?? 0) > 0
|
|
477
|
-
? <ShieldAlert className="h-4 w-4" />
|
|
478
|
-
: <ShieldCheck className="h-4 w-4" />
|
|
479
|
-
}
|
|
480
|
-
{(caps?.healthIssueCount ?? 0) > 0 ? (
|
|
481
|
-
<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">
|
|
482
|
-
{caps?.healthIssueCount}
|
|
483
|
-
</span>
|
|
484
|
-
) : null}
|
|
485
|
-
</button>
|
|
486
|
-
</Popover.Trigger>
|
|
487
|
-
</Tooltip.Trigger>
|
|
488
|
-
<Tooltip.Portal>
|
|
489
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
490
|
-
{(caps?.healthIssueCount ?? 0) > 0
|
|
491
|
-
? `Document health — ${caps?.healthIssueCount} issue${(caps?.healthIssueCount ?? 0) !== 1 ? "s" : ""}`
|
|
492
|
-
: "Document health — no issues"
|
|
493
|
-
}
|
|
494
|
-
</Tooltip.Content>
|
|
495
|
-
</Tooltip.Portal>
|
|
496
|
-
</Tooltip.Root>
|
|
497
|
-
<Popover.Portal>
|
|
498
|
-
<Popover.Content
|
|
499
|
-
className="w-[360px] max-h-[480px] overflow-y-auto rounded-lg bg-canvas shadow-lg ring-1 ring-border p-3 z-50"
|
|
500
|
-
sideOffset={8}
|
|
501
|
-
align="end"
|
|
502
|
-
>
|
|
503
|
-
<TwHealthPanel
|
|
504
|
-
blockedReasons={props.blockedReasons}
|
|
505
|
-
compatibility={props.compatibility}
|
|
506
|
-
warnings={props.warnings}
|
|
507
|
-
/>
|
|
508
|
-
</Popover.Content>
|
|
509
|
-
</Popover.Portal>
|
|
510
|
-
</Popover.Root>
|
|
511
|
-
) : null}
|
|
512
|
-
|
|
513
|
-
<div className="mx-1 h-4 w-px bg-border" />
|
|
514
|
-
|
|
515
|
-
{/* Export button */}
|
|
516
|
-
<Tooltip.Root>
|
|
517
|
-
<Tooltip.Trigger asChild>
|
|
518
|
-
<button
|
|
519
|
-
type="button"
|
|
520
|
-
disabled={caps ? !caps.canExport : true}
|
|
521
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
522
|
-
className={[
|
|
523
|
-
"inline-flex h-7 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold transition-colors outline-none",
|
|
524
|
-
focusRingClass,
|
|
525
|
-
caps?.exportBlocked
|
|
526
|
-
? "cursor-not-allowed text-danger opacity-50"
|
|
527
|
-
: "text-accent hover:bg-accent-soft",
|
|
528
|
-
].join(" ")}
|
|
529
|
-
onClick={props.onExport}
|
|
530
|
-
>
|
|
531
|
-
<Download className="h-3.5 w-3.5" />
|
|
532
|
-
Export .docx
|
|
533
|
-
</button>
|
|
534
|
-
</Tooltip.Trigger>
|
|
535
|
-
<Tooltip.Portal>
|
|
536
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
537
|
-
{caps?.exportBlocked
|
|
538
|
-
? "Export blocked by unsupported content"
|
|
539
|
-
: "Export document"}
|
|
540
|
-
</Tooltip.Content>
|
|
541
|
-
</Tooltip.Portal>
|
|
542
|
-
</Tooltip.Root>
|
|
543
|
-
</div>
|
|
544
|
-
</header>
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
function ToolbarParagraphStyleSelect(props: {
|
|
549
|
-
styles: StyleCatalogSnapshot["paragraphs"];
|
|
550
|
-
value?: string;
|
|
551
|
-
disabled: boolean;
|
|
552
|
-
onValueChange?: (styleId: string) => void;
|
|
553
|
-
}) {
|
|
554
|
-
const resolvedValue =
|
|
555
|
-
props.value && props.styles.some((style) => style.styleId === props.value)
|
|
556
|
-
? props.value
|
|
557
|
-
: "";
|
|
558
|
-
|
|
559
|
-
return (
|
|
560
|
-
<Select.Root
|
|
561
|
-
disabled={props.disabled}
|
|
562
|
-
onValueChange={(value) => props.onValueChange?.(value)}
|
|
563
|
-
value={resolvedValue}
|
|
564
|
-
>
|
|
565
|
-
<Select.Trigger
|
|
566
|
-
aria-label="Paragraph style"
|
|
567
|
-
aria-disabled={props.disabled || undefined}
|
|
568
|
-
data-disabled={props.disabled ? "" : undefined}
|
|
569
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
570
|
-
className={`inline-flex h-7 min-w-[8.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2.5 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
571
|
-
>
|
|
572
|
-
<Select.Value placeholder="Style" />
|
|
573
|
-
<Select.Icon>
|
|
574
|
-
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
575
|
-
</Select.Icon>
|
|
576
|
-
</Select.Trigger>
|
|
577
|
-
<Select.Portal>
|
|
578
|
-
<Select.Content
|
|
579
|
-
align="start"
|
|
580
|
-
className="z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border"
|
|
581
|
-
position="popper"
|
|
582
|
-
sideOffset={8}
|
|
583
|
-
>
|
|
584
|
-
<Select.Viewport className="p-1">
|
|
585
|
-
{props.styles.map((style) => (
|
|
586
|
-
<Select.Item
|
|
587
|
-
className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass}`}
|
|
588
|
-
key={style.styleId}
|
|
589
|
-
value={style.styleId}
|
|
590
|
-
>
|
|
591
|
-
<Select.ItemText>{style.displayName}</Select.ItemText>
|
|
592
|
-
</Select.Item>
|
|
593
|
-
))}
|
|
594
|
-
</Select.Viewport>
|
|
595
|
-
</Select.Content>
|
|
596
|
-
</Select.Portal>
|
|
597
|
-
</Select.Root>
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function ToolbarFontFamilySelect(props: {
|
|
602
|
-
value?: string;
|
|
603
|
-
disabled: boolean;
|
|
604
|
-
onValueChange?: (fontFamily: string) => void;
|
|
605
|
-
}) {
|
|
606
|
-
const resolvedValue = props.value && FONT_FAMILIES.includes(props.value) ? props.value : "";
|
|
607
|
-
|
|
608
|
-
return (
|
|
609
|
-
<Select.Root
|
|
610
|
-
disabled={props.disabled}
|
|
611
|
-
onValueChange={(value) => props.onValueChange?.(value)}
|
|
612
|
-
value={resolvedValue}
|
|
613
|
-
>
|
|
614
|
-
<Select.Trigger
|
|
615
|
-
aria-label="Font family"
|
|
616
|
-
aria-disabled={props.disabled || undefined}
|
|
617
|
-
data-disabled={props.disabled ? "" : undefined}
|
|
618
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
619
|
-
className={`inline-flex h-7 min-w-[7rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
620
|
-
>
|
|
621
|
-
<Select.Value placeholder="Font" />
|
|
622
|
-
<Select.Icon>
|
|
623
|
-
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
624
|
-
</Select.Icon>
|
|
625
|
-
</Select.Trigger>
|
|
626
|
-
<Select.Portal>
|
|
627
|
-
<Select.Content
|
|
628
|
-
align="start"
|
|
629
|
-
className="z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border"
|
|
630
|
-
position="popper"
|
|
631
|
-
sideOffset={8}
|
|
632
|
-
>
|
|
633
|
-
<Select.Viewport className="p-1">
|
|
634
|
-
{FONT_FAMILIES.map((font) => (
|
|
635
|
-
<Select.Item
|
|
636
|
-
className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass}`}
|
|
637
|
-
key={font}
|
|
638
|
-
value={font}
|
|
639
|
-
>
|
|
640
|
-
<Select.ItemText>{font}</Select.ItemText>
|
|
641
|
-
</Select.Item>
|
|
642
|
-
))}
|
|
643
|
-
</Select.Viewport>
|
|
644
|
-
</Select.Content>
|
|
645
|
-
</Select.Portal>
|
|
646
|
-
</Select.Root>
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function ToolbarFontSizeSelect(props: {
|
|
651
|
-
value?: number;
|
|
652
|
-
disabled: boolean;
|
|
653
|
-
onValueChange?: (fontSize: number) => void;
|
|
654
|
-
}) {
|
|
655
|
-
const resolvedValue =
|
|
656
|
-
typeof props.value === "number" && FONT_SIZES.includes(props.value) ? String(props.value) : "";
|
|
657
|
-
|
|
658
|
-
return (
|
|
659
|
-
<Select.Root
|
|
660
|
-
disabled={props.disabled}
|
|
661
|
-
onValueChange={(value) => props.onValueChange?.(Number(value))}
|
|
662
|
-
value={resolvedValue}
|
|
663
|
-
>
|
|
664
|
-
<Select.Trigger
|
|
665
|
-
aria-label="Font size"
|
|
666
|
-
aria-disabled={props.disabled || undefined}
|
|
667
|
-
data-disabled={props.disabled ? "" : undefined}
|
|
668
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
669
|
-
className={`inline-flex h-7 min-w-[4rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
670
|
-
>
|
|
671
|
-
<Select.Value placeholder="Size" />
|
|
672
|
-
<Select.Icon>
|
|
673
|
-
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
674
|
-
</Select.Icon>
|
|
675
|
-
</Select.Trigger>
|
|
676
|
-
<Select.Portal>
|
|
677
|
-
<Select.Content
|
|
678
|
-
align="start"
|
|
679
|
-
className="z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border"
|
|
680
|
-
position="popper"
|
|
681
|
-
sideOffset={8}
|
|
682
|
-
>
|
|
683
|
-
<Select.Viewport className="p-1">
|
|
684
|
-
{FONT_SIZES.map((size) => (
|
|
685
|
-
<Select.Item
|
|
686
|
-
className={`flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass}`}
|
|
687
|
-
key={size}
|
|
688
|
-
value={String(size)}
|
|
689
|
-
>
|
|
690
|
-
<Select.ItemText>{size}</Select.ItemText>
|
|
691
|
-
</Select.Item>
|
|
692
|
-
))}
|
|
693
|
-
</Select.Viewport>
|
|
694
|
-
</Select.Content>
|
|
695
|
-
</Select.Portal>
|
|
696
|
-
</Select.Root>
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function ToolbarFormattingOverflow(props: {
|
|
701
|
-
disabled: boolean;
|
|
702
|
-
formattingState?: FormattingStateSnapshot;
|
|
703
|
-
onToggleStrikethrough?: () => void;
|
|
704
|
-
onToggleSuperscript?: () => void;
|
|
705
|
-
onToggleSubscript?: () => void;
|
|
706
|
-
}) {
|
|
707
|
-
const [open, setOpen] = React.useState(false);
|
|
708
|
-
|
|
709
|
-
return (
|
|
710
|
-
<div className="relative">
|
|
711
|
-
<Tooltip.Root>
|
|
712
|
-
<Tooltip.Trigger asChild>
|
|
713
|
-
<button
|
|
714
|
-
type="button"
|
|
715
|
-
aria-label="More text formatting"
|
|
716
|
-
aria-expanded={open}
|
|
717
|
-
disabled={props.disabled}
|
|
718
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
719
|
-
onClick={() => setOpen((value) => !value)}
|
|
720
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
721
|
-
>
|
|
722
|
-
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
723
|
-
</button>
|
|
724
|
-
</Tooltip.Trigger>
|
|
725
|
-
<Tooltip.Portal>
|
|
726
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
727
|
-
More text formatting
|
|
728
|
-
</Tooltip.Content>
|
|
729
|
-
</Tooltip.Portal>
|
|
730
|
-
</Tooltip.Root>
|
|
731
|
-
{open ? (
|
|
732
|
-
<div className="absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
733
|
-
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
734
|
-
Text styling
|
|
735
|
-
</div>
|
|
736
|
-
<div className="grid grid-cols-3 gap-1">
|
|
737
|
-
<ToolbarPopoverActionButton
|
|
738
|
-
active={props.formattingState?.strikethrough ?? false}
|
|
739
|
-
ariaLabel="Strikethrough"
|
|
740
|
-
disabled={props.disabled}
|
|
741
|
-
icon={<Strikethrough className="h-3.5 w-3.5" />}
|
|
742
|
-
onClick={() => {
|
|
743
|
-
props.onToggleStrikethrough?.();
|
|
744
|
-
setOpen(false);
|
|
745
|
-
}}
|
|
746
|
-
/>
|
|
747
|
-
<ToolbarPopoverActionButton
|
|
748
|
-
active={props.formattingState?.superscript ?? false}
|
|
749
|
-
ariaLabel="Superscript"
|
|
750
|
-
disabled={props.disabled}
|
|
751
|
-
icon={<Superscript className="h-3.5 w-3.5" />}
|
|
752
|
-
onClick={() => {
|
|
753
|
-
props.onToggleSuperscript?.();
|
|
754
|
-
setOpen(false);
|
|
755
|
-
}}
|
|
756
|
-
/>
|
|
757
|
-
<ToolbarPopoverActionButton
|
|
758
|
-
active={props.formattingState?.subscript ?? false}
|
|
759
|
-
ariaLabel="Subscript"
|
|
760
|
-
disabled={props.disabled}
|
|
761
|
-
icon={<Subscript className="h-3.5 w-3.5" />}
|
|
762
|
-
onClick={() => {
|
|
763
|
-
props.onToggleSubscript?.();
|
|
764
|
-
setOpen(false);
|
|
765
|
-
}}
|
|
766
|
-
/>
|
|
767
|
-
</div>
|
|
768
|
-
</div>
|
|
769
|
-
) : null}
|
|
770
|
-
</div>
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
function ToolbarColorPopover(props: {
|
|
775
|
-
ariaLabel: string;
|
|
776
|
-
colors: ReadonlyArray<{ value: string | null; label: string }>;
|
|
777
|
-
disabled: boolean;
|
|
778
|
-
icon: React.ReactNode;
|
|
779
|
-
title: string;
|
|
780
|
-
onSelect: (value: string | null) => void;
|
|
781
|
-
}) {
|
|
782
|
-
const [open, setOpen] = React.useState(false);
|
|
783
|
-
|
|
784
|
-
return (
|
|
785
|
-
<div className="relative">
|
|
786
|
-
<Tooltip.Root>
|
|
787
|
-
<Tooltip.Trigger asChild>
|
|
788
|
-
<button
|
|
789
|
-
type="button"
|
|
790
|
-
aria-label={props.ariaLabel}
|
|
791
|
-
aria-expanded={open}
|
|
792
|
-
disabled={props.disabled}
|
|
793
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
794
|
-
onClick={() => setOpen((value) => !value)}
|
|
795
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
796
|
-
>
|
|
797
|
-
{props.icon}
|
|
798
|
-
</button>
|
|
799
|
-
</Tooltip.Trigger>
|
|
800
|
-
<Tooltip.Portal>
|
|
801
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
802
|
-
{props.title}
|
|
803
|
-
</Tooltip.Content>
|
|
804
|
-
</Tooltip.Portal>
|
|
805
|
-
</Tooltip.Root>
|
|
806
|
-
{open ? (
|
|
807
|
-
<div className="absolute left-0 top-9 z-50 w-[180px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
808
|
-
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
809
|
-
{props.title}
|
|
810
|
-
</div>
|
|
811
|
-
<div className="grid grid-cols-3 gap-1">
|
|
812
|
-
{props.colors.map((color) => (
|
|
813
|
-
<button
|
|
814
|
-
key={`${props.ariaLabel}-${color.label}`}
|
|
815
|
-
type="button"
|
|
816
|
-
aria-label={`${props.title} ${color.label}`}
|
|
817
|
-
disabled={props.disabled}
|
|
818
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
819
|
-
onClick={() => {
|
|
820
|
-
props.onSelect(color.value);
|
|
821
|
-
setOpen(false);
|
|
822
|
-
}}
|
|
823
|
-
className={`inline-flex h-8 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 ${
|
|
824
|
-
color.value ? "" : "bg-surface"
|
|
825
|
-
} ${focusRingClass}`}
|
|
826
|
-
style={color.value ? { backgroundColor: color.value } : undefined}
|
|
827
|
-
>
|
|
828
|
-
{color.value ? <span className="sr-only">{color.label}</span> : "None"}
|
|
829
|
-
</button>
|
|
830
|
-
))}
|
|
831
|
-
</div>
|
|
832
|
-
</div>
|
|
833
|
-
) : null}
|
|
834
|
-
</div>
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
function ToolbarAlignmentPopover(props: {
|
|
839
|
-
activeAlignment?: FormattingAlignment;
|
|
840
|
-
disabled: boolean;
|
|
841
|
-
onSelect: (alignment: FormattingAlignment) => void;
|
|
842
|
-
}) {
|
|
843
|
-
const [open, setOpen] = React.useState(false);
|
|
844
|
-
const alignments = [
|
|
845
|
-
{ value: "left" as const, label: "Align left", icon: <AlignLeft className="h-3.5 w-3.5" /> },
|
|
846
|
-
{ value: "center" as const, label: "Align center", icon: <AlignCenter className="h-3.5 w-3.5" /> },
|
|
847
|
-
{ value: "right" as const, label: "Align right", icon: <AlignRight className="h-3.5 w-3.5" /> },
|
|
848
|
-
{ value: "justify" as const, label: "Align justify", icon: <AlignJustify className="h-3.5 w-3.5" /> },
|
|
849
|
-
];
|
|
850
|
-
|
|
851
|
-
return (
|
|
852
|
-
<div className="relative">
|
|
853
|
-
<Tooltip.Root>
|
|
854
|
-
<Tooltip.Trigger asChild>
|
|
855
|
-
<button
|
|
856
|
-
type="button"
|
|
857
|
-
aria-label="Paragraph alignment"
|
|
858
|
-
aria-expanded={open}
|
|
859
|
-
disabled={props.disabled}
|
|
860
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
861
|
-
onClick={() => setOpen((value) => !value)}
|
|
862
|
-
className={`inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
863
|
-
>
|
|
864
|
-
{(alignments.find((entry) => entry.value === props.activeAlignment) ?? alignments[0])?.icon}
|
|
865
|
-
</button>
|
|
866
|
-
</Tooltip.Trigger>
|
|
867
|
-
<Tooltip.Portal>
|
|
868
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
869
|
-
Paragraph alignment
|
|
870
|
-
</Tooltip.Content>
|
|
871
|
-
</Tooltip.Portal>
|
|
872
|
-
</Tooltip.Root>
|
|
873
|
-
{open ? (
|
|
874
|
-
<div className="absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
875
|
-
<div className="mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
876
|
-
Paragraph alignment
|
|
877
|
-
</div>
|
|
878
|
-
<div className="grid grid-cols-2 gap-1">
|
|
879
|
-
{alignments.map((entry) => (
|
|
880
|
-
<ToolbarPopoverActionButton
|
|
881
|
-
key={entry.value}
|
|
882
|
-
active={props.activeAlignment === entry.value}
|
|
883
|
-
ariaLabel={entry.label}
|
|
884
|
-
disabled={props.disabled}
|
|
885
|
-
icon={entry.icon}
|
|
886
|
-
onClick={() => {
|
|
887
|
-
props.onSelect(entry.value);
|
|
888
|
-
setOpen(false);
|
|
889
|
-
}}
|
|
890
|
-
/>
|
|
891
|
-
))}
|
|
892
|
-
</div>
|
|
893
|
-
</div>
|
|
894
|
-
) : null}
|
|
895
|
-
</div>
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function ToolbarInsertMenu(props: {
|
|
900
|
-
disabled: boolean;
|
|
901
|
-
onInsertPageBreak?: () => void;
|
|
902
|
-
onInsertTable?: () => void;
|
|
903
|
-
onInsertSectionBreak?: (type: SectionBreakType) => void;
|
|
904
|
-
onInsertImage?: (options: InsertImageOptions) => void;
|
|
905
|
-
}) {
|
|
906
|
-
const [open, setOpen] = React.useState(false);
|
|
907
|
-
|
|
908
|
-
async function handleImageChange(event: React.ChangeEvent<HTMLInputElement>): Promise<void> {
|
|
909
|
-
const file = event.target.files?.[0];
|
|
910
|
-
if (!file || props.disabled || !props.onInsertImage) {
|
|
911
|
-
event.target.value = "";
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
const data = new Uint8Array(await file.arrayBuffer());
|
|
915
|
-
props.onInsertImage({
|
|
916
|
-
data,
|
|
917
|
-
mimeType: file.type || "image/png",
|
|
918
|
-
altText: file.name,
|
|
919
|
-
});
|
|
920
|
-
setOpen(false);
|
|
921
|
-
event.target.value = "";
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
return (
|
|
925
|
-
<div className="relative">
|
|
926
|
-
<Tooltip.Root>
|
|
927
|
-
<Tooltip.Trigger asChild>
|
|
928
|
-
<button
|
|
929
|
-
type="button"
|
|
930
|
-
aria-label="Insert"
|
|
931
|
-
aria-expanded={open}
|
|
932
|
-
disabled={props.disabled}
|
|
933
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
934
|
-
onClick={() => setOpen((value) => !value)}
|
|
935
|
-
className={`inline-flex h-7 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
936
|
-
>
|
|
937
|
-
Insert
|
|
938
|
-
<ChevronDown className="h-3.5 w-3.5 text-tertiary" />
|
|
939
|
-
</button>
|
|
940
|
-
</Tooltip.Trigger>
|
|
941
|
-
<Tooltip.Portal>
|
|
942
|
-
<Tooltip.Content className="rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50" sideOffset={6}>
|
|
943
|
-
Insert
|
|
944
|
-
</Tooltip.Content>
|
|
945
|
-
</Tooltip.Portal>
|
|
946
|
-
</Tooltip.Root>
|
|
947
|
-
{open ? (
|
|
948
|
-
<div className="absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border">
|
|
949
|
-
<div className="space-y-1">
|
|
950
|
-
<ToolbarMenuButton
|
|
951
|
-
ariaLabel="Insert page break"
|
|
952
|
-
disabled={props.disabled || !props.onInsertPageBreak}
|
|
953
|
-
icon={<Minus className="h-3.5 w-3.5" />}
|
|
954
|
-
label="Page break"
|
|
955
|
-
onClick={() => {
|
|
956
|
-
props.onInsertPageBreak?.();
|
|
957
|
-
setOpen(false);
|
|
958
|
-
}}
|
|
959
|
-
/>
|
|
960
|
-
<ToolbarMenuButton
|
|
961
|
-
ariaLabel="Insert table"
|
|
962
|
-
disabled={props.disabled || !props.onInsertTable}
|
|
963
|
-
icon={<Rows3 className="h-3.5 w-3.5" />}
|
|
964
|
-
label="Table"
|
|
965
|
-
onClick={() => {
|
|
966
|
-
props.onInsertTable?.();
|
|
967
|
-
setOpen(false);
|
|
968
|
-
}}
|
|
969
|
-
/>
|
|
970
|
-
<label
|
|
971
|
-
className={`flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 text-xs font-medium text-primary transition-colors hover:bg-surface ${
|
|
972
|
-
props.disabled || !props.onInsertImage ? "pointer-events-none opacity-40" : ""
|
|
973
|
-
}`}
|
|
974
|
-
>
|
|
975
|
-
<ImagePlus className="h-3.5 w-3.5 text-secondary" />
|
|
976
|
-
<span>Image</span>
|
|
977
|
-
<input
|
|
978
|
-
accept="image/png,image/jpeg,image/gif"
|
|
979
|
-
aria-label="Insert image"
|
|
980
|
-
className="sr-only"
|
|
981
|
-
disabled={props.disabled || !props.onInsertImage}
|
|
982
|
-
type="file"
|
|
983
|
-
onChange={(event) => {
|
|
984
|
-
void handleImageChange(event);
|
|
985
|
-
}}
|
|
986
|
-
/>
|
|
987
|
-
</label>
|
|
988
|
-
<ToolbarMenuButton
|
|
989
|
-
ariaLabel="Insert next-page section break"
|
|
990
|
-
disabled={props.disabled || !props.onInsertSectionBreak}
|
|
991
|
-
icon={<FileText className="h-3.5 w-3.5" />}
|
|
992
|
-
label="Next-page section break"
|
|
993
|
-
onClick={() => {
|
|
994
|
-
props.onInsertSectionBreak?.("nextPage");
|
|
995
|
-
setOpen(false);
|
|
996
|
-
}}
|
|
997
|
-
/>
|
|
998
|
-
</div>
|
|
999
|
-
</div>
|
|
1000
|
-
) : null}
|
|
1001
|
-
</div>
|
|
1002
|
-
);
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
function ToolbarPopoverActionButton(props: {
|
|
1006
|
-
active: boolean;
|
|
1007
|
-
ariaLabel: string;
|
|
1008
|
-
disabled: boolean;
|
|
1009
|
-
icon: React.ReactNode;
|
|
1010
|
-
onClick?: () => void;
|
|
1011
|
-
}) {
|
|
1012
|
-
return (
|
|
1013
|
-
<button
|
|
1014
|
-
type="button"
|
|
1015
|
-
aria-label={props.ariaLabel}
|
|
1016
|
-
aria-pressed={props.active}
|
|
1017
|
-
disabled={props.disabled}
|
|
1018
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1019
|
-
onClick={props.onClick}
|
|
1020
|
-
className={`inline-flex h-8 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${
|
|
1021
|
-
props.active ? "bg-accent-soft text-accent" : "bg-canvas text-secondary hover:bg-surface"
|
|
1022
|
-
} ${focusRingClass}`}
|
|
1023
|
-
>
|
|
1024
|
-
{props.icon}
|
|
1025
|
-
</button>
|
|
1026
|
-
);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
function ToolbarMenuButton(props: {
|
|
1030
|
-
ariaLabel: string;
|
|
1031
|
-
disabled: boolean;
|
|
1032
|
-
icon: React.ReactNode;
|
|
1033
|
-
label: string;
|
|
1034
|
-
onClick?: () => void;
|
|
1035
|
-
}) {
|
|
1036
|
-
return (
|
|
1037
|
-
<button
|
|
1038
|
-
type="button"
|
|
1039
|
-
aria-label={props.ariaLabel}
|
|
1040
|
-
disabled={props.disabled}
|
|
1041
|
-
onMouseDown={preserveEditorSelectionMouseDown}
|
|
1042
|
-
onClick={props.onClick}
|
|
1043
|
-
className={`flex h-8 w-full items-center gap-2 rounded-md px-2 text-left text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass}`}
|
|
1044
|
-
>
|
|
1045
|
-
<span className="text-secondary">{props.icon}</span>
|
|
1046
|
-
<span>{props.label}</span>
|
|
1047
|
-
</button>
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
function storyLabel(target: EditorStoryTarget): string {
|
|
1052
|
-
switch (target.kind) {
|
|
1053
|
-
case "header":
|
|
1054
|
-
return `Header (${target.variant})`;
|
|
1055
|
-
case "footer":
|
|
1056
|
-
return `Footer (${target.variant})`;
|
|
1057
|
-
case "footnote":
|
|
1058
|
-
return "Footnote";
|
|
1059
|
-
case "endnote":
|
|
1060
|
-
return "Endnote";
|
|
1061
|
-
default:
|
|
1062
|
-
return "Document";
|
|
1063
|
-
}
|
|
1064
|
-
}
|