ooxml-ui 0.31.0 → 1.1.0
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/README.md +16 -0
- package/custom-elements.json +10 -0
- package/dist/{chunk-OXKWNNNJ.js → chunk-3FPW7M6U.js} +1 -1
- package/dist/{chunk-XDUYS4KK.js → chunk-BEN3453I.js} +1 -1
- package/dist/{chunk-HZ422LTR.js → chunk-BOX43CZD.js} +1 -0
- package/dist/{chunk-3GE2NHNG.js → chunk-KVKKXRL7.js} +121 -83
- package/dist/{chunk-2QW4QE56.js → chunk-M5DTJSOV.js} +105 -70
- package/dist/{chunk-ZV2Z4IWH.js → chunk-USDLALB5.js} +4 -1
- package/dist/{chunk-RAZAJMHK.js → chunk-ZU7GX4SY.js} +4 -4
- package/dist/controls.js +4 -4
- package/dist/docx/hard-break-command.d.ts +1 -3
- package/dist/docx/link-commands.d.ts +4 -15
- package/dist/docx/localization-strings.d.ts +1 -1
- package/dist/docx/localization.d.ts +0 -1
- package/dist/docx/note-editor.d.ts +3 -2
- package/dist/docx/run-styles.d.ts +1 -1
- package/dist/docx/schema.d.ts +1 -1
- package/dist/docx/toggle-commands.d.ts +2 -8
- package/dist/docx.js +118 -321
- package/dist/form/gradient-direction-gallery.d.ts +19 -0
- package/dist/form/gradient-gallery-preview.d.ts +3 -0
- package/dist/form/gradient-preset-gallery.d.ts +16 -0
- package/dist/form/gradient-stop-track.d.ts +20 -0
- package/dist/form/number-range.d.ts +12 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.js +7 -7
- package/dist/menu/menu-button.d.ts +10 -0
- package/dist/menu/menu-button.d.ts.map +1 -1
- package/dist/pptx/{chart-3d-view-scene-IANLDFIE.mjs → chart-3d-view-scene-2KRQ5667.mjs} +2 -2
- package/dist/pptx/{chart-3d-view-scene-3DK5TUBZ.cjs → chart-3d-view-scene-36DMZWZ3.cjs} +37 -37
- package/dist/pptx/{chunk-C42ZL35K.mjs → chunk-ADOC5JLI.mjs} +1 -1
- package/dist/pptx/{chunk-QM4FGZG4.cjs → chunk-AIL3BNZS.cjs} +31 -131
- package/dist/pptx/{chunk-WLETAW3T.cjs → chunk-CJU4P5FT.cjs} +33 -13
- package/dist/pptx/{chunk-6SCK57RJ.cjs → chunk-KPZWYF44.cjs} +220 -342
- package/dist/pptx/{chunk-XQZWZMGZ.cjs → chunk-SM3CWDIQ.cjs} +7 -17
- package/dist/pptx/{chunk-JSBGBVBH.mjs → chunk-VRFNAF3Q.mjs} +7 -17
- package/dist/pptx/{chunk-EVGSFPUV.mjs → chunk-Y2W4SZER.mjs} +71 -202
- package/dist/pptx/{chunk-7JEMQW6R.mjs → chunk-YARCML7O.mjs} +11 -122
- package/dist/pptx/{chunk-OHE5UXST.cjs → chunk-YHA6XAB3.cjs} +1 -1
- package/dist/pptx/{chunk-X2F6PA2D.mjs → chunk-YOGDIDPB.mjs} +22 -3
- package/dist/pptx/dom/index.cjs +238 -238
- package/dist/pptx/dom/index.mjs +8 -8
- package/dist/pptx/export/html2canvas-clone.cjs +2 -2
- package/dist/pptx/export/html2canvas-clone.mjs +1 -1
- package/dist/pptx/i18n/index.cjs +6 -6
- package/dist/pptx/i18n/index.mjs +1 -1
- package/dist/pptx/i18n/translator.d.ts.map +1 -1
- package/dist/pptx/index.cjs +3006 -2990
- package/dist/pptx/index.mjs +5 -5
- package/dist/pptx/render/chart-combo-stock.cjs +3 -3
- package/dist/pptx/render/chart-combo-stock.mjs +1 -1
- package/dist/pptx/render/chart-manual-layout.d.ts +2 -20
- package/dist/pptx/render/chart-manual-layout.d.ts.map +1 -1
- package/dist/pptx/render/chart-overlays.cjs +7 -7
- package/dist/pptx/render/chart-overlays.mjs +1 -1
- package/dist/pptx/render/chart-surface-treemap.cjs +4 -4
- package/dist/pptx/render/chart-surface-treemap.mjs +1 -1
- package/dist/pptx/render/chart-svg-def-types.d.ts +6 -1
- package/dist/pptx/render/chart-svg-def-types.d.ts.map +1 -1
- package/dist/pptx/render/chart-view-model-dom-helpers.d.ts +1 -1
- package/dist/pptx/render/chart-view-model-dom-helpers.d.ts.map +1 -1
- package/dist/pptx/render/chart-view-model.cjs +41 -41
- package/dist/pptx/render/chart-view-model.mjs +1 -1
- package/dist/pptx/render/chart-waterfall-map.cjs +6 -6
- package/dist/pptx/render/chart-waterfall-map.mjs +1 -1
- package/dist/pptx/render/connector-elbow-geometry.d.ts +7 -43
- package/dist/pptx/render/connector-elbow-geometry.d.ts.map +1 -1
- package/dist/pptx/render/element-interaction.d.ts +1 -1
- package/dist/pptx/render/element-interaction.d.ts.map +1 -1
- package/dist/pptx/render/gradient-picker.d.ts.map +1 -1
- package/dist/pptx/render/index.d.ts +2 -0
- package/dist/pptx/render/index.d.ts.map +1 -1
- package/dist/pptx/render/path-gradient-rect.d.ts +1 -75
- package/dist/pptx/render/path-gradient-rect.d.ts.map +1 -1
- package/dist/pptx/render/rotate-handle-placement.d.ts +1 -27
- package/dist/pptx/render/rotate-handle-placement.d.ts.map +1 -1
- package/dist/pptx/render/rotation-drag.d.ts +1 -7
- package/dist/pptx/render/rotation-drag.d.ts.map +1 -1
- package/dist/pptx/render/viewer-root-options.d.ts +46 -0
- package/dist/pptx/render/viewer-root-options.d.ts.map +1 -0
- package/dist/pptx/web-components/registration-contract.d.ts.map +1 -1
- package/dist/presence.js +2 -2
- package/dist/registry.d.ts +2 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/smartart.js +2 -2
- package/dist/teams/app/files-panel.d.ts +8 -3
- package/dist/teams/app/teams-app.d.ts +1 -0
- package/dist/teams/app/teams-settings.d.ts +6 -0
- package/dist/teams.js +5 -5
- package/dist/visio/render-fill.d.ts +2 -1
- package/dist/visio/render-svg.d.ts +2 -0
- package/dist/visio/ribbon-action.d.ts +8 -1
- package/dist/visio/ribbon-parts.d.ts +2 -1
- package/dist/visio/ribbon-router.d.ts +4 -2
- package/dist/visio/viewer-commands.d.ts +3 -3
- package/dist/visio/viewer-draw-tool.d.ts +16 -6
- package/dist/visio/viewer-handle-events.d.ts +19 -0
- package/dist/visio/viewer-line-endpoints.d.ts +14 -0
- package/dist/visio/viewer-rotation-handle.d.ts +14 -0
- package/dist/visio/viewer-rotation-preview.d.ts +7 -0
- package/dist/visio.js +742 -90
- package/dist/xlsx/chart-area-pane.d.ts +9 -0
- package/dist/xlsx/chart-fill-binding.d.ts +23 -0
- package/dist/xlsx/chart-gradient-preview.d.ts +8 -0
- package/dist/xlsx/chart-series-fill.d.ts +2 -1
- package/dist/xlsx/chart-series-gradient-type.d.ts +9 -0
- package/dist/xlsx/chart-series-gradient.d.ts +8 -0
- package/dist/xlsx/chart-series-pane.d.ts +2 -1
- package/dist/xlsx/chart-series-range-preview.d.ts +7 -0
- package/dist/xlsx/chart-series-transparency.d.ts +2 -1
- package/dist/xlsx/grid/measure.d.ts +5 -0
- package/dist/xlsx/grid/styles.d.ts +1 -1
- package/dist/xlsx.js +965 -105
- package/package.json +3 -3
package/dist/visio.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { attachKeyTips, registerControls, readOfficeProfile, sanitizeOfficeProfile, writeOfficeProfile } from './chunk-
|
|
2
|
-
import { getIcon, registerIcon } from './chunk-
|
|
3
|
-
import { definePresence } from './chunk-
|
|
4
|
-
import './chunk-
|
|
1
|
+
import { attachKeyTips, registerControls, readOfficeProfile, sanitizeOfficeProfile, writeOfficeProfile } from './chunk-M5DTJSOV.js';
|
|
2
|
+
import { getIcon, registerIcon } from './chunk-BOX43CZD.js';
|
|
3
|
+
import { definePresence } from './chunk-3FPW7M6U.js';
|
|
4
|
+
import './chunk-USDLALB5.js';
|
|
5
5
|
import './chunk-JFJXBYRD.js';
|
|
6
|
-
import { FOREIGN_VECTOR_SCENE_LIMITS, EMPTY_TEXT_SEARCH, EMPTY_EDIT_STATE, assertViewableDocument, indexDocumentText, searchDocumentText, validateSearchQuery, MAX_INPUT_BYTES, DocumentHistory, snapshotEdits, ForeignVectorBudget, VISIO_IDENTITY_TRANSFORM, composeVisioTransform, compatibilityNotes, compatibilityText, visioGradientInstances, visioRenderedGradientStopCount, inspectForeignVectorResource, copySnapshotScene, editErrorMessage, eventKeys, propertyKeys, TEXT_SEARCH_LIMITS, isEditCancellation, visioPageInsertCommand, textSearchStatus, visioFillPatternTransform, visioRenderedGradientStops,
|
|
6
|
+
import { FOREIGN_VECTOR_SCENE_LIMITS, EMPTY_TEXT_SEARCH, EMPTY_EDIT_STATE, assertViewableDocument, indexDocumentText, searchDocumentText, validateSearchQuery, MAX_INPUT_BYTES, DocumentHistory, snapshotEdits, ForeignVectorBudget, VISIO_IDENTITY_TRANSFORM, composeVisioTransform, compatibilityNotes, compatibilityText, visioGradientInstances, visioRenderedGradientStopCount, inspectForeignVectorResource, copySnapshotScene, editErrorMessage, eventKeys, propertyKeys, TEXT_SEARCH_LIMITS, isEditCancellation, visioPageInsertCommand, visioQuarterTurnCommand, visioLocalRotationShape, visioStraightLineHandles, visioPageEditToDrawing, textSearchStatus, visioRotationPreviewTransform, visioFillPatternTransform, visioRenderedGradientStops, visioStrokeGradient, visioNextShapeId } from 'ooxml-core/visio/ui';
|
|
7
7
|
export { TEXT_SEARCH_LIMITS, compatibilityNotes, compatibilityText, demoDocument, eventKeys, propertyKeys } from 'ooxml-core/visio/ui';
|
|
8
8
|
import { VISIO_VISIBILITY_LIMITS, loadVisio, getVisioPageLayers, resolveVisioPageVisibility, visioFilledArrow, layoutVisioFilledArrowLine, visioOpenArrowPath } from 'ooxml-core/visio';
|
|
9
|
+
import { resolveRotateHandlePlacement, createRotationDrag } from 'ooxml-core/geometry';
|
|
9
10
|
|
|
10
11
|
function createWorkerEditor(factory, timeoutMs = 2e4) {
|
|
11
12
|
const create = (() => {
|
|
@@ -1043,10 +1044,10 @@ summary::marker { color:var(--_vv-muted); font-size:10px; }
|
|
|
1043
1044
|
var backstage_default = "/* Visio's File tab and backstage. */\n:host {\n position: relative;\n}\n/* The shared office-ui-backstage draws the File column; these style Visio's page content. */\n.backstage {\n --office-accent: var(--_vv-accent);\n --office-accent-foreground: var(--vv-accent-ink, #fff);\n}\n.backstage-page h1 {\n margin: 0 0 24px;\n font-size: 34px;\n font-weight: 300;\n}\n.backstage-page h2 {\n margin: 20px 0 8px;\n font-size: 16px;\n font-weight: 600;\n}\n.backstage-file {\n margin-top: 0 !important;\n font-size: 20px !important;\n font-weight: 400 !important;\n overflow-wrap: anywhere;\n}\n.backstage-facts {\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: 8px 24px;\n max-width: 520px;\n margin: 0;\n font-size: 13px;\n}\n.backstage-facts dt {\n color: var(--_vv-muted);\n}\n.backstage-facts dd {\n margin: 0;\n overflow-wrap: anywhere;\n}\n.backstage-action {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n width: min(420px, 100%);\n min-height: 64px;\n margin: 0 0 10px;\n padding: 10px 16px;\n text-align: left;\n background: var(--_vv-surface);\n}\n.backstage-action strong {\n font-size: 14px;\n font-weight: 600;\n}\n.backstage-action span,\n.backstage-muted,\n.backstage-note-count {\n color: var(--_vv-muted);\n font-size: 12px;\n}\n.backstage-templates {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n.backstage-templates .backstage-action,\n.backstage-templates ::slotted(*) {\n width: 200px;\n min-height: 120px;\n}\n/* Account: the shared profile editor, then Visio's product information. */\n.backstage-page office-ui-account {\n max-width: 560px;\n}\n.backstage-product {\n margin-top: 24px;\n}\n/* Share with people: a live session between windows of this browser. */\n.share-panel {\n max-width: 560px;\n margin-bottom: 16px;\n}\n.share-panel h2 {\n margin-top: 0 !important;\n}\n.share-field {\n display: grid;\n gap: 4px;\n max-width: 320px;\n margin: 12px 0;\n font-size: 13px;\n}\n.share-room {\n min-height: 30px;\n padding: 2px 8px;\n border: 1px solid var(--_vv-border);\n border-radius: 3px;\n background: var(--_vv-surface);\n color: inherit;\n font: inherit;\n}\n.share-buttons button {\n min-height: 32px;\n padding: 0 16px;\n}\n.share-buttons button[hidden] {\n display: none;\n}\n.share-status {\n min-height: 20px;\n font-size: 13px;\n}\n";
|
|
1044
1045
|
|
|
1045
1046
|
// raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/visio/styles/canvas.css?raw
|
|
1046
|
-
var canvas_default = "/*\n * Drawing canvas focus. SVG outlines are drawn in drawing units (inches here), so a browser\n * focus ring on a shape becomes a page-sized blob. Shapes show focus with a dashed accent\n * stroke instead, only for keyboard focus; selection keeps its solid accent stroke.\n */\n.paper [data-shape-id]:focus,\n.paper [data-shape-id]:focus-visible {\n outline: none;\n}\n.paper [data-shape-id]:focus-visible > [data-geometry] {\n stroke: var(--vv-focus, var(--_vv-accent)) !important;\n stroke-width: 0.04 !important;\n stroke-dasharray: 0.08 0.05;\n}\n";
|
|
1047
|
+
var canvas_default = "/*\n * Drawing canvas focus. SVG outlines are drawn in drawing units (inches here), so a browser\n * focus ring on a shape becomes a page-sized blob. Shapes show focus with a dashed accent\n * stroke instead, only for keyboard focus; selection keeps its solid accent stroke.\n */\n.paper [data-shape-id]:focus,\n.paper [data-shape-id]:focus-visible {\n outline: none;\n}\n.paper [data-shape-id]:focus-visible > [data-geometry] {\n stroke: var(--vv-focus, var(--_vv-accent)) !important;\n stroke-width: 0.04 !important;\n stroke-dasharray: 0.08 0.05;\n}\n\n.paper [data-line-endpoint],\n.rotation-overlay [data-rotation-handle] {\n fill: var(--_vv-accent);\n stroke: white;\n stroke-width: 1.5px;\n cursor: crosshair;\n touch-action: none;\n pointer-events: all;\n}\n.paper .endpoint-preview,\n.paper .rotation-preview,\n.rotation-overlay .rotation-stem {\n stroke: var(--_vv-accent);\n stroke-width: 1.5px;\n stroke-dasharray: 5 3;\n pointer-events: none;\n}\n\n.rotation-overlay {\n position: absolute;\n z-index: 1;\n pointer-events: none;\n}\n";
|
|
1047
1048
|
|
|
1048
1049
|
// raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/visio/styles/ribbon.css?raw
|
|
1049
|
-
var ribbon_default = "/*\n * Microsoft Visio ribbon layout on the shared ooxml-ui controls. Viewer theme tokens feed the\n * shared --office-* tokens, so host themes still apply. Loaded after the legacy styles.ts.\n */\n:host {\n --office-foreground: var(--_vv-ink);\n --office-muted-foreground: var(--_vv-muted);\n --office-background: var(--_vv-surface);\n --office-surface: var(--_vv-accent-soft);\n --office-border: var(--_vv-border);\n --office-accent: var(--_vv-accent);\n --office-accent-foreground: var(--vv-accent-ink, #fff);\n --office-ring: var(--vv-focus, var(--_vv-accent));\n --office-font: 'Segoe UI', Arial, sans-serif;\n}\n\n/* Tab row: Quick Access Toolbar, then Visio's tabs. */\n.ribbon-head {\n display: flex;\n align-items: stretch;\n min-width: 0;\n border-bottom: 1px solid var(--_vv-border);\n}\n.qat {\n display: flex;\n align-items: center;\n gap: 0;\n padding: 0 6px;\n border-right: 1px solid var(--_vv-border);\n}\n.qat office-ui-button {\n --office-target-size: 30px;\n}\n.ribbon-head .ribbon-tabs {\n flex: 0 1 auto;\n min-width: 0;\n height: 36px;\n border-bottom: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.ribbon-head .ribbon-tabs button {\n flex: none;\n min-height: 35px;\n padding: 6px 12px;\n font-size: 13px;\n}\n\n/* Panels: groups of large commands and three-row stacks, as dense as Visio's. */\n.ribbon-content {\n min-height: 96px;\n padding: 2px 2px 0;\n}\n.ribbon-row {\n display: flex;\n flex-wrap: nowrap;\n align-items: stretch;\n gap: 0;\n padding: 0;\n border: 0;\n background: transparent;\n}\n.ribbon-row office-ui-ribbon-group,\n.tools-content office-ui-ribbon-group {\n min-width: 0;\n}\noffice-ui-ribbon-group office-ui-button,\noffice-ui-ribbon-group office-ui-menu-button {\n --office-target-size: 24px;\n}\noffice-ui-ribbon-group [data-size='large'] {\n --office-target-size: 30px;\n}\noffice-ui-button[variant='stacked']::part(button) {\n min-width: 48px;\n min-height: 66px;\n padding: 2px 6px;\n font-size: 12px;\n}\noffice-ui-menu-button[variant='stacked']::part(button) {\n min-width: 48px;\n min-height: 50px;\n font-size: 12px;\n}\noffice-ui-ribbon-group [data-size='small']::part(button) {\n justify-content: flex-start;\n font-size: 12px;\n}\noffice-ui-ribbon-stack:not([orientation]) {\n justify-content: flex-start;\n padding-top: 2px;\n}\noffice-ui-ribbon-stack:not([orientation]) > * {\n min-height: 24px;\n}\noffice-ui-ribbon-group > office-ui-ribbon-stack > office-ui-ribbon-stack[orientation] {\n min-height: 32px;\n}\noffice-ui-select[data-combo] {\n --office-target-size: 24px;\n}\noffice-ui-menu-button[data-active]::part(button) {\n background: var(--_vv-accent-soft);\n box-shadow: inset 0 0 0 1px var(--_vv-accent);\n}\n[data-unsupported] {\n cursor: not-allowed;\n}\n.ribbon-check {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 24px;\n padding: 0 6px;\n font-size: 12px;\n white-space: nowrap;\n cursor: pointer;\n}\n.ribbon-check[data-unsupported] {\n color: var(--_vv-muted);\n opacity: 0.6;\n}\n.ribbon-hint {\n display: none;\n}\n\n/* Drawing window with optional rulers. */\n.canvas-area {\n display: grid;\n flex: 1;\n min-width: 0;\n min-height: 0;\n grid-template-columns: 0 1fr;\n grid-template-rows: 0 1fr;\n}\n.canvas-area[data-ruler='true'] {\n grid-template-columns: 18px 1fr;\n grid-template-rows: 18px 1fr;\n}\n.canvas-area > .viewport {\n grid-column: 2;\n grid-row: 2;\n}\n.ruler,\n.ruler-corner {\n display: none;\n background: var(--_vv-surface);\n}\n.canvas-area[data-ruler='true'] > .ruler,\n.canvas-area[data-ruler='true'] > .ruler-corner {\n display: block;\n}\n.ruler-corner {\n grid-column: 1;\n grid-row: 1;\n border-right: 1px solid var(--_vv-border);\n border-bottom: 1px solid var(--_vv-border);\n}\n/* The shared office-ui-ruler draws ticks, labels and its own edge. */\n.ruler-h {\n grid-column: 2;\n grid-row: 1;\n}\n.ruler-v {\n grid-column: 1;\n grid-row: 2;\n}\n\n.page-bar {\n display: flex;\n flex: none;\n align-items: stretch;\n min-width: 0;\n border-top: 1px solid var(--_vv-border);\n background: var(--_vv-secondary);\n}\n.page-bar office-ui-menu-button {\n --office-target-size: 28px;\n flex: none;\n padding: 0 2px;\n}\n.page-tabs {\n flex: 1;\n min-width: 0;\n border-top: 0;\n}\noffice-ui-status-bar.status {\n gap: 16px;\n min-height: 29px;\n padding: 0 8px 0 12px;\n font-size: 11px;\n background: var(--_vv-surface);\n color: var(--_vv-muted);\n}\noffice-ui-status-item[value=''] {\n display: none;\n}\noffice-ui-status-item[data-page-status] {\n color: var(--_vv-ink);\n}\n.status-message {\n flex: 1;\n min-width: 0;\n}\n.viewport[data-tool='rectangle'],\n.viewport[data-tool='rectangle'] .paper [data-shape-id] {\n cursor: crosshair;\n}\n.viewport[data-tool='rectangle'] {\n touch-action: none;\n}\n.draw-preview {\n fill: var(--_vv-accent-soft);\n stroke: var(--_vv-accent);\n stroke-width: 1.5px;\n stroke-dasharray: 4 3;\n pointer-events: none;\n}\n.viewport[data-grid='true'] .paper {\n --_vv-grid-minor: rgb(0 0 0 / 7%);\n --_vv-grid-major: rgb(0 0 0 / 16%);\n background-image:\n linear-gradient(to right, var(--_vv-grid-major) 1px, transparent 1px),\n linear-gradient(to top, var(--_vv-grid-major) 1px, transparent 1px),\n linear-gradient(to right, var(--_vv-grid-minor) 1px, transparent 1px),\n linear-gradient(to top, var(--_vv-grid-minor) 1px, transparent 1px);\n background-size:\n var(--vv-inch, 96px) var(--vv-inch, 96px),\n var(--vv-inch, 96px) var(--vv-inch, 96px),\n calc(var(--vv-inch, 96px) / 4) calc(var(--vv-inch, 96px) / 4),\n calc(var(--vv-inch, 96px) / 4) calc(var(--vv-inch, 96px) / 4);\n background-position: left bottom;\n}\n:host(:fullscreen) {\n width: 100vw;\n height: 100vh;\n}\n\n@media (max-width: 760px) {\n .ribbon-row {\n flex: 1;\n min-width: 0;\n }\n .qat office-ui-button {\n --office-target-size: 44px;\n }\n .ribbon-tools .tools-content office-ui-ribbon-group {\n width: 100%;\n }\n office-ui-status-bar.status {\n gap: 4px;\n min-height: 48px;\n }\n office-ui-status-bar .notes-strip,\n office-ui-status-bar .status-message,\n office-ui-status-item[data-shape-status],\n office-ui-status-item[data-page-status] {\n display: none;\n }\n .zoom-controls {\n flex: 1;\n min-width: 0;\n --office-zoom-slider-width: 0;\n }\n}\n\n/* Visio's Tell me box sits right after the tabs. */\n.tell-me {\n flex: 0 1 280px;\n min-width: 140px;\n margin: 0 8px;\n --office-target-size: 28px;\n}\n.presentation-mode {\n --office-target-size: 24px;\n}\n@media (max-width: 760px) {\n .tell-me {\n display: none;\n }\n}\n";
|
|
1050
|
+
var ribbon_default = "/*\n * Microsoft Visio ribbon layout on the shared ooxml-ui controls. Viewer theme tokens feed the\n * shared --office-* tokens, so host themes still apply. Loaded after the legacy styles.ts.\n */\n:host {\n --office-foreground: var(--_vv-ink);\n --office-muted-foreground: var(--_vv-muted);\n --office-background: var(--_vv-surface);\n --office-surface: var(--_vv-accent-soft);\n --office-border: var(--_vv-border);\n --office-accent: var(--_vv-accent);\n --office-accent-foreground: var(--vv-accent-ink, #fff);\n --office-ring: var(--vv-focus, var(--_vv-accent));\n --office-font: 'Segoe UI', Arial, sans-serif;\n}\n\n/* Tab row: Quick Access Toolbar, then Visio's tabs. */\n.ribbon-head {\n display: flex;\n align-items: stretch;\n min-width: 0;\n border-bottom: 1px solid var(--_vv-border);\n}\n.qat {\n display: flex;\n align-items: center;\n gap: 0;\n padding: 0 6px;\n border-right: 1px solid var(--_vv-border);\n}\n.qat office-ui-button {\n --office-target-size: 30px;\n}\n.ribbon-head .ribbon-tabs {\n flex: 0 1 auto;\n min-width: 0;\n height: 36px;\n border-bottom: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.ribbon-head .ribbon-tabs button {\n flex: none;\n min-height: 35px;\n padding: 6px 12px;\n font-size: 13px;\n}\n\n/* Panels: groups of large commands and three-row stacks, as dense as Visio's. */\n.ribbon-content {\n min-height: 96px;\n padding: 2px 2px 0;\n}\n.ribbon-row {\n display: flex;\n flex-wrap: nowrap;\n align-items: stretch;\n gap: 0;\n padding: 0;\n border: 0;\n background: transparent;\n}\n.ribbon-row office-ui-ribbon-group,\n.tools-content office-ui-ribbon-group {\n min-width: 0;\n}\noffice-ui-ribbon-group office-ui-button,\noffice-ui-ribbon-group office-ui-menu-button {\n --office-target-size: 24px;\n}\noffice-ui-ribbon-group [data-size='large'] {\n --office-target-size: 30px;\n}\noffice-ui-button[variant='stacked']::part(button) {\n min-width: 48px;\n min-height: 66px;\n padding: 2px 6px;\n font-size: 12px;\n}\noffice-ui-menu-button[variant='stacked']::part(button) {\n min-width: 48px;\n min-height: 50px;\n font-size: 12px;\n}\noffice-ui-ribbon-group [data-size='small']::part(button) {\n justify-content: flex-start;\n font-size: 12px;\n}\noffice-ui-ribbon-stack:not([orientation]) {\n justify-content: flex-start;\n padding-top: 2px;\n}\noffice-ui-ribbon-stack:not([orientation]) > * {\n min-height: 24px;\n}\noffice-ui-ribbon-group > office-ui-ribbon-stack > office-ui-ribbon-stack[orientation] {\n min-height: 32px;\n}\noffice-ui-select[data-combo] {\n --office-target-size: 24px;\n}\noffice-ui-menu-button[data-active]::part(button) {\n background: var(--_vv-accent-soft);\n box-shadow: inset 0 0 0 1px var(--_vv-accent);\n}\n[data-unsupported] {\n cursor: not-allowed;\n}\n.ribbon-check {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 24px;\n padding: 0 6px;\n font-size: 12px;\n white-space: nowrap;\n cursor: pointer;\n}\n.ribbon-check[data-unsupported] {\n color: var(--_vv-muted);\n opacity: 0.6;\n}\n.ribbon-hint {\n display: none;\n}\n\n/* Drawing window with optional rulers. */\n.canvas-area {\n display: grid;\n flex: 1;\n min-width: 0;\n min-height: 0;\n grid-template-columns: 0 1fr;\n grid-template-rows: 0 1fr;\n}\n.canvas-area[data-ruler='true'] {\n grid-template-columns: 18px 1fr;\n grid-template-rows: 18px 1fr;\n}\n.canvas-area > .viewport {\n grid-column: 2;\n grid-row: 2;\n}\n.ruler,\n.ruler-corner {\n display: none;\n background: var(--_vv-surface);\n}\n.canvas-area[data-ruler='true'] > .ruler,\n.canvas-area[data-ruler='true'] > .ruler-corner {\n display: block;\n}\n.ruler-corner {\n grid-column: 1;\n grid-row: 1;\n border-right: 1px solid var(--_vv-border);\n border-bottom: 1px solid var(--_vv-border);\n}\n/* The shared office-ui-ruler draws ticks, labels and its own edge. */\n.ruler-h {\n grid-column: 2;\n grid-row: 1;\n}\n.ruler-v {\n grid-column: 1;\n grid-row: 2;\n}\n\n.page-bar {\n display: flex;\n flex: none;\n align-items: stretch;\n min-width: 0;\n border-top: 1px solid var(--_vv-border);\n background: var(--_vv-secondary);\n}\n.page-bar office-ui-menu-button {\n --office-target-size: 28px;\n flex: none;\n padding: 0 2px;\n}\n.page-tabs {\n flex: 1;\n min-width: 0;\n border-top: 0;\n}\noffice-ui-status-bar.status {\n gap: 16px;\n min-height: 29px;\n padding: 0 8px 0 12px;\n font-size: 11px;\n background: var(--_vv-surface);\n color: var(--_vv-muted);\n}\noffice-ui-status-item[value=''] {\n display: none;\n}\noffice-ui-status-item[data-page-status] {\n color: var(--_vv-ink);\n}\n.status-message {\n flex: 1;\n min-width: 0;\n}\n.viewport[data-tool='ellipse'],\n.viewport[data-tool='ellipse'] .paper [data-shape-id],\n.viewport[data-tool='rectangle'],\n.viewport[data-tool='rectangle'] .paper [data-shape-id],\n.viewport[data-tool='line'],\n.viewport[data-tool='line'] .paper [data-shape-id] {\n cursor: crosshair;\n}\n.viewport[data-tool='ellipse'],\n.viewport[data-tool='rectangle'],\n.viewport[data-tool='line'] {\n touch-action: none;\n}\n.draw-preview {\n fill: var(--_vv-accent-soft);\n stroke: var(--_vv-accent);\n stroke-width: 1.5px;\n stroke-dasharray: 4 3;\n pointer-events: none;\n}\n.viewport[data-grid='true'] .paper {\n --_vv-grid-minor: rgb(0 0 0 / 7%);\n --_vv-grid-major: rgb(0 0 0 / 16%);\n background-image:\n linear-gradient(to right, var(--_vv-grid-major) 1px, transparent 1px),\n linear-gradient(to top, var(--_vv-grid-major) 1px, transparent 1px),\n linear-gradient(to right, var(--_vv-grid-minor) 1px, transparent 1px),\n linear-gradient(to top, var(--_vv-grid-minor) 1px, transparent 1px);\n background-size:\n var(--vv-inch, 96px) var(--vv-inch, 96px),\n var(--vv-inch, 96px) var(--vv-inch, 96px),\n calc(var(--vv-inch, 96px) / 4) calc(var(--vv-inch, 96px) / 4),\n calc(var(--vv-inch, 96px) / 4) calc(var(--vv-inch, 96px) / 4);\n background-position: left bottom;\n}\n:host(:fullscreen) {\n width: 100vw;\n height: 100vh;\n}\n\n@media (max-width: 760px) {\n .ribbon-row {\n flex: 1;\n min-width: 0;\n }\n .qat office-ui-button {\n --office-target-size: 44px;\n }\n .ribbon-tools .tools-content office-ui-ribbon-group {\n width: 100%;\n }\n office-ui-status-bar.status {\n gap: 4px;\n min-height: 48px;\n }\n office-ui-status-bar .notes-strip,\n office-ui-status-bar .status-message,\n office-ui-status-item[data-shape-status],\n office-ui-status-item[data-page-status] {\n display: none;\n }\n .zoom-controls {\n flex: 1;\n min-width: 0;\n --office-zoom-slider-width: 0;\n }\n}\n\n/* Visio's Tell me box sits right after the tabs. */\n.tell-me {\n flex: 0 1 280px;\n min-width: 140px;\n margin: 0 8px;\n --office-target-size: 28px;\n}\n.presentation-mode {\n --office-target-size: 24px;\n}\n@media (max-width: 760px) {\n .tell-me {\n display: none;\n }\n}\n";
|
|
1050
1051
|
|
|
1051
1052
|
// raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/visio/styles/shapes.css?raw
|
|
1052
1053
|
var shapes_default = "/* Visio's Shapes window: stencil views, More/Quick Shapes rows and a two-column master grid. */\n.shapes-pane {\n display: flex;\n flex: 0 0 232px;\n flex-direction: column;\n width: 232px;\n min-width: 0;\n overflow: hidden;\n border-right: 1px solid var(--_vv-border);\n background: var(--_vv-secondary);\n}\n.shapes-pane .pane-heading {\n height: 42px;\n font-size: 18px;\n font-weight: 400;\n color: var(--_vv-accent);\n}\n.pane-collapse {\n min-height: 28px;\n width: 28px;\n padding: 0;\n border-color: transparent;\n background: transparent;\n font-size: 18px;\n line-height: 1;\n}\n.shapes-views {\n display: flex;\n gap: 14px;\n padding: 4px 12px 0;\n border-bottom: 1px solid var(--_vv-border);\n}\n.shapes-views button {\n position: relative;\n min-height: 28px;\n padding: 2px 0 6px;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: var(--_vv-muted);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.shapes-views button[aria-selected='true'] {\n color: var(--_vv-ink);\n}\n.shapes-views button[aria-selected='true']::after {\n content: '';\n position: absolute;\n inset: auto 0 -1px;\n height: 2px;\n background: var(--_vv-accent);\n}\n#shapes-stencils,\n#shapes-search {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 4px 0 8px;\n}\n.shapes-row {\n display: flex;\n align-items: center;\n width: 100%;\n min-height: 30px;\n padding: 0 12px;\n border: 0;\n border-radius: 0;\n background: transparent;\n font-size: 12px;\n text-align: left;\n}\n.shapes-row::after {\n content: '\u203A';\n margin-left: auto;\n font-size: 15px;\n}\n.stencil-title {\n margin: 2px 0;\n padding: 6px 12px;\n border-left: 3px solid var(--_vv-accent);\n background: var(--_vv-accent-soft);\n font-size: 12px;\n font-weight: 600;\n}\n.masters {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 2px;\n margin: 0;\n padding: 4px 6px;\n list-style: none;\n}\n.masters li[hidden] {\n display: none;\n}\n.master {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 40px;\n padding: 4px 6px;\n border-color: transparent;\n background: transparent;\n font-size: 11px;\n line-height: 1.2;\n text-align: left;\n cursor: grab;\n}\n.master svg {\n flex: none;\n width: 28px;\n height: 28px;\n fill: var(--_vv-surface);\n stroke: var(--_vv-accent);\n stroke-width: 1.2;\n stroke-linejoin: round;\n}\n.master[data-unsupported] {\n cursor: not-allowed;\n opacity: 0.45;\n}\n.master[data-unsupported]:hover {\n border-color: transparent;\n background: transparent;\n}\n.shapes-search-field {\n display: block;\n width: calc(100% - 24px);\n margin: 6px 12px;\n}\n.shapes-empty {\n margin: 8px 12px;\n color: var(--_vv-muted);\n font-size: 12px;\n}\n@media (max-width: 760px) {\n .shapes-pane {\n position: absolute;\n inset: 0 auto 0 0;\n z-index: 3;\n width: min(260px, 100%);\n box-shadow: 8px 0 24px #0002;\n }\n .master {\n min-height: 44px;\n }\n}\n.shapes-strip {\n display: flex;\n flex: 0 0 28px;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n width: 28px;\n min-width: 28px;\n padding: 8px 0;\n border: 0;\n border-right: 1px solid var(--_vv-border);\n border-radius: 0;\n background: var(--_vv-secondary);\n color: var(--_vv-accent);\n font-size: 16px;\n}\n.shapes-strip[hidden] {\n display: none;\n}\n.shapes-strip-label {\n writing-mode: vertical-rl;\n color: var(--_vv-ink);\n font-size: 12px;\n}\n/* Visio's floating Pan & Zoom window. */\n.pan-zoom {\n position: absolute;\n right: 16px;\n bottom: 16px;\n z-index: 5;\n width: 240px;\n border: 1px solid var(--_vv-border);\n background: var(--_vv-surface);\n box-shadow: 0 6px 20px rgb(0 0 0 / 18%);\n}\n.pan-zoom[hidden] {\n display: none;\n}\n.pan-zoom-heading {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 2px 4px 2px 10px;\n border-bottom: 1px solid var(--_vv-border);\n font-size: 12px;\n font-weight: 600;\n}\n.pan-zoom-close {\n min-height: 24px;\n width: 24px;\n padding: 0;\n border-color: transparent;\n background: transparent;\n}\n.pan-zoom-view {\n display: grid;\n place-items: center;\n height: 180px;\n padding: 8px;\n background: var(--_vv-bg);\n cursor: crosshair;\n touch-action: none;\n}\n.pan-zoom-view svg {\n max-width: 100%;\n max-height: 100%;\n background: #fff;\n box-shadow: 0 1px 3px rgb(0 0 0 / 20%);\n}\n.pan-zoom-frame {\n fill: rgb(220 38 38 / 8%);\n stroke: #dc2626;\n stroke-width: 2px;\n}\n";
|
|
@@ -1088,13 +1089,14 @@ function command(doc, spec) {
|
|
|
1088
1089
|
});
|
|
1089
1090
|
return el2;
|
|
1090
1091
|
}
|
|
1091
|
-
function menu(doc, spec) {
|
|
1092
|
+
function menu(doc, spec, submenu = false) {
|
|
1092
1093
|
const el2 = doc.createElement("office-ui-menu-button");
|
|
1093
1094
|
const actions = /* @__PURE__ */ new Map();
|
|
1094
1095
|
const { action: _main, ...dropdown2 } = spec;
|
|
1095
1096
|
const reason = spec.unsupported ?? spec.items.find((item) => item.unsupported)?.unsupported;
|
|
1096
1097
|
decorate(el2, spec.split ? spec : { ...dropdown2, ...reason ? { unsupported: reason } : {} });
|
|
1097
|
-
|
|
1098
|
+
const usable = (item) => !!item.action || !!item.items?.some(usable);
|
|
1099
|
+
if (spec.split ? spec.action : spec.items.some(usable)) {
|
|
1098
1100
|
el2.removeAttribute("disabled");
|
|
1099
1101
|
delete el2.dataset.unsupported;
|
|
1100
1102
|
el2.setAttribute("title", spec.label);
|
|
@@ -1104,18 +1106,27 @@ function menu(doc, spec) {
|
|
|
1104
1106
|
if (spec.size === "icon") el2.setAttribute("icon-only", "");
|
|
1105
1107
|
el2.dataset.size = spec.size ?? "large";
|
|
1106
1108
|
el2.dataset.menu = spec.id;
|
|
1109
|
+
if (submenu) {
|
|
1110
|
+
el2.setAttribute("submenu", "");
|
|
1111
|
+
el2.removeAttribute("variant");
|
|
1112
|
+
}
|
|
1107
1113
|
if (spec.split) actions.set(spec.id, spec.action);
|
|
1108
|
-
|
|
1109
|
-
const entry = doc.createElement("office-ui-menu-item");
|
|
1110
|
-
decorate(entry, item);
|
|
1114
|
+
const collect = (item) => {
|
|
1111
1115
|
actions.set(item.id, item.action);
|
|
1116
|
+
item.items?.forEach(collect);
|
|
1117
|
+
};
|
|
1118
|
+
for (const item of spec.items) {
|
|
1119
|
+
const entry = item.items ? menu(doc, { ...item, items: item.items }, true) : doc.createElement("office-ui-menu-item");
|
|
1120
|
+
if (!item.items) decorate(entry, item);
|
|
1121
|
+
collect(item);
|
|
1112
1122
|
el2.append(entry);
|
|
1113
1123
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1124
|
+
if (!submenu)
|
|
1125
|
+
el2.addEventListener("office-command", (event) => {
|
|
1126
|
+
event.stopPropagation();
|
|
1127
|
+
const action2 = actions.get(event.detail.command);
|
|
1128
|
+
if (action2) emitRibbonAction(el2, action2);
|
|
1129
|
+
});
|
|
1119
1130
|
return el2;
|
|
1120
1131
|
}
|
|
1121
1132
|
function stack(doc, children, horizontal = false) {
|
|
@@ -1417,7 +1428,7 @@ var TEXT2 = "Needs core text formatting edits.";
|
|
|
1417
1428
|
var STYLE2 = "Needs core fill, line and effect edits.";
|
|
1418
1429
|
var ARRANGE = "Needs core alignment, position, z-order and grouping edits.";
|
|
1419
1430
|
var CONNECT = "Needs core connector and glue edits.";
|
|
1420
|
-
var SHAPES = "
|
|
1431
|
+
var SHAPES = "This shape type cannot be created yet.";
|
|
1421
1432
|
var SELECT = "Needs multi-shape selection.";
|
|
1422
1433
|
var small2 = (spec) => ({ size: "small", ...spec });
|
|
1423
1434
|
var icon2 = (spec) => ({ size: "icon", ...spec });
|
|
@@ -1459,8 +1470,21 @@ function buildHomePanel(doc, panel) {
|
|
|
1459
1470
|
action: { type: "tool", tool: "rectangle" },
|
|
1460
1471
|
checked: false
|
|
1461
1472
|
},
|
|
1462
|
-
{
|
|
1463
|
-
|
|
1473
|
+
{
|
|
1474
|
+
id: "ellipse",
|
|
1475
|
+
label: "Ellipse",
|
|
1476
|
+
action: { type: "tool", tool: "ellipse" },
|
|
1477
|
+
keys: ["Control+9", "Ctrl+9"],
|
|
1478
|
+
checked: false
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
id: "line-tool",
|
|
1482
|
+
label: "Line",
|
|
1483
|
+
icon: "line",
|
|
1484
|
+
action: { type: "tool", tool: "line" },
|
|
1485
|
+
keys: ["Control+6", "Ctrl+6"],
|
|
1486
|
+
checked: false
|
|
1487
|
+
},
|
|
1464
1488
|
{ id: "freeform", label: "Freeform", unsupported: SHAPES },
|
|
1465
1489
|
{ id: "arc", label: "Arc", unsupported: SHAPES },
|
|
1466
1490
|
{ id: "pencil", label: "Pencil", icon: "pencil", unsupported: SHAPES }
|
|
@@ -1547,7 +1571,32 @@ function buildHomePanel(doc, panel) {
|
|
|
1547
1571
|
unsupported: ARRANGE,
|
|
1548
1572
|
items: [
|
|
1549
1573
|
{ id: "auto-align", label: "Auto Align & Space", unsupported: ARRANGE },
|
|
1550
|
-
{
|
|
1574
|
+
{
|
|
1575
|
+
id: "rotate",
|
|
1576
|
+
label: "Rotate Shapes",
|
|
1577
|
+
items: [
|
|
1578
|
+
{
|
|
1579
|
+
id: "rotate-left",
|
|
1580
|
+
label: "Rotate Left 90\xB0",
|
|
1581
|
+
action: { type: "rotate", direction: "left" }
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
id: "rotate-right",
|
|
1585
|
+
label: "Rotate Right 90\xB0",
|
|
1586
|
+
action: { type: "rotate", direction: "right" }
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
id: "flip-horizontal",
|
|
1590
|
+
label: "Flip Horizontal",
|
|
1591
|
+
action: { type: "flip", axis: "horizontal" }
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
id: "flip-vertical",
|
|
1595
|
+
label: "Flip Vertical",
|
|
1596
|
+
action: { type: "flip", axis: "vertical" }
|
|
1597
|
+
}
|
|
1598
|
+
]
|
|
1599
|
+
}
|
|
1551
1600
|
]
|
|
1552
1601
|
}),
|
|
1553
1602
|
stack(doc, [
|
|
@@ -3800,44 +3849,50 @@ function same(a, b) {
|
|
|
3800
3849
|
var SNAP = 1 / 16;
|
|
3801
3850
|
var MIN_SIZE = SNAP;
|
|
3802
3851
|
var snap = (value) => Math.round(value / SNAP) * SNAP;
|
|
3803
|
-
function
|
|
3804
|
-
let max = 0;
|
|
3805
|
-
const pending = [...page2.shapes];
|
|
3806
|
-
while (pending.length) {
|
|
3807
|
-
const shape = pending.pop();
|
|
3808
|
-
if (/^\d{1,9}$/.test(shape.id)) max = Math.max(max, Number(shape.id));
|
|
3809
|
-
pending.push(...shape.children);
|
|
3810
|
-
}
|
|
3811
|
-
return String(max + 1);
|
|
3812
|
-
}
|
|
3813
|
-
function pagePoint(svg, page2, event) {
|
|
3852
|
+
function pagePoint(svg, page2, event, options = {}) {
|
|
3814
3853
|
const matrix2 = svg.getScreenCTM?.();
|
|
3815
3854
|
if (!matrix2) return void 0;
|
|
3816
3855
|
const point = new DOMPoint(event.clientX, event.clientY).matrixTransform(matrix2.inverse());
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3856
|
+
const coordinate = (value, maximum) => {
|
|
3857
|
+
const bounded = options.bounded === false ? value : Math.max(0, Math.min(maximum, value));
|
|
3858
|
+
return options.snap === false ? bounded : snap(bounded);
|
|
3820
3859
|
};
|
|
3860
|
+
return { x: coordinate(point.x, page2.width), y: coordinate(point.y, page2.height) };
|
|
3821
3861
|
}
|
|
3822
3862
|
async function insertRectangle(controller, page2, centre, size) {
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3863
|
+
return insertBox(controller, page2, centre, size, "rectangle");
|
|
3864
|
+
}
|
|
3865
|
+
async function insertBox(controller, page2, centre, size, kind) {
|
|
3866
|
+
const shapeId = visioNextShapeId(page2);
|
|
3867
|
+
return insertGeometry(controller, page2, {
|
|
3868
|
+
type: kind === "ellipse" ? "create-ellipse" : "create-rectangle",
|
|
3869
|
+
pageId: page2.id,
|
|
3870
|
+
shapeId,
|
|
3871
|
+
x: centre.x,
|
|
3872
|
+
y: page2.height - centre.y,
|
|
3873
|
+
width: size.width,
|
|
3874
|
+
height: size.height
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3877
|
+
async function insertLine(controller, page2, begin, end) {
|
|
3878
|
+
return insertGeometry(controller, page2, {
|
|
3879
|
+
type: "create-line",
|
|
3880
|
+
pageId: page2.id,
|
|
3881
|
+
shapeId: visioNextShapeId(page2),
|
|
3882
|
+
beginX: begin.x,
|
|
3883
|
+
beginY: page2.height - begin.y,
|
|
3884
|
+
endX: end.x,
|
|
3885
|
+
endY: page2.height - end.y
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
async function insertGeometry(controller, page2, command2) {
|
|
3889
|
+
const { shapeId, pageId } = command2;
|
|
3890
|
+
await controller.applyEdits([visioPageEditToDrawing(page2, command2)]);
|
|
3836
3891
|
const created = controller.state.document?.pages.find((candidate) => candidate.id === pageId)?.shapes.find((shape) => shape.id === shapeId);
|
|
3837
3892
|
if (created) controller.selectShape({ id: shapeId, name: created.name, pageId });
|
|
3838
3893
|
return shapeId;
|
|
3839
3894
|
}
|
|
3840
|
-
var
|
|
3895
|
+
var ShapeDrawTool = class {
|
|
3841
3896
|
constructor(viewport, controller, options) {
|
|
3842
3897
|
this.viewport = viewport;
|
|
3843
3898
|
this.controller = controller;
|
|
@@ -3859,7 +3914,7 @@ var RectangleDrawTool = class {
|
|
|
3859
3914
|
viewport.addEventListener(
|
|
3860
3915
|
"click",
|
|
3861
3916
|
(event) => {
|
|
3862
|
-
if (this.options.
|
|
3917
|
+
if (this.options.tool()) event.stopImmediatePropagation();
|
|
3863
3918
|
},
|
|
3864
3919
|
{ ...options, capture: true }
|
|
3865
3920
|
);
|
|
@@ -3867,6 +3922,13 @@ var RectangleDrawTool = class {
|
|
|
3867
3922
|
viewport.addEventListener("pointermove", (event) => this.#move(event), options);
|
|
3868
3923
|
viewport.addEventListener("pointerup", (event) => void this.#finish(event), options);
|
|
3869
3924
|
viewport.addEventListener("pointercancel", () => this.#cancel(), options);
|
|
3925
|
+
viewport.addEventListener(
|
|
3926
|
+
"lostpointercapture",
|
|
3927
|
+
(event) => {
|
|
3928
|
+
if (event.pointerId === this.#drag?.pointer) this.#cancel();
|
|
3929
|
+
},
|
|
3930
|
+
options
|
|
3931
|
+
);
|
|
3870
3932
|
viewport.addEventListener(
|
|
3871
3933
|
"keydown",
|
|
3872
3934
|
(event) => {
|
|
@@ -3884,61 +3946,87 @@ var RectangleDrawTool = class {
|
|
|
3884
3946
|
};
|
|
3885
3947
|
}
|
|
3886
3948
|
#start(event) {
|
|
3887
|
-
|
|
3949
|
+
const kind = this.options.tool();
|
|
3950
|
+
if (!kind || event.button !== 0 || this.#drag) return;
|
|
3888
3951
|
const state = this.controller.state;
|
|
3889
3952
|
const page2 = state.document?.pages[state.pageIndex];
|
|
3890
3953
|
const svg = this.viewport.querySelector("svg.paper");
|
|
3891
3954
|
if (!page2 || !svg || !state.edit.sourceAvailable || state.edit.busy) return;
|
|
3892
|
-
const start = pagePoint(svg, page2, event);
|
|
3955
|
+
const start = pagePoint(svg, page2, event, { bounded: false });
|
|
3893
3956
|
if (!start) return;
|
|
3894
3957
|
event.preventDefault();
|
|
3895
|
-
const rect = this.viewport.ownerDocument.createElementNS(
|
|
3958
|
+
const rect = this.viewport.ownerDocument.createElementNS(
|
|
3959
|
+
"http://www.w3.org/2000/svg",
|
|
3960
|
+
kind === "line" ? "line" : kind === "ellipse" ? "ellipse" : "rect"
|
|
3961
|
+
);
|
|
3896
3962
|
rect.classList.add("draw-preview");
|
|
3897
3963
|
rect.setAttribute("vector-effect", "non-scaling-stroke");
|
|
3898
3964
|
svg.append(rect);
|
|
3899
|
-
this.#drag = { pointer: event.pointerId, svg, page: page2, ...start, rect };
|
|
3965
|
+
this.#drag = { pointer: event.pointerId, svg, page: page2, ...start, rect, kind };
|
|
3900
3966
|
this.viewport.setPointerCapture?.(event.pointerId);
|
|
3901
3967
|
this.#update(start);
|
|
3902
3968
|
}
|
|
3903
3969
|
#move(event) {
|
|
3904
3970
|
if (!this.#drag || event.pointerId !== this.#drag.pointer) return;
|
|
3905
|
-
const point = pagePoint(this.#drag.svg, this.#drag.page, event
|
|
3971
|
+
const point = pagePoint(this.#drag.svg, this.#drag.page, event, {
|
|
3972
|
+
bounded: false
|
|
3973
|
+
});
|
|
3906
3974
|
if (point) this.#update(point);
|
|
3907
3975
|
}
|
|
3908
3976
|
#update(point) {
|
|
3909
|
-
const { x, y, rect } = this.#drag;
|
|
3977
|
+
const { x, y, rect, kind } = this.#drag;
|
|
3978
|
+
if (kind === "line") {
|
|
3979
|
+
for (const [name, value] of Object.entries({ x1: x, y1: y, x2: point.x, y2: point.y }))
|
|
3980
|
+
rect.setAttribute(name, String(value));
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
if (kind === "ellipse") {
|
|
3984
|
+
for (const [name, value] of Object.entries({
|
|
3985
|
+
cx: (x + point.x) / 2,
|
|
3986
|
+
cy: (y + point.y) / 2,
|
|
3987
|
+
rx: Math.abs(point.x - x) / 2,
|
|
3988
|
+
ry: Math.abs(point.y - y) / 2
|
|
3989
|
+
}))
|
|
3990
|
+
rect.setAttribute(name, String(value));
|
|
3991
|
+
return;
|
|
3992
|
+
}
|
|
3910
3993
|
rect.setAttribute("x", String(Math.min(x, point.x)));
|
|
3911
3994
|
rect.setAttribute("y", String(Math.min(y, point.y)));
|
|
3912
3995
|
rect.setAttribute("width", String(Math.abs(point.x - x)));
|
|
3913
3996
|
rect.setAttribute("height", String(Math.abs(point.y - y)));
|
|
3914
3997
|
}
|
|
3915
3998
|
#cancel() {
|
|
3916
|
-
this.#drag
|
|
3999
|
+
const drag = this.#drag;
|
|
4000
|
+
drag?.rect.remove();
|
|
3917
4001
|
this.#drag = void 0;
|
|
4002
|
+
if (drag && this.viewport.hasPointerCapture?.(drag.pointer))
|
|
4003
|
+
this.viewport.releasePointerCapture(drag.pointer);
|
|
3918
4004
|
}
|
|
3919
4005
|
async #finish(event) {
|
|
3920
4006
|
const drag = this.#drag;
|
|
3921
4007
|
if (!drag || event.pointerId !== drag.pointer) return;
|
|
3922
|
-
const end = pagePoint(drag.svg, drag.page, event) ?? drag;
|
|
4008
|
+
const end = pagePoint(drag.svg, drag.page, event, { bounded: false }) ?? drag;
|
|
3923
4009
|
this.#cancel();
|
|
3924
4010
|
const width = Math.abs(end.x - drag.x), height = Math.abs(end.y - drag.y);
|
|
3925
|
-
if (width < MIN_SIZE || height < MIN_SIZE) {
|
|
3926
|
-
this.options.announce(
|
|
4011
|
+
if (drag.kind === "line" ? Math.hypot(width, height) < MIN_SIZE : width < MIN_SIZE || height < MIN_SIZE) {
|
|
4012
|
+
this.options.announce(`Drag on the page to draw a ${drag.kind}.`);
|
|
3927
4013
|
return;
|
|
3928
4014
|
}
|
|
3929
4015
|
const state = this.controller.state;
|
|
3930
|
-
if (state.document?.pages[state.pageIndex] !== drag.page)
|
|
4016
|
+
if (state.document?.pages[state.pageIndex] !== drag.page || this.options.tool() !== drag.kind)
|
|
4017
|
+
return;
|
|
3931
4018
|
const request = ++this.#request;
|
|
3932
4019
|
try {
|
|
3933
|
-
const shapeId = await
|
|
4020
|
+
const shapeId = drag.kind === "line" ? await insertLine(this.controller, drag.page, drag, end) : await insertBox(
|
|
3934
4021
|
this.controller,
|
|
3935
4022
|
drag.page,
|
|
3936
4023
|
{ x: (drag.x + end.x) / 2, y: (drag.y + end.y) / 2 },
|
|
3937
|
-
{ width, height }
|
|
4024
|
+
{ width, height },
|
|
4025
|
+
drag.kind
|
|
3938
4026
|
);
|
|
3939
4027
|
if (request !== this.#request) return;
|
|
3940
4028
|
this.options.announce(
|
|
3941
|
-
`Rectangle ${shapeId} added (${+width.toFixed(4)} \xD7 ${+height.toFixed(4)} in).`
|
|
4029
|
+
drag.kind === "line" ? `Line ${shapeId} added.` : `${drag.kind === "ellipse" ? "Ellipse" : "Rectangle"} ${shapeId} added (${+width.toFixed(4)} \xD7 ${+height.toFixed(4)} in).`
|
|
3942
4030
|
);
|
|
3943
4031
|
} catch (error) {
|
|
3944
4032
|
if (request === this.#request && !isEditCancellation(error))
|
|
@@ -4092,13 +4180,29 @@ var ViewerGeometryControls = class {
|
|
|
4092
4180
|
this.controller = controller;
|
|
4093
4181
|
this.draftChanged = draftChanged;
|
|
4094
4182
|
const field = (name) => panel.querySelector(`[data-geometry-field="${name}"]`);
|
|
4183
|
+
const doc = panel.ownerDocument;
|
|
4184
|
+
const label = doc.createElement("label");
|
|
4185
|
+
label.textContent = "Angle (degrees) ";
|
|
4186
|
+
const angle = doc.createElement("input");
|
|
4187
|
+
angle.type = "number";
|
|
4188
|
+
angle.step = "any";
|
|
4189
|
+
angle.dataset.geometryField = "angle";
|
|
4190
|
+
label.append(angle);
|
|
4191
|
+
const actions = panel.querySelector("[data-geometry] .edit-actions");
|
|
4192
|
+
actions.before(label);
|
|
4193
|
+
const rotate = doc.createElement("button");
|
|
4194
|
+
rotate.type = "button";
|
|
4195
|
+
rotate.dataset.geometryAction = "rotate-shape";
|
|
4196
|
+
rotate.textContent = "Rotate selected";
|
|
4197
|
+
actions.append(rotate);
|
|
4095
4198
|
this.#fields = {
|
|
4096
4199
|
id: field("id"),
|
|
4097
4200
|
text: field("text"),
|
|
4098
4201
|
x: field("x"),
|
|
4099
4202
|
y: field("y"),
|
|
4100
4203
|
width: field("width"),
|
|
4101
|
-
height: field("height")
|
|
4204
|
+
height: field("height"),
|
|
4205
|
+
angle
|
|
4102
4206
|
};
|
|
4103
4207
|
this.#buttons = [...panel.querySelectorAll("[data-geometry-action]")];
|
|
4104
4208
|
this.#error = panel.querySelector("[data-geometry-error]");
|
|
@@ -4157,7 +4261,7 @@ var ViewerGeometryControls = class {
|
|
|
4157
4261
|
width,
|
|
4158
4262
|
height,
|
|
4159
4263
|
...this.#fields.text.value ? { text: this.#fields.text.value } : {}
|
|
4160
|
-
} : type === "move-shape" ? { type, ...target, x, y } : type === "resize-shape" ? { type, ...target, width, height } : { type: "delete-shape", ...target };
|
|
4264
|
+
} : type === "move-shape" ? { type, ...target, x, y } : type === "resize-shape" ? { type, ...target, width, height } : type === "rotate-shape" ? { type, ...target, angle: this.#fields.angle.valueAsNumber * Math.PI / 180 } : { type: "delete-shape", ...target };
|
|
4161
4265
|
const request = ++this.#request;
|
|
4162
4266
|
this.#error.hidden = true;
|
|
4163
4267
|
try {
|
|
@@ -4204,9 +4308,11 @@ var ViewerGeometryControls = class {
|
|
|
4204
4308
|
const size = [this.#fields.width, this.#fields.height].every(
|
|
4205
4309
|
(field) => Number.isFinite(field.valueAsNumber) && field.valueAsNumber > 0
|
|
4206
4310
|
);
|
|
4311
|
+
const selected = selectedShape(state.document, state.selectedShape, state.pageIndex);
|
|
4312
|
+
const lineSize = selected?.kind === "connector" && selected.height === 0 && Number.isFinite(this.#fields.width.valueAsNumber) && this.#fields.width.valueAsNumber > 0 && this.#fields.height.valueAsNumber === 0;
|
|
4207
4313
|
for (const button of this.#buttons) {
|
|
4208
4314
|
const action2 = button.dataset.geometryAction;
|
|
4209
|
-
button.disabled = !available || (action2 === "create-rectangle" ? !this.#fields.id.value || !position || !size : !state.selectedShape || action2 === "move-shape" && !position || action2 === "resize-shape" && !size);
|
|
4315
|
+
button.disabled = !available || (action2 === "create-rectangle" ? !this.#fields.id.value || !position || !size : !state.selectedShape || action2 === "move-shape" && !position || action2 === "resize-shape" && !size && !lineSize || action2 === "rotate-shape" && (!Number.isFinite(this.#fields.angle.valueAsNumber) || selected?.kind === "connector"));
|
|
4210
4316
|
}
|
|
4211
4317
|
}
|
|
4212
4318
|
};
|
|
@@ -4672,6 +4778,17 @@ function fillPaint(style, defs, resources, world, pageHeight) {
|
|
|
4672
4778
|
}
|
|
4673
4779
|
return gradientPaint(paint, defs);
|
|
4674
4780
|
}
|
|
4781
|
+
function linePaint(style, defs, resources, world, pageHeight, size) {
|
|
4782
|
+
const gradient = visioStrokeGradient(style.lineGradient, style.lineWidth, size);
|
|
4783
|
+
if (!gradient) return safeColor(style.lineColor);
|
|
4784
|
+
return fillPaint(
|
|
4785
|
+
{ fill: style.lineColor, fillGradient: gradient },
|
|
4786
|
+
defs,
|
|
4787
|
+
resources,
|
|
4788
|
+
world,
|
|
4789
|
+
pageHeight
|
|
4790
|
+
);
|
|
4791
|
+
}
|
|
4675
4792
|
function gradientPaint(paint, defs, normalized = false, rotation) {
|
|
4676
4793
|
const gradient = svgElement(paint.type === "radial" ? "radialGradient" : "linearGradient");
|
|
4677
4794
|
gradient.id = `visio-fill-${++gradientId}`;
|
|
@@ -5436,7 +5553,7 @@ function renderPage(model, page2, options = {}) {
|
|
|
5436
5553
|
vectorBudget: new ForeignVectorBudget(),
|
|
5437
5554
|
renderable: /* @__PURE__ */ new WeakMap(),
|
|
5438
5555
|
visible,
|
|
5439
|
-
interactive: !options.static
|
|
5556
|
+
interactive: !options.static && options.interactive !== false
|
|
5440
5557
|
};
|
|
5441
5558
|
try {
|
|
5442
5559
|
for (const layer of pages)
|
|
@@ -5445,7 +5562,7 @@ function renderPage(model, page2, options = {}) {
|
|
|
5445
5562
|
context.resources.dispose();
|
|
5446
5563
|
throw error;
|
|
5447
5564
|
}
|
|
5448
|
-
if (
|
|
5565
|
+
if (context.interactive) {
|
|
5449
5566
|
const first = svg.querySelector("[data-shape-id]");
|
|
5450
5567
|
first?.setAttribute("tabindex", "0");
|
|
5451
5568
|
}
|
|
@@ -5493,6 +5610,10 @@ function drawShape(shape, parent, context, pageId, parentTransform = VISIO_IDENT
|
|
|
5493
5610
|
function drawOwn(shape, group2, context, world) {
|
|
5494
5611
|
const { warnings, defs, resources } = context;
|
|
5495
5612
|
const fill = shape.geometry.some((geometry) => geometry.fill) ? fillPaint(shape.style, defs, resources, world, context.pageHeight) : "none";
|
|
5613
|
+
const stroke = shape.geometry.some((geometry) => geometry.stroke) && shape.style.linePattern !== 0 ? linePaint(shape.style, defs, resources, world, context.pageHeight, [
|
|
5614
|
+
shape.width,
|
|
5615
|
+
shape.height
|
|
5616
|
+
]) : "none";
|
|
5496
5617
|
for (const geometry of shape.geometry) {
|
|
5497
5618
|
if (++context.nodes > 5e4) {
|
|
5498
5619
|
warnings.add(
|
|
@@ -5507,7 +5628,7 @@ function drawOwn(shape, group2, context, world) {
|
|
|
5507
5628
|
path.setAttribute("fill", geometry.fill ? fill : "none");
|
|
5508
5629
|
path.setAttribute("fill-opacity", String(shape.style.fillOpacity));
|
|
5509
5630
|
const stroked = geometry.stroke && shape.style.linePattern !== 0;
|
|
5510
|
-
path.setAttribute("stroke", stroked ?
|
|
5631
|
+
path.setAttribute("stroke", stroked ? stroke : "none");
|
|
5511
5632
|
path.setAttribute("stroke-width", String(shape.style.lineWidth));
|
|
5512
5633
|
path.setAttribute("stroke-opacity", String(shape.style.lineOpacity));
|
|
5513
5634
|
path.setAttribute("stroke-linejoin", "round");
|
|
@@ -5656,6 +5777,10 @@ function routeRibbonAction(targets, action2) {
|
|
|
5656
5777
|
return targets.history(action2.key);
|
|
5657
5778
|
case "delete":
|
|
5658
5779
|
return targets.deleteSelection();
|
|
5780
|
+
case "rotate":
|
|
5781
|
+
return targets.rotateSelection(action2.direction);
|
|
5782
|
+
case "flip":
|
|
5783
|
+
return targets.flipSelection(action2.axis);
|
|
5659
5784
|
case "tool":
|
|
5660
5785
|
return targets.setTool(action2.tool);
|
|
5661
5786
|
case "grid":
|
|
@@ -5946,14 +6071,16 @@ var ViewerCommands = class {
|
|
|
5946
6071
|
this.#pageOrder = new ViewerPageOrder(host.root, host.controller);
|
|
5947
6072
|
this.#pageRename = new ViewerPageRename(host.root, host.controller);
|
|
5948
6073
|
this.#pageDelete = new ViewerPageDelete(host.root, host.controller);
|
|
5949
|
-
this.#draw = new
|
|
5950
|
-
|
|
6074
|
+
this.#draw = new ShapeDrawTool(host.viewport, host.controller, {
|
|
6075
|
+
tool: () => this.#tool === "pointer" ? void 0 : this.#tool,
|
|
5951
6076
|
announce: host.announce
|
|
5952
6077
|
});
|
|
5953
6078
|
this.#targets = {
|
|
5954
6079
|
controller: host.controller,
|
|
5955
6080
|
history: (key) => this.#history(key),
|
|
5956
6081
|
deleteSelection: () => this.#delete(),
|
|
6082
|
+
rotateSelection: (direction) => this.#transform({ type: "rotate", direction }),
|
|
6083
|
+
flipSelection: (axis) => this.#transform({ type: "flip", axis }),
|
|
5957
6084
|
setTool: (tool) => this.setTool(tool),
|
|
5958
6085
|
toggleGrid: () => {
|
|
5959
6086
|
this.#grid = !this.#grid;
|
|
@@ -6058,9 +6185,10 @@ var ViewerCommands = class {
|
|
|
6058
6185
|
};
|
|
6059
6186
|
}
|
|
6060
6187
|
setTool(tool) {
|
|
6061
|
-
if (tool
|
|
6188
|
+
if (tool !== "pointer" && !this.#canEdit(this.host.controller.state)) return;
|
|
6062
6189
|
this.#tool = tool;
|
|
6063
6190
|
this.render(this.host.controller.state);
|
|
6191
|
+
this.host.toolChanged?.();
|
|
6064
6192
|
}
|
|
6065
6193
|
#canEdit(state) {
|
|
6066
6194
|
return state.edit.sourceAvailable && !state.loading && !state.edit.busy;
|
|
@@ -6094,15 +6222,35 @@ var ViewerCommands = class {
|
|
|
6094
6222
|
if (index >= 0) this.host.controller.setPage(index);
|
|
6095
6223
|
}, "Inserted a blank page.");
|
|
6096
6224
|
}
|
|
6225
|
+
#transform(action2) {
|
|
6226
|
+
const state = this.host.controller.state;
|
|
6227
|
+
const page2 = state.document?.pages[state.pageIndex];
|
|
6228
|
+
if (!page2 || !state.selectedShape || !this.#canEdit(state)) return;
|
|
6229
|
+
const command2 = action2.type === "rotate" ? visioQuarterTurnCommand(page2, state.selectedShape.id, action2.direction) : visioLocalRotationShape(page2, state.selectedShape.id, false) ? {
|
|
6230
|
+
type: "flip-shape",
|
|
6231
|
+
pageId: page2.id,
|
|
6232
|
+
shapeId: state.selectedShape.id,
|
|
6233
|
+
axis: action2.axis
|
|
6234
|
+
} : void 0;
|
|
6235
|
+
if (command2)
|
|
6236
|
+
void this.#edit(
|
|
6237
|
+
() => this.host.controller.applyEdits([command2]),
|
|
6238
|
+
action2.type === "rotate" ? `Rotated ${action2.direction} 90\xB0.` : `Flipped ${action2.axis}.`
|
|
6239
|
+
);
|
|
6240
|
+
}
|
|
6097
6241
|
async #edit(action2, success) {
|
|
6098
6242
|
const request = ++this.#pending;
|
|
6099
6243
|
const { root, viewport } = this.host;
|
|
6100
6244
|
const canvasFocused = viewport.contains(root.activeElement);
|
|
6245
|
+
const generation = this.host.controller.documentGeneration;
|
|
6101
6246
|
try {
|
|
6102
6247
|
await action2();
|
|
6103
6248
|
if (canvasFocused && !viewport.contains(root.activeElement))
|
|
6104
6249
|
viewport.focus({ preventScroll: true });
|
|
6105
|
-
if (success && request === this.#pending)
|
|
6250
|
+
if (success && request === this.#pending)
|
|
6251
|
+
this.host.announce(
|
|
6252
|
+
this.host.controller.documentGeneration === generation ? "No changes were made." : success
|
|
6253
|
+
);
|
|
6106
6254
|
} catch (error) {
|
|
6107
6255
|
if (request === this.#pending && !isEditCancellation(error) && !this.host.controller.state.edit.error)
|
|
6108
6256
|
this.host.announce(editErrorMessage(error));
|
|
@@ -6132,6 +6280,8 @@ var ViewerCommands = class {
|
|
|
6132
6280
|
return { type: "history", key: "redo" };
|
|
6133
6281
|
if (control && key === "1") return { type: "tool", tool: "pointer" };
|
|
6134
6282
|
if (control && key === "8") return { type: "tool", tool: "rectangle" };
|
|
6283
|
+
if (control && key === "9") return { type: "tool", tool: "ellipse" };
|
|
6284
|
+
if (control && key === "6") return { type: "tool", tool: "line" };
|
|
6135
6285
|
if (!control && key === "Delete" && state.selectedShape) return { type: "delete" };
|
|
6136
6286
|
if (!control && key === "F2" && state.document)
|
|
6137
6287
|
return { type: "reveal", panel: "edit", focusText: true };
|
|
@@ -6154,8 +6304,14 @@ var ViewerCommands = class {
|
|
|
6154
6304
|
const button = (name) => root.querySelector(`[command="${name}"]`);
|
|
6155
6305
|
const box = (name) => root.querySelector(`[data-check="${name}"]`);
|
|
6156
6306
|
const editing = this.#canEdit(state);
|
|
6157
|
-
if (this.#tool
|
|
6307
|
+
if (this.#tool !== "pointer" && !state.edit.sourceAvailable) this.#tool = "pointer";
|
|
6158
6308
|
const page2 = state.document?.pages[state.pageIndex];
|
|
6309
|
+
const rotating = editing && !!page2 && !!state.selectedShape && !!visioLocalRotationShape(page2, state.selectedShape.id);
|
|
6310
|
+
for (const name of ["rotate-left", "rotate-right"]) button(name).disabled = !rotating;
|
|
6311
|
+
const flipping = rotating && !!page2 && !!state.selectedShape && !!visioLocalRotationShape(page2, state.selectedShape.id, false);
|
|
6312
|
+
for (const name of ["flip-horizontal", "flip-vertical"]) button(name).disabled = !flipping;
|
|
6313
|
+
root.querySelector('[data-menu="rotate"]').disabled = !rotating;
|
|
6314
|
+
root.querySelector('[data-menu="position"]').disabled = !rotating;
|
|
6159
6315
|
button("undo").disabled = !state.edit.canUndo || state.edit.busy || state.loading;
|
|
6160
6316
|
button("redo").disabled = !state.edit.canRedo || state.edit.busy || state.loading;
|
|
6161
6317
|
button("pointer").setAttribute("pressed", String(this.#tool === "pointer"));
|
|
@@ -6164,6 +6320,10 @@ var ViewerCommands = class {
|
|
|
6164
6320
|
rectangle.disabled = !editing || !page2;
|
|
6165
6321
|
button("rectangle-item").setAttribute("checked", String(this.#tool === "rectangle"));
|
|
6166
6322
|
button("rectangle-item").disabled = !editing || !page2;
|
|
6323
|
+
button("line-tool").setAttribute("checked", String(this.#tool === "line"));
|
|
6324
|
+
button("ellipse").setAttribute("checked", String(this.#tool === "ellipse"));
|
|
6325
|
+
button("ellipse").disabled = !editing || !page2;
|
|
6326
|
+
button("line-tool").disabled = !editing || !page2;
|
|
6167
6327
|
rectangle.title = state.edit.sourceAvailable ? "Rectangle (Ctrl+8)" : "Rectangle (Ctrl+8): open a .vsdx file to draw. Model-only documents are read only.";
|
|
6168
6328
|
box("grid").checked = this.#grid;
|
|
6169
6329
|
box("grid").disabled = !page2;
|
|
@@ -6181,6 +6341,464 @@ var ViewerCommands = class {
|
|
|
6181
6341
|
}
|
|
6182
6342
|
};
|
|
6183
6343
|
|
|
6344
|
+
// src/visio/viewer-handle-events.ts
|
|
6345
|
+
function handleGestureIsCurrent(drag, state, active) {
|
|
6346
|
+
return state.document === drag.document && state.document?.pages[state.pageIndex] === drag.page && state.selectedShape?.id === drag.shapeId && drag.svg.isConnected && !state.loading && !state.edit.busy && active;
|
|
6347
|
+
}
|
|
6348
|
+
function wireHandleEvents(viewport, callbacks) {
|
|
6349
|
+
const Abort = viewport.ownerDocument.defaultView?.AbortController ?? AbortController;
|
|
6350
|
+
const events = new Abort(), options = { signal: events.signal, capture: true };
|
|
6351
|
+
let suppressClick = false;
|
|
6352
|
+
viewport.addEventListener(
|
|
6353
|
+
"pointerdown",
|
|
6354
|
+
(event) => {
|
|
6355
|
+
suppressClick = false;
|
|
6356
|
+
callbacks.start(event);
|
|
6357
|
+
},
|
|
6358
|
+
options
|
|
6359
|
+
);
|
|
6360
|
+
viewport.addEventListener("pointermove", (event) => callbacks.move(event), options);
|
|
6361
|
+
viewport.addEventListener(
|
|
6362
|
+
"pointerup",
|
|
6363
|
+
(event) => {
|
|
6364
|
+
if (event.pointerId === callbacks.pointer()) suppressClick = true;
|
|
6365
|
+
void callbacks.finish(event);
|
|
6366
|
+
},
|
|
6367
|
+
options
|
|
6368
|
+
);
|
|
6369
|
+
const cancel = (event) => {
|
|
6370
|
+
if (event.pointerId === callbacks.pointer()) callbacks.cancel();
|
|
6371
|
+
};
|
|
6372
|
+
viewport.addEventListener("pointercancel", cancel, options);
|
|
6373
|
+
viewport.addEventListener("lostpointercapture", cancel, options);
|
|
6374
|
+
viewport.addEventListener(
|
|
6375
|
+
"keydown",
|
|
6376
|
+
(event) => {
|
|
6377
|
+
if (event.key === "Escape" && callbacks.pointer() !== void 0) {
|
|
6378
|
+
event.preventDefault();
|
|
6379
|
+
event.stopImmediatePropagation();
|
|
6380
|
+
suppressClick = true;
|
|
6381
|
+
callbacks.cancel();
|
|
6382
|
+
}
|
|
6383
|
+
},
|
|
6384
|
+
options
|
|
6385
|
+
);
|
|
6386
|
+
viewport.addEventListener(
|
|
6387
|
+
"click",
|
|
6388
|
+
(event) => {
|
|
6389
|
+
if (suppressClick) {
|
|
6390
|
+
suppressClick = false;
|
|
6391
|
+
event.stopImmediatePropagation();
|
|
6392
|
+
}
|
|
6393
|
+
},
|
|
6394
|
+
options
|
|
6395
|
+
);
|
|
6396
|
+
return () => {
|
|
6397
|
+
callbacks.cancel();
|
|
6398
|
+
events.abort();
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
6401
|
+
|
|
6402
|
+
// src/visio/viewer-line-endpoints.ts
|
|
6403
|
+
var SVG = "http://www.w3.org/2000/svg";
|
|
6404
|
+
var pointOptions = { snap: false, bounded: false };
|
|
6405
|
+
var ViewerLineEndpoints = class {
|
|
6406
|
+
constructor(viewport, controller, options) {
|
|
6407
|
+
this.viewport = viewport;
|
|
6408
|
+
this.controller = controller;
|
|
6409
|
+
this.options = options;
|
|
6410
|
+
}
|
|
6411
|
+
viewport;
|
|
6412
|
+
controller;
|
|
6413
|
+
options;
|
|
6414
|
+
#drag;
|
|
6415
|
+
#request = 0;
|
|
6416
|
+
#overlay;
|
|
6417
|
+
render(state) {
|
|
6418
|
+
const page2 = state.document?.pages[state.pageIndex];
|
|
6419
|
+
if (this.#drag) {
|
|
6420
|
+
if (handleGestureIsCurrent(this.#drag, state, this.options.active())) return;
|
|
6421
|
+
this.#cancel();
|
|
6422
|
+
}
|
|
6423
|
+
this.#overlay?.remove();
|
|
6424
|
+
this.#overlay = void 0;
|
|
6425
|
+
if (!page2 || !state.selectedShape || !state.edit.sourceAvailable || state.edit.busy || state.loading || !this.options.active())
|
|
6426
|
+
return;
|
|
6427
|
+
const shape = page2.shapes.find((shape2) => shape2.id === state.selectedShape.id);
|
|
6428
|
+
if (!shape || !visioStraightLineHandles(shape) || page2.connectors.some(
|
|
6429
|
+
(connection) => connection.fromShapeId === shape.id || connection.toShapeId === shape.id
|
|
6430
|
+
))
|
|
6431
|
+
return;
|
|
6432
|
+
const group2 = Array.from(this.viewport.querySelectorAll("[data-shape-id]")).find(
|
|
6433
|
+
(group3) => group3.dataset.shapeId === shape.id && group3.dataset.selected === "true"
|
|
6434
|
+
);
|
|
6435
|
+
if (!group2) return;
|
|
6436
|
+
const svg = group2.ownerSVGElement;
|
|
6437
|
+
if (!svg) return;
|
|
6438
|
+
const overlay = this.viewport.ownerDocument.createElementNS(SVG, "g");
|
|
6439
|
+
overlay.dataset.lineEndpointOverlay = "";
|
|
6440
|
+
const rootMatrix = svg.getCTM?.(), groupMatrix = group2.getCTM?.();
|
|
6441
|
+
if (rootMatrix && groupMatrix) {
|
|
6442
|
+
const matrix2 = rootMatrix.inverse().multiply(groupMatrix);
|
|
6443
|
+
overlay.setAttribute(
|
|
6444
|
+
"transform",
|
|
6445
|
+
`matrix(${matrix2.a} ${matrix2.b} ${matrix2.c} ${matrix2.d} ${matrix2.e} ${matrix2.f})`
|
|
6446
|
+
);
|
|
6447
|
+
}
|
|
6448
|
+
const screen = group2.getScreenCTM?.();
|
|
6449
|
+
const screenScale = screen ? Math.hypot(screen.a, screen.b) : 96 * state.zoom;
|
|
6450
|
+
for (const endpoint of ["begin", "end"]) {
|
|
6451
|
+
const handle = this.viewport.ownerDocument.createElementNS(SVG, "circle");
|
|
6452
|
+
handle.dataset.lineEndpoint = endpoint;
|
|
6453
|
+
handle.dataset.lineShapeId = shape.id;
|
|
6454
|
+
handle.setAttribute("cx", String(endpoint === "begin" ? 0 : shape.width));
|
|
6455
|
+
handle.setAttribute("cy", "0");
|
|
6456
|
+
handle.setAttribute("r", String(5 / screenScale));
|
|
6457
|
+
handle.setAttribute("vector-effect", "non-scaling-stroke");
|
|
6458
|
+
handle.setAttribute("aria-label", `Drag ${endpoint} endpoint`);
|
|
6459
|
+
overlay.append(handle);
|
|
6460
|
+
}
|
|
6461
|
+
svg.append(overlay);
|
|
6462
|
+
this.#overlay = overlay;
|
|
6463
|
+
}
|
|
6464
|
+
wire() {
|
|
6465
|
+
const dispose = wireHandleEvents(this.viewport, {
|
|
6466
|
+
pointer: () => this.#drag?.pointer,
|
|
6467
|
+
start: (event) => this.#start(event),
|
|
6468
|
+
move: (event) => this.#move(event),
|
|
6469
|
+
finish: (event) => this.#finish(event),
|
|
6470
|
+
cancel: () => this.#cancel()
|
|
6471
|
+
});
|
|
6472
|
+
return () => {
|
|
6473
|
+
++this.#request;
|
|
6474
|
+
dispose();
|
|
6475
|
+
this.#overlay?.remove();
|
|
6476
|
+
this.#overlay = void 0;
|
|
6477
|
+
};
|
|
6478
|
+
}
|
|
6479
|
+
#start(event) {
|
|
6480
|
+
const handle = event.target?.closest?.("[data-line-endpoint]");
|
|
6481
|
+
const state = this.controller.state, page2 = state.document?.pages[state.pageIndex];
|
|
6482
|
+
const svg = this.viewport.querySelector("svg.paper");
|
|
6483
|
+
const endpoint = handle?.dataset.lineEndpoint;
|
|
6484
|
+
if (!handle || !svg || !page2 || !state.document || !state.selectedShape || this.#drag || event.button !== 0 || state.edit.busy || state.loading || !state.edit.sourceAvailable || !this.options.active() || endpoint !== "begin" && endpoint !== "end")
|
|
6485
|
+
return;
|
|
6486
|
+
const other = handle.parentElement?.querySelector(
|
|
6487
|
+
`[data-line-endpoint="${endpoint === "begin" ? "end" : "begin"}"]`
|
|
6488
|
+
);
|
|
6489
|
+
const matrix2 = other?.getScreenCTM();
|
|
6490
|
+
if (!other || !matrix2) return;
|
|
6491
|
+
const centre = new DOMPoint(other.cx.baseVal.value, 0).matrixTransform(matrix2);
|
|
6492
|
+
const fixed = pagePoint(svg, page2, { clientX: centre.x, clientY: centre.y }, pointOptions);
|
|
6493
|
+
const start = pagePoint(svg, page2, event, pointOptions);
|
|
6494
|
+
if (!fixed || !start) return;
|
|
6495
|
+
event.preventDefault();
|
|
6496
|
+
event.stopImmediatePropagation();
|
|
6497
|
+
const preview2 = this.viewport.ownerDocument.createElementNS(SVG, "line");
|
|
6498
|
+
preview2.classList.add("endpoint-preview");
|
|
6499
|
+
preview2.setAttribute("vector-effect", "non-scaling-stroke");
|
|
6500
|
+
for (const [name, value] of Object.entries({
|
|
6501
|
+
x1: fixed.x,
|
|
6502
|
+
y1: fixed.y,
|
|
6503
|
+
x2: start.x,
|
|
6504
|
+
y2: start.y
|
|
6505
|
+
}))
|
|
6506
|
+
preview2.setAttribute(name, String(value));
|
|
6507
|
+
svg.append(preview2);
|
|
6508
|
+
this.#drag = {
|
|
6509
|
+
pointer: event.pointerId,
|
|
6510
|
+
svg,
|
|
6511
|
+
page: page2,
|
|
6512
|
+
document: state.document,
|
|
6513
|
+
shapeId: state.selectedShape.id,
|
|
6514
|
+
endpoint,
|
|
6515
|
+
preview: preview2,
|
|
6516
|
+
startX: event.clientX,
|
|
6517
|
+
startY: event.clientY
|
|
6518
|
+
};
|
|
6519
|
+
this.viewport.setPointerCapture?.(event.pointerId);
|
|
6520
|
+
}
|
|
6521
|
+
#move(event) {
|
|
6522
|
+
const drag = this.#drag;
|
|
6523
|
+
if (!drag || event.pointerId !== drag.pointer) return;
|
|
6524
|
+
const point = pagePoint(drag.svg, drag.page, event, pointOptions);
|
|
6525
|
+
if (point) {
|
|
6526
|
+
drag.preview.setAttribute("x2", String(point.x));
|
|
6527
|
+
drag.preview.setAttribute("y2", String(point.y));
|
|
6528
|
+
}
|
|
6529
|
+
event.preventDefault();
|
|
6530
|
+
event.stopImmediatePropagation();
|
|
6531
|
+
}
|
|
6532
|
+
#cancel() {
|
|
6533
|
+
const drag = this.#drag;
|
|
6534
|
+
this.#drag = void 0;
|
|
6535
|
+
drag?.preview.remove();
|
|
6536
|
+
if (drag && this.viewport.hasPointerCapture?.(drag.pointer))
|
|
6537
|
+
this.viewport.releasePointerCapture(drag.pointer);
|
|
6538
|
+
}
|
|
6539
|
+
async #finish(event) {
|
|
6540
|
+
const drag = this.#drag;
|
|
6541
|
+
if (!drag || event.pointerId !== drag.pointer) return;
|
|
6542
|
+
event.preventDefault();
|
|
6543
|
+
event.stopImmediatePropagation();
|
|
6544
|
+
const point = pagePoint(drag.svg, drag.page, event, pointOptions);
|
|
6545
|
+
this.#cancel();
|
|
6546
|
+
if (!point || Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 0.5 || this.controller.state.document !== drag.document)
|
|
6547
|
+
return;
|
|
6548
|
+
const request = ++this.#request;
|
|
6549
|
+
try {
|
|
6550
|
+
await this.controller.applyEdits([
|
|
6551
|
+
visioPageEditToDrawing(drag.page, {
|
|
6552
|
+
type: "move-line-endpoint",
|
|
6553
|
+
pageId: drag.page.id,
|
|
6554
|
+
shapeId: drag.shapeId,
|
|
6555
|
+
endpoint: drag.endpoint,
|
|
6556
|
+
x: point.x,
|
|
6557
|
+
y: drag.page.height - point.y
|
|
6558
|
+
})
|
|
6559
|
+
]);
|
|
6560
|
+
if (request === this.#request)
|
|
6561
|
+
this.options.announce(`${drag.endpoint === "begin" ? "Begin" : "End"} endpoint moved.`);
|
|
6562
|
+
} catch (error) {
|
|
6563
|
+
if (request === this.#request && !isEditCancellation(error))
|
|
6564
|
+
this.options.announce(editErrorMessage(error));
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6567
|
+
};
|
|
6568
|
+
function createRotationPreview(model, page2, shape, source) {
|
|
6569
|
+
const element = svgElement("g");
|
|
6570
|
+
element.classList.add("rotation-shape-preview");
|
|
6571
|
+
element.setAttribute("aria-hidden", "true");
|
|
6572
|
+
element.style.pointerEvents = "none";
|
|
6573
|
+
const visibility = source.style.visibility;
|
|
6574
|
+
let result;
|
|
6575
|
+
source.after(element);
|
|
6576
|
+
source.style.visibility = "hidden";
|
|
6577
|
+
return {
|
|
6578
|
+
element,
|
|
6579
|
+
update(angle) {
|
|
6580
|
+
const transform = visioRotationPreviewTransform(shape, angle);
|
|
6581
|
+
if (!transform) return;
|
|
6582
|
+
const previewPage = {
|
|
6583
|
+
...page2,
|
|
6584
|
+
shapes: [{ ...shape, transform, rotation: { ...shape.rotation, angle } }]
|
|
6585
|
+
};
|
|
6586
|
+
delete previewPage.backgroundPageId;
|
|
6587
|
+
const next = renderPage({ ...model, pages: [previewPage] }, previewPage, {
|
|
6588
|
+
interactive: false
|
|
6589
|
+
});
|
|
6590
|
+
const defs = next.svg.querySelector(":scope > defs");
|
|
6591
|
+
const root = next.svg.querySelector(":scope > g");
|
|
6592
|
+
element.replaceChildren(defs, ...root.children);
|
|
6593
|
+
result?.dispose();
|
|
6594
|
+
result = next;
|
|
6595
|
+
},
|
|
6596
|
+
dispose() {
|
|
6597
|
+
element.remove();
|
|
6598
|
+
result?.dispose();
|
|
6599
|
+
result = void 0;
|
|
6600
|
+
source.style.visibility = visibility;
|
|
6601
|
+
}
|
|
6602
|
+
};
|
|
6603
|
+
}
|
|
6604
|
+
|
|
6605
|
+
// src/visio/viewer-rotation-handle.ts
|
|
6606
|
+
var SVG2 = "http://www.w3.org/2000/svg";
|
|
6607
|
+
var pointOptions2 = { snap: false, bounded: false };
|
|
6608
|
+
var ViewerRotationHandle = class {
|
|
6609
|
+
constructor(viewport, controller, options) {
|
|
6610
|
+
this.viewport = viewport;
|
|
6611
|
+
this.controller = controller;
|
|
6612
|
+
this.options = options;
|
|
6613
|
+
}
|
|
6614
|
+
viewport;
|
|
6615
|
+
controller;
|
|
6616
|
+
options;
|
|
6617
|
+
#drag;
|
|
6618
|
+
#request = 0;
|
|
6619
|
+
#overlay;
|
|
6620
|
+
render(state) {
|
|
6621
|
+
if (this.#drag) {
|
|
6622
|
+
if (handleGestureIsCurrent(this.#drag, state, this.options.active())) return;
|
|
6623
|
+
this.#cancel();
|
|
6624
|
+
}
|
|
6625
|
+
this.#overlay?.remove();
|
|
6626
|
+
this.#overlay = void 0;
|
|
6627
|
+
const page2 = state.document?.pages[state.pageIndex], selection2 = state.selectedShape;
|
|
6628
|
+
if (!page2 || !selection2 || !state.edit.sourceAvailable || state.loading || state.edit.busy || !this.options.active())
|
|
6629
|
+
return;
|
|
6630
|
+
const shape = visioLocalRotationShape(page2, selection2.id);
|
|
6631
|
+
if (!shape?.rotation) return;
|
|
6632
|
+
const group2 = Array.from(this.viewport.querySelectorAll("[data-shape-id]")).find(
|
|
6633
|
+
(group3) => group3.dataset.shapeId === shape.id && group3.dataset.selected === "true"
|
|
6634
|
+
);
|
|
6635
|
+
const svg = group2?.ownerSVGElement, matrix2 = svg?.getScreenCTM?.();
|
|
6636
|
+
if (!group2 || !svg || !matrix2) return;
|
|
6637
|
+
const center = { x: shape.rotation.pinX, y: page2.height - shape.rotation.pinY };
|
|
6638
|
+
const rect = group2.getBoundingClientRect(), bounds = this.viewport.getBoundingClientRect();
|
|
6639
|
+
const location = resolveRotateHandlePlacement({
|
|
6640
|
+
preferred: { x: (rect.left + rect.right) / 2, y: rect.top - 24 },
|
|
6641
|
+
selection: rect,
|
|
6642
|
+
bounds,
|
|
6643
|
+
hitWidth: 14,
|
|
6644
|
+
hitHeight: 14,
|
|
6645
|
+
obstacles: []
|
|
6646
|
+
});
|
|
6647
|
+
if (!location) return;
|
|
6648
|
+
const point = { x: location.x - bounds.left, y: location.y - bounds.top };
|
|
6649
|
+
const pivot = new DOMPoint(center.x, center.y).matrixTransform(matrix2);
|
|
6650
|
+
const overlay = this.viewport.ownerDocument.createElementNS(SVG2, "svg");
|
|
6651
|
+
overlay.classList.add("rotation-overlay");
|
|
6652
|
+
overlay.style.left = `${this.viewport.scrollLeft}px`;
|
|
6653
|
+
overlay.style.top = `${this.viewport.scrollTop}px`;
|
|
6654
|
+
overlay.setAttribute("width", String(bounds.width));
|
|
6655
|
+
overlay.setAttribute("height", String(bounds.height));
|
|
6656
|
+
overlay.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
|
|
6657
|
+
overlay.dataset.rotationOverlay = "";
|
|
6658
|
+
const stem = this.viewport.ownerDocument.createElementNS(SVG2, "line");
|
|
6659
|
+
for (const [name, value] of Object.entries({
|
|
6660
|
+
x1: pivot.x - bounds.left,
|
|
6661
|
+
y1: pivot.y - bounds.top,
|
|
6662
|
+
x2: point.x,
|
|
6663
|
+
y2: point.y
|
|
6664
|
+
}))
|
|
6665
|
+
stem.setAttribute(name, String(value));
|
|
6666
|
+
stem.classList.add("rotation-stem");
|
|
6667
|
+
stem.setAttribute("vector-effect", "non-scaling-stroke");
|
|
6668
|
+
const handle = this.viewport.ownerDocument.createElementNS(SVG2, "circle");
|
|
6669
|
+
handle.dataset.rotationHandle = shape.id;
|
|
6670
|
+
handle.setAttribute("cx", String(point.x));
|
|
6671
|
+
handle.setAttribute("cy", String(point.y));
|
|
6672
|
+
handle.setAttribute("r", "6");
|
|
6673
|
+
handle.setAttribute("vector-effect", "non-scaling-stroke");
|
|
6674
|
+
handle.setAttribute("aria-label", "Drag rotation handle");
|
|
6675
|
+
overlay.append(stem, handle);
|
|
6676
|
+
this.viewport.append(overlay);
|
|
6677
|
+
this.#overlay = overlay;
|
|
6678
|
+
}
|
|
6679
|
+
wire() {
|
|
6680
|
+
const dispose = wireHandleEvents(this.viewport, {
|
|
6681
|
+
pointer: () => this.#drag?.pointer,
|
|
6682
|
+
start: (event) => this.#start(event),
|
|
6683
|
+
move: (event) => this.#move(event),
|
|
6684
|
+
finish: (event) => this.#finish(event),
|
|
6685
|
+
cancel: () => this.#cancel()
|
|
6686
|
+
});
|
|
6687
|
+
const refresh = () => this.render(this.controller.state);
|
|
6688
|
+
this.viewport.addEventListener("scroll", refresh, { passive: true });
|
|
6689
|
+
const Observer = this.viewport.ownerDocument.defaultView?.ResizeObserver;
|
|
6690
|
+
const observer = Observer ? new Observer(refresh) : void 0;
|
|
6691
|
+
observer?.observe(this.viewport);
|
|
6692
|
+
return () => {
|
|
6693
|
+
this.viewport.removeEventListener("scroll", refresh);
|
|
6694
|
+
observer?.disconnect();
|
|
6695
|
+
++this.#request;
|
|
6696
|
+
dispose();
|
|
6697
|
+
this.#overlay?.remove();
|
|
6698
|
+
this.#overlay = void 0;
|
|
6699
|
+
};
|
|
6700
|
+
}
|
|
6701
|
+
#start(event) {
|
|
6702
|
+
const handle = event.target?.closest?.("[data-rotation-handle]");
|
|
6703
|
+
const state = this.controller.state, page2 = state.document?.pages[state.pageIndex];
|
|
6704
|
+
const shape = page2?.shapes.find((shape2) => shape2.id === handle?.dataset.rotationHandle);
|
|
6705
|
+
const svg = this.viewport.querySelector("svg.paper");
|
|
6706
|
+
if (!handle || !svg || !page2 || !state.document || !shape?.rotation || state.selectedShape?.id !== shape.id || this.#drag || event.button !== 0 || state.edit.busy || state.loading || !state.edit.sourceAvailable || !this.options.active())
|
|
6707
|
+
return;
|
|
6708
|
+
const source = Array.from(svg.querySelectorAll("[data-shape-id]")).find(
|
|
6709
|
+
(group2) => group2.dataset.shapeId === shape.id
|
|
6710
|
+
);
|
|
6711
|
+
if (!source) return;
|
|
6712
|
+
const start = pagePoint(svg, page2, event, pointOptions2);
|
|
6713
|
+
if (!start) return;
|
|
6714
|
+
this.viewport.focus({ preventScroll: true });
|
|
6715
|
+
const center = { x: shape.rotation.pinX, y: page2.height - shape.rotation.pinY };
|
|
6716
|
+
const preview2 = this.viewport.ownerDocument.createElementNS(SVG2, "line");
|
|
6717
|
+
preview2.classList.add("rotation-preview");
|
|
6718
|
+
preview2.setAttribute("vector-effect", "non-scaling-stroke");
|
|
6719
|
+
for (const [name, value] of Object.entries({
|
|
6720
|
+
x1: center.x,
|
|
6721
|
+
y1: center.y,
|
|
6722
|
+
x2: start.x,
|
|
6723
|
+
y2: start.y
|
|
6724
|
+
}))
|
|
6725
|
+
preview2.setAttribute(name, String(value));
|
|
6726
|
+
svg.append(preview2);
|
|
6727
|
+
this.#drag = {
|
|
6728
|
+
pointer: event.pointerId,
|
|
6729
|
+
svg,
|
|
6730
|
+
page: page2,
|
|
6731
|
+
document: state.document,
|
|
6732
|
+
shapeId: shape.id,
|
|
6733
|
+
preview: preview2,
|
|
6734
|
+
shapePreview: createRotationPreview(state.document, page2, shape, source),
|
|
6735
|
+
startX: event.clientX,
|
|
6736
|
+
startY: event.clientY,
|
|
6737
|
+
center,
|
|
6738
|
+
rotate: createRotationDrag(center, start, -shape.rotation.angle * 180 / Math.PI, {
|
|
6739
|
+
unwrapped: true
|
|
6740
|
+
})
|
|
6741
|
+
};
|
|
6742
|
+
try {
|
|
6743
|
+
this.#drag.shapePreview.update(shape.rotation.angle);
|
|
6744
|
+
} catch (error) {
|
|
6745
|
+
this.#cancel();
|
|
6746
|
+
this.options.announce(editErrorMessage(error));
|
|
6747
|
+
return;
|
|
6748
|
+
}
|
|
6749
|
+
event.preventDefault();
|
|
6750
|
+
event.stopImmediatePropagation();
|
|
6751
|
+
this.viewport.setPointerCapture?.(event.pointerId);
|
|
6752
|
+
}
|
|
6753
|
+
#move(event) {
|
|
6754
|
+
const drag = this.#drag;
|
|
6755
|
+
if (!drag || event.pointerId !== drag.pointer) return;
|
|
6756
|
+
const point = pagePoint(drag.svg, drag.page, event, pointOptions2);
|
|
6757
|
+
if (point) {
|
|
6758
|
+
const angle = -drag.rotate(point) * Math.PI / 180;
|
|
6759
|
+
try {
|
|
6760
|
+
drag.shapePreview.update(angle);
|
|
6761
|
+
} catch (error) {
|
|
6762
|
+
this.#cancel();
|
|
6763
|
+
this.options.announce(editErrorMessage(error));
|
|
6764
|
+
}
|
|
6765
|
+
drag.preview.setAttribute("x2", String(point.x));
|
|
6766
|
+
drag.preview.setAttribute("y2", String(point.y));
|
|
6767
|
+
}
|
|
6768
|
+
event.preventDefault();
|
|
6769
|
+
event.stopImmediatePropagation();
|
|
6770
|
+
}
|
|
6771
|
+
#cancel() {
|
|
6772
|
+
const drag = this.#drag;
|
|
6773
|
+
this.#drag = void 0;
|
|
6774
|
+
drag?.preview.remove();
|
|
6775
|
+
drag?.shapePreview.dispose();
|
|
6776
|
+
if (drag && this.viewport.hasPointerCapture?.(drag.pointer))
|
|
6777
|
+
this.viewport.releasePointerCapture(drag.pointer);
|
|
6778
|
+
}
|
|
6779
|
+
async #finish(event) {
|
|
6780
|
+
const drag = this.#drag;
|
|
6781
|
+
if (!drag || event.pointerId !== drag.pointer) return;
|
|
6782
|
+
event.preventDefault();
|
|
6783
|
+
event.stopImmediatePropagation();
|
|
6784
|
+
const point = pagePoint(drag.svg, drag.page, event, pointOptions2);
|
|
6785
|
+
const angle = point ? -drag.rotate(point) * Math.PI / 180 : void 0;
|
|
6786
|
+
this.#cancel();
|
|
6787
|
+
if (angle === void 0 || Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 0.5 || this.controller.state.document !== drag.document)
|
|
6788
|
+
return;
|
|
6789
|
+
const request = ++this.#request;
|
|
6790
|
+
try {
|
|
6791
|
+
await this.controller.applyEdits([
|
|
6792
|
+
{ type: "rotate-shape", pageId: drag.page.id, shapeId: drag.shapeId, angle }
|
|
6793
|
+
]);
|
|
6794
|
+
if (request === this.#request) this.options.announce("Shape rotated.");
|
|
6795
|
+
} catch (error) {
|
|
6796
|
+
if (request === this.#request && !isEditCancellation(error))
|
|
6797
|
+
this.options.announce(editErrorMessage(error));
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6800
|
+
};
|
|
6801
|
+
|
|
6184
6802
|
// src/visio/office-ui.ts
|
|
6185
6803
|
var VISIO_ICONS = {
|
|
6186
6804
|
visioLayers: "m2.5 7 7.5-4 7.5 4-7.5 4ZM2.5 10.5l7.5 4 7.5-4M2.5 14l7.5 4 7.5-4",
|
|
@@ -6289,14 +6907,15 @@ function estimatePageSvgBytes(model, page2, maxBytes) {
|
|
|
6289
6907
|
reserve(
|
|
6290
6908
|
4096 + geometry.path.length * 6 + shape.style.lineColor.length * 24 + shape.style.fill.length * 6
|
|
6291
6909
|
);
|
|
6292
|
-
const
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
reserve(gradients * (256 + stop.color.length * 6));
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6910
|
+
for (const gradient of [shape.style.fillGradient, shape.style.lineGradient]) {
|
|
6911
|
+
const gradients = visioGradientInstances(gradient);
|
|
6912
|
+
reserve(1024 * gradients);
|
|
6913
|
+
for (const stop of gradient?.stops ?? []) reserve(gradients * (256 + stop.color.length * 6));
|
|
6914
|
+
if (gradient)
|
|
6915
|
+
reserve(
|
|
6916
|
+
gradients * (visioRenderedGradientStopCount(gradient) - gradient.stops.length) * (256 + 7 * 6)
|
|
6917
|
+
);
|
|
6918
|
+
}
|
|
6300
6919
|
if (shape.style.fillPattern)
|
|
6301
6920
|
reserve(1536 + 4 * Math.ceil(shape.style.fillPattern.bytes.byteLength / 3));
|
|
6302
6921
|
if (shape.image) {
|
|
@@ -6593,14 +7212,16 @@ function validationCost(model) {
|
|
|
6593
7212
|
);
|
|
6594
7213
|
for (const geometry of shape.geometry) strings(geometry.path);
|
|
6595
7214
|
if (shape.style.fillPattern) work += 64;
|
|
6596
|
-
const
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
7215
|
+
for (const gradient of [shape.style.fillGradient, shape.style.lineGradient]) {
|
|
7216
|
+
const gradients = visioGradientInstances(gradient);
|
|
7217
|
+
work += gradients * 16;
|
|
7218
|
+
for (const stop of gradient?.stops ?? []) {
|
|
7219
|
+
work += 3 * gradients;
|
|
7220
|
+
strings(stop.color);
|
|
7221
|
+
}
|
|
7222
|
+
if (gradient)
|
|
7223
|
+
work += gradients * 10 * (visioRenderedGradientStopCount(gradient) - gradient.stops.length);
|
|
6601
7224
|
}
|
|
6602
|
-
if (shape.style.fillGradient)
|
|
6603
|
-
work += gradients * 10 * (visioRenderedGradientStopCount(shape.style.fillGradient) - shape.style.fillGradient.stops.length);
|
|
6604
7225
|
for (const run of shape.text.runs) {
|
|
6605
7226
|
work += 8;
|
|
6606
7227
|
strings(run.text, run.fontFamily, run.color);
|
|
@@ -6642,6 +7263,9 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6642
7263
|
#shapeStatus;
|
|
6643
7264
|
#announcement;
|
|
6644
7265
|
#commands;
|
|
7266
|
+
#lineEndpoints;
|
|
7267
|
+
#rotationHandle;
|
|
7268
|
+
#handleState;
|
|
6645
7269
|
#rulers;
|
|
6646
7270
|
#canvas;
|
|
6647
7271
|
#panZoom;
|
|
@@ -6713,6 +7337,10 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6713
7337
|
root: this.#root,
|
|
6714
7338
|
viewport: this.#viewport,
|
|
6715
7339
|
controller: this.controller,
|
|
7340
|
+
toolChanged: () => {
|
|
7341
|
+
this.#lineEndpoints.render(this.controller.state);
|
|
7342
|
+
this.#rotationHandle.render(this.controller.state);
|
|
7343
|
+
},
|
|
6716
7344
|
fit: (mode) => this.#fit(mode),
|
|
6717
7345
|
togglePane: (pane) => this.#chrome.togglePane(pane),
|
|
6718
7346
|
reveal: (panel, focusText) => this.#chrome.reveal(panel, focusText),
|
|
@@ -6728,6 +7356,20 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6728
7356
|
}
|
|
6729
7357
|
});
|
|
6730
7358
|
this.#profile = new ViewerProfile(this.#root);
|
|
7359
|
+
this.#lineEndpoints = new ViewerLineEndpoints(this.#viewport, this.controller, {
|
|
7360
|
+
active: () => this.#commands.tool === "pointer",
|
|
7361
|
+
announce: (message) => {
|
|
7362
|
+
this.#announcement = message;
|
|
7363
|
+
this.#status.textContent = message;
|
|
7364
|
+
}
|
|
7365
|
+
});
|
|
7366
|
+
this.#rotationHandle = new ViewerRotationHandle(this.#viewport, this.controller, {
|
|
7367
|
+
active: () => this.#commands.tool === "pointer",
|
|
7368
|
+
announce: (message) => {
|
|
7369
|
+
this.#announcement = message;
|
|
7370
|
+
this.#status.textContent = message;
|
|
7371
|
+
}
|
|
7372
|
+
});
|
|
6731
7373
|
this.#share = new ViewerShare(this.#root, this.controller, () => this.#profile.profile);
|
|
6732
7374
|
this.#backstage = new ViewerBackstage({
|
|
6733
7375
|
root: this.#root,
|
|
@@ -6911,6 +7553,8 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6911
7553
|
#wireInputs() {
|
|
6912
7554
|
const disposeChrome = this.#chrome.wire();
|
|
6913
7555
|
const disposeCommands = this.#commands.wire();
|
|
7556
|
+
const disposeLineEndpoints = this.#lineEndpoints.wire();
|
|
7557
|
+
const disposeRotation = this.#rotationHandle.wire();
|
|
6914
7558
|
const disposeBackstage = this.#backstage.wire();
|
|
6915
7559
|
const disposeMenus = wireContextMenus(this.#root, this.#viewport, this.controller);
|
|
6916
7560
|
const disposeTellMe = wireTellMe(this.#root);
|
|
@@ -6946,6 +7590,8 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6946
7590
|
return () => {
|
|
6947
7591
|
disposeChrome();
|
|
6948
7592
|
disposeCommands();
|
|
7593
|
+
disposeLineEndpoints();
|
|
7594
|
+
disposeRotation();
|
|
6949
7595
|
disposeBackstage();
|
|
6950
7596
|
disposeMenus();
|
|
6951
7597
|
disposeTellMe();
|
|
@@ -6968,6 +7614,12 @@ var VisioViewerElement = class extends BaseElement {
|
|
|
6968
7614
|
if (this.#suspended) return;
|
|
6969
7615
|
const page2 = state.document?.pages[state.pageIndex];
|
|
6970
7616
|
const changed = this.#canvas.render(state);
|
|
7617
|
+
const previous = this.#handleState;
|
|
7618
|
+
this.#handleState = state;
|
|
7619
|
+
if (changed || !previous || previous.zoom !== state.zoom || previous.selectedShape !== state.selectedShape || previous.loading !== state.loading || previous.edit.busy !== state.edit.busy || previous.edit.sourceAvailable !== state.edit.sourceAvailable) {
|
|
7620
|
+
this.#lineEndpoints.render(state);
|
|
7621
|
+
this.#rotationHandle.render(state);
|
|
7622
|
+
}
|
|
6971
7623
|
if (changed) this.#announcement = void 0;
|
|
6972
7624
|
this.#zoomSlider.value = Math.round(state.zoom * 100);
|
|
6973
7625
|
this.#zoomSlider.disabled = !page2;
|