pptx-angular-viewer 3.19.1 → 3.20.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/CHANGELOG.md +18 -0
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-BKazjZCl.mjs → pptx-angular-viewer-chat-history-idb-amzrYVHP.mjs} +2 -2
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-BKazjZCl.mjs.map → pptx-angular-viewer-chat-history-idb-amzrYVHP.mjs.map} +1 -1
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-D2wmF5iB.mjs → pptx-angular-viewer-pptx-angular-viewer-DP0uS-ib.mjs} +1822 -693
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-D2wmF5iB.mjs.map → pptx-angular-viewer-pptx-angular-viewer-DP0uS-ib.mjs.map} +1 -1
- package/fesm2022/pptx-angular-viewer.mjs +1 -1
- package/i18n/de.cjs +11 -0
- package/i18n/de.mjs +11 -0
- package/i18n/es.cjs +11 -0
- package/i18n/es.mjs +11 -0
- package/i18n/fr.cjs +11 -0
- package/i18n/fr.mjs +11 -0
- package/i18n/zh-CN.cjs +11 -0
- package/i18n/zh-CN.mjs +11 -0
- package/package.json +2 -2
- package/types/pptx-angular-viewer.d.ts +257 -149
- package/types/pptx-angular-viewer.d.ts.map +1 -1
|
@@ -8,7 +8,7 @@ import DOMPurify from 'dompurify';
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import * as mcp from 'pptx-viewer-mcp';
|
|
10
10
|
import * as mcpSchemas from 'pptx-viewer-mcp/schemas';
|
|
11
|
-
import { LucideX, LucideSquare, LucideCircle, LucideSlash, LucideCopy, LucideTrash2, LucideChevronsUp, LucideChevronsDown, LucideArrowUp, LucideArrowDown, LucideGroup, LucideUngroup, LucideTextAlignStart, LucideTextAlignCenter, LucideTextAlignEnd, LucideChevronUp, LucideChevronDown, LucideAlignHorizontalSpaceAround, LucideAlignVerticalSpaceAround, LucideLayers, LucideMessageSquare,
|
|
11
|
+
import { LucideX, LucideSquare, LucideCircle, LucideSlash, LucideCopy, LucideTrash2, LucideChevronsUp, LucideChevronsDown, LucideArrowUp, LucideArrowDown, LucideGroup, LucideUngroup, LucideTextAlignStart, LucideTextAlignCenter, LucideTextAlignEnd, LucideChevronUp, LucideChevronDown, LucideAlignHorizontalSpaceAround, LucideAlignVerticalSpaceAround, LucidePlus, LucidePaintbrush, LucideFilter, LucideLayers, LucideMessageSquare, LucideSettings2, LucideStickyNote, LucideDownload, LucideMenu, LucidePresentation, LucideRedo, LucideShare2, LucideSparkles, LucideUndo, LucideChevronRight, LucideChevronLeft, LucideEraser, LucideHighlighter, LucideMousePointer2, LucidePanelRight, LucidePenTool, LucideTimer, LucideArrowLeftRight, LucideCaptions, LucideCirclePause, LucideCirclePlay, LucideGrid2x2, LucideMonitor, LucideMonitorOff, LucideRotateCcw, LucideScan, LucideZoomIn, LucideZoomOut, LucideSave, LucidePlay, LucidePrinter, LucideFileDown, LucideSearch, LucideStar, LucideMoveRight, LucideMousePointerClick, LucideClock3, LucidePencil, LucideType, LucideMinus, LucideSpline, LucideArrowLeft, LucideFilePlus2, LucideFolderOpen, LucideHome, LucideInfo, LucideSettings, LucideUpload, LucideUserRound, LucideAArrowUp, LucideAArrowDown, LucideRemoveFormatting, LucideList, LucideListOrdered, LucideListIndentDecrease, LucideListIndentIncrease, LucideTextAlignJustify, LucideClipboardPaste, LucideFolderPlus, LucideLayoutGrid, LucideLayoutTemplate, LucideScissors, LucideLink, LucideImage, LucideVideo, LucideDatabase, LucidePanelLeft, LucideEllipsis, LucideEye, LucideEyeOff, LucideColumns2, LucideCheck, LucideBug, LucideSend, LucideCrosshair, LucideGitMerge, LucidePin, LucidePinOff, LucideHistory, LucideMessageSquarePlus, LucideWrench, LucideLoaderCircle, LucideTriangleAlert, LucideShapes, LucidePalette, LucideTable, LucideChartColumn, LucideMove, LucideNavigation, LucideFilm, LucideBot, LucideUser } from '@lucide/angular';
|
|
12
12
|
import { XMLParser } from 'fast-xml-parser';
|
|
13
13
|
import JSZip from 'jszip';
|
|
14
14
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -4974,6 +4974,78 @@ function resolveGroupChildFill(child, parentGroupFill) {
|
|
|
4974
4974
|
return getComputedFillStyle(child, parentGroupFill);
|
|
4975
4975
|
}
|
|
4976
4976
|
|
|
4977
|
+
/**
|
|
4978
|
+
* The exact algebraic inverse of {@link getElementTransform} (from
|
|
4979
|
+
* `element-style-transform.ts`), restricted to rotation + flip (no skew: a
|
|
4980
|
+
* skewed image fill with `rotWithShape="0"` is rare enough that an exact
|
|
4981
|
+
* shear inverse is not worth the added complexity yet).
|
|
4982
|
+
*
|
|
4983
|
+
* Derivation: `getElementTransform` emits the CSS list
|
|
4984
|
+
* `rotate(θ) scaleX(fx) scaleY(fy)`, i.e. the matrix product `R * Sx * Sy`
|
|
4985
|
+
* (CSS applies the RIGHTMOST function to the point first). Its inverse is
|
|
4986
|
+
* `Sy⁻¹ * Sx⁻¹ * R⁻¹ = Sy * Sx * R(-θ)` (flips are self-inverse), which as a
|
|
4987
|
+
* CSS list is `scaleY(fy) scaleX(fx) rotate(-θ deg)` - order matters, and
|
|
4988
|
+
* differs from the forward transform's order.
|
|
4989
|
+
*
|
|
4990
|
+
* @param element - The element whose `rotation`/`flipHorizontal`/`flipVertical` are read.
|
|
4991
|
+
* @returns A CSS transform string, or `undefined` if no counter-transform is needed.
|
|
4992
|
+
*/
|
|
4993
|
+
function getFrameInverseTransform(element) {
|
|
4994
|
+
const transforms = [];
|
|
4995
|
+
if (element.flipVertical) {
|
|
4996
|
+
transforms.push('scaleY(-1)');
|
|
4997
|
+
}
|
|
4998
|
+
if (element.flipHorizontal) {
|
|
4999
|
+
transforms.push('scaleX(-1)');
|
|
5000
|
+
}
|
|
5001
|
+
if (element.rotation) {
|
|
5002
|
+
transforms.push(`rotate(${-element.rotation}deg)`);
|
|
5003
|
+
}
|
|
5004
|
+
return transforms.length > 0 ? transforms.join(' ') : undefined;
|
|
5005
|
+
}
|
|
5006
|
+
/**
|
|
5007
|
+
* CSS `transform` to apply to a picture's / image fill's CONTENT layer (the
|
|
5008
|
+
* `<img>`, or a `background-image` layer) so it renders upright/unmirrored,
|
|
5009
|
+
* fixed to the page frame, when the owning element's image fill explicitly
|
|
5010
|
+
* disables `rotWithShape` (`ShapeStyle.fillImageRotWithShape === false`).
|
|
5011
|
+
*
|
|
5012
|
+
* `undefined` (apply nothing; keep today's behaviour) when:
|
|
5013
|
+
* - the element has no shape styling, or its fill is not an image fill with
|
|
5014
|
+
* `rotWithShape` explicitly false (the default, `true`/unset, means the
|
|
5015
|
+
* fill rotates/flips WITH the shape, which is what already happens when
|
|
5016
|
+
* the content layer has no transform of its own), or
|
|
5017
|
+
* - the shape has no rotation/flip to counteract in the first place.
|
|
5018
|
+
*
|
|
5019
|
+
* Callers MUST keep the shape's own geometry mask (clip-path/border-radius)
|
|
5020
|
+
* on the STATIONARY frame container (never on this same content layer): the
|
|
5021
|
+
* container inherits the shape's rotate/flip so the visible clipped silhouette
|
|
5022
|
+
* still matches PowerPoint, while the content layer's own counter-transform
|
|
5023
|
+
* only affects its pixels, not the ancestor's clip region. A binding whose
|
|
5024
|
+
* content layer sometimes also carries its own copy of that clip-path (as a
|
|
5025
|
+
* micro-optimisation for the untransformed case) must suppress that copy
|
|
5026
|
+
* whenever this function returns a value, or the clip-path would double-flip
|
|
5027
|
+
* along with the content.
|
|
5028
|
+
*
|
|
5029
|
+
* Not applied when the content layer already carries a competing transform
|
|
5030
|
+
* (an `a:srcRect` source-crop translate/scale, see `getImageFitStyle`): the
|
|
5031
|
+
* two transforms' origins conflict (crop uses `top left`, this uses the
|
|
5032
|
+
* element's own `center`), so combining them is left as a follow-up; the far
|
|
5033
|
+
* more common crop takes priority. Pass `hasCompetingTransform: true` to skip
|
|
5034
|
+
* in that case.
|
|
5035
|
+
*
|
|
5036
|
+
* @param element - The element to inspect (reads `element.shapeStyle.fillImageRotWithShape`).
|
|
5037
|
+
* @param hasCompetingTransform - True when the content layer already has its own transform for an `a:srcRect` crop.
|
|
5038
|
+
*/
|
|
5039
|
+
function getImageFillCounterTransform(element, hasCompetingTransform = false) {
|
|
5040
|
+
if (hasCompetingTransform || !hasShapeProperties(element)) {
|
|
5041
|
+
return undefined;
|
|
5042
|
+
}
|
|
5043
|
+
if (element.shapeStyle?.fillImageRotWithShape !== false) {
|
|
5044
|
+
return undefined;
|
|
5045
|
+
}
|
|
5046
|
+
return getFrameInverseTransform(element);
|
|
5047
|
+
}
|
|
5048
|
+
|
|
4977
5049
|
/**
|
|
4978
5050
|
* OOXML drawing-colour resolution: colour-choice parsing (srgb/scrgb/sys/scheme/
|
|
4979
5051
|
* hsl/preset), the 26 colour transforms, scheme inheritance, and alpha
|
|
@@ -13432,12 +13504,79 @@ function compileEquationTemplateMathMl() {
|
|
|
13432
13504
|
const NICE_STEPS = [1, 2, 2.5, 5, 10];
|
|
13433
13505
|
/** The fraction of the data span added as headroom beyond the last data point. */
|
|
13434
13506
|
const HEADROOM = 0.05;
|
|
13507
|
+
/**
|
|
13508
|
+
* Plot-area points of vertical room PowerPoint reserves per gridline before it
|
|
13509
|
+
* asks for another one, and the ceiling/floor on how many it will ever ask
|
|
13510
|
+
* for. See {@link axisTargetIntervals}'s doc comment for the PowerPoint COM
|
|
13511
|
+
* measurements these were fit to.
|
|
13512
|
+
*/
|
|
13513
|
+
const POINTS_PER_GRIDLINE = 20;
|
|
13514
|
+
const MIN_TARGET_INTERVALS = 1;
|
|
13515
|
+
const MAX_TARGET_INTERVALS = 10;
|
|
13516
|
+
/**
|
|
13517
|
+
* Pixels per point in the "px" unit `computePlotLayout` (and every renderer in
|
|
13518
|
+
* this engine) works in: 96 px per inch, 72 points per inch, so 4/3 px per pt.
|
|
13519
|
+
* Mirrors `EMU_PER_PIXEL` (core/constants.ts): `EMU_PER_POINT / EMU_PER_PIXEL`
|
|
13520
|
+
* reduces to the same 4/3.
|
|
13521
|
+
*/
|
|
13522
|
+
const PX_PER_POINT$1 = 4 / 3;
|
|
13523
|
+
/** {@link niceValueAxisBounds}'s `targetIntervals` when no plot height is known. */
|
|
13524
|
+
const DEFAULT_TARGET_INTERVALS = 4;
|
|
13435
13525
|
/**
|
|
13436
13526
|
* How close the smaller end of the data has to be to the larger before the axis
|
|
13437
13527
|
* stops anchoring at zero. Below 5/6, zero anchoring keeps the bars readable;
|
|
13438
13528
|
* above it, the data is a narrow band and zero would squash it flat.
|
|
13439
13529
|
*/
|
|
13440
13530
|
const ZERO_ANCHOR_RATIO = 5 / 6;
|
|
13531
|
+
/**
|
|
13532
|
+
* How many gridline intervals PowerPoint's automatic value-axis scale aims
|
|
13533
|
+
* for, given the chart's plot-area height in the "px" unit `computePlotLayout`
|
|
13534
|
+
* returns (96 px/inch; see `PX_PER_POINT` above).
|
|
13535
|
+
*
|
|
13536
|
+
* This is NOT a universal constant (see this module's original doc comment,
|
|
13537
|
+
* which admitted as much: "matches the axes PowerPoint draws for a chart of
|
|
13538
|
+
* ORDINARY height"): a short chart gets fewer, coarser gridlines than a tall
|
|
13539
|
+
* one, up to a point, because PowerPoint only has room to label so many
|
|
13540
|
+
* without the axis text crowding.
|
|
13541
|
+
*
|
|
13542
|
+
* Measured via PowerPoint COM automation (`New-Object -ComObject
|
|
13543
|
+
* PowerPoint.Application`, `Shapes.AddChart2`, reading back
|
|
13544
|
+
* `chart.Axes(2).MinimumScale/.MaximumScale/.MajorUnit`): building a fresh
|
|
13545
|
+
* clustered-column chart at increasing shape heights with the SAME 8-point,
|
|
13546
|
+
* single-category data (values 1.2-3.2, the exact case reported against this
|
|
13547
|
+
* engine, which this engine rendered 0-4 while PowerPoint drew 0-3.5):
|
|
13548
|
+
*
|
|
13549
|
+
* | chart height | plot height | PowerPoint axis | gridlines |
|
|
13550
|
+
* |--------------|-------------|------------------|-----------|
|
|
13551
|
+
* | 100pt | ~33pt | 0 to 5, unit 5 | 1 |
|
|
13552
|
+
* | 150pt | ~83pt | 0 to 4, unit 1 | 4 |
|
|
13553
|
+
* | 200pt | ~133pt | 0 to 3.5, unit 0.5 | 7 |
|
|
13554
|
+
* | 300pt | ~233pt | 0 to 3.5, unit 0.5 | 7 |
|
|
13555
|
+
* | 1000pt | ~933pt | 0 to 3.5, unit 0.5 | 7 (unchanged) |
|
|
13556
|
+
*
|
|
13557
|
+
* Cross-checked against two more datasets (0-52 and 0-9) at the same set of
|
|
13558
|
+
* heights to separate "the target interval count" from "which nice-number
|
|
13559
|
+
* bucket a particular span happens to round into": PowerPoint never asked for
|
|
13560
|
+
* more than about 10 intervals even at a 1000pt-tall chart, it only asks for
|
|
13561
|
+
* FEWER on a plot area too short to fit that many labels. That saturation
|
|
13562
|
+
* (not a straight line) is why this is clamped at the top, not just scaled.
|
|
13563
|
+
*
|
|
13564
|
+
* Modelled as one gridline interval per ~20pt of plot height (roughly one
|
|
13565
|
+
* axis-label line plus padding), clamped to `[1, 10]`. This will not be exact
|
|
13566
|
+
* for every data range: the actual PowerPoint algorithm also nudges the count
|
|
13567
|
+
* by how the span happens to snap to a nice number (see the module doc
|
|
13568
|
+
* comment), which this simplified model does not attempt to reproduce. It is
|
|
13569
|
+
* a large improvement over hard-coding the short-chart answer (4) for every
|
|
13570
|
+
* chart regardless of size, which is the bug this was added to fix.
|
|
13571
|
+
*/
|
|
13572
|
+
function axisTargetIntervals(plotHeightPx) {
|
|
13573
|
+
if (!Number.isFinite(plotHeightPx) || plotHeightPx <= 0) {
|
|
13574
|
+
return DEFAULT_TARGET_INTERVALS;
|
|
13575
|
+
}
|
|
13576
|
+
const plotHeightPt = plotHeightPx / PX_PER_POINT$1;
|
|
13577
|
+
const raw = Math.round(plotHeightPt / POINTS_PER_GRIDLINE);
|
|
13578
|
+
return Math.min(MAX_TARGET_INTERVALS, Math.max(MIN_TARGET_INTERVALS, raw));
|
|
13579
|
+
}
|
|
13441
13580
|
/** The smallest nice step at or above `value`. */
|
|
13442
13581
|
function niceAxisStep(value) {
|
|
13443
13582
|
if (!Number.isFinite(value) || value <= 0) {
|
|
@@ -13462,10 +13601,13 @@ function snap(value, unit, direction) {
|
|
|
13462
13601
|
* @param dataMin Smallest plotted value.
|
|
13463
13602
|
* @param dataMax Largest plotted value.
|
|
13464
13603
|
* @param targetIntervals Roughly how many gridline steps to aim for. The snap to
|
|
13465
|
-
* a round unit means the result is usually one fewer.
|
|
13466
|
-
*
|
|
13604
|
+
* a round unit means the result is usually one fewer. Defaults to 4 only
|
|
13605
|
+
* when the caller has no idea how tall the chart's plot area is; pass
|
|
13606
|
+
* {@link axisTargetIntervals}`(plotHeightPx)` instead whenever that height is
|
|
13607
|
+
* known; see that function's doc comment for why a fixed constant undershot
|
|
13608
|
+
* real PowerPoint on any chart of ordinary-or-larger size.
|
|
13467
13609
|
*/
|
|
13468
|
-
function niceValueAxisBounds(dataMin, dataMax, targetIntervals =
|
|
13610
|
+
function niceValueAxisBounds(dataMin, dataMax, targetIntervals = DEFAULT_TARGET_INTERVALS) {
|
|
13469
13611
|
if (!Number.isFinite(dataMin) || !Number.isFinite(dataMax)) {
|
|
13470
13612
|
return { min: 0, max: 1, majorUnit: 0.5 };
|
|
13471
13613
|
}
|
|
@@ -14059,13 +14201,19 @@ function paletteColor$1(index, styleId, colorPalette) {
|
|
|
14059
14201
|
* sensibly, padded, and rounded out to whole major units. See
|
|
14060
14202
|
* `chart-axis-nice.ts` for the rules. Running the axis to the raw data maximum
|
|
14061
14203
|
* instead put the top gridline on whatever the tallest bar happened to be.
|
|
14204
|
+
*
|
|
14205
|
+
* @param plotHeightPx The chart's plot-area height (same "px" unit
|
|
14206
|
+
* `computePlotLayout` returns), when the caller has it. PowerPoint's real
|
|
14207
|
+
* gridline count depends on how much vertical room the axis has to label
|
|
14208
|
+
* them in; omitting this falls back to a constant tuned for a short chart
|
|
14209
|
+
* (see `axisTargetIntervals`'s doc comment).
|
|
14062
14210
|
*/
|
|
14063
|
-
function computeValueRange$1(series) {
|
|
14211
|
+
function computeValueRange$1(series, plotHeightPx) {
|
|
14064
14212
|
const allValues = series.flatMap((s) => s.values);
|
|
14065
14213
|
if (allValues.length === 0) {
|
|
14066
14214
|
return { min: 0, max: 1, span: 1 };
|
|
14067
14215
|
}
|
|
14068
|
-
const { min, max, majorUnit } = niceValueAxisBounds(Math.min(...allValues), Math.max(...allValues));
|
|
14216
|
+
const { min, max, majorUnit } = niceValueAxisBounds(Math.min(...allValues), Math.max(...allValues), plotHeightPx !== undefined ? axisTargetIntervals(plotHeightPx) : undefined);
|
|
14069
14217
|
return { min, max, span: Math.max(max - min, Number.EPSILON), majorUnit };
|
|
14070
14218
|
}
|
|
14071
14219
|
/**
|
|
@@ -14195,13 +14343,23 @@ function resolveCategoryLabels(chartData) {
|
|
|
14195
14343
|
* SVG. PowerPoint decks routinely write `<c:spPr><a:noFill/></c:spPr>` on
|
|
14196
14344
|
* `c:chartSpace` and `c:plotArea` precisely so the chart floats on the slide;
|
|
14197
14345
|
* the wash boxed every such chart into a visible grey panel that is not in the
|
|
14198
|
-
* source. Honouring the authored fill
|
|
14199
|
-
*
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
*
|
|
14203
|
-
*
|
|
14204
|
-
*
|
|
14346
|
+
* source. Honouring the authored fill (an explicit `<a:noFill/>`) was the
|
|
14347
|
+
* first fix, but a chart with NO `c:spPr` at all (the common case: only a
|
|
14348
|
+
* deliberately-styled chart writes one) still fell back to the same synthetic
|
|
14349
|
+
* wash, which is just as wrong: real PowerPoint never paints a highlight box
|
|
14350
|
+
* behind an unstyled chart, it is fully transparent like the `noFill` case.
|
|
14351
|
+
* Measured against a real-world deck (a plain single-series bar chart with no
|
|
14352
|
+
* `c:chartSpace/c:spPr` at all) PowerPoint's own export shows no grey
|
|
14353
|
+
* anywhere; this module now treats "no fill recorded" the same whether the
|
|
14354
|
+
* source said `noFill` explicitly or said nothing, matching `plotAreaFill`
|
|
14355
|
+
* below, which never had a synthetic default in the first place.
|
|
14356
|
+
*/
|
|
14357
|
+
/**
|
|
14358
|
+
* The wash the bindings used to paint whenever a chart declared no fill of
|
|
14359
|
+
* its own. No longer used as an automatic default for real chart data (see
|
|
14360
|
+
* the module doc comment); kept only for `buildFallbackViewModel`'s
|
|
14361
|
+
* unsupported/empty-chart placeholder, which has no chart data to read a fill
|
|
14362
|
+
* from and still wants a visible box to represent "a chart goes here".
|
|
14205
14363
|
*/
|
|
14206
14364
|
const DEFAULT_CHART_AREA_FILL = '#0f172a11';
|
|
14207
14365
|
/** Resolve one recorded fill value to an SVG `fill`, or `undefined` to skip. */
|
|
@@ -14213,10 +14371,11 @@ function resolve(fill, fallback) {
|
|
|
14213
14371
|
}
|
|
14214
14372
|
/**
|
|
14215
14373
|
* SVG `fill` for the chart-area rect, or `undefined` when nothing should be
|
|
14216
|
-
* painted
|
|
14374
|
+
* painted: the source declared `a:noFill`, or (matching real PowerPoint)
|
|
14375
|
+
* simply never wrote a `c:chartSpace/c:spPr` at all.
|
|
14217
14376
|
*/
|
|
14218
14377
|
function chartAreaFill(chartData) {
|
|
14219
|
-
return resolve(chartData?.style?.chartAreaFill,
|
|
14378
|
+
return resolve(chartData?.style?.chartAreaFill, undefined);
|
|
14220
14379
|
}
|
|
14221
14380
|
/**
|
|
14222
14381
|
* SVG `fill` for the plot-area rect, or `undefined` when nothing should be
|
|
@@ -14621,15 +14780,24 @@ function computeLogValueRange(series, logBase) {
|
|
|
14621
14780
|
logBase,
|
|
14622
14781
|
};
|
|
14623
14782
|
}
|
|
14624
|
-
/**
|
|
14625
|
-
|
|
14783
|
+
/**
|
|
14784
|
+
* Compute a value range using one parsed value axis's scale and bounds.
|
|
14785
|
+
*
|
|
14786
|
+
* @param plotHeightPx The chart's plot-area height, when known; forwarded to
|
|
14787
|
+
* `computeValueRange`'s automatic-scale gridline count (see
|
|
14788
|
+
* `axisTargetIntervals`'s doc comment). Not used on the log-scale branch,
|
|
14789
|
+
* which snaps to powers of the log base regardless of chart size.
|
|
14790
|
+
*/
|
|
14791
|
+
function computeValueRangeForAxis(series, axis, plotHeightPx) {
|
|
14626
14792
|
const requestedLogBase = axis?.logBase ?? 10;
|
|
14627
14793
|
const logBase = axis?.logScale && Number.isFinite(requestedLogBase) && requestedLogBase > 1
|
|
14628
14794
|
? requestedLogBase
|
|
14629
14795
|
: axis?.logScale
|
|
14630
14796
|
? 10
|
|
14631
14797
|
: undefined;
|
|
14632
|
-
const automatic = logBase
|
|
14798
|
+
const automatic = logBase
|
|
14799
|
+
? computeLogValueRange(series, logBase)
|
|
14800
|
+
: computeValueRange$1(series, plotHeightPx);
|
|
14633
14801
|
const validMin = typeof axis?.min === 'number' && Number.isFinite(axis.min) && (!logBase || axis.min > 0)
|
|
14634
14802
|
? axis.min
|
|
14635
14803
|
: undefined;
|
|
@@ -14725,11 +14893,15 @@ function generateMinorAxisTicks(range, axis) {
|
|
|
14725
14893
|
function findLogAxis(axes) {
|
|
14726
14894
|
return axes?.find((axis) => axis.axisType === 'valAx' && axis.logScale);
|
|
14727
14895
|
}
|
|
14728
|
-
/**
|
|
14729
|
-
|
|
14896
|
+
/**
|
|
14897
|
+
* Compute the primary value range for a chart.
|
|
14898
|
+
*
|
|
14899
|
+
* @param plotHeightPx See `computeValueRangeForAxis`'s parameter of the same name.
|
|
14900
|
+
*/
|
|
14901
|
+
function computeValueRangeForChart(series, axes, plotHeightPx) {
|
|
14730
14902
|
const primaryAxis = axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos === 'l') ??
|
|
14731
14903
|
axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos !== 'r');
|
|
14732
|
-
return computeValueRangeForAxis(series, primaryAxis);
|
|
14904
|
+
return computeValueRangeForAxis(series, primaryAxis, plotHeightPx);
|
|
14733
14905
|
}
|
|
14734
14906
|
|
|
14735
14907
|
// ── Display units ────────────────────────────────────────────────
|
|
@@ -14797,12 +14969,34 @@ function hasSecondaryValueAxis(axes) {
|
|
|
14797
14969
|
}
|
|
14798
14970
|
return axes.some((a) => a.axisType === 'valAx' && a.axPos === 'r');
|
|
14799
14971
|
}
|
|
14800
|
-
|
|
14972
|
+
function categoryAxes(axes) {
|
|
14973
|
+
return axes?.filter((a) => a.axisType === 'catAx' || a.axisType === 'dateAx') ?? [];
|
|
14974
|
+
}
|
|
14975
|
+
/**
|
|
14976
|
+
* Check whether the chart has a genuine SECOND category axis positioned at the
|
|
14977
|
+
* top (a combo chart with one bottom + one top category axis). A single
|
|
14978
|
+
* category axis that itself carries `axPos="t"` (the only axis, just
|
|
14979
|
+
* relocated) is NOT a secondary axis: see `isPrimaryCategoryAxisAtTop`.
|
|
14980
|
+
*/
|
|
14801
14981
|
function hasSecondaryCategoryAxis(axes) {
|
|
14802
|
-
|
|
14982
|
+
const cats = categoryAxes(axes);
|
|
14983
|
+
return cats.length > 1 && cats.some((a) => a.axPos === 't');
|
|
14984
|
+
}
|
|
14985
|
+
/**
|
|
14986
|
+
* Check whether the chart's PRIMARY (and, in the common case, only) category
|
|
14987
|
+
* axis is positioned at the top of the plot area (`c:catAx/c:axPos val="t"`).
|
|
14988
|
+
* Mirrors the axis selection in `chart-category-axis.ts`'s
|
|
14989
|
+
* `primaryCategoryAxis`: prefer a non-top axis when one exists (the top one is
|
|
14990
|
+
* then the genuine secondary axis, handled separately), otherwise fall back to
|
|
14991
|
+
* the first category axis, which is the only one there is.
|
|
14992
|
+
*/
|
|
14993
|
+
function isPrimaryCategoryAxisAtTop(axes) {
|
|
14994
|
+
const cats = categoryAxes(axes);
|
|
14995
|
+
if (cats.length === 0) {
|
|
14803
14996
|
return false;
|
|
14804
14997
|
}
|
|
14805
|
-
|
|
14998
|
+
const primary = cats.find((a) => a.axPos !== 't') ?? cats[0];
|
|
14999
|
+
return primary.axPos === 't';
|
|
14806
15000
|
}
|
|
14807
15001
|
/** Get the secondary value axis formatting, if present. */
|
|
14808
15002
|
function getSecondaryValueAxis(axes) {
|
|
@@ -14823,6 +15017,7 @@ function computeLayoutOptions(axes, dataTable, seriesCount) {
|
|
|
14823
15017
|
return {
|
|
14824
15018
|
hasSecondaryValueAxis: hasSecondaryValueAxis(axes),
|
|
14825
15019
|
hasSecondaryCategoryAxis: hasSecondaryCategoryAxis(axes),
|
|
15020
|
+
categoryAxisAtTop: isPrimaryCategoryAxisAtTop(axes),
|
|
14826
15021
|
hasDataTable: Boolean(dataTable),
|
|
14827
15022
|
dataTableRowCount: dataTable ? seriesCount : undefined,
|
|
14828
15023
|
};
|
|
@@ -14935,6 +15130,160 @@ function shade(hex, amount) {
|
|
|
14935
15130
|
return rgbToHex$3(r * (1 - amount), g * (1 - amount), b * (1 - amount));
|
|
14936
15131
|
}
|
|
14937
15132
|
|
|
15133
|
+
/**
|
|
15134
|
+
* chart-marker-shape.ts: framework-agnostic marker-symbol primitive builder for
|
|
15135
|
+
* line / scatter / area chart data points.
|
|
15136
|
+
*
|
|
15137
|
+
* OOXML line/scatter series carry a `c:marker` with a `c:symbol` (circle,
|
|
15138
|
+
* diamond, square, triangle, star, x, plus, dot, dash, none, ...) and an
|
|
15139
|
+
* optional `c:size` (2-72 points). The base view-model historically drew every
|
|
15140
|
+
* data point as a fixed-radius circle, ignoring both. This helper resolves the
|
|
15141
|
+
* parsed marker into the correct `SvgPrimitive` at the requested size, and
|
|
15142
|
+
* returns `null` for `symbol === 'none'` so no dot is drawn.
|
|
15143
|
+
*
|
|
15144
|
+
* @module chart-marker-shape
|
|
15145
|
+
*/
|
|
15146
|
+
/**
|
|
15147
|
+
* PowerPoint's automatic marker-symbol cycle: the fixed shape sequence a
|
|
15148
|
+
* "Line/Scatter with Markers" chart cycles through when a series' `c:marker`
|
|
15149
|
+
* carries no `c:symbol` (only its fill/line colour is "automatic"; the shape
|
|
15150
|
+
* itself was assumed to always be a circle before this was measured).
|
|
15151
|
+
*
|
|
15152
|
+
* COM-measured ground truth (`PowerPoint.Application`, a stacked
|
|
15153
|
+
* Line-with-Markers chart, N series each with `<c:marker><c:spPr>.../></c:marker>`
|
|
15154
|
+
* and no `c:symbol`, `SeriesCollection(i).MarkerStyle` read back after a real
|
|
15155
|
+
* save/reopen round trip): idx 0..8 resolved to
|
|
15156
|
+
* Diamond, Square, Triangle, X, Star, Circle, Plus, Dot, Dash - a clean
|
|
15157
|
+
* 9-element cycle with no partial/ambiguous entries across 9 independent
|
|
15158
|
+
* series (this is ALSO what the chart-style-driven, explicit-symbol path a
|
|
15159
|
+
* modern "Line with Markers" preset writes on save resolves every series to a
|
|
15160
|
+
* uniform Circle, so that path is unrelated: this cycle is specifically
|
|
15161
|
+
* what PowerPoint's renderer falls back to for a chart with no `c:symbol` at
|
|
15162
|
+
* all). Confirmed against a real-world deck: series `c:idx="1"`/`"2"` (a
|
|
15163
|
+
* gapped, 1-based pair, not `0`/`1`) resolved to Square/Triangle exactly as
|
|
15164
|
+
* this cycle predicts (`AUTOMATIC_MARKER_CYCLE[1]` / `[2]`).
|
|
15165
|
+
*/
|
|
15166
|
+
const AUTOMATIC_MARKER_CYCLE = [
|
|
15167
|
+
'diamond',
|
|
15168
|
+
'square',
|
|
15169
|
+
'triangle',
|
|
15170
|
+
'x',
|
|
15171
|
+
'star',
|
|
15172
|
+
'circle',
|
|
15173
|
+
'plus',
|
|
15174
|
+
'dot',
|
|
15175
|
+
'dash',
|
|
15176
|
+
];
|
|
15177
|
+
/**
|
|
15178
|
+
* Resolve the automatic marker shape for a series with no authored
|
|
15179
|
+
* `c:symbol`, keyed by the series' `c:idx` (or its array position when the
|
|
15180
|
+
* chart has none): {@link AUTOMATIC_MARKER_CYCLE} repeats every 9 series.
|
|
15181
|
+
*/
|
|
15182
|
+
function automaticMarkerSymbol(seriesIdx) {
|
|
15183
|
+
const length = AUTOMATIC_MARKER_CYCLE.length;
|
|
15184
|
+
const normalized = ((seriesIdx % length) + length) % length;
|
|
15185
|
+
return AUTOMATIC_MARKER_CYCLE[normalized];
|
|
15186
|
+
}
|
|
15187
|
+
/** Resolve the drawn radius (px) from the parsed point size or the default. */
|
|
15188
|
+
function markerRadius(size, defaultRadius) {
|
|
15189
|
+
if (size === undefined || !Number.isFinite(size) || size <= 0) {
|
|
15190
|
+
return defaultRadius;
|
|
15191
|
+
}
|
|
15192
|
+
// OOXML marker size is a point diameter; treat 1pt ~ 1px at chart scale.
|
|
15193
|
+
return size / 2;
|
|
15194
|
+
}
|
|
15195
|
+
/** Build the SVG points string for a regular/irregular polygon vertex list. */
|
|
15196
|
+
function polygonPoints(vertices) {
|
|
15197
|
+
return vertices.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(' ');
|
|
15198
|
+
}
|
|
15199
|
+
function starVertices(cx, cy, r) {
|
|
15200
|
+
const inner = r * 0.5;
|
|
15201
|
+
const out = [];
|
|
15202
|
+
for (let i = 0; i < 10; i++) {
|
|
15203
|
+
const radius = i % 2 === 0 ? r : inner;
|
|
15204
|
+
const angle = -Math.PI / 2 + (Math.PI * i) / 5;
|
|
15205
|
+
out.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)]);
|
|
15206
|
+
}
|
|
15207
|
+
return out;
|
|
15208
|
+
}
|
|
15209
|
+
/**
|
|
15210
|
+
* Build the marker primitive for one data point, honouring `symbol` and `size`.
|
|
15211
|
+
* Returns `null` when `symbol === 'none'` (draw nothing).
|
|
15212
|
+
*/
|
|
15213
|
+
function buildMarkerPrimitive(input) {
|
|
15214
|
+
const { symbol, cx, cy, fill, part } = input;
|
|
15215
|
+
if (symbol === 'none') {
|
|
15216
|
+
return null;
|
|
15217
|
+
}
|
|
15218
|
+
const r = markerRadius(input.size, input.defaultRadius);
|
|
15219
|
+
const stroke = fill;
|
|
15220
|
+
switch (symbol) {
|
|
15221
|
+
case 'square':
|
|
15222
|
+
return { kind: 'rect', x: cx - r, y: cy - r, w: r * 2, h: r * 2, fill, part };
|
|
15223
|
+
case 'dash':
|
|
15224
|
+
return { kind: 'rect', x: cx - r, y: cy - r * 0.32, w: r * 2, h: r * 0.64, fill, part };
|
|
15225
|
+
case 'diamond':
|
|
15226
|
+
return {
|
|
15227
|
+
kind: 'polygon',
|
|
15228
|
+
points: polygonPoints([
|
|
15229
|
+
[cx, cy - r],
|
|
15230
|
+
[cx + r, cy],
|
|
15231
|
+
[cx, cy + r],
|
|
15232
|
+
[cx - r, cy],
|
|
15233
|
+
]),
|
|
15234
|
+
fill,
|
|
15235
|
+
stroke,
|
|
15236
|
+
strokeWidth: 0,
|
|
15237
|
+
part,
|
|
15238
|
+
};
|
|
15239
|
+
case 'triangle':
|
|
15240
|
+
return {
|
|
15241
|
+
kind: 'polygon',
|
|
15242
|
+
points: polygonPoints([
|
|
15243
|
+
[cx, cy - r],
|
|
15244
|
+
[cx + r * 0.9, cy + r * 0.75],
|
|
15245
|
+
[cx - r * 0.9, cy + r * 0.75],
|
|
15246
|
+
]),
|
|
15247
|
+
fill,
|
|
15248
|
+
stroke,
|
|
15249
|
+
strokeWidth: 0,
|
|
15250
|
+
part,
|
|
15251
|
+
};
|
|
15252
|
+
case 'star':
|
|
15253
|
+
return {
|
|
15254
|
+
kind: 'polygon',
|
|
15255
|
+
points: polygonPoints(starVertices(cx, cy, r)),
|
|
15256
|
+
fill,
|
|
15257
|
+
stroke,
|
|
15258
|
+
strokeWidth: 0,
|
|
15259
|
+
part,
|
|
15260
|
+
};
|
|
15261
|
+
case 'plus':
|
|
15262
|
+
return {
|
|
15263
|
+
kind: 'path',
|
|
15264
|
+
d: `M${(cx - r).toFixed(2)},${cy.toFixed(2)} L${(cx + r).toFixed(2)},${cy.toFixed(2)} M${cx.toFixed(2)},${(cy - r).toFixed(2)} L${cx.toFixed(2)},${(cy + r).toFixed(2)}`,
|
|
15265
|
+
fill: 'none',
|
|
15266
|
+
stroke,
|
|
15267
|
+
strokeWidth: Math.max(1, r * 0.4),
|
|
15268
|
+
part,
|
|
15269
|
+
};
|
|
15270
|
+
case 'x':
|
|
15271
|
+
return {
|
|
15272
|
+
kind: 'path',
|
|
15273
|
+
d: `M${(cx - r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx + r).toFixed(2)},${(cy + r).toFixed(2)} M${(cx + r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx - r).toFixed(2)},${(cy + r).toFixed(2)}`,
|
|
15274
|
+
fill: 'none',
|
|
15275
|
+
stroke,
|
|
15276
|
+
strokeWidth: Math.max(1, r * 0.4),
|
|
15277
|
+
part,
|
|
15278
|
+
};
|
|
15279
|
+
case 'dot':
|
|
15280
|
+
return { kind: 'circle', cx, cy, r: Math.max(r * 0.6, 1), fill, part };
|
|
15281
|
+
default:
|
|
15282
|
+
// circle / auto / picture / undefined -> filled circle.
|
|
15283
|
+
return { kind: 'circle', cx, cy, r, fill, part };
|
|
15284
|
+
}
|
|
15285
|
+
}
|
|
15286
|
+
|
|
14938
15287
|
/**
|
|
14939
15288
|
* chart-datapoint-style.ts: framework-agnostic resolution of per-data-point
|
|
14940
15289
|
* formatting overrides (`c:dPt`).
|
|
@@ -14995,11 +15344,25 @@ function resolveDataPointExplosion(series, pointIndex) {
|
|
|
14995
15344
|
* looked at `series.marker`, so editing a point marker changed the file and
|
|
14996
15345
|
* left the canvas untouched. Resolving it here fixes every binding at once,
|
|
14997
15346
|
* since they all paint the same shared view model.
|
|
15347
|
+
*
|
|
15348
|
+
* A resolved symbol of `'auto'` (or a marker present with no `c:symbol` at
|
|
15349
|
+
* all, which the core parser also normalises to `'auto'`) is resolved to a
|
|
15350
|
+
* concrete shape via {@link automaticMarkerSymbol}, keyed by `series.idx`
|
|
15351
|
+
* (falling back to `seriesPosition`, the series' array index, when the chart
|
|
15352
|
+
* carries no `c:idx`). A series/point with NO marker element at all
|
|
15353
|
+
* (`marker` and `point` both `undefined`) keeps `symbol: undefined`
|
|
15354
|
+
* unchanged: that is a chart type that draws no marker at all, not an
|
|
15355
|
+
* automatic one, and must not start cycling shapes.
|
|
14998
15356
|
*/
|
|
14999
|
-
function resolveDataPointMarker(series, pointIndex) {
|
|
15357
|
+
function resolveDataPointMarker(series, pointIndex, seriesPosition = 0) {
|
|
15000
15358
|
const point = findDataPoint(series, pointIndex)?.marker;
|
|
15359
|
+
const hasMarker = point !== undefined || series.marker !== undefined;
|
|
15360
|
+
const rawSymbol = point?.symbol ?? series.marker?.symbol;
|
|
15361
|
+
const symbol = hasMarker && (rawSymbol === undefined || rawSymbol === 'auto')
|
|
15362
|
+
? automaticMarkerSymbol(series.idx ?? seriesPosition)
|
|
15363
|
+
: rawSymbol;
|
|
15001
15364
|
return {
|
|
15002
|
-
symbol
|
|
15365
|
+
symbol,
|
|
15003
15366
|
size: point?.size ?? series.marker?.size,
|
|
15004
15367
|
fill: point?.spPr?.fillColor ?? series.marker?.spPr?.fillColor,
|
|
15005
15368
|
};
|
|
@@ -15028,6 +15391,26 @@ function upsertDataPoint(dataPoints, point) {
|
|
|
15028
15391
|
}
|
|
15029
15392
|
return next;
|
|
15030
15393
|
}
|
|
15394
|
+
// Picture-fill resolution (c:pictureOptions, the bare-a:blipFill
|
|
15395
|
+
// impliedPicture case, and bar3D face targeting) lives in
|
|
15396
|
+
// chart-datapoint-picture-resolve.ts, split out to keep this file within the
|
|
15397
|
+
// repo's ~300-LOC limit: `resolveActiveDataPointPicture`,
|
|
15398
|
+
// `resolveDataPointPictureFill`, `resolveBarFaceTargets`, `DataPointPictureFill`,
|
|
15399
|
+
// `BarPictureFace`, `BarFaceTargets`.
|
|
15400
|
+
|
|
15401
|
+
/**
|
|
15402
|
+
* chart-datapoint-picture-resolve.ts: framework-agnostic resolution of a
|
|
15403
|
+
* data point's (or its series') picture fill into a paintable descriptor.
|
|
15404
|
+
*
|
|
15405
|
+
* Split out of `chart-datapoint-style.ts` to keep that file within the
|
|
15406
|
+
* repo's ~300-LOC limit: that file resolves per-point formatting in
|
|
15407
|
+
* general (fill colour, marker, explosion); this one owns specifically the
|
|
15408
|
+
* picture-fill half (`c:pictureOptions` AND the bare-`a:blipFill`
|
|
15409
|
+
* `impliedPicture` case, plus `bar3D` face targeting), which had grown into
|
|
15410
|
+
* its own coherent concern.
|
|
15411
|
+
*
|
|
15412
|
+
* @module chart-datapoint-picture-resolve
|
|
15413
|
+
*/
|
|
15031
15414
|
/** PowerPoint's own default picture-fill format when `c:pictureFormat` is absent. */
|
|
15032
15415
|
const DEFAULT_PICTURE_FORMAT = 'stretch';
|
|
15033
15416
|
/**
|
|
@@ -15036,14 +15419,28 @@ const DEFAULT_PICTURE_FORMAT = 'stretch';
|
|
|
15036
15419
|
* `c:ser/c:pictureOptions` (C2-G9 series-level half). A point with its own
|
|
15037
15420
|
* picture wins OUTRIGHT (not merged field-by-field with the series' flags),
|
|
15038
15421
|
* matching how a `c:dPt/c:spPr` fully replaces the series' formatting
|
|
15039
|
-
* elsewhere in this resolver (
|
|
15422
|
+
* elsewhere in this resolver (`resolveDataPointFill`, `chart-datapoint-style.ts`).
|
|
15423
|
+
*
|
|
15424
|
+
* `impliedPicture` (a bare `a:blipFill` with no `c:pictureOptions` sibling,
|
|
15425
|
+
* see `PptxChartDataPoint.impliedPicture`'s doc comment) is checked at the
|
|
15426
|
+
* SAME tier as `picture`, once `picture` itself did not resolve an image: a
|
|
15427
|
+
* point's own fill (explicit flags or implied) still wins outright over the
|
|
15428
|
+
* series', and vice versa.
|
|
15040
15429
|
*/
|
|
15041
15430
|
function resolveActiveDataPointPicture(series, pointIndex) {
|
|
15042
|
-
const
|
|
15043
|
-
|
|
15431
|
+
const point = findDataPoint(series, pointIndex);
|
|
15432
|
+
const pointPicture = point?.picture?.imageUrl
|
|
15433
|
+
? point.picture
|
|
15434
|
+
: point?.impliedPicture?.imageUrl
|
|
15435
|
+
? point.impliedPicture
|
|
15436
|
+
: undefined;
|
|
15437
|
+
if (pointPicture) {
|
|
15044
15438
|
return pointPicture;
|
|
15045
15439
|
}
|
|
15046
|
-
|
|
15440
|
+
if (series.picture?.imageUrl) {
|
|
15441
|
+
return series.picture;
|
|
15442
|
+
}
|
|
15443
|
+
return series.impliedPicture?.imageUrl ? series.impliedPicture : undefined;
|
|
15047
15444
|
}
|
|
15048
15445
|
/**
|
|
15049
15446
|
* Resolve which 3-D bar/column faces a picture fill paints
|
|
@@ -15101,6 +15498,7 @@ function resolveDataPointPictureFill(series, pointIndex, seriesIndex, face) {
|
|
|
15101
15498
|
...(format !== 'stretch' && picture.pictureStackUnit !== undefined
|
|
15102
15499
|
? { tileHeightPx: chartFontPx(picture.pictureStackUnit) }
|
|
15103
15500
|
: {}),
|
|
15501
|
+
...(picture.opacity !== undefined ? { opacity: picture.opacity } : {}),
|
|
15104
15502
|
};
|
|
15105
15503
|
}
|
|
15106
15504
|
|
|
@@ -15187,7 +15585,11 @@ function applyDataPointPictureFills(chartData, elementId, primitives) {
|
|
|
15187
15585
|
}
|
|
15188
15586
|
const patternId = `${elementId}-${resolved.patternId}`;
|
|
15189
15587
|
defs.push(buildPictureFillPatternDef(patternId, resolved.imageUrl, resolved.format, primitive, resolved.tileHeightPx));
|
|
15190
|
-
return {
|
|
15588
|
+
return {
|
|
15589
|
+
...primitive,
|
|
15590
|
+
fill: `url(#${patternId})`,
|
|
15591
|
+
...(resolved.opacity !== undefined ? { opacity: resolved.opacity } : {}),
|
|
15592
|
+
};
|
|
15191
15593
|
});
|
|
15192
15594
|
return { primitives: next, defs };
|
|
15193
15595
|
}
|
|
@@ -15687,10 +16089,70 @@ function computeChartLegendLayout(vm) {
|
|
|
15687
16089
|
fontWeight: textStyle?.bold ? 'bold' : 'normal',
|
|
15688
16090
|
fontStyle: textStyle?.italic ? 'italic' : 'normal',
|
|
15689
16091
|
fontFamily: textStyle?.fontFamily,
|
|
16092
|
+
...(entry.lineSwatch ? { lineSwatch: entry.lineSwatch } : {}),
|
|
15690
16093
|
};
|
|
15691
16094
|
});
|
|
15692
16095
|
}
|
|
15693
16096
|
|
|
16097
|
+
/** Legend swatch line width in px: a fixed icon size, independent of the plotted series' own stroke width. */
|
|
16098
|
+
const LEGEND_LINE_SWATCH_WIDTH = 2;
|
|
16099
|
+
/** Legend swatch marker radius in px: a fixed icon size, independent of the series' own `c:marker/c:size`. */
|
|
16100
|
+
const LEGEND_MARKER_RADIUS = 3;
|
|
16101
|
+
/** Vertical centre of the swatch box (matches the rect swatch's `y: -7, height: 10`). */
|
|
16102
|
+
const SWATCH_CENTER_Y = -2;
|
|
16103
|
+
/** Swatch box width in px (matches the rect swatch's `width: 10`). */
|
|
16104
|
+
const SWATCH_WIDTH = 10;
|
|
16105
|
+
/**
|
|
16106
|
+
* Which chart kinds get a line-style legend swatch instead of a filled rect.
|
|
16107
|
+
*
|
|
16108
|
+
* Deliberately narrow: only kinds whose whole plot is a drawn line/marker
|
|
16109
|
+
* (line, scatter). A filled radar or an area chart still reads as a filled
|
|
16110
|
+
* region in PowerPoint's own legend, so they keep the rect swatch; a
|
|
16111
|
+
* bar+line combo resolves its line series individually (see
|
|
16112
|
+
* `chart-combo.ts`), not through this whole-chart check.
|
|
16113
|
+
*/
|
|
16114
|
+
function resolveLegendSwatchKind(kind) {
|
|
16115
|
+
return kind === 'line' || kind === 'scatter' ? 'line' : 'rect';
|
|
16116
|
+
}
|
|
16117
|
+
/**
|
|
16118
|
+
* Build the line-style legend swatch primitives for one series.
|
|
16119
|
+
*
|
|
16120
|
+
* The marker uses the series' OWN marker (`c:ser/c:marker`), never a `c:dPt`
|
|
16121
|
+
* per-point override: a legend sample represents the whole series, matching
|
|
16122
|
+
* PowerPoint. `pointIndex: -1` is not a real data point, so
|
|
16123
|
+
* {@link resolveDataPointMarker}'s `c:dPt` lookup never matches it. The marker
|
|
16124
|
+
* is always drawn at {@link LEGEND_MARKER_RADIUS}, ignoring the series' own
|
|
16125
|
+
* `c:marker/c:size`, so the legend icon stays a consistent small size
|
|
16126
|
+
* regardless of how large the plotted markers are.
|
|
16127
|
+
*/
|
|
16128
|
+
function buildLineLegendSwatch(series, color, seriesPosition = 0) {
|
|
16129
|
+
const resolved = resolveDataPointMarker(series, -1, seriesPosition), primitives = [];
|
|
16130
|
+
if (!series.lineNoFill) {
|
|
16131
|
+
primitives.push({
|
|
16132
|
+
kind: 'line',
|
|
16133
|
+
x1: 0,
|
|
16134
|
+
y1: SWATCH_CENTER_Y,
|
|
16135
|
+
x2: SWATCH_WIDTH,
|
|
16136
|
+
y2: SWATCH_CENTER_Y,
|
|
16137
|
+
stroke: color,
|
|
16138
|
+
strokeWidth: LEGEND_LINE_SWATCH_WIDTH,
|
|
16139
|
+
});
|
|
16140
|
+
}
|
|
16141
|
+
const marker = buildMarkerPrimitive({
|
|
16142
|
+
symbol: resolved.symbol,
|
|
16143
|
+
// Deliberately ignore the series' authored size (see doc comment above).
|
|
16144
|
+
size: undefined,
|
|
16145
|
+
cx: SWATCH_WIDTH / 2,
|
|
16146
|
+
cy: SWATCH_CENTER_Y,
|
|
16147
|
+
fill: resolved.fill ?? color,
|
|
16148
|
+
defaultRadius: LEGEND_MARKER_RADIUS,
|
|
16149
|
+
});
|
|
16150
|
+
if (marker) {
|
|
16151
|
+
primitives.push(marker);
|
|
16152
|
+
}
|
|
16153
|
+
return { primitives };
|
|
16154
|
+
}
|
|
16155
|
+
|
|
15694
16156
|
const DEFAULT_COLOR$3 = '#64748b';
|
|
15695
16157
|
function dashArray(style, width) {
|
|
15696
16158
|
if (!style || style === 'solid') {
|
|
@@ -15835,8 +16297,12 @@ function seriesColor(series, index, colorPalette) {
|
|
|
15835
16297
|
/**
|
|
15836
16298
|
* Automatic Y-axis range, on PowerPoint's terms. See `chart-axis-nice.ts`; this
|
|
15837
16299
|
* mirrors `computeValueRange` in `chart-helpers.ts`.
|
|
16300
|
+
*
|
|
16301
|
+
* @param plotHeightPx The chart's plot-area height (`PlotLayout.plotHeight`),
|
|
16302
|
+
* when the caller has it; see `axisTargetIntervals`'s doc comment for why
|
|
16303
|
+
* this changes the gridline count PowerPoint's automatic scale picks.
|
|
15838
16304
|
*/
|
|
15839
|
-
function computeValueRange(series) {
|
|
16305
|
+
function computeValueRange(series, plotHeightPx) {
|
|
15840
16306
|
let dataMin = Number.POSITIVE_INFINITY, dataMax = Number.NEGATIVE_INFINITY;
|
|
15841
16307
|
for (const item of series) {
|
|
15842
16308
|
for (const value of item.values) {
|
|
@@ -15851,14 +16317,16 @@ function computeValueRange(series) {
|
|
|
15851
16317
|
if (dataMin === Number.POSITIVE_INFINITY) {
|
|
15852
16318
|
return { min: 0, max: 1, span: 1 };
|
|
15853
16319
|
}
|
|
15854
|
-
const { min, max, majorUnit } = niceValueAxisBounds(dataMin, dataMax);
|
|
16320
|
+
const { min, max, majorUnit } = niceValueAxisBounds(dataMin, dataMax, plotHeightPx !== undefined ? axisTargetIntervals(plotHeightPx) : undefined);
|
|
15855
16321
|
return { min, max, span: Math.max(max - min, Number.EPSILON), majorUnit };
|
|
15856
16322
|
}
|
|
15857
16323
|
/**
|
|
15858
16324
|
* Value range for a stacked bar: the per-category sums, then the same automatic
|
|
15859
16325
|
* scale as any other value axis.
|
|
16326
|
+
*
|
|
16327
|
+
* @param plotHeightPx See `computeValueRange`'s parameter of the same name.
|
|
15860
16328
|
*/
|
|
15861
|
-
function computeStackedValueRange(series, catCount) {
|
|
16329
|
+
function computeStackedValueRange(series, catCount, plotHeightPx) {
|
|
15862
16330
|
let maxSum = 0, minSum = 0;
|
|
15863
16331
|
for (let ci = 0; ci < catCount; ci++) {
|
|
15864
16332
|
let pos = 0, neg = 0;
|
|
@@ -15874,7 +16342,7 @@ function computeStackedValueRange(series, catCount) {
|
|
|
15874
16342
|
maxSum = Math.max(maxSum, pos);
|
|
15875
16343
|
minSum = Math.min(minSum, neg);
|
|
15876
16344
|
}
|
|
15877
|
-
const { min, max, majorUnit } = niceValueAxisBounds(Math.min(minSum, 0), Math.max(maxSum, 0));
|
|
16345
|
+
const { min, max, majorUnit } = niceValueAxisBounds(Math.min(minSum, 0), Math.max(maxSum, 0), plotHeightPx !== undefined ? axisTargetIntervals(plotHeightPx) : undefined);
|
|
15878
16346
|
return { min, max, span: Math.max(max - min, Number.EPSILON), majorUnit };
|
|
15879
16347
|
}
|
|
15880
16348
|
/**
|
|
@@ -15937,6 +16405,46 @@ function buildMarkTooltip(seriesName, categoryLabel, value, numberFormat) {
|
|
|
15937
16405
|
return label.length > 0 ? `${label}: ${formatted}` : formatted;
|
|
15938
16406
|
}
|
|
15939
16407
|
|
|
16408
|
+
/**
|
|
16409
|
+
* Build the base legend entries + anchor position for a chart view model.
|
|
16410
|
+
*
|
|
16411
|
+
* @param swatchKind Which legend entries draw a line + marker sample instead
|
|
16412
|
+
* of the default filled rect (see `chart-legend-swatch.ts`). A single value
|
|
16413
|
+
* applies to every entry (the whole-chart line/scatter case); an array
|
|
16414
|
+
* resolves per series index (a bar+line combo, where only the line series
|
|
16415
|
+
* get the line sample). Defaults to `'rect'` for every existing caller that
|
|
16416
|
+
* predates line-aware legends.
|
|
16417
|
+
*/
|
|
16418
|
+
function buildLegend(series, colorPalette, svgWidth, legendPos, svgHeight, plotTop, swatchKind = 'rect') {
|
|
16419
|
+
const legend = series.map((s, i) => {
|
|
16420
|
+
const color = seriesColor(s, i, colorPalette), kind = Array.isArray(swatchKind) ? (swatchKind[i] ?? 'rect') : swatchKind;
|
|
16421
|
+
return {
|
|
16422
|
+
color,
|
|
16423
|
+
label: s.name,
|
|
16424
|
+
...(kind === 'line' ? { lineSwatch: buildLineLegendSwatch(s, color, i) } : {}),
|
|
16425
|
+
};
|
|
16426
|
+
});
|
|
16427
|
+
let legendX = svgWidth / 2, legendY = svgHeight - 8, legendAnchor = 'middle';
|
|
16428
|
+
// `tr` shares `'r'`'s coordinates (a right-aligned column starting at
|
|
16429
|
+
// plotTop): that is already "top-right corner"; it just does not reserve
|
|
16430
|
+
// plot-area space the way a reserved `'r'` legend does (see computePlotLayout).
|
|
16431
|
+
const side = resolveLegendPlacement(legendPos).side;
|
|
16432
|
+
if (side === 'r') {
|
|
16433
|
+
legendX = svgWidth - 75;
|
|
16434
|
+
legendY = plotTop;
|
|
16435
|
+
legendAnchor = 'start';
|
|
16436
|
+
}
|
|
16437
|
+
else if (side === 'l') {
|
|
16438
|
+
legendX = 4;
|
|
16439
|
+
legendY = plotTop;
|
|
16440
|
+
legendAnchor = 'start';
|
|
16441
|
+
}
|
|
16442
|
+
else if (side === 't') {
|
|
16443
|
+
legendY = 28;
|
|
16444
|
+
}
|
|
16445
|
+
return { legend, legendX, legendY, legendAnchor };
|
|
16446
|
+
}
|
|
16447
|
+
|
|
15940
16448
|
/**
|
|
15941
16449
|
* chart-view-model-bars.ts: bar / column rectangle and line-point geometry of
|
|
15942
16450
|
* the chart engine. Split out of `chart-view-model.ts`, which re-exports
|
|
@@ -16933,119 +17441,6 @@ function smoothLinePath(points) {
|
|
|
16933
17441
|
return d;
|
|
16934
17442
|
}
|
|
16935
17443
|
|
|
16936
|
-
/**
|
|
16937
|
-
* chart-marker-shape.ts: framework-agnostic marker-symbol primitive builder for
|
|
16938
|
-
* line / scatter / area chart data points.
|
|
16939
|
-
*
|
|
16940
|
-
* OOXML line/scatter series carry a `c:marker` with a `c:symbol` (circle,
|
|
16941
|
-
* diamond, square, triangle, star, x, plus, dot, dash, none, ...) and an
|
|
16942
|
-
* optional `c:size` (2-72 points). The base view-model historically drew every
|
|
16943
|
-
* data point as a fixed-radius circle, ignoring both. This helper resolves the
|
|
16944
|
-
* parsed marker into the correct `SvgPrimitive` at the requested size, and
|
|
16945
|
-
* returns `null` for `symbol === 'none'` so no dot is drawn.
|
|
16946
|
-
*
|
|
16947
|
-
* @module chart-marker-shape
|
|
16948
|
-
*/
|
|
16949
|
-
/** Resolve the drawn radius (px) from the parsed point size or the default. */
|
|
16950
|
-
function markerRadius(size, defaultRadius) {
|
|
16951
|
-
if (size === undefined || !Number.isFinite(size) || size <= 0) {
|
|
16952
|
-
return defaultRadius;
|
|
16953
|
-
}
|
|
16954
|
-
// OOXML marker size is a point diameter; treat 1pt ~ 1px at chart scale.
|
|
16955
|
-
return size / 2;
|
|
16956
|
-
}
|
|
16957
|
-
/** Build the SVG points string for a regular/irregular polygon vertex list. */
|
|
16958
|
-
function polygonPoints(vertices) {
|
|
16959
|
-
return vertices.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(' ');
|
|
16960
|
-
}
|
|
16961
|
-
function starVertices(cx, cy, r) {
|
|
16962
|
-
const inner = r * 0.5;
|
|
16963
|
-
const out = [];
|
|
16964
|
-
for (let i = 0; i < 10; i++) {
|
|
16965
|
-
const radius = i % 2 === 0 ? r : inner;
|
|
16966
|
-
const angle = -Math.PI / 2 + (Math.PI * i) / 5;
|
|
16967
|
-
out.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)]);
|
|
16968
|
-
}
|
|
16969
|
-
return out;
|
|
16970
|
-
}
|
|
16971
|
-
/**
|
|
16972
|
-
* Build the marker primitive for one data point, honouring `symbol` and `size`.
|
|
16973
|
-
* Returns `null` when `symbol === 'none'` (draw nothing).
|
|
16974
|
-
*/
|
|
16975
|
-
function buildMarkerPrimitive(input) {
|
|
16976
|
-
const { symbol, cx, cy, fill, part } = input;
|
|
16977
|
-
if (symbol === 'none') {
|
|
16978
|
-
return null;
|
|
16979
|
-
}
|
|
16980
|
-
const r = markerRadius(input.size, input.defaultRadius);
|
|
16981
|
-
const stroke = fill;
|
|
16982
|
-
switch (symbol) {
|
|
16983
|
-
case 'square':
|
|
16984
|
-
return { kind: 'rect', x: cx - r, y: cy - r, w: r * 2, h: r * 2, fill, part };
|
|
16985
|
-
case 'dash':
|
|
16986
|
-
return { kind: 'rect', x: cx - r, y: cy - r * 0.32, w: r * 2, h: r * 0.64, fill, part };
|
|
16987
|
-
case 'diamond':
|
|
16988
|
-
return {
|
|
16989
|
-
kind: 'polygon',
|
|
16990
|
-
points: polygonPoints([
|
|
16991
|
-
[cx, cy - r],
|
|
16992
|
-
[cx + r, cy],
|
|
16993
|
-
[cx, cy + r],
|
|
16994
|
-
[cx - r, cy],
|
|
16995
|
-
]),
|
|
16996
|
-
fill,
|
|
16997
|
-
stroke,
|
|
16998
|
-
strokeWidth: 0,
|
|
16999
|
-
part,
|
|
17000
|
-
};
|
|
17001
|
-
case 'triangle':
|
|
17002
|
-
return {
|
|
17003
|
-
kind: 'polygon',
|
|
17004
|
-
points: polygonPoints([
|
|
17005
|
-
[cx, cy - r],
|
|
17006
|
-
[cx + r * 0.9, cy + r * 0.75],
|
|
17007
|
-
[cx - r * 0.9, cy + r * 0.75],
|
|
17008
|
-
]),
|
|
17009
|
-
fill,
|
|
17010
|
-
stroke,
|
|
17011
|
-
strokeWidth: 0,
|
|
17012
|
-
part,
|
|
17013
|
-
};
|
|
17014
|
-
case 'star':
|
|
17015
|
-
return {
|
|
17016
|
-
kind: 'polygon',
|
|
17017
|
-
points: polygonPoints(starVertices(cx, cy, r)),
|
|
17018
|
-
fill,
|
|
17019
|
-
stroke,
|
|
17020
|
-
strokeWidth: 0,
|
|
17021
|
-
part,
|
|
17022
|
-
};
|
|
17023
|
-
case 'plus':
|
|
17024
|
-
return {
|
|
17025
|
-
kind: 'path',
|
|
17026
|
-
d: `M${(cx - r).toFixed(2)},${cy.toFixed(2)} L${(cx + r).toFixed(2)},${cy.toFixed(2)} M${cx.toFixed(2)},${(cy - r).toFixed(2)} L${cx.toFixed(2)},${(cy + r).toFixed(2)}`,
|
|
17027
|
-
fill: 'none',
|
|
17028
|
-
stroke,
|
|
17029
|
-
strokeWidth: Math.max(1, r * 0.4),
|
|
17030
|
-
part,
|
|
17031
|
-
};
|
|
17032
|
-
case 'x':
|
|
17033
|
-
return {
|
|
17034
|
-
kind: 'path',
|
|
17035
|
-
d: `M${(cx - r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx + r).toFixed(2)},${(cy + r).toFixed(2)} M${(cx + r).toFixed(2)},${(cy - r).toFixed(2)} L${(cx - r).toFixed(2)},${(cy + r).toFixed(2)}`,
|
|
17036
|
-
fill: 'none',
|
|
17037
|
-
stroke,
|
|
17038
|
-
strokeWidth: Math.max(1, r * 0.4),
|
|
17039
|
-
part,
|
|
17040
|
-
};
|
|
17041
|
-
case 'dot':
|
|
17042
|
-
return { kind: 'circle', cx, cy, r: Math.max(r * 0.6, 1), fill, part };
|
|
17043
|
-
default:
|
|
17044
|
-
// circle / auto / picture / undefined -> filled circle.
|
|
17045
|
-
return { kind: 'circle', cx, cy, r, fill, part };
|
|
17046
|
-
}
|
|
17047
|
-
}
|
|
17048
|
-
|
|
17049
17444
|
/**
|
|
17050
17445
|
* Chart-element pixel box a manual `c:dLbl/c:layout` drag is measured
|
|
17051
17446
|
* against. Exported for `chart-cartesian-bubbles.ts`, which shares this
|
|
@@ -17073,8 +17468,8 @@ function xyMarkTooltip(seriesName, xVal, yVal, numberFormat) {
|
|
|
17073
17468
|
* fewer points than the series declares and the override must still land on the
|
|
17074
17469
|
* point the author picked.
|
|
17075
17470
|
*/
|
|
17076
|
-
function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part, opacity, title) {
|
|
17077
|
-
const marker = resolveDataPointMarker(series, pointIndex), m = buildMarkerPrimitive({
|
|
17471
|
+
function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part, opacity, title, seriesPosition = part.seriesIndex ?? 0) {
|
|
17472
|
+
const marker = resolveDataPointMarker(series, pointIndex, seriesPosition), m = buildMarkerPrimitive({
|
|
17078
17473
|
symbol: marker.symbol,
|
|
17079
17474
|
size: marker.size,
|
|
17080
17475
|
cx,
|
|
@@ -17635,14 +18030,31 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
17635
18030
|
varyColorsSingle = chartData.varyColors === true && series.length === 1;
|
|
17636
18031
|
if (grouping === 'clustered') {
|
|
17637
18032
|
const seriesCount = Math.max(series.length, 1), barGroupWidth = layout.plotWidth / Math.max(catCount, 1),
|
|
18033
|
+
// Honour c:overlap (% overlap between adjacent series). overlap=0 reproduces
|
|
18034
|
+
// the original side-by-side layout exactly. Read before singleBarWidth: a
|
|
18035
|
+
// gapWidth-based bar width must size itself so the OVERLAPPED cluster (not
|
|
18036
|
+
// seriesCount side-by-side bars) fills the gap-reduced group width; see the
|
|
18037
|
+
// comment below.
|
|
18038
|
+
overlap = chartData.barOverlap ?? 0,
|
|
17638
18039
|
// Honour c:gapWidth (gap between clusters, % of a bar width) when parsed;
|
|
17639
18040
|
// otherwise keep the legacy 0.7-of-group heuristic byte-for-byte.
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
//
|
|
17644
|
-
//
|
|
17645
|
-
|
|
18041
|
+
//
|
|
18042
|
+
// COM-verified ground truth (PowerPoint Object 16, single category, six
|
|
18043
|
+
// series, gapWidth=5, overlap=23): dividing by seriesCount alone (the
|
|
18044
|
+
// pre-existing formula) sizes every bar as if the cluster were laid out
|
|
18045
|
+
// SIDE BY SIDE, then shrinks it further by overlap when computing `step`
|
|
18046
|
+
// below - so at high overlap the bars render far too NARROW (in the
|
|
18047
|
+
// limit, overlap=100 should make every bar in a cluster the same width
|
|
18048
|
+
// as a single-series bar, independent of seriesCount, since they fully
|
|
18049
|
+
// coincide; the old formula kept shrinking by 1/seriesCount regardless).
|
|
18050
|
+
// The fix: first shrink the group width by the gap (exactly as before),
|
|
18051
|
+
// then divide by how many bar-widths the OVERLAPPED cluster actually
|
|
18052
|
+
// spans (`1 + (seriesCount - 1) * (1 - overlap / 100)`, the same
|
|
18053
|
+
// relationship `clusterWidth` below re-derives from `step`), not by the
|
|
18054
|
+
// raw series count.
|
|
18055
|
+
overlapSpan = 1 + (seriesCount - 1) * (1 - overlap / 100), singleBarWidth = chartData.barGapWidth !== undefined
|
|
18056
|
+
? barGroupWidth / ((1 + Math.max(chartData.barGapWidth, 0) / 100) * overlapSpan)
|
|
18057
|
+
: (barGroupWidth * 0.7) / seriesCount, step = singleBarWidth * (1 - overlap / 100), clusterWidth = singleBarWidth + step * (seriesCount - 1), groupOffset = (barGroupWidth - clusterWidth) / 2;
|
|
17646
18058
|
for (let displayIndex = 0; displayIndex < catCount; displayIndex++) {
|
|
17647
18059
|
const sourceIndex = sourceIndices[displayIndex] ?? displayIndex;
|
|
17648
18060
|
for (let si = 0; si < series.length; si++) {
|
|
@@ -19074,11 +19486,15 @@ function barFill(chartData, series, seriesIndex, pointIndex) {
|
|
|
19074
19486
|
* clustered / stacked / percentStacked geometry with the axes transposed.
|
|
19075
19487
|
*/
|
|
19076
19488
|
function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
19077
|
-
const layout = computePlotLayout(element.width, element.height, chartData, true), catCount = Math.max(categoryLabels.length, 1), series = chartData.series, grouping = chartData.grouping ?? 'clustered', isStacked = grouping === 'stacked' || grouping === 'percentStacked', isPercent = grouping === 'percentStacked',
|
|
19489
|
+
const layout = computePlotLayout(element.width, element.height, chartData, true), catCount = Math.max(categoryLabels.length, 1), series = chartData.series, grouping = chartData.grouping ?? 'clustered', isStacked = grouping === 'stacked' || grouping === 'percentStacked', isPercent = grouping === 'percentStacked',
|
|
19490
|
+
// The value axis runs along the WIDTH here (categories run down the left,
|
|
19491
|
+
// values along the bottom: this builder transposes the column engine), so
|
|
19492
|
+
// the gridline-count budget is keyed off plotWidth, not plotHeight.
|
|
19493
|
+
range = isPercent
|
|
19078
19494
|
? { min: 0, max: 100, span: 100 }
|
|
19079
19495
|
: isStacked
|
|
19080
|
-
? computeStackedValueRange(series, catCount)
|
|
19081
|
-
: computeValueRangeForChart(series, chartData.axes), { gridlines, axisLabels } = buildTransposedValueAxis(range, layout), zeroX = valueToX(0, range, layout.plotLeft, layout.plotRight), zeroLine = range.min < 0 && range.max > 0
|
|
19496
|
+
? computeStackedValueRange(series, catCount, layout.plotWidth)
|
|
19497
|
+
: computeValueRangeForChart(series, chartData.axes, layout.plotWidth), { gridlines, axisLabels } = buildTransposedValueAxis(range, layout), zeroX = valueToX(0, range, layout.plotLeft, layout.plotRight), zeroLine = range.min < 0 && range.max > 0
|
|
19082
19498
|
? {
|
|
19083
19499
|
kind: 'line',
|
|
19084
19500
|
x1: zeroX,
|
|
@@ -19090,9 +19506,14 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
19090
19506
|
}
|
|
19091
19507
|
: undefined, primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, band = layout.plotHeight / catCount;
|
|
19092
19508
|
if (!isStacked) {
|
|
19093
|
-
const seriesCount = Math.max(series.length, 1),
|
|
19094
|
-
|
|
19095
|
-
|
|
19509
|
+
const seriesCount = Math.max(series.length, 1),
|
|
19510
|
+
// See chart-cartesian-bars.ts's `buildBars` (clustered branch) for the
|
|
19511
|
+
// COM-verified reasoning: the bar's own size must divide the gap-reduced
|
|
19512
|
+
// band by how many bar-heights the OVERLAPPED cluster spans, not by the
|
|
19513
|
+
// raw series count, or a high-overlap cluster renders far too thin.
|
|
19514
|
+
overlap = chartData.barOverlap ?? 0, overlapSpan = 1 + (seriesCount - 1) * (1 - overlap / 100), singleBarHeight = chartData.barGapWidth !== undefined
|
|
19515
|
+
? band / ((1 + Math.max(chartData.barGapWidth, 0) / 100) * overlapSpan)
|
|
19516
|
+
: (band * 0.7) / seriesCount, step = singleBarHeight * (1 - overlap / 100), clusterHeight = singleBarHeight + step * (seriesCount - 1), groupOffset = (band - clusterHeight) / 2;
|
|
19096
19517
|
for (let ci = 0; ci < catCount; ci++) {
|
|
19097
19518
|
for (let si = 0; si < series.length; si++) {
|
|
19098
19519
|
const val = series[si].values[ci] ?? 0, y = layout.plotTop + band * ci + groupOffset + step * si, valX = valueToX(val, range, layout.plotLeft, layout.plotRight), x = Math.min(zeroX, valX), w = Math.max(Math.abs(valX - zeroX), 1);
|
|
@@ -19789,11 +20210,11 @@ function computeTrendlinePrimitives(chartData, catCount, layout, range, mode = '
|
|
|
19789
20210
|
* @module chart-overlays
|
|
19790
20211
|
*/
|
|
19791
20212
|
|
|
19792
|
-
function stackedRange(chartData, catCount, isPercent) {
|
|
20213
|
+
function stackedRange(chartData, catCount, isPercent, plotHeightPx) {
|
|
19793
20214
|
if (isPercent) {
|
|
19794
20215
|
return { min: 0, max: 100, span: 100 };
|
|
19795
20216
|
}
|
|
19796
|
-
return computeStackedValueRange(chartData.series, catCount);
|
|
20217
|
+
return computeStackedValueRange(chartData.series, catCount, plotHeightPx);
|
|
19797
20218
|
}
|
|
19798
20219
|
/**
|
|
19799
20220
|
* Build the enriched cartesian view-model for bar / line / area / scatter /
|
|
@@ -19844,13 +20265,13 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
19844
20265
|
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
19845
20266
|
const primaryRange = isStacked
|
|
19846
20267
|
? {
|
|
19847
|
-
...stackedRange(chartData, catCount, isPercent),
|
|
20268
|
+
...stackedRange(chartData, catCount, isPercent, layout.plotHeight),
|
|
19848
20269
|
...(primaryAxis?.orientation === 'maxMin' ? { reverseOrder: true } : {}),
|
|
19849
20270
|
}
|
|
19850
|
-
: computeValueRangeForChart(primaryPlotSeries.length > 0 ? primaryPlotSeries : chartData.series, chartData.axes);
|
|
20271
|
+
: computeValueRangeForChart(primaryPlotSeries.length > 0 ? primaryPlotSeries : chartData.series, chartData.axes, layout.plotHeight);
|
|
19851
20272
|
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
19852
20273
|
const secondaryRange = useSecondary && secondaryPlotSeries.length > 0
|
|
19853
|
-
? computeValueRangeForAxis(secondaryPlotSeries, chartData.axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos === 'r'))
|
|
20274
|
+
? computeValueRangeForAxis(secondaryPlotSeries, chartData.axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos === 'r'), layout.plotHeight)
|
|
19854
20275
|
: undefined;
|
|
19855
20276
|
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
19856
20277
|
const axisRes = buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, catCount);
|
|
@@ -19863,7 +20284,7 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
19863
20284
|
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
19864
20285
|
const legendPos = chartData.style?.legendPosition ?? 'b';
|
|
19865
20286
|
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
19866
|
-
const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
|
|
20287
|
+
const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop, resolveLegendSwatchKind(kind));
|
|
19867
20288
|
let plot;
|
|
19868
20289
|
if (kind === 'bar') {
|
|
19869
20290
|
plot = buildBars(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, isStacked ? (isPercent ? 'percentStacked' : 'stacked') : 'clustered', sourceIndices);
|
|
@@ -20075,9 +20496,9 @@ function buildComboViewModel(element, chartData, categoryLabels) {
|
|
|
20075
20496
|
const primaryAxisId = getPrimaryValueAxisId(chartData.axes);
|
|
20076
20497
|
const primaryAxis = chartData.axes?.find((axis) => axis.axisId === primaryAxisId);
|
|
20077
20498
|
const secondaryAxisFormatting = getSecondaryValueAxis(chartData.axes);
|
|
20078
|
-
const primaryRange = computeValueRangeForAxis(primarySeries, primaryAxis);
|
|
20499
|
+
const primaryRange = computeValueRangeForAxis(primarySeries, primaryAxis, layout.plotHeight);
|
|
20079
20500
|
const secondaryRange = secondary.length > 0
|
|
20080
|
-
? computeValueRangeForAxis(secondary.map((entry) => entry.series), secondaryAxisFormatting)
|
|
20501
|
+
? computeValueRangeForAxis(secondary.map((entry) => entry.series), secondaryAxisFormatting, layout.plotHeight)
|
|
20081
20502
|
: undefined;
|
|
20082
20503
|
const primaryCategoryAxis = chartData.axes?.find((axis) => (axis.axisType === 'catAx' || axis.axisType === 'dateAx') &&
|
|
20083
20504
|
axis.axisId === primaryAxis?.crossAxisId);
|
|
@@ -20096,7 +20517,11 @@ function buildComboViewModel(element, chartData, categoryLabels) {
|
|
|
20096
20517
|
const horizontalAxis = buildCartesianHorizontalAxis(chartData, categoryLabels, layout, 'combo', primaryRange, secondaryRange);
|
|
20097
20518
|
const sourceIndices = horizontalAxis.sourceIndices;
|
|
20098
20519
|
const legendPos = chartData.style?.legendPosition ?? 'b';
|
|
20099
|
-
|
|
20520
|
+
// The combo layout below is fixed: series 0 is the bar, every other series
|
|
20521
|
+
// is a line (see `barSeries`/`lineSeries` just below), so the legend swatch
|
|
20522
|
+
// follows the same split (bar -> rect, line -> line+marker sample).
|
|
20523
|
+
const comboSwatchKinds = chartData.series.map((_s, i) => i === 0 ? 'rect' : 'line');
|
|
20524
|
+
const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop, comboSwatchKinds);
|
|
20100
20525
|
const primitives = [];
|
|
20101
20526
|
const dataLabels = [];
|
|
20102
20527
|
// Drop / hi-low / up-down helper lines, drawn behind the combo marks.
|
|
@@ -20232,7 +20657,7 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
20232
20657
|
// Through `computeValueRangeForChart`, not the bare linear helper: a stock
|
|
20233
20658
|
// chart is as entitled to a log or display-unit value axis as any other
|
|
20234
20659
|
// cartesian kind, and the bare helper silently ignores `c:scaling`.
|
|
20235
|
-
const range = computeValueRangeForChart(chartData.series, chartData.axes);
|
|
20660
|
+
const range = computeValueRangeForChart(chartData.series, chartData.axes, layout.plotHeight);
|
|
20236
20661
|
const valueAxis = chartData.axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos !== 'r');
|
|
20237
20662
|
const categoryAxis = chartData.axes?.find((axis) => (axis.axisType === 'catAx' || axis.axisType === 'dateAx') &&
|
|
20238
20663
|
axis.axisId === valueAxis?.crossAxisId);
|
|
@@ -21053,22 +21478,26 @@ function buildSunburstViewModel(element, chartData, categoryLabels) {
|
|
|
21053
21478
|
}
|
|
21054
21479
|
|
|
21055
21480
|
/**
|
|
21056
|
-
* Filter deleted entries and attach per-entry text-style overrides
|
|
21057
|
-
*
|
|
21058
|
-
*
|
|
21481
|
+
* Filter deleted entries and attach per-entry text-style overrides, falling
|
|
21482
|
+
* back to the chart-level legend default (`PptxChartStyle.legendTextStyle`,
|
|
21483
|
+
* itself `c:legend/c:txPr` or `c:chartSpace/c:txPr`) for any entry that has no
|
|
21484
|
+
* `c:legendEntry/c:txPr` override of its own. Returns the input array
|
|
21485
|
+
* unchanged (same reference) when there is nothing to apply, so a chart with
|
|
21486
|
+
* neither kind of override pays no extra cost.
|
|
21059
21487
|
*/
|
|
21060
|
-
function applyLegendEntryOverrides(legend, entries) {
|
|
21061
|
-
if (!entries || entries.length === 0) {
|
|
21488
|
+
function applyLegendEntryOverrides(legend, entries, defaultTextStyle) {
|
|
21489
|
+
if ((!entries || entries.length === 0) && !defaultTextStyle) {
|
|
21062
21490
|
return legend;
|
|
21063
21491
|
}
|
|
21064
|
-
const byIndex = new Map(entries.map((entry) => [entry.index, entry]));
|
|
21492
|
+
const byIndex = new Map((entries ?? []).map((entry) => [entry.index, entry]));
|
|
21065
21493
|
const result = [];
|
|
21066
21494
|
legend.forEach((item, index) => {
|
|
21067
21495
|
const override = byIndex.get(index);
|
|
21068
21496
|
if (override?.deleted) {
|
|
21069
21497
|
return;
|
|
21070
21498
|
}
|
|
21071
|
-
|
|
21499
|
+
const textStyle = override?.textStyle ?? defaultTextStyle;
|
|
21500
|
+
result.push(textStyle ? { ...item, textStyle } : item);
|
|
21072
21501
|
});
|
|
21073
21502
|
return result;
|
|
21074
21503
|
}
|
|
@@ -21467,7 +21896,7 @@ function buildSurfaceWallPanels(cols, rows, cellSize, zScale, offsetX, offsetY,
|
|
|
21467
21896
|
*/
|
|
21468
21897
|
function buildIsometricSurfaceViewModel(element, chartData, categoryLabels) {
|
|
21469
21898
|
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
21470
|
-
const range = computeValueRange(chartData.series);
|
|
21899
|
+
const range = computeValueRange(chartData.series, layout.plotHeight);
|
|
21471
21900
|
const catCount = Math.max(categoryLabels.length, 1);
|
|
21472
21901
|
const seriesCount = chartData.series.length;
|
|
21473
21902
|
// Grid cell count (vertices = cells + 1 in each dimension).
|
|
@@ -21613,7 +22042,7 @@ function buildIsometricSurfaceViewModel(element, chartData, categoryLabels) {
|
|
|
21613
22042
|
*/
|
|
21614
22043
|
function buildFlatSurfaceViewModel(element, chartData, categoryLabels) {
|
|
21615
22044
|
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
21616
|
-
const range = computeValueRange(chartData.series);
|
|
22045
|
+
const range = computeValueRange(chartData.series, layout.plotHeight);
|
|
21617
22046
|
const catCount = Math.max(categoryLabels.length, 1);
|
|
21618
22047
|
const seriesCount = chartData.series.length;
|
|
21619
22048
|
const cellW = layout.plotWidth / Math.max(catCount - 1, 1);
|
|
@@ -22712,9 +23141,12 @@ function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
|
|
|
22712
23141
|
}
|
|
22713
23142
|
|
|
22714
23143
|
/**
|
|
22715
|
-
* chart-view-model-layout.ts: plot-area layout
|
|
22716
|
-
*
|
|
22717
|
-
* `
|
|
23144
|
+
* chart-view-model-layout.ts: plot-area layout math for the chart engine
|
|
23145
|
+
* (the plot inset, legend/title/data-table/secondary-axis reservations, and
|
|
23146
|
+
* `c:plotArea/c:layout/c:manualLayout` overrides). Chart chrome (gridlines,
|
|
23147
|
+
* axis / category labels) lives in `chart-view-model-chrome.ts`; legend
|
|
23148
|
+
* building lives in `chart-legend-build.ts`. All three are re-exported by
|
|
23149
|
+
* `chart-view-model.ts`.
|
|
22718
23150
|
*
|
|
22719
23151
|
* @module chart-view-model-layout
|
|
22720
23152
|
*/
|
|
@@ -22753,6 +23185,16 @@ function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, opti
|
|
|
22753
23185
|
// a 475x174 frame got a 475x180 viewBox, squeezing y by 0.967).
|
|
22754
23186
|
const svgWidth = Math.max(1, elementWidth), svgHeight = Math.max(1, elementHeight);
|
|
22755
23187
|
let plotLeft = hasAxes ? 48 : 8, plotTop = 8, plotRight = svgWidth - 8, plotBottom = svgHeight - (hasAxes ? categoryAxisBand(chartData) : 8);
|
|
23188
|
+
// `c:catAx/c:axPos val="t"` on the PRIMARY (and, in the common case, only)
|
|
23189
|
+
// category axis: PowerPoint draws that axis's tick labels above the plot,
|
|
23190
|
+
// not below it. Reserving the label band at the bottom regardless of
|
|
23191
|
+
// `axPos` (the historical behaviour here) leaves no room at the top, so the
|
|
23192
|
+
// labels get drawn outside the chart's own SVG bounds and clip against its
|
|
23193
|
+
// edge. Move the reservation to match where the labels actually land.
|
|
23194
|
+
if (hasAxes && options?.categoryAxisAtTop) {
|
|
23195
|
+
plotBottom = svgHeight - 8;
|
|
23196
|
+
plotTop += categoryAxisBand(chartData);
|
|
23197
|
+
}
|
|
22756
23198
|
const style = chartData.style, legendPos = style?.legendPosition ?? 'b';
|
|
22757
23199
|
if (style?.hasTitle) {
|
|
22758
23200
|
plotTop += 20;
|
|
@@ -22808,121 +23250,6 @@ function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, opti
|
|
|
22808
23250
|
plotHeight,
|
|
22809
23251
|
};
|
|
22810
23252
|
}
|
|
22811
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
22812
|
-
// Chrome helpers
|
|
22813
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
22814
|
-
const GRIDLINE_COLOR$1 = '#e2e8f0';
|
|
22815
|
-
const AXIS_LABEL_COLOR = '#64748b';
|
|
22816
|
-
const ZERO_LINE_COLOR = '#94a3b8';
|
|
22817
|
-
const TICK_COUNT$1 = 5;
|
|
22818
|
-
/**
|
|
22819
|
-
* Tick values for a range: one per major unit when the automatic scale supplied
|
|
22820
|
-
* one (it snapped the bounds to whole multiples, so this lands on round numbers
|
|
22821
|
-
* exactly as PowerPoint does), otherwise an even division of the span.
|
|
22822
|
-
* Exported for the transposed (horizontal-bar) axis builder.
|
|
22823
|
-
*/
|
|
22824
|
-
function axisTickValues(range) {
|
|
22825
|
-
const unit = range.majorUnit;
|
|
22826
|
-
if (unit !== undefined && Number.isFinite(unit) && unit > 0 && !range.logScale) {
|
|
22827
|
-
const steps = Math.round((range.max - range.min) / unit);
|
|
22828
|
-
if (steps >= 1 && steps <= 100) {
|
|
22829
|
-
return Array.from({ length: steps + 1 }, (_unused, index) => range.min + unit * index);
|
|
22830
|
-
}
|
|
22831
|
-
}
|
|
22832
|
-
return Array.from({ length: TICK_COUNT$1 + 1 }, (_unused, index) => range.min + (range.span / TICK_COUNT$1) * index);
|
|
22833
|
-
}
|
|
22834
|
-
function buildGridlinesAndLabels(range, layout,
|
|
22835
|
-
/**
|
|
22836
|
-
* `false` keeps the tick labels but draws no gridlines: a value axis whose
|
|
22837
|
-
* `c:majorGridlines` is absent (see `shouldRenderMajorGridlines`).
|
|
22838
|
-
*/
|
|
22839
|
-
showMajorGridlines = true) {
|
|
22840
|
-
const gridlines = [], axisLabels = [];
|
|
22841
|
-
for (const val of axisTickValues(range)) {
|
|
22842
|
-
const y = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
22843
|
-
if (showMajorGridlines) {
|
|
22844
|
-
gridlines.push({
|
|
22845
|
-
kind: 'line',
|
|
22846
|
-
x1: layout.plotLeft,
|
|
22847
|
-
y1: y,
|
|
22848
|
-
x2: layout.plotRight,
|
|
22849
|
-
y2: y,
|
|
22850
|
-
stroke: GRIDLINE_COLOR$1,
|
|
22851
|
-
strokeWidth: 1,
|
|
22852
|
-
});
|
|
22853
|
-
}
|
|
22854
|
-
axisLabels.push({
|
|
22855
|
-
kind: 'text',
|
|
22856
|
-
x: layout.plotLeft - 4,
|
|
22857
|
-
y,
|
|
22858
|
-
text: formatAxisValue(val),
|
|
22859
|
-
fontSize: DEFAULT_CHART_TEXT_PX,
|
|
22860
|
-
fill: AXIS_LABEL_COLOR,
|
|
22861
|
-
textAnchor: 'end',
|
|
22862
|
-
dominantBaseline: 'central',
|
|
22863
|
-
});
|
|
22864
|
-
}
|
|
22865
|
-
return { gridlines, axisLabels };
|
|
22866
|
-
}
|
|
22867
|
-
function buildZeroLine(range, layout) {
|
|
22868
|
-
if (range.min >= 0 || range.max <= 0) {
|
|
22869
|
-
return undefined;
|
|
22870
|
-
}
|
|
22871
|
-
const y = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
22872
|
-
return {
|
|
22873
|
-
kind: 'line',
|
|
22874
|
-
x1: layout.plotLeft,
|
|
22875
|
-
y1: y,
|
|
22876
|
-
x2: layout.plotRight,
|
|
22877
|
-
y2: y,
|
|
22878
|
-
stroke: ZERO_LINE_COLOR,
|
|
22879
|
-
strokeWidth: 1,
|
|
22880
|
-
};
|
|
22881
|
-
}
|
|
22882
|
-
function buildCategoryLabels(categoryLabels, layout, catSpacing) {
|
|
22883
|
-
const catCount = Math.max(categoryLabels.length, 1);
|
|
22884
|
-
return categoryLabels.map((label, i) => {
|
|
22885
|
-
const x = catSpacing === 'bar'
|
|
22886
|
-
? layout.plotLeft + (layout.plotWidth / catCount) * (i + 0.5)
|
|
22887
|
-
: catCount > 1
|
|
22888
|
-
? layout.plotLeft + (layout.plotWidth / (catCount - 1)) * i
|
|
22889
|
-
: layout.plotLeft + layout.plotWidth / 2;
|
|
22890
|
-
return {
|
|
22891
|
-
kind: 'text',
|
|
22892
|
-
x,
|
|
22893
|
-
y: layout.plotBottom + 12,
|
|
22894
|
-
text: label,
|
|
22895
|
-
fontSize: DEFAULT_CHART_TEXT_PX,
|
|
22896
|
-
fill: AXIS_LABEL_COLOR,
|
|
22897
|
-
textAnchor: 'middle',
|
|
22898
|
-
};
|
|
22899
|
-
});
|
|
22900
|
-
}
|
|
22901
|
-
function buildLegend(series, colorPalette, svgWidth, legendPos, svgHeight, plotTop) {
|
|
22902
|
-
const legend = series.map((s, i) => ({
|
|
22903
|
-
color: seriesColor(s, i, colorPalette),
|
|
22904
|
-
label: s.name,
|
|
22905
|
-
}));
|
|
22906
|
-
let legendX = svgWidth / 2, legendY = svgHeight - 8, legendAnchor = 'middle';
|
|
22907
|
-
// `tr` shares `'r'`'s coordinates (a right-aligned column starting at
|
|
22908
|
-
// plotTop): that is already "top-right corner"; it just does not reserve
|
|
22909
|
-
// plot-area space the way a reserved `'r'` legend does (see computePlotLayout).
|
|
22910
|
-
const side = resolveLegendPlacement(legendPos).side;
|
|
22911
|
-
if (side === 'r') {
|
|
22912
|
-
legendX = svgWidth - 75;
|
|
22913
|
-
legendY = plotTop;
|
|
22914
|
-
legendAnchor = 'start';
|
|
22915
|
-
}
|
|
22916
|
-
else if (side === 'l') {
|
|
22917
|
-
legendX = 4;
|
|
22918
|
-
legendY = plotTop;
|
|
22919
|
-
legendAnchor = 'start';
|
|
22920
|
-
}
|
|
22921
|
-
else if (side === 't') {
|
|
22922
|
-
legendY = 28;
|
|
22923
|
-
}
|
|
22924
|
-
return { legend, legendX, legendY, legendAnchor };
|
|
22925
|
-
}
|
|
22926
23253
|
|
|
22927
23254
|
/**
|
|
22928
23255
|
* chart-view-model-radar.ts: the radar / spider view-model builder. Split out
|
|
@@ -23924,14 +24251,20 @@ function buildWaterfallSteps(values, options) {
|
|
|
23924
24251
|
}
|
|
23925
24252
|
return steps;
|
|
23926
24253
|
}
|
|
23927
|
-
/**
|
|
23928
|
-
|
|
24254
|
+
/**
|
|
24255
|
+
* Compute a value range containing every cumulative and absolute bar endpoint.
|
|
24256
|
+
*
|
|
24257
|
+
* @param plotHeightPx The chart's plot-area height, when known; see
|
|
24258
|
+
* `axisTargetIntervals`'s doc comment for why this changes the automatic
|
|
24259
|
+
* scale's gridline count.
|
|
24260
|
+
*/
|
|
24261
|
+
function computeWaterfallRange(steps, plotHeightPx) {
|
|
23929
24262
|
return computeValueRange([
|
|
23930
24263
|
{
|
|
23931
24264
|
name: 'Waterfall range',
|
|
23932
24265
|
values: steps.flatMap((step) => [step.startValue, step.endValue]),
|
|
23933
24266
|
},
|
|
23934
|
-
]);
|
|
24267
|
+
], plotHeightPx);
|
|
23935
24268
|
}
|
|
23936
24269
|
|
|
23937
24270
|
/**
|
|
@@ -23974,7 +24307,7 @@ function buildWaterfallViewModel(element, chartData, categoryLabels) {
|
|
|
23974
24307
|
const series = chartData.series[0];
|
|
23975
24308
|
const values = series?.values ?? [];
|
|
23976
24309
|
const steps = buildWaterfallSteps(values, series?.waterfallOptions);
|
|
23977
|
-
const range = computeWaterfallRange(steps);
|
|
24310
|
+
const range = computeWaterfallRange(steps, layout.plotHeight);
|
|
23978
24311
|
const catCount = Math.max(categoryLabels.length, values.length, 1);
|
|
23979
24312
|
const barWidth = (layout.plotWidth / catCount) * 0.6;
|
|
23980
24313
|
const gap = (layout.plotWidth / catCount) * 0.2;
|
|
@@ -24173,7 +24506,7 @@ function withDataPointPictureFills(vm, chartData, elementId) {
|
|
|
24173
24506
|
* `chart-legend-entries.ts`.
|
|
24174
24507
|
*/
|
|
24175
24508
|
function withLegendEntries(vm, chartData) {
|
|
24176
|
-
const legend = applyLegendEntryOverrides(vm.legend, chartData.style?.legendEntries);
|
|
24509
|
+
const legend = applyLegendEntryOverrides(vm.legend, chartData.style?.legendEntries, chartData.style?.legendTextStyle);
|
|
24177
24510
|
return legend === vm.legend ? vm : { ...vm, legend };
|
|
24178
24511
|
}
|
|
24179
24512
|
/**
|
|
@@ -24258,6 +24591,107 @@ function buildFlatViewModel(element, chartData, categoryLabels, kind) {
|
|
|
24258
24591
|
return buildCartesianViewModel(element, chartData, categoryLabels, kind);
|
|
24259
24592
|
}
|
|
24260
24593
|
|
|
24594
|
+
/**
|
|
24595
|
+
* chart-view-model-chrome.ts: chart chrome (value-axis gridlines/labels, zero
|
|
24596
|
+
* line, category labels) shared by the cartesian chart kinds. Split out of
|
|
24597
|
+
* `chart-view-model-layout.ts` to keep that module's plot layout math under
|
|
24598
|
+
* the repo's 300-LOC file guideline; re-exported by `chart-view-model.ts`.
|
|
24599
|
+
* Legend building (`buildLegend`) lives in `chart-legend-build.ts`, not here.
|
|
24600
|
+
*
|
|
24601
|
+
* @module chart-view-model-chrome
|
|
24602
|
+
*/
|
|
24603
|
+
/* eslint-disable one-var -- this module predates the rule and combining every
|
|
24604
|
+
sibling `const`/`let` in a function into one comma-list (oxlint's own
|
|
24605
|
+
`--fix` cannot do this safely once a non-declaration statement sits between
|
|
24606
|
+
them) would churn geometry code far beyond this change's scope. */
|
|
24607
|
+
const GRIDLINE_COLOR$1 = '#e2e8f0';
|
|
24608
|
+
const AXIS_LABEL_COLOR = '#64748b';
|
|
24609
|
+
const ZERO_LINE_COLOR = '#94a3b8';
|
|
24610
|
+
const TICK_COUNT$1 = 5;
|
|
24611
|
+
/**
|
|
24612
|
+
* Tick values for a range: one per major unit when the automatic scale supplied
|
|
24613
|
+
* one (it snapped the bounds to whole multiples, so this lands on round numbers
|
|
24614
|
+
* exactly as PowerPoint does), otherwise an even division of the span.
|
|
24615
|
+
* Exported for the transposed (horizontal-bar) axis builder.
|
|
24616
|
+
*/
|
|
24617
|
+
function axisTickValues(range) {
|
|
24618
|
+
const unit = range.majorUnit;
|
|
24619
|
+
if (unit !== undefined && Number.isFinite(unit) && unit > 0 && !range.logScale) {
|
|
24620
|
+
const steps = Math.round((range.max - range.min) / unit);
|
|
24621
|
+
if (steps >= 1 && steps <= 100) {
|
|
24622
|
+
return Array.from({ length: steps + 1 }, (_unused, index) => range.min + unit * index);
|
|
24623
|
+
}
|
|
24624
|
+
}
|
|
24625
|
+
return Array.from({ length: TICK_COUNT$1 + 1 }, (_unused, index) => range.min + (range.span / TICK_COUNT$1) * index);
|
|
24626
|
+
}
|
|
24627
|
+
function buildGridlinesAndLabels(range, layout,
|
|
24628
|
+
/**
|
|
24629
|
+
* `false` keeps the tick labels but draws no gridlines: a value axis whose
|
|
24630
|
+
* `c:majorGridlines` is absent (see `shouldRenderMajorGridlines`).
|
|
24631
|
+
*/
|
|
24632
|
+
showMajorGridlines = true) {
|
|
24633
|
+
const gridlines = [], axisLabels = [];
|
|
24634
|
+
for (const val of axisTickValues(range)) {
|
|
24635
|
+
const y = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
24636
|
+
if (showMajorGridlines) {
|
|
24637
|
+
gridlines.push({
|
|
24638
|
+
kind: 'line',
|
|
24639
|
+
x1: layout.plotLeft,
|
|
24640
|
+
y1: y,
|
|
24641
|
+
x2: layout.plotRight,
|
|
24642
|
+
y2: y,
|
|
24643
|
+
stroke: GRIDLINE_COLOR$1,
|
|
24644
|
+
strokeWidth: 1,
|
|
24645
|
+
});
|
|
24646
|
+
}
|
|
24647
|
+
axisLabels.push({
|
|
24648
|
+
kind: 'text',
|
|
24649
|
+
x: layout.plotLeft - 4,
|
|
24650
|
+
y,
|
|
24651
|
+
text: formatAxisValue(val),
|
|
24652
|
+
fontSize: DEFAULT_CHART_TEXT_PX,
|
|
24653
|
+
fill: AXIS_LABEL_COLOR,
|
|
24654
|
+
textAnchor: 'end',
|
|
24655
|
+
dominantBaseline: 'central',
|
|
24656
|
+
});
|
|
24657
|
+
}
|
|
24658
|
+
return { gridlines, axisLabels };
|
|
24659
|
+
}
|
|
24660
|
+
function buildZeroLine(range, layout) {
|
|
24661
|
+
if (range.min >= 0 || range.max <= 0) {
|
|
24662
|
+
return undefined;
|
|
24663
|
+
}
|
|
24664
|
+
const y = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
24665
|
+
return {
|
|
24666
|
+
kind: 'line',
|
|
24667
|
+
x1: layout.plotLeft,
|
|
24668
|
+
y1: y,
|
|
24669
|
+
x2: layout.plotRight,
|
|
24670
|
+
y2: y,
|
|
24671
|
+
stroke: ZERO_LINE_COLOR,
|
|
24672
|
+
strokeWidth: 1,
|
|
24673
|
+
};
|
|
24674
|
+
}
|
|
24675
|
+
function buildCategoryLabels(categoryLabels, layout, catSpacing) {
|
|
24676
|
+
const catCount = Math.max(categoryLabels.length, 1);
|
|
24677
|
+
return categoryLabels.map((label, i) => {
|
|
24678
|
+
const x = catSpacing === 'bar'
|
|
24679
|
+
? layout.plotLeft + (layout.plotWidth / catCount) * (i + 0.5)
|
|
24680
|
+
: catCount > 1
|
|
24681
|
+
? layout.plotLeft + (layout.plotWidth / (catCount - 1)) * i
|
|
24682
|
+
: layout.plotLeft + layout.plotWidth / 2;
|
|
24683
|
+
return {
|
|
24684
|
+
kind: 'text',
|
|
24685
|
+
x,
|
|
24686
|
+
y: layout.plotBottom + 12,
|
|
24687
|
+
text: label,
|
|
24688
|
+
fontSize: DEFAULT_CHART_TEXT_PX,
|
|
24689
|
+
fill: AXIS_LABEL_COLOR,
|
|
24690
|
+
textAnchor: 'middle',
|
|
24691
|
+
};
|
|
24692
|
+
});
|
|
24693
|
+
}
|
|
24694
|
+
|
|
24261
24695
|
/**
|
|
24262
24696
|
* chart-view-model-types.ts: the framework-neutral descriptor types of the
|
|
24263
24697
|
* chart engine (plot layout, interactive part refs, SVG primitives, legend
|
|
@@ -24309,7 +24743,9 @@ function buildFlatViewModel(element, chartData, categoryLabels, kind) {
|
|
|
24309
24743
|
*
|
|
24310
24744
|
* chart-view-model-types descriptor types (primitives, view-model, layout)
|
|
24311
24745
|
* chart-view-model-scale palette, value ranges, axis value formatting
|
|
24312
|
-
* chart-view-model-layout plot layout
|
|
24746
|
+
* chart-view-model-layout plot layout math (inset + reservations)
|
|
24747
|
+
* chart-view-model-chrome gridlines, axis/category labels
|
|
24748
|
+
* chart-legend-build legend entries (colour/line swatch) + anchor position
|
|
24313
24749
|
* chart-view-model-bars bar / column rects, line points
|
|
24314
24750
|
* chart-view-model-points pie, scatter, bubble, radar geometry
|
|
24315
24751
|
* chart-view-model-kinds supported kinds + preserveAspectRatio
|
|
@@ -25487,27 +25923,36 @@ function setChartCellValue(data, seriesIndex, categoryIndex, raw) {
|
|
|
25487
25923
|
* Hide a currently-visible series (PowerPoint's Chart Filters "uncheck a
|
|
25488
25924
|
* series"): moves it out of {@link PptxChartData.series} into
|
|
25489
25925
|
* {@link PptxChartData.filteredSeries}, keeping its data and identity. The
|
|
25490
|
-
* chart must keep at least one visible series.
|
|
25491
|
-
*
|
|
25492
|
-
*
|
|
25926
|
+
* chart must keep at least one visible series.
|
|
25927
|
+
*
|
|
25928
|
+
* The new filtered entry's `idx`/`order` reuse the series' OWN `c:idx` when
|
|
25929
|
+
* it has one (every real OOXML `c:ser` does): this value is what
|
|
25930
|
+
* {@link restoreFilteredSeries} and the Chart Filters popover
|
|
25931
|
+
* (`chart-quick-actions.ts`'s `buildFilters`) key a series' position on, so a
|
|
25932
|
+
* chart with non-sequential idx values (a filtered series in the original
|
|
25933
|
+
* file, or one authored idx 1/2 rather than 0/1, both real-world cases)
|
|
25934
|
+
* keeps its true identity instead of being renumbered to "the smallest idx
|
|
25935
|
+
* not yet taken," which used to reorder the Chart Filters list and lose a
|
|
25936
|
+
* restored series' original position. Only a source series with no idx at
|
|
25937
|
+
* all (a synthetic/malformed chart) falls back to that smallest-free-slot
|
|
25938
|
+
* scheme, to guarantee every idx in the combined visible+filtered set stays
|
|
25939
|
+
* distinct.
|
|
25493
25940
|
*/
|
|
25494
25941
|
function hideChartSeries(data, seriesIndex) {
|
|
25495
25942
|
if (data.series.length <= 1 || seriesIndex < 0 || seriesIndex >= data.series.length) {
|
|
25496
25943
|
return null;
|
|
25497
25944
|
}
|
|
25498
25945
|
const series = data.series[seriesIndex];
|
|
25499
|
-
|
|
25500
|
-
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
|
|
25506
|
-
|
|
25507
|
-
|
|
25508
|
-
|
|
25509
|
-
while (takenIndices.has(idx)) {
|
|
25510
|
-
idx++;
|
|
25946
|
+
let idx = series.idx;
|
|
25947
|
+
if (idx === undefined) {
|
|
25948
|
+
const taken = new Set([
|
|
25949
|
+
...data.series.map((_, i) => i).filter((i) => i !== seriesIndex),
|
|
25950
|
+
...(data.filteredSeries ?? []).map((f) => f.idx),
|
|
25951
|
+
]);
|
|
25952
|
+
idx = 0;
|
|
25953
|
+
while (taken.has(idx)) {
|
|
25954
|
+
idx++;
|
|
25955
|
+
}
|
|
25511
25956
|
}
|
|
25512
25957
|
const filteredEntry = {
|
|
25513
25958
|
idx,
|
|
@@ -25516,6 +25961,11 @@ function hideChartSeries(data, seriesIndex) {
|
|
|
25516
25961
|
categories: data.categories,
|
|
25517
25962
|
values: series.values,
|
|
25518
25963
|
...(series.uniqueId ? { uniqueId: series.uniqueId } : {}),
|
|
25964
|
+
// Full snapshot so restoreFilteredSeries can bring back everything
|
|
25965
|
+
// (fill/line formatting, marker, data labels, ...), not just name and
|
|
25966
|
+
// values: this used to silently reset a series with a picture fill
|
|
25967
|
+
// or any other non-default formatting to plain defaults on restore.
|
|
25968
|
+
originalSeries: series,
|
|
25519
25969
|
};
|
|
25520
25970
|
return {
|
|
25521
25971
|
...data,
|
|
@@ -25526,11 +25976,15 @@ function hideChartSeries(data, seriesIndex) {
|
|
|
25526
25976
|
/**
|
|
25527
25977
|
* Restore a series PowerPoint's Chart Filters hid (the reverse of
|
|
25528
25978
|
* {@link hideChartSeries}): moves it from {@link PptxChartData.filteredSeries}
|
|
25529
|
-
* back into {@link PptxChartData.series}
|
|
25530
|
-
*
|
|
25531
|
-
*
|
|
25532
|
-
*
|
|
25533
|
-
*
|
|
25979
|
+
* back into {@link PptxChartData.series}, reinserted at the position its
|
|
25980
|
+
* `idx` puts it among the currently-visible series (not always appended
|
|
25981
|
+
* last, which used to leave a restored series permanently reordered to the
|
|
25982
|
+
* end of the chart, its legend, and its plot/stacking order). When the
|
|
25983
|
+
* filtered entry itself carries no cached name (PowerPoint auto-generated
|
|
25984
|
+
* one, e.g. "Series 3"), falls back to {@link PptxChartData.filteredSeriesTitle};
|
|
25985
|
+
* when it carries no cached categories, falls back to
|
|
25986
|
+
* {@link PptxChartData.filteredCategoryTitle} or the chart's current
|
|
25987
|
+
* categories.
|
|
25534
25988
|
*/
|
|
25535
25989
|
function restoreFilteredSeries(data, filteredIndex) {
|
|
25536
25990
|
const filtered = data.filteredSeries?.[filteredIndex];
|
|
@@ -25542,15 +25996,33 @@ function restoreFilteredSeries(data, filteredIndex) {
|
|
|
25542
25996
|
const values = filtered.values ??
|
|
25543
25997
|
(filtered.categories ?? data.filteredCategoryTitle)?.map(() => 0) ??
|
|
25544
25998
|
categories.map(() => 0);
|
|
25545
|
-
|
|
25546
|
-
|
|
25547
|
-
|
|
25548
|
-
|
|
25549
|
-
|
|
25999
|
+
// A series hidden by hideChartSeries this session carries a full
|
|
26000
|
+
// originalSeries snapshot: use it as-is (fill/line formatting, marker,
|
|
26001
|
+
// data labels, everything) rather than the bare name/values reconstruction
|
|
26002
|
+
// below, which used to reset a series with a picture fill or any other
|
|
26003
|
+
// non-default formatting to plain defaults on restore. A filtered series
|
|
26004
|
+
// that came from parsing the file (already hidden when opened) has no
|
|
26005
|
+
// snapshot to draw on, since the parser only reads name/cat/val/uniqueId
|
|
26006
|
+
// off the real c15:ser node; that case keeps the bare reconstruction.
|
|
26007
|
+
const restored = filtered.originalSeries
|
|
26008
|
+
? { ...filtered.originalSeries, idx: filtered.idx }
|
|
26009
|
+
: {
|
|
26010
|
+
name,
|
|
26011
|
+
values,
|
|
26012
|
+
idx: filtered.idx,
|
|
26013
|
+
...(filtered.uniqueId ? { uniqueId: filtered.uniqueId } : {}),
|
|
26014
|
+
};
|
|
26015
|
+
// The first currently-visible series whose own idx sorts after the
|
|
26016
|
+
// restored one; splicing in there reproduces its original relative
|
|
26017
|
+
// position. A series with no idx info at all falls back to its array
|
|
26018
|
+
// position, matching hideChartSeries's own fallback.
|
|
26019
|
+
const insertAt = data.series.findIndex((s, i) => (s.idx ?? i) > filtered.idx);
|
|
26020
|
+
const nextSeries = [...data.series];
|
|
26021
|
+
nextSeries.splice(insertAt === -1 ? nextSeries.length : insertAt, 0, restored);
|
|
25550
26022
|
const remainingFiltered = (data.filteredSeries ?? []).filter((_, i) => i !== filteredIndex);
|
|
25551
26023
|
return {
|
|
25552
26024
|
...data,
|
|
25553
|
-
series:
|
|
26025
|
+
series: nextSeries,
|
|
25554
26026
|
...(remainingFiltered.length > 0
|
|
25555
26027
|
? { filteredSeries: remainingFiltered }
|
|
25556
26028
|
: { filteredSeries: undefined }),
|
|
@@ -25581,6 +26053,248 @@ function setDataLabelsRangeCache(series, pointIndex, text) {
|
|
|
25581
26053
|
};
|
|
25582
26054
|
}
|
|
25583
26055
|
|
|
26056
|
+
/** The curated preset list: (id, label key, source `c:style/@val`). */
|
|
26057
|
+
const CHART_STYLE_PRESETS = [
|
|
26058
|
+
{ id: 'colorful', labelKey: 'pptx.chart.styleColorful', styleId: 2 },
|
|
26059
|
+
{ id: 'monochrome', labelKey: 'pptx.chart.styleMonochrome', styleId: 10 },
|
|
26060
|
+
{ id: 'colorfulLight', labelKey: 'pptx.chart.styleColorfulLight', styleId: 18 },
|
|
26061
|
+
{ id: 'colorfulDark', labelKey: 'pptx.chart.styleColorfulDark', styleId: 26 },
|
|
26062
|
+
{ id: 'mutedDark', labelKey: 'pptx.chart.styleMutedDark', styleId: 34 },
|
|
26063
|
+
{ id: 'pastel', labelKey: 'pptx.chart.stylePastel', styleId: 42 },
|
|
26064
|
+
];
|
|
26065
|
+
function paletteEquals(a, b) {
|
|
26066
|
+
if (!b || a.length !== b.length) {
|
|
26067
|
+
return false;
|
|
26068
|
+
}
|
|
26069
|
+
return a.every((c, i) => c.toLowerCase() === b[i]?.toLowerCase());
|
|
26070
|
+
}
|
|
26071
|
+
/** Resolve the id -> styleId map entry, or `undefined` for an unknown id. */
|
|
26072
|
+
function findPreset(id) {
|
|
26073
|
+
return CHART_STYLE_PRESETS.find((p) => p.id === id);
|
|
26074
|
+
}
|
|
26075
|
+
/**
|
|
26076
|
+
* Build the "Chart Styles" gallery descriptor for the currently-selected
|
|
26077
|
+
* chart: every preset's resolved colours plus whether it is the one
|
|
26078
|
+
* currently applied (its resolved palette matches `chartData.colorPalette`).
|
|
26079
|
+
*/
|
|
26080
|
+
function buildChartStylePresets(chartData) {
|
|
26081
|
+
return CHART_STYLE_PRESETS.map(({ id, labelKey, styleId }) => {
|
|
26082
|
+
const colors = getChartStylePalette(styleId);
|
|
26083
|
+
return {
|
|
26084
|
+
id,
|
|
26085
|
+
labelKey,
|
|
26086
|
+
colors,
|
|
26087
|
+
applied: paletteEquals(colors, chartData.colorPalette),
|
|
26088
|
+
};
|
|
26089
|
+
});
|
|
26090
|
+
}
|
|
26091
|
+
/** Drop a data point's own fill override, keeping every other field. */
|
|
26092
|
+
function clearDataPointFill(point) {
|
|
26093
|
+
const { spPr, marker, ...rest } = point;
|
|
26094
|
+
const nextSpPr = spPr && { ...spPr, fillColor: undefined };
|
|
26095
|
+
const nextMarker = marker?.spPr && {
|
|
26096
|
+
...marker,
|
|
26097
|
+
spPr: { ...marker.spPr, fillColor: undefined },
|
|
26098
|
+
};
|
|
26099
|
+
return { ...rest, ...(spPr && { spPr: nextSpPr }), ...(nextMarker && { marker: nextMarker }) };
|
|
26100
|
+
}
|
|
26101
|
+
/** Drop a series' own colour overrides (and its points'), keeping shape/symbol/size. */
|
|
26102
|
+
function clearSeriesColor(series) {
|
|
26103
|
+
const { color: _color, marker, dataPoints, ...rest } = series;
|
|
26104
|
+
const nextMarker = marker?.spPr && { ...marker, spPr: { ...marker.spPr, fillColor: undefined } };
|
|
26105
|
+
return {
|
|
26106
|
+
...rest,
|
|
26107
|
+
...(marker && { marker: nextMarker ?? marker }),
|
|
26108
|
+
...(dataPoints && { dataPoints: dataPoints.map(clearDataPointFill) }),
|
|
26109
|
+
};
|
|
26110
|
+
}
|
|
26111
|
+
/**
|
|
26112
|
+
* Apply a "Chart Styles" preset by id: resolves its palette via
|
|
26113
|
+
* `getChartStylePalette`, writes it onto `colorPalette` (also mirroring
|
|
26114
|
+
* `style.styleId`), and clears every series'/data-point's own explicit fill
|
|
26115
|
+
* so the new palette is not immediately shadowed by pre-existing colours
|
|
26116
|
+
* (see this module's header). Returns `null` for an unknown preset id so a
|
|
26117
|
+
* caller can no-op instead of silently clearing the chart's palette.
|
|
26118
|
+
*/
|
|
26119
|
+
function applyChartStylePreset(chartData, presetId) {
|
|
26120
|
+
const preset = findPreset(presetId);
|
|
26121
|
+
if (!preset) {
|
|
26122
|
+
return null;
|
|
26123
|
+
}
|
|
26124
|
+
const colors = [...getChartStylePalette(preset.styleId)];
|
|
26125
|
+
return {
|
|
26126
|
+
...chartData,
|
|
26127
|
+
colorPalette: colors,
|
|
26128
|
+
style: { ...chartData.style, styleId: preset.styleId },
|
|
26129
|
+
series: chartData.series.map(clearSeriesColor),
|
|
26130
|
+
};
|
|
26131
|
+
}
|
|
26132
|
+
|
|
26133
|
+
const AXIS_TYPES_WITH_TITLES = [
|
|
26134
|
+
'catAx',
|
|
26135
|
+
'valAx',
|
|
26136
|
+
'dateAx',
|
|
26137
|
+
];
|
|
26138
|
+
/** Default axis title text PowerPoint inserts when a title is turned on with none set. */
|
|
26139
|
+
const DEFAULT_AXIS_TITLE_TEXT = 'Axis Title';
|
|
26140
|
+
/**
|
|
26141
|
+
* Set/clear `deleted` on every category/value/date axis (PowerPoint's "Axes"
|
|
26142
|
+
* checklist item hides the whole axis, not just its gridlines/title).
|
|
26143
|
+
*/
|
|
26144
|
+
function chartAxesVisibilityPatch(chartData, show) {
|
|
26145
|
+
const axes = (chartData.axes ?? []).map((axis) => AXIS_TYPES_WITH_TITLES.includes(axis.axisType) ? { ...axis, deleted: !show } : axis);
|
|
26146
|
+
return { axes };
|
|
26147
|
+
}
|
|
26148
|
+
/** Whether at least one axis is currently visible (not `deleted`). */
|
|
26149
|
+
function chartAxesVisibilityState(chartData) {
|
|
26150
|
+
const relevant = (chartData.axes ?? []).filter((axis) => AXIS_TYPES_WITH_TITLES.includes(axis.axisType));
|
|
26151
|
+
return relevant.length === 0 || relevant.some((axis) => !axis.deleted);
|
|
26152
|
+
}
|
|
26153
|
+
/**
|
|
26154
|
+
* Set/clear `titleText` on every category/value axis. Turning off blanks the
|
|
26155
|
+
* text (PowerPoint's own "None" removes the title box entirely, which this
|
|
26156
|
+
* codebase models as absent/empty `titleText`, see `chart-axis-render.ts`);
|
|
26157
|
+
* turning on with no existing text fills in the same placeholder PowerPoint
|
|
26158
|
+
* inserts ("Axis Title").
|
|
26159
|
+
*/
|
|
26160
|
+
function chartAxisTitlesPatch(chartData, show) {
|
|
26161
|
+
const axes = (chartData.axes ?? []).map((axis) => {
|
|
26162
|
+
if (!AXIS_TYPES_WITH_TITLES.includes(axis.axisType)) {
|
|
26163
|
+
return axis;
|
|
26164
|
+
}
|
|
26165
|
+
if (!show) {
|
|
26166
|
+
return { ...axis, titleText: undefined };
|
|
26167
|
+
}
|
|
26168
|
+
return { ...axis, titleText: axis.titleText || DEFAULT_AXIS_TITLE_TEXT };
|
|
26169
|
+
});
|
|
26170
|
+
return { axes };
|
|
26171
|
+
}
|
|
26172
|
+
/** Whether at least one axis currently shows a non-empty title. */
|
|
26173
|
+
function chartAxisTitlesState(chartData) {
|
|
26174
|
+
return (chartData.axes ?? []).some((axis) => AXIS_TYPES_WITH_TITLES.includes(axis.axisType) && Boolean(axis.titleText));
|
|
26175
|
+
}
|
|
26176
|
+
/**
|
|
26177
|
+
* Apply one "Chart Elements" checklist toggle. The single entry point every
|
|
26178
|
+
* binding's quick-action popover calls on click; see this module's header.
|
|
26179
|
+
*/
|
|
26180
|
+
function applyChartElementToggle(chartData, key, checked) {
|
|
26181
|
+
switch (key) {
|
|
26182
|
+
case 'title':
|
|
26183
|
+
return patchChartData$1(chartData, { style: { ...chartData.style, hasTitle: checked } });
|
|
26184
|
+
case 'legend':
|
|
26185
|
+
return patchChartData$1(chartData, { style: { ...chartData.style, hasLegend: checked } });
|
|
26186
|
+
case 'dataLabels':
|
|
26187
|
+
return patchChartData$1(chartData, { style: { ...chartData.style, hasDataLabels: checked } });
|
|
26188
|
+
case 'gridlines':
|
|
26189
|
+
return patchChartData$1(chartData, chartGridlinesPatch(chartData, checked));
|
|
26190
|
+
case 'axes':
|
|
26191
|
+
return patchChartData$1(chartData, chartAxesVisibilityPatch(chartData, checked));
|
|
26192
|
+
case 'axisTitles':
|
|
26193
|
+
return patchChartData$1(chartData, chartAxisTitlesPatch(chartData, checked));
|
|
26194
|
+
default:
|
|
26195
|
+
return chartData;
|
|
26196
|
+
}
|
|
26197
|
+
}
|
|
26198
|
+
|
|
26199
|
+
/** Button edge length, in the same units as the caller's `selectionBox`. */
|
|
26200
|
+
const CHART_QUICK_ACTION_BUTTON_SIZE = 24;
|
|
26201
|
+
/** Gap between stacked buttons. */
|
|
26202
|
+
const BUTTON_GAP = 6;
|
|
26203
|
+
/** Gap between the chart's selection box and the button column, matching PowerPoint's offset. */
|
|
26204
|
+
const OUTSIDE_GAP = 8;
|
|
26205
|
+
const BUTTON_ORDER = [
|
|
26206
|
+
{ id: 'elements', labelKey: 'pptx.chart.quickElements' },
|
|
26207
|
+
{ id: 'styles', labelKey: 'pptx.chart.quickStyles' },
|
|
26208
|
+
{ id: 'filters', labelKey: 'pptx.chart.quickFilters' },
|
|
26209
|
+
];
|
|
26210
|
+
function buildButtons(selectionBox, showFilters) {
|
|
26211
|
+
const x = selectionBox.x + selectionBox.width + OUTSIDE_GAP;
|
|
26212
|
+
return BUTTON_ORDER.filter((entry) => entry.id !== 'filters' || showFilters).map((entry, i) => ({
|
|
26213
|
+
...entry,
|
|
26214
|
+
x,
|
|
26215
|
+
y: selectionBox.y + i * (CHART_QUICK_ACTION_BUTTON_SIZE + BUTTON_GAP),
|
|
26216
|
+
size: CHART_QUICK_ACTION_BUTTON_SIZE,
|
|
26217
|
+
}));
|
|
26218
|
+
}
|
|
26219
|
+
function buildElements(chartData) {
|
|
26220
|
+
const items = [
|
|
26221
|
+
{ key: 'title', labelKey: 'pptx.chart.showTitle', checked: chartData.style?.hasTitle ?? false },
|
|
26222
|
+
{
|
|
26223
|
+
key: 'legend',
|
|
26224
|
+
labelKey: 'pptx.chart.showLegend',
|
|
26225
|
+
checked: chartData.style?.hasLegend ?? false,
|
|
26226
|
+
},
|
|
26227
|
+
{
|
|
26228
|
+
key: 'gridlines',
|
|
26229
|
+
labelKey: 'pptx.chart.showGridlines',
|
|
26230
|
+
checked: chartGridlinesState(chartData),
|
|
26231
|
+
},
|
|
26232
|
+
{
|
|
26233
|
+
key: 'dataLabels',
|
|
26234
|
+
labelKey: 'pptx.chart.showDataLabels',
|
|
26235
|
+
checked: chartData.style?.hasDataLabels ?? false,
|
|
26236
|
+
},
|
|
26237
|
+
];
|
|
26238
|
+
if (chartData.axes && chartData.axes.length > 0) {
|
|
26239
|
+
items.push({
|
|
26240
|
+
key: 'axes',
|
|
26241
|
+
labelKey: 'pptx.chart.quickAxes',
|
|
26242
|
+
checked: chartAxesVisibilityState(chartData),
|
|
26243
|
+
});
|
|
26244
|
+
items.push({
|
|
26245
|
+
key: 'axisTitles',
|
|
26246
|
+
labelKey: 'pptx.chart.quickAxisTitles',
|
|
26247
|
+
checked: chartAxisTitlesState(chartData),
|
|
26248
|
+
});
|
|
26249
|
+
}
|
|
26250
|
+
return items;
|
|
26251
|
+
}
|
|
26252
|
+
function buildFilters(chartData) {
|
|
26253
|
+
const visibleSeries = chartData.series.map((series, index) => ({
|
|
26254
|
+
key: `visible-${index}`,
|
|
26255
|
+
name: series.name,
|
|
26256
|
+
visible: true,
|
|
26257
|
+
seriesIndex: index,
|
|
26258
|
+
sortKey: series.idx ?? index,
|
|
26259
|
+
}));
|
|
26260
|
+
const filtered = (chartData.filteredSeries ?? []).map((entry, index) => ({
|
|
26261
|
+
key: `filtered-${index}`,
|
|
26262
|
+
name: entry.name ?? chartData.filteredSeriesTitle ?? `Series ${index + 1}`,
|
|
26263
|
+
visible: false,
|
|
26264
|
+
filteredIndex: index,
|
|
26265
|
+
sortKey: entry.idx,
|
|
26266
|
+
}));
|
|
26267
|
+
// PowerPoint's Chart Filters list never reorders rows as they're (un)checked:
|
|
26268
|
+
// every series keeps its original position, keyed by its persistent c:idx
|
|
26269
|
+
// (falling back to array position for a chart with no idx info at all),
|
|
26270
|
+
// not by which of the two underlying arrays (series/filteredSeries)
|
|
26271
|
+
// currently holds it. Concatenating visible-then-filtered instead made a
|
|
26272
|
+
// hidden series jump to the bottom of the list (reported as "unticking
|
|
26273
|
+
// some of them changes the ordering").
|
|
26274
|
+
const series = [...visibleSeries, ...filtered]
|
|
26275
|
+
.sort((a, b) => a.sortKey - b.sortKey)
|
|
26276
|
+
.map(({ sortKey: _sortKey, ...rest }) => rest);
|
|
26277
|
+
return { visible: series.length > 1, series };
|
|
26278
|
+
}
|
|
26279
|
+
/**
|
|
26280
|
+
* Build the quick-action descriptor for the currently-selected element.
|
|
26281
|
+
* Returns `null` when the quick actions should not render (nothing selected,
|
|
26282
|
+
* the selection is not a chart, or the chart has no data yet).
|
|
26283
|
+
*/
|
|
26284
|
+
function buildChartQuickActionsDescriptor(input) {
|
|
26285
|
+
if (!input.isChartSelected || !input.chartData) {
|
|
26286
|
+
return null;
|
|
26287
|
+
}
|
|
26288
|
+
const { chartData, selectionBox } = input;
|
|
26289
|
+
const filters = buildFilters(chartData);
|
|
26290
|
+
return {
|
|
26291
|
+
buttons: buildButtons(selectionBox, filters.visible),
|
|
26292
|
+
elements: buildElements(chartData),
|
|
26293
|
+
filters,
|
|
26294
|
+
styles: { presets: buildChartStylePresets(chartData) },
|
|
26295
|
+
};
|
|
26296
|
+
}
|
|
26297
|
+
|
|
25584
26298
|
const TWO_PI = Math.PI * 2;
|
|
25585
26299
|
/** Keep the dragged span strictly inside (0, 2*PI) so the share never hits 0 or 1. */
|
|
25586
26300
|
const ANGLE_EPSILON = TWO_PI * 0.001;
|
|
@@ -25684,9 +26398,12 @@ function buildStackedDragGeometry(element, chartData, seriesIndex, pointIndex) {
|
|
|
25684
26398
|
if (!draggedValues || pointIndex < 0 || pointIndex >= draggedValues.length) {
|
|
25685
26399
|
return null;
|
|
25686
26400
|
}
|
|
25687
|
-
const percent = grouping === 'percentStacked', catCount = Math.max(chartData.categories.length, ...seriesValues.map((values) => values.length), pointIndex + 1), plots = computeStackedSeriesPlots(seriesValues, catCount, percent), baseValue = plots[seriesIndex]?.base[pointIndex] ?? 0, otherAbsSum = seriesValues.reduce((sum, values, si) => (si === seriesIndex ? sum : sum + Math.abs(values[pointIndex] ?? 0)), 0),
|
|
26401
|
+
const percent = grouping === 'percentStacked', catCount = Math.max(chartData.categories.length, ...seriesValues.map((values) => values.length), pointIndex + 1), plots = computeStackedSeriesPlots(seriesValues, catCount, percent), baseValue = plots[seriesIndex]?.base[pointIndex] ?? 0, otherAbsSum = seriesValues.reduce((sum, values, si) => (si === seriesIndex ? sum : sum + Math.abs(values[pointIndex] ?? 0)), 0), layoutOpts = computeLayoutOptions(chartData.axes, chartData.dataTable, chartData.series.length), layout = computePlotLayout(element.width, element.height, chartData, true, layoutOpts),
|
|
26402
|
+
// Computed from the SAME `layout.plotHeight` `chart-cartesian.ts`'s
|
|
26403
|
+
// `stackedRange` uses, so a drag never disagrees with what is on screen.
|
|
26404
|
+
range = percent
|
|
25688
26405
|
? { min: 0, max: 100, span: 100 }
|
|
25689
|
-
: computeStackedValueRange(chartData.series, catCount
|
|
26406
|
+
: computeStackedValueRange(chartData.series, catCount, layout.plotHeight);
|
|
25690
26407
|
return {
|
|
25691
26408
|
range,
|
|
25692
26409
|
plotTop: layout.plotTop,
|
|
@@ -48661,6 +49378,17 @@ const translationsEn = {
|
|
|
48661
49378
|
'pptx.chart.chartFilters': 'Chart filters',
|
|
48662
49379
|
'pptx.chart.hideSeries': 'Hide {{name}}',
|
|
48663
49380
|
'pptx.chart.showSeries': 'Show {{name}}',
|
|
49381
|
+
'pptx.chart.quickElements': 'Chart Elements',
|
|
49382
|
+
'pptx.chart.quickStyles': 'Chart Styles',
|
|
49383
|
+
'pptx.chart.quickFilters': 'Chart Filters',
|
|
49384
|
+
'pptx.chart.quickAxes': 'Axes',
|
|
49385
|
+
'pptx.chart.quickAxisTitles': 'Axis Titles',
|
|
49386
|
+
'pptx.chart.styleColorful': 'Colorful',
|
|
49387
|
+
'pptx.chart.styleMonochrome': 'Monochrome',
|
|
49388
|
+
'pptx.chart.styleColorfulLight': 'Colorful Light',
|
|
49389
|
+
'pptx.chart.styleColorfulDark': 'Colorful Dark',
|
|
49390
|
+
'pptx.chart.styleMutedDark': 'Muted Dark',
|
|
49391
|
+
'pptx.chart.stylePastel': 'Pastel',
|
|
48664
49392
|
'pptx.chart.valueFromCells': 'Value from cells: {{name}}',
|
|
48665
49393
|
'pptx.chart.removeCategory': 'Remove category',
|
|
48666
49394
|
'pptx.chart.axes': 'Axes',
|
|
@@ -77006,7 +77734,9 @@ function buildSurfaceChart3DData(chartData, categoryLabels, options) {
|
|
|
77006
77734
|
if (seriesCount === 0 || catCount === 0) {
|
|
77007
77735
|
return null;
|
|
77008
77736
|
}
|
|
77009
|
-
|
|
77737
|
+
// See the matching comment in `bar-chart-3d-data.ts`: the element height
|
|
77738
|
+
// stands in for a plot-area height this 3D scene doesn't separately compute.
|
|
77739
|
+
const range = computeValueRange(chartData.series, options.height);
|
|
77010
77740
|
const heightMap = new Float32Array(seriesCount * catCount);
|
|
77011
77741
|
const colorMap = new Float32Array(seriesCount * catCount * 3);
|
|
77012
77742
|
// Raw (un-normalised) values, kept alongside the [0,1] heightMap so the
|
|
@@ -78147,11 +78877,16 @@ function buildBarChart3DData(chartData, categoryLabels, options) {
|
|
|
78147
78877
|
const isPercent = grouping === 'percentStacked';
|
|
78148
78878
|
const horizontal = chartData.barDirection === 'bar';
|
|
78149
78879
|
const points = buildPoints$1(chartData, cols, isPercent);
|
|
78880
|
+
// No separate plot-area inset exists for the 3D scene the way the flat SVG
|
|
78881
|
+
// engine's `computePlotLayout` reserves one, so the full element height
|
|
78882
|
+
// stands in for it; see `axisTargetIntervals`'s doc comment for why this
|
|
78883
|
+
// only makes the automatic scale saturate at its gridline cap slightly
|
|
78884
|
+
// sooner; it does not change the "ordinary chart" result this was fixed for.
|
|
78150
78885
|
const range = isPercent
|
|
78151
78886
|
? { min: 0, max: 100, span: 100 }
|
|
78152
78887
|
: grouping === 'stacked'
|
|
78153
|
-
? computeStackedValueRange(chartData.series, cols)
|
|
78154
|
-
: computeValueRange(chartData.series);
|
|
78888
|
+
? computeStackedValueRange(chartData.series, cols, options.height)
|
|
78889
|
+
: computeValueRange(chartData.series, options.height);
|
|
78155
78890
|
const depthPercent = chartData.view3D?.depthPercent;
|
|
78156
78891
|
const boxes = layoutBarChart3D(points, cols, rows, range, grouping, depthPercent, horizontal);
|
|
78157
78892
|
return {
|
|
@@ -78338,7 +79073,9 @@ function buildCartesianLine3DSceneData(chartData, categoryLabels, options) {
|
|
|
78338
79073
|
return null;
|
|
78339
79074
|
}
|
|
78340
79075
|
const points = buildPoints(chartData, cols);
|
|
78341
|
-
|
|
79076
|
+
// See the matching comment in `bar-chart-3d-data.ts`: the element height
|
|
79077
|
+
// stands in for a plot-area height this 3D scene doesn't separately compute.
|
|
79078
|
+
const range = computeValueRange(chartData.series, options.height);
|
|
78342
79079
|
const depthPercent = chartData.view3D?.depthPercent;
|
|
78343
79080
|
const series = layoutCartesianLine3DSeries(points, cols, rows, range, depthPercent);
|
|
78344
79081
|
return {
|
|
@@ -103932,6 +104669,26 @@ function segmentFrameCount(durationMs, fps) {
|
|
|
103932
104669
|
}
|
|
103933
104670
|
return Math.max(1, Math.ceil(durationMs / (1000 / fps)));
|
|
103934
104671
|
}
|
|
104672
|
+
/**
|
|
104673
|
+
* Stop every track on a `canvas.captureStream()` `MediaStream`.
|
|
104674
|
+
*
|
|
104675
|
+
* `MediaRecorder.stop()` only stops the recorder; the underlying capture
|
|
104676
|
+
* stream it was constructed from keeps its track live and compositing frames
|
|
104677
|
+
* from the source canvas indefinitely otherwise. Every binding's `recordWebm`
|
|
104678
|
+
* driver must call this once recording is done - on success, on a
|
|
104679
|
+
* `MediaRecorder` error, and on `AbortSignal` cancellation alike - or each
|
|
104680
|
+
* video export leaks one live capture track for the rest of the page's
|
|
104681
|
+
* lifetime, degrading a session that exports more than once. Pass
|
|
104682
|
+
* `recorder.stream` (every `MediaRecorder` exposes the stream it was built
|
|
104683
|
+
* from) so callers need not keep a separate reference to the stream.
|
|
104684
|
+
*
|
|
104685
|
+
* @param stream - The `MediaStream` to stop, typically `recorder.stream`.
|
|
104686
|
+
*/
|
|
104687
|
+
function stopCaptureStream(stream) {
|
|
104688
|
+
for (const track of stream.getTracks()) {
|
|
104689
|
+
track.stop();
|
|
104690
|
+
}
|
|
104691
|
+
}
|
|
103935
104692
|
|
|
103936
104693
|
/**
|
|
103937
104694
|
* CRC-32 (PNG's checksum, ISO 3309 / ITU-T V.42 polynomial 0xEDB88320) pure
|
|
@@ -106873,7 +107630,7 @@ function createLocalStorageBackend(namespace) {
|
|
|
106873
107630
|
/** Try IndexedDB first; fall back to localStorage on any failure. */
|
|
106874
107631
|
async function resolveBackend(dbName, namespace) {
|
|
106875
107632
|
try {
|
|
106876
|
-
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-
|
|
107633
|
+
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-amzrYVHP.mjs');
|
|
106877
107634
|
const db = await openChatDb(dbName);
|
|
106878
107635
|
return createIdbBackend(db);
|
|
106879
107636
|
}
|
|
@@ -117545,6 +118302,457 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
117545
118302
|
type: Injectable
|
|
117546
118303
|
}] });
|
|
117547
118304
|
|
|
118305
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118306
|
+
// Direct part-editing gate
|
|
118307
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118308
|
+
/**
|
|
118309
|
+
* May this chart's individual parts (title, series, data points) be entered
|
|
118310
|
+
* for direct on-canvas editing?
|
|
118311
|
+
*
|
|
118312
|
+
* Pure so it is unit-testable without a full Angular injection context:
|
|
118313
|
+
* `ChartElementViewComponent`'s constructor runs an `effect()` that needs a
|
|
118314
|
+
* `ChangeDetectionScheduler` this package's TestBed-free suite doesn't
|
|
118315
|
+
* provide. G8: `a:graphicFrameLocks/@noDrilldown` forbids the drill-down,
|
|
118316
|
+
* even when the chart is otherwise selected + editable.
|
|
118317
|
+
*/
|
|
118318
|
+
function chartCanEditParts(editable, isSelected, hasEditor, element) {
|
|
118319
|
+
return editable && isSelected && hasEditor && canDrillDown(element);
|
|
118320
|
+
}
|
|
118321
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118322
|
+
// Value-drag commit gate
|
|
118323
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118324
|
+
/**
|
|
118325
|
+
* The chart data a finished drag should commit, or null when nothing should
|
|
118326
|
+
* be committed (cancelled, or the press never became a drag).
|
|
118327
|
+
*/
|
|
118328
|
+
function chartDragCommitData(session, commit) {
|
|
118329
|
+
if (!commit || !session?.moved) {
|
|
118330
|
+
return null;
|
|
118331
|
+
}
|
|
118332
|
+
return session.lastData;
|
|
118333
|
+
}
|
|
118334
|
+
/**
|
|
118335
|
+
* Same gate as {@link chartDragCommitData}, for a pie/radar/stacked mark drag
|
|
118336
|
+
* ({@link ChartMarkDragState} carries the same `moved`/`lastData` shape as the
|
|
118337
|
+
* cartesian value-drag session, just resolved through a different geometry).
|
|
118338
|
+
*/
|
|
118339
|
+
function chartMarkDragCommitData(session, commit) {
|
|
118340
|
+
if (!commit || !session?.moved) {
|
|
118341
|
+
return null;
|
|
118342
|
+
}
|
|
118343
|
+
return session.lastData;
|
|
118344
|
+
}
|
|
118345
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118346
|
+
// 3D value-drag commit data (bar3D/line3D/area3D/pie3D/surface3D: no drag
|
|
118347
|
+
// state machine of their own, the shared three.js scene reports the
|
|
118348
|
+
// live/final value directly)
|
|
118349
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118350
|
+
/**
|
|
118351
|
+
* The chart data a 3D scene's `onValueDragPreview`/`onValueDragCommit` should
|
|
118352
|
+
* apply, or `null` when the part carries no point index (a series-level part,
|
|
118353
|
+
* or a part shape the 3D scenes never actually emit for a value drag) or there
|
|
118354
|
+
* is no chart data to update against.
|
|
118355
|
+
*
|
|
118356
|
+
* Pure so it is unit-testable without a WebGL context: `BarChart3DRendererComponent`
|
|
118357
|
+
* / `LineChart3DRendererComponent` / `AreaChart3DRendererComponent` /
|
|
118358
|
+
* `PieChart3DRendererComponent` / `SurfaceChart3DRendererComponent` forward
|
|
118359
|
+
* their `valueDragPreview`/`valueDragCommit` outputs here via
|
|
118360
|
+
* `ChartElementViewComponent`.
|
|
118361
|
+
*/
|
|
118362
|
+
function chart3DPointValueUpdate(chartData, part, value) {
|
|
118363
|
+
if (!chartData || part.pointIndex === undefined) {
|
|
118364
|
+
return null;
|
|
118365
|
+
}
|
|
118366
|
+
return withChartPointValue(chartData, part.seriesIndex, part.pointIndex, value);
|
|
118367
|
+
}
|
|
118368
|
+
/**
|
|
118369
|
+
* Commit an on-canvas chart edit through the editor's normal element-update
|
|
118370
|
+
* path (the exact channel the inspector uses: one history snapshot per call).
|
|
118371
|
+
* No-op without an editor or when the element is not on any slide.
|
|
118372
|
+
*
|
|
118373
|
+
* `templateSlideId` is the id of the slide the hosting canvas renders (from
|
|
118374
|
+
* `SLIDE_CONTEXT`); it resolves template (master/layout) chart elements, which
|
|
118375
|
+
* live in the per-slide template store rather than in `slides[].elements`.
|
|
118376
|
+
*/
|
|
118377
|
+
function commitChartElementData(editor, elementId, chartData, templateSlideId) {
|
|
118378
|
+
if (!editor) {
|
|
118379
|
+
return;
|
|
118380
|
+
}
|
|
118381
|
+
const slideIndex = findOwningSlideIndex(editor.slides(), elementId, templateSlideId);
|
|
118382
|
+
if (slideIndex < 0) {
|
|
118383
|
+
return;
|
|
118384
|
+
}
|
|
118385
|
+
editor.updateElement(slideIndex, elementId, { chartData });
|
|
118386
|
+
}
|
|
118387
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118388
|
+
// Interaction stylesheet (shared base rules + Angular's own badge/editor CSS)
|
|
118389
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118390
|
+
const STYLE_ELEMENT_ID = 'pptx-ng-chart-interaction-styles';
|
|
118391
|
+
/**
|
|
118392
|
+
* Angular-only interaction CSS, injected once into `document.head` alongside
|
|
118393
|
+
* the shared `[data-chart-part]` / selected-mark rules from
|
|
118394
|
+
* `ensureChartInteractionStyles` (`pptx-viewer-shared`). Component styles are
|
|
118395
|
+
* view-encapsulated in Angular, so they cannot reach into the chart
|
|
118396
|
+
* renderer's SVG or style the badge/title-input this component projects
|
|
118397
|
+
* next to it.
|
|
118398
|
+
*/
|
|
118399
|
+
const INTERACTION_CSS = `
|
|
118400
|
+
.pptx-ng-chart-view { position: relative; width: 100%; height: 100%; }
|
|
118401
|
+
.pptx-ng-chart-drag-badge { position: absolute; top: 4px; right: 4px; z-index: 10; border-radius: 4px; background: rgba(37, 99, 235, 0.9); padding: 2px 6px; font-size: 10px; font-weight: 500; color: #fff; pointer-events: none; }
|
|
118402
|
+
.pptx-ng-chart-title-input { position: absolute; left: 50%; top: 2px; z-index: 10; width: 60%; transform: translateX(-50%); border: 1px solid #94a3b8; border-radius: 4px; background: #fff; padding: 2px 4px; text-align: center; font-size: 11px; color: #0f172a; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); }
|
|
118403
|
+
`;
|
|
118404
|
+
/**
|
|
118405
|
+
* Inject the interaction stylesheets for chart part hit targets: the shared
|
|
118406
|
+
* base rules (singleton, shared across all five bindings) plus Angular's own
|
|
118407
|
+
* badge/title-input CSS (singleton, this binding only).
|
|
118408
|
+
*/
|
|
118409
|
+
function ensureChartInteractionStyles() {
|
|
118410
|
+
ensureChartInteractionStyles$1();
|
|
118411
|
+
if (typeof document === 'undefined' || document.getElementById(STYLE_ELEMENT_ID)) {
|
|
118412
|
+
return;
|
|
118413
|
+
}
|
|
118414
|
+
const style = document.createElement('style');
|
|
118415
|
+
style.id = STYLE_ELEMENT_ID;
|
|
118416
|
+
style.textContent = INTERACTION_CSS;
|
|
118417
|
+
document.head.appendChild(style);
|
|
118418
|
+
}
|
|
118419
|
+
|
|
118420
|
+
/**
|
|
118421
|
+
* chart-quick-actions-overlay.component.ts: PowerPoint's three floating
|
|
118422
|
+
* quick-action icons ("Chart Elements" +, "Chart Styles" paintbrush, "Chart
|
|
118423
|
+
* Filters" funnel) shown just outside a selected chart's top-right corner.
|
|
118424
|
+
*
|
|
118425
|
+
* Selector: `pptx-chart-quick-actions-overlay`
|
|
118426
|
+
*
|
|
118427
|
+
* Mounted inside `SlideCanvasComponent`'s template as a sibling of the
|
|
118428
|
+
* resize/rotate/adjust handle blocks, inside the same scaled
|
|
118429
|
+
* `.pptx-ng-canvas-stage`. Angular has no CSS inverse-scale variable (unlike
|
|
118430
|
+
* React's `--pptx-handle-inverse-scale`): this component follows the SAME
|
|
118431
|
+
* convention `selection-geometry.ts`'s `computeHandleBoxes`/
|
|
118432
|
+
* `computeCornerHandle` already use, computing every screen-constant
|
|
118433
|
+
* quantity (button size, gaps) as `screenPx / zoom` while leaving the
|
|
118434
|
+
* element's own x/y/width/height in raw slide px.
|
|
118435
|
+
*
|
|
118436
|
+
* All decision-making (which buttons render, checklist/gallery/filter state)
|
|
118437
|
+
* comes from the shared `buildChartQuickActionsDescriptor`; this component
|
|
118438
|
+
* only renders three buttons plus whichever popover is open, and commits
|
|
118439
|
+
* through `commitChartElementData` (the same on-canvas commit helper
|
|
118440
|
+
* `ChartElementViewComponent` uses for mark-drag/inline-title edits), so
|
|
118441
|
+
* undo/redo and the save round-trip are shared with every other on-canvas
|
|
118442
|
+
* chart edit. See the shared module's header for scope notes (Series-only
|
|
118443
|
+
* Chart Filters, no trendline/error-bar/up-down-bars rows).
|
|
118444
|
+
*
|
|
118445
|
+
* @module angular-viewer/chart-quick-actions-overlay
|
|
118446
|
+
*/
|
|
118447
|
+
class ChartQuickActionsOverlayComponent {
|
|
118448
|
+
constructor() {
|
|
118449
|
+
this.element = input.required(/* @ts-ignore */
|
|
118450
|
+
...(ngDevMode ? [{ debugName: "element" }] : /* istanbul ignore next */ []));
|
|
118451
|
+
this.canEdit = input(true, /* @ts-ignore */
|
|
118452
|
+
...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
|
|
118453
|
+
/** Effective stage zoom, so screen-constant sizes/gaps divide it out. */
|
|
118454
|
+
this.zoom = input(1, /* @ts-ignore */
|
|
118455
|
+
...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
118456
|
+
this.CHART_QUICK_ACTION_BUTTON_SIZE = CHART_QUICK_ACTION_BUTTON_SIZE;
|
|
118457
|
+
this.editor = inject(EditorStateService, { optional: true });
|
|
118458
|
+
this.slideContext = inject(SLIDE_CONTEXT, { optional: true });
|
|
118459
|
+
this.hostRef = inject((ElementRef));
|
|
118460
|
+
this.open = signal(null, /* @ts-ignore */
|
|
118461
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
118462
|
+
/** The stage scale, guarded against 0. */
|
|
118463
|
+
this.scale = computed(() => this.zoom() || 1, /* @ts-ignore */
|
|
118464
|
+
...(ngDevMode ? [{ debugName: "scale" }] : /* istanbul ignore next */ []));
|
|
118465
|
+
/**
|
|
118466
|
+
* The descriptor's geometry, computed against a SCREEN-space box (the
|
|
118467
|
+
* element's box multiplied by the stage scale): every constant the shared
|
|
118468
|
+
* function adds (gaps, button size) is then correctly screen-constant, and
|
|
118469
|
+
* {@link screenPx} divides each resulting quantity back down to the slide
|
|
118470
|
+
* px this component renders in, mirroring `selection-geometry.ts`'s
|
|
118471
|
+
* `computeHandleBoxes`.
|
|
118472
|
+
*/
|
|
118473
|
+
this.descriptor = computed(() => {
|
|
118474
|
+
const el = this.element();
|
|
118475
|
+
const scale = this.scale();
|
|
118476
|
+
return buildChartQuickActionsDescriptor({
|
|
118477
|
+
isChartSelected: true,
|
|
118478
|
+
chartData: el.chartData,
|
|
118479
|
+
selectionBox: {
|
|
118480
|
+
x: el.x * scale,
|
|
118481
|
+
y: el.y * scale,
|
|
118482
|
+
width: el.width * scale,
|
|
118483
|
+
height: el.height * scale,
|
|
118484
|
+
},
|
|
118485
|
+
});
|
|
118486
|
+
}, /* @ts-ignore */
|
|
118487
|
+
...(ngDevMode ? [{ debugName: "descriptor" }] : /* istanbul ignore next */ []));
|
|
118488
|
+
}
|
|
118489
|
+
/** Close whichever popover is open on an outside click, mirroring every other binding's overlay. */
|
|
118490
|
+
onDocumentMouseDown(event) {
|
|
118491
|
+
if (this.open() && !this.hostRef.nativeElement.contains(event.target)) {
|
|
118492
|
+
this.open.set(null);
|
|
118493
|
+
}
|
|
118494
|
+
}
|
|
118495
|
+
/** Convert a screen-space quantity (from {@link descriptor}) back to slide px. */
|
|
118496
|
+
screenPx(value) {
|
|
118497
|
+
return value / this.scale();
|
|
118498
|
+
}
|
|
118499
|
+
toggle(id) {
|
|
118500
|
+
this.open.update((current) => (current === id ? null : id));
|
|
118501
|
+
}
|
|
118502
|
+
commit(chartData) {
|
|
118503
|
+
commitChartElementData(this.editor, this.element().id, chartData, this.slideContext?.slideId() ?? null);
|
|
118504
|
+
}
|
|
118505
|
+
onElementToggle(key, event) {
|
|
118506
|
+
const chartData = this.element().chartData;
|
|
118507
|
+
if (!chartData) {
|
|
118508
|
+
return;
|
|
118509
|
+
}
|
|
118510
|
+
const checked = event.target.checked;
|
|
118511
|
+
this.commit(applyChartElementToggle(chartData, key, checked));
|
|
118512
|
+
}
|
|
118513
|
+
onStylePreset(presetId) {
|
|
118514
|
+
const chartData = this.element().chartData;
|
|
118515
|
+
if (!chartData) {
|
|
118516
|
+
return;
|
|
118517
|
+
}
|
|
118518
|
+
const next = applyChartStylePreset(chartData, presetId);
|
|
118519
|
+
if (next) {
|
|
118520
|
+
this.commit(next);
|
|
118521
|
+
}
|
|
118522
|
+
}
|
|
118523
|
+
onFilterToggle(seriesIndex, filteredIndex) {
|
|
118524
|
+
const chartData = this.element().chartData;
|
|
118525
|
+
if (!chartData) {
|
|
118526
|
+
return;
|
|
118527
|
+
}
|
|
118528
|
+
const next = seriesIndex !== undefined
|
|
118529
|
+
? hideChartSeries(chartData, seriesIndex)
|
|
118530
|
+
: restoreFilteredSeries(chartData, filteredIndex);
|
|
118531
|
+
if (next) {
|
|
118532
|
+
this.commit(next);
|
|
118533
|
+
}
|
|
118534
|
+
}
|
|
118535
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ChartQuickActionsOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
118536
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ChartQuickActionsOverlayComponent, isStandalone: true, selector: "pptx-chart-quick-actions-overlay", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-pptx-chart-quick-actions": "true", "data-export-ignore": "true" }, listeners: { "pointerdown": "$event.stopPropagation()", "document:mousedown": "onDocumentMouseDown($event)" } }, ngImport: i0, template: `
|
|
118537
|
+
@if (descriptor(); as d) {
|
|
118538
|
+
@for (button of d.buttons; track button.id) {
|
|
118539
|
+
<button
|
|
118540
|
+
type="button"
|
|
118541
|
+
class="pptx-ng-chart-quick-btn"
|
|
118542
|
+
[disabled]="!canEdit()"
|
|
118543
|
+
[attr.data-testid]="'chart-quick-action-' + button.id"
|
|
118544
|
+
[attr.aria-label]="button.labelKey | translate"
|
|
118545
|
+
[title]="button.labelKey | translate"
|
|
118546
|
+
[style.left.px]="screenPx(button.x)"
|
|
118547
|
+
[style.top.px]="screenPx(button.y)"
|
|
118548
|
+
[style.width.px]="screenPx(button.size)"
|
|
118549
|
+
[style.height.px]="screenPx(button.size)"
|
|
118550
|
+
(click)="toggle(button.id)"
|
|
118551
|
+
>
|
|
118552
|
+
@if (button.id === 'elements') {
|
|
118553
|
+
<svg lucidePlus class="pptx-ng-chart-quick-icon"></svg>
|
|
118554
|
+
} @else if (button.id === 'styles') {
|
|
118555
|
+
<svg lucidePaintbrush class="pptx-ng-chart-quick-icon"></svg>
|
|
118556
|
+
} @else {
|
|
118557
|
+
<svg lucideFilter class="pptx-ng-chart-quick-icon"></svg>
|
|
118558
|
+
}
|
|
118559
|
+
</button>
|
|
118560
|
+
|
|
118561
|
+
@if (open() === button.id && button.id === 'elements') {
|
|
118562
|
+
<div
|
|
118563
|
+
class="pptx-ng-chart-quick-card"
|
|
118564
|
+
data-testid="chart-quick-elements-popover"
|
|
118565
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118566
|
+
[style.top.px]="screenPx(button.y)"
|
|
118567
|
+
>
|
|
118568
|
+
<h4 class="pptx-ng-chart-quick-heading">
|
|
118569
|
+
{{ 'pptx.chart.quickElements' | translate }}
|
|
118570
|
+
</h4>
|
|
118571
|
+
@for (item of d.elements; track item.key) {
|
|
118572
|
+
<label class="pptx-ng-chart-quick-check">
|
|
118573
|
+
<input
|
|
118574
|
+
type="checkbox"
|
|
118575
|
+
[disabled]="!canEdit()"
|
|
118576
|
+
[checked]="item.checked"
|
|
118577
|
+
[attr.data-testid]="'chart-quick-element-' + item.key"
|
|
118578
|
+
(change)="onElementToggle(item.key, $event)"
|
|
118579
|
+
/>
|
|
118580
|
+
<span>{{ item.labelKey | translate }}</span>
|
|
118581
|
+
</label>
|
|
118582
|
+
}
|
|
118583
|
+
</div>
|
|
118584
|
+
}
|
|
118585
|
+
|
|
118586
|
+
@if (open() === button.id && button.id === 'styles') {
|
|
118587
|
+
<div
|
|
118588
|
+
class="pptx-ng-chart-quick-card pptx-ng-chart-quick-styles"
|
|
118589
|
+
data-testid="chart-quick-styles-popover"
|
|
118590
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118591
|
+
[style.top.px]="screenPx(button.y)"
|
|
118592
|
+
>
|
|
118593
|
+
@for (preset of d.styles.presets; track preset.id) {
|
|
118594
|
+
<button
|
|
118595
|
+
type="button"
|
|
118596
|
+
class="pptx-ng-chart-quick-swatch"
|
|
118597
|
+
[class.is-applied]="preset.applied"
|
|
118598
|
+
[disabled]="!canEdit()"
|
|
118599
|
+
[attr.data-testid]="'chart-quick-style-' + preset.id"
|
|
118600
|
+
[attr.aria-label]="preset.labelKey | translate"
|
|
118601
|
+
[attr.aria-pressed]="preset.applied"
|
|
118602
|
+
[title]="preset.labelKey | translate"
|
|
118603
|
+
(click)="onStylePreset(preset.id)"
|
|
118604
|
+
>
|
|
118605
|
+
<span class="pptx-ng-chart-quick-swatch-row">
|
|
118606
|
+
@for (c of preset.colors.slice(0, 5); track $index) {
|
|
118607
|
+
<span [style.background-color]="c"></span>
|
|
118608
|
+
}
|
|
118609
|
+
</span>
|
|
118610
|
+
</button>
|
|
118611
|
+
}
|
|
118612
|
+
</div>
|
|
118613
|
+
}
|
|
118614
|
+
|
|
118615
|
+
@if (open() === button.id && button.id === 'filters') {
|
|
118616
|
+
<div
|
|
118617
|
+
class="pptx-ng-chart-quick-card"
|
|
118618
|
+
data-testid="chart-quick-filters-popover"
|
|
118619
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118620
|
+
[style.top.px]="screenPx(button.y)"
|
|
118621
|
+
>
|
|
118622
|
+
<h4 class="pptx-ng-chart-quick-heading">{{ 'pptx.chart.quickFilters' | translate }}</h4>
|
|
118623
|
+
@for (row of d.filters.series; track row.key) {
|
|
118624
|
+
<label class="pptx-ng-chart-quick-check">
|
|
118625
|
+
<input
|
|
118626
|
+
type="checkbox"
|
|
118627
|
+
[disabled]="!canEdit()"
|
|
118628
|
+
[checked]="row.visible"
|
|
118629
|
+
[attr.data-testid]="'chart-quick-filter-' + row.key"
|
|
118630
|
+
(change)="onFilterToggle(row.seriesIndex, row.filteredIndex)"
|
|
118631
|
+
/>
|
|
118632
|
+
<span class="pptx-ng-chart-quick-name">{{ row.name }}</span>
|
|
118633
|
+
</label>
|
|
118634
|
+
}
|
|
118635
|
+
</div>
|
|
118636
|
+
}
|
|
118637
|
+
}
|
|
118638
|
+
}
|
|
118639
|
+
`, isInline: true, styles: [":host{position:absolute;left:0;top:0;z-index:59}.pptx-ng-chart-quick-btn{position:absolute;display:flex;align-items:center;justify-content:center;border-radius:4px;border:1px solid #d1d5db;background:#fff;color:#374151;cursor:pointer}.pptx-ng-chart-quick-btn:hover{background:#f3f4f6}.pptx-ng-chart-quick-btn:disabled{opacity:.6;cursor:not-allowed}.pptx-ng-chart-quick-icon{width:14px;height:14px}.pptx-ng-chart-quick-card{position:absolute;z-index:10;display:flex;flex-direction:column;gap:.35rem;min-width:11rem;padding:.5rem;border-radius:6px;border:1px solid var(--pptx-inspector-border, #444);background:var(--pptx-inspector-input-bg, #2d2d2d);box-shadow:0 4px 16px #00000059}.pptx-ng-chart-quick-heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-ng-chart-quick-check{display:flex;align-items:center;gap:.35rem;font-size:11px;cursor:pointer}.pptx-ng-chart-quick-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pptx-ng-chart-quick-styles{display:grid;grid-template-columns:repeat(3,1fr);gap:.35rem;min-width:12rem}.pptx-ng-chart-quick-swatch{border-radius:4px;overflow:hidden;border:1px solid var(--pptx-inspector-border, #444);padding:0;cursor:pointer}.pptx-ng-chart-quick-swatch.is-applied{border-color:var(--pptx-inspector-active, #0078d4);box-shadow:0 0 0 1px var(--pptx-inspector-active, #0078d4)}.pptx-ng-chart-quick-swatch-row{display:flex;height:20px;width:100%}.pptx-ng-chart-quick-swatch-row>span{flex:1 1 auto}\n"], dependencies: [{ kind: "component", type: LucidePlus, selector: "svg[lucidePlus]" }, { kind: "component", type: LucidePaintbrush, selector: "svg[lucidePaintbrush]" }, { kind: "component", type: LucideFilter, selector: "svg[lucideFunnel], svg[lucideFilter]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
118640
|
+
}
|
|
118641
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ChartQuickActionsOverlayComponent, decorators: [{
|
|
118642
|
+
type: Component,
|
|
118643
|
+
args: [{ selector: 'pptx-chart-quick-actions-overlay', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe, LucidePlus, LucidePaintbrush, LucideFilter], host: {
|
|
118644
|
+
'data-pptx-chart-quick-actions': 'true',
|
|
118645
|
+
'data-export-ignore': 'true',
|
|
118646
|
+
'(pointerdown)': '$event.stopPropagation()',
|
|
118647
|
+
}, template: `
|
|
118648
|
+
@if (descriptor(); as d) {
|
|
118649
|
+
@for (button of d.buttons; track button.id) {
|
|
118650
|
+
<button
|
|
118651
|
+
type="button"
|
|
118652
|
+
class="pptx-ng-chart-quick-btn"
|
|
118653
|
+
[disabled]="!canEdit()"
|
|
118654
|
+
[attr.data-testid]="'chart-quick-action-' + button.id"
|
|
118655
|
+
[attr.aria-label]="button.labelKey | translate"
|
|
118656
|
+
[title]="button.labelKey | translate"
|
|
118657
|
+
[style.left.px]="screenPx(button.x)"
|
|
118658
|
+
[style.top.px]="screenPx(button.y)"
|
|
118659
|
+
[style.width.px]="screenPx(button.size)"
|
|
118660
|
+
[style.height.px]="screenPx(button.size)"
|
|
118661
|
+
(click)="toggle(button.id)"
|
|
118662
|
+
>
|
|
118663
|
+
@if (button.id === 'elements') {
|
|
118664
|
+
<svg lucidePlus class="pptx-ng-chart-quick-icon"></svg>
|
|
118665
|
+
} @else if (button.id === 'styles') {
|
|
118666
|
+
<svg lucidePaintbrush class="pptx-ng-chart-quick-icon"></svg>
|
|
118667
|
+
} @else {
|
|
118668
|
+
<svg lucideFilter class="pptx-ng-chart-quick-icon"></svg>
|
|
118669
|
+
}
|
|
118670
|
+
</button>
|
|
118671
|
+
|
|
118672
|
+
@if (open() === button.id && button.id === 'elements') {
|
|
118673
|
+
<div
|
|
118674
|
+
class="pptx-ng-chart-quick-card"
|
|
118675
|
+
data-testid="chart-quick-elements-popover"
|
|
118676
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118677
|
+
[style.top.px]="screenPx(button.y)"
|
|
118678
|
+
>
|
|
118679
|
+
<h4 class="pptx-ng-chart-quick-heading">
|
|
118680
|
+
{{ 'pptx.chart.quickElements' | translate }}
|
|
118681
|
+
</h4>
|
|
118682
|
+
@for (item of d.elements; track item.key) {
|
|
118683
|
+
<label class="pptx-ng-chart-quick-check">
|
|
118684
|
+
<input
|
|
118685
|
+
type="checkbox"
|
|
118686
|
+
[disabled]="!canEdit()"
|
|
118687
|
+
[checked]="item.checked"
|
|
118688
|
+
[attr.data-testid]="'chart-quick-element-' + item.key"
|
|
118689
|
+
(change)="onElementToggle(item.key, $event)"
|
|
118690
|
+
/>
|
|
118691
|
+
<span>{{ item.labelKey | translate }}</span>
|
|
118692
|
+
</label>
|
|
118693
|
+
}
|
|
118694
|
+
</div>
|
|
118695
|
+
}
|
|
118696
|
+
|
|
118697
|
+
@if (open() === button.id && button.id === 'styles') {
|
|
118698
|
+
<div
|
|
118699
|
+
class="pptx-ng-chart-quick-card pptx-ng-chart-quick-styles"
|
|
118700
|
+
data-testid="chart-quick-styles-popover"
|
|
118701
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118702
|
+
[style.top.px]="screenPx(button.y)"
|
|
118703
|
+
>
|
|
118704
|
+
@for (preset of d.styles.presets; track preset.id) {
|
|
118705
|
+
<button
|
|
118706
|
+
type="button"
|
|
118707
|
+
class="pptx-ng-chart-quick-swatch"
|
|
118708
|
+
[class.is-applied]="preset.applied"
|
|
118709
|
+
[disabled]="!canEdit()"
|
|
118710
|
+
[attr.data-testid]="'chart-quick-style-' + preset.id"
|
|
118711
|
+
[attr.aria-label]="preset.labelKey | translate"
|
|
118712
|
+
[attr.aria-pressed]="preset.applied"
|
|
118713
|
+
[title]="preset.labelKey | translate"
|
|
118714
|
+
(click)="onStylePreset(preset.id)"
|
|
118715
|
+
>
|
|
118716
|
+
<span class="pptx-ng-chart-quick-swatch-row">
|
|
118717
|
+
@for (c of preset.colors.slice(0, 5); track $index) {
|
|
118718
|
+
<span [style.background-color]="c"></span>
|
|
118719
|
+
}
|
|
118720
|
+
</span>
|
|
118721
|
+
</button>
|
|
118722
|
+
}
|
|
118723
|
+
</div>
|
|
118724
|
+
}
|
|
118725
|
+
|
|
118726
|
+
@if (open() === button.id && button.id === 'filters') {
|
|
118727
|
+
<div
|
|
118728
|
+
class="pptx-ng-chart-quick-card"
|
|
118729
|
+
data-testid="chart-quick-filters-popover"
|
|
118730
|
+
[style.left.px]="screenPx(button.x + CHART_QUICK_ACTION_BUTTON_SIZE + 4)"
|
|
118731
|
+
[style.top.px]="screenPx(button.y)"
|
|
118732
|
+
>
|
|
118733
|
+
<h4 class="pptx-ng-chart-quick-heading">{{ 'pptx.chart.quickFilters' | translate }}</h4>
|
|
118734
|
+
@for (row of d.filters.series; track row.key) {
|
|
118735
|
+
<label class="pptx-ng-chart-quick-check">
|
|
118736
|
+
<input
|
|
118737
|
+
type="checkbox"
|
|
118738
|
+
[disabled]="!canEdit()"
|
|
118739
|
+
[checked]="row.visible"
|
|
118740
|
+
[attr.data-testid]="'chart-quick-filter-' + row.key"
|
|
118741
|
+
(change)="onFilterToggle(row.seriesIndex, row.filteredIndex)"
|
|
118742
|
+
/>
|
|
118743
|
+
<span class="pptx-ng-chart-quick-name">{{ row.name }}</span>
|
|
118744
|
+
</label>
|
|
118745
|
+
}
|
|
118746
|
+
</div>
|
|
118747
|
+
}
|
|
118748
|
+
}
|
|
118749
|
+
}
|
|
118750
|
+
`, styles: [":host{position:absolute;left:0;top:0;z-index:59}.pptx-ng-chart-quick-btn{position:absolute;display:flex;align-items:center;justify-content:center;border-radius:4px;border:1px solid #d1d5db;background:#fff;color:#374151;cursor:pointer}.pptx-ng-chart-quick-btn:hover{background:#f3f4f6}.pptx-ng-chart-quick-btn:disabled{opacity:.6;cursor:not-allowed}.pptx-ng-chart-quick-icon{width:14px;height:14px}.pptx-ng-chart-quick-card{position:absolute;z-index:10;display:flex;flex-direction:column;gap:.35rem;min-width:11rem;padding:.5rem;border-radius:6px;border:1px solid var(--pptx-inspector-border, #444);background:var(--pptx-inspector-input-bg, #2d2d2d);box-shadow:0 4px 16px #00000059}.pptx-ng-chart-quick-heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-ng-chart-quick-check{display:flex;align-items:center;gap:.35rem;font-size:11px;cursor:pointer}.pptx-ng-chart-quick-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pptx-ng-chart-quick-styles{display:grid;grid-template-columns:repeat(3,1fr);gap:.35rem;min-width:12rem}.pptx-ng-chart-quick-swatch{border-radius:4px;overflow:hidden;border:1px solid var(--pptx-inspector-border, #444);padding:0;cursor:pointer}.pptx-ng-chart-quick-swatch.is-applied{border-color:var(--pptx-inspector-active, #0078d4);box-shadow:0 0 0 1px var(--pptx-inspector-active, #0078d4)}.pptx-ng-chart-quick-swatch-row{display:flex;height:20px;width:100%}.pptx-ng-chart-quick-swatch-row>span{flex:1 1 auto}\n"] }]
|
|
118751
|
+
}], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], onDocumentMouseDown: [{
|
|
118752
|
+
type: HostListener,
|
|
118753
|
+
args: ['document:mousedown', ['$event']]
|
|
118754
|
+
}] } });
|
|
118755
|
+
|
|
117548
118756
|
/**
|
|
117549
118757
|
* Pure geometry for interactive element drag/resize.
|
|
117550
118758
|
*
|
|
@@ -119375,227 +120583,235 @@ class ChartRendererComponent {
|
|
|
119375
120583
|
@if (legendItems().length > 0) {
|
|
119376
120584
|
@for (item of legendItems(); track $index) {
|
|
119377
120585
|
<g [attr.transform]="legendTransform(item)">
|
|
119378
|
-
|
|
119379
|
-
|
|
119380
|
-
|
|
119381
|
-
|
|
119382
|
-
|
|
119383
|
-
|
|
119384
|
-
|
|
119385
|
-
|
|
119386
|
-
|
|
119387
|
-
|
|
119388
|
-
|
|
119389
|
-
|
|
119390
|
-
|
|
119391
|
-
[attr.
|
|
119392
|
-
|
|
119393
|
-
[attr.font-
|
|
119394
|
-
|
|
119395
|
-
|
|
119396
|
-
|
|
119397
|
-
|
|
119398
|
-
|
|
119399
|
-
|
|
119400
|
-
|
|
119401
|
-
|
|
119402
|
-
}
|
|
119403
|
-
|
|
119404
|
-
|
|
119405
|
-
|
|
119406
|
-
|
|
119407
|
-
|
|
119408
|
-
|
|
119409
|
-
|
|
119410
|
-
|
|
119411
|
-
|
|
119412
|
-
|
|
119413
|
-
|
|
119414
|
-
|
|
119415
|
-
|
|
119416
|
-
|
|
119417
|
-
|
|
119418
|
-
|
|
119419
|
-
|
|
119420
|
-
|
|
119421
|
-
|
|
119422
|
-
|
|
119423
|
-
|
|
119424
|
-
|
|
119425
|
-
|
|
119426
|
-
|
|
119427
|
-
|
|
119428
|
-
[attr.
|
|
119429
|
-
[attr.
|
|
119430
|
-
|
|
119431
|
-
|
|
119432
|
-
|
|
120586
|
+
@if (item.lineSwatch) {
|
|
120587
|
+
<g pptx-chart-primitives [primitives]="item.lineSwatch.primitives"></g>
|
|
120588
|
+
} @else {
|
|
120589
|
+
<rect
|
|
120590
|
+
x="0"
|
|
120591
|
+
y="-7"
|
|
120592
|
+
[attr.width]="swatchSize"
|
|
120593
|
+
[attr.height]="swatchSize"
|
|
120594
|
+
rx="2"
|
|
120595
|
+
[attr.fill]="item.color"
|
|
120596
|
+
/>
|
|
120597
|
+
}
|
|
120598
|
+
<text
|
|
120599
|
+
[attr.x]="swatchSize + 3"
|
|
120600
|
+
y="3"
|
|
120601
|
+
[attr.font-size]="item.fontSize"
|
|
120602
|
+
[attr.fill]="item.fill"
|
|
120603
|
+
[attr.font-weight]="item.fontWeight"
|
|
120604
|
+
[attr.font-style]="item.fontStyle"
|
|
120605
|
+
[attr.font-family]="item.fontFamily ?? null"
|
|
120606
|
+
>
|
|
120607
|
+
{{ item.label }}
|
|
120608
|
+
</text>
|
|
120609
|
+
</g>
|
|
120610
|
+
}
|
|
120611
|
+
}
|
|
120612
|
+
</svg>
|
|
120613
|
+
`, isInline: true, dependencies: [{ kind: "component", type: ChartPrimitivesComponent, selector: "g[pptx-chart-primitives]", inputs: ["primitives"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
120614
|
+
}
|
|
120615
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ChartRendererComponent, decorators: [{
|
|
120616
|
+
type: Component,
|
|
120617
|
+
args: [{
|
|
120618
|
+
selector: 'pptx-chart-renderer',
|
|
120619
|
+
standalone: true,
|
|
120620
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
120621
|
+
imports: [ChartPrimitivesComponent],
|
|
120622
|
+
template: `
|
|
120623
|
+
<svg
|
|
120624
|
+
[attr.width]="vm().svgWidth"
|
|
120625
|
+
[attr.height]="vm().svgHeight"
|
|
120626
|
+
[attr.viewBox]="viewBox()"
|
|
120627
|
+
class="pptx-ng-chart-svg"
|
|
120628
|
+
style="overflow: visible; display: block; pointer-events: none;"
|
|
120629
|
+
>
|
|
120630
|
+
<!-- c:dPt/c:pictureOptions picture-fill patterns, rendered before anything
|
|
120631
|
+
references them via fill="url(#...)" -->
|
|
120632
|
+
@if ((vm().defs ?? []).length > 0) {
|
|
120633
|
+
<defs>
|
|
120634
|
+
@for (def of vm().defs ?? []; track def.id) {
|
|
120635
|
+
<pattern
|
|
120636
|
+
[attr.id]="def.id"
|
|
120637
|
+
[attr.patternUnits]="def.patternUnits"
|
|
120638
|
+
[attr.x]="def.x"
|
|
120639
|
+
[attr.y]="def.y"
|
|
120640
|
+
[attr.width]="def.width"
|
|
120641
|
+
[attr.height]="def.height"
|
|
120642
|
+
>
|
|
120643
|
+
<image
|
|
120644
|
+
[attr.href]="def.href"
|
|
120645
|
+
x="0"
|
|
120646
|
+
y="0"
|
|
120647
|
+
[attr.width]="def.width"
|
|
120648
|
+
[attr.height]="def.height"
|
|
120649
|
+
[attr.preserveAspectRatio]="def.preserveAspectRatio"
|
|
120650
|
+
/>
|
|
120651
|
+
</pattern>
|
|
120652
|
+
}
|
|
120653
|
+
</defs>
|
|
120654
|
+
}
|
|
120655
|
+
|
|
120656
|
+
<!-- Chart-area fill; absent when the deck declares c:chartSpace/a:noFill -->
|
|
120657
|
+
@if (vm().areaFill) {
|
|
120658
|
+
<rect
|
|
120659
|
+
x="0"
|
|
120660
|
+
y="0"
|
|
120661
|
+
[attr.width]="vm().svgWidth"
|
|
120662
|
+
[attr.height]="vm().svgHeight"
|
|
120663
|
+
[attr.rx]="vm().areaRadius"
|
|
120664
|
+
[attr.fill]="vm().areaFill"
|
|
120665
|
+
/>
|
|
120666
|
+
}
|
|
120667
|
+
|
|
120668
|
+
<!-- Chart title (data-chart-part enables in-place editing in edit mode) -->
|
|
120669
|
+
@if (vm().title) {
|
|
120670
|
+
<text
|
|
120671
|
+
[attr.x]="vm().titleX"
|
|
120672
|
+
[attr.y]="vm().titleY"
|
|
120673
|
+
text-anchor="middle"
|
|
120674
|
+
[attr.font-size]="vm().titleStyle?.fontSize ?? 12"
|
|
120675
|
+
[attr.font-weight]="vm().titleStyle?.fontWeight ?? 600"
|
|
120676
|
+
[attr.font-family]="vm().titleStyle?.fontFamily"
|
|
120677
|
+
[attr.fill]="vm().titleStyle?.fill ?? '#1e293b'"
|
|
120678
|
+
data-chart-part="title"
|
|
120679
|
+
>
|
|
120680
|
+
@if (vm().titleRunSpans; as titleRunSpans) {
|
|
120681
|
+
@for (run of titleRunSpans; track $index) {
|
|
120682
|
+
<tspan
|
|
120683
|
+
[attr.font-size]="run.fontSize"
|
|
120684
|
+
[attr.font-weight]="run.fontWeight"
|
|
120685
|
+
[attr.font-style]="run.fontStyle"
|
|
120686
|
+
[attr.font-family]="run.fontFamily"
|
|
120687
|
+
[attr.fill]="run.fill"
|
|
120688
|
+
>
|
|
120689
|
+
{{ run.text }}
|
|
120690
|
+
</tspan>
|
|
120691
|
+
}
|
|
120692
|
+
} @else {
|
|
120693
|
+
{{ vm().title }}
|
|
120694
|
+
}
|
|
120695
|
+
</text>
|
|
120696
|
+
}
|
|
120697
|
+
|
|
120698
|
+
<!-- Gridlines -->
|
|
120699
|
+
@for (gl of vm().gridlines; track $index) {
|
|
120700
|
+
<line
|
|
120701
|
+
[attr.x1]="gl.x1"
|
|
120702
|
+
[attr.y1]="gl.y1"
|
|
120703
|
+
[attr.x2]="gl.x2"
|
|
120704
|
+
[attr.y2]="gl.y2"
|
|
120705
|
+
[attr.stroke]="gl.stroke"
|
|
120706
|
+
[attr.stroke-width]="gl.strokeWidth"
|
|
120707
|
+
/>
|
|
120708
|
+
}
|
|
120709
|
+
|
|
120710
|
+
<!-- Value-axis labels -->
|
|
120711
|
+
@for (lbl of vm().axisLabels; track $index) {
|
|
120712
|
+
<text
|
|
120713
|
+
[attr.x]="lbl.x"
|
|
120714
|
+
[attr.y]="lbl.y"
|
|
120715
|
+
[attr.text-anchor]="lbl.textAnchor"
|
|
120716
|
+
[attr.font-size]="lbl.fontSize"
|
|
120717
|
+
[attr.fill]="lbl.fill"
|
|
120718
|
+
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
120719
|
+
>
|
|
120720
|
+
{{ lbl.text }}
|
|
120721
|
+
</text>
|
|
120722
|
+
}
|
|
120723
|
+
|
|
120724
|
+
<!-- Secondary value-axis gridlines -->
|
|
120725
|
+
@for (gl of vm().secondaryGridlines ?? []; track $index) {
|
|
120726
|
+
<line
|
|
120727
|
+
[attr.x1]="gl.x1"
|
|
120728
|
+
[attr.y1]="gl.y1"
|
|
120729
|
+
[attr.x2]="gl.x2"
|
|
120730
|
+
[attr.y2]="gl.y2"
|
|
120731
|
+
[attr.stroke]="gl.stroke"
|
|
120732
|
+
[attr.stroke-width]="gl.strokeWidth"
|
|
120733
|
+
[attr.stroke-dasharray]="gl.dashArray"
|
|
120734
|
+
[attr.opacity]="gl.opacity ?? 1"
|
|
120735
|
+
/>
|
|
120736
|
+
}
|
|
120737
|
+
|
|
120738
|
+
<!-- Secondary value-axis labels -->
|
|
120739
|
+
@for (lbl of vm().secondaryAxisLabels ?? []; track $index) {
|
|
120740
|
+
<text
|
|
120741
|
+
[attr.x]="lbl.x"
|
|
120742
|
+
[attr.y]="lbl.y"
|
|
120743
|
+
[attr.text-anchor]="lbl.textAnchor"
|
|
120744
|
+
[attr.font-size]="lbl.fontSize"
|
|
120745
|
+
[attr.fill]="lbl.fill"
|
|
120746
|
+
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
120747
|
+
[attr.opacity]="lbl.opacity ?? 1"
|
|
120748
|
+
[attr.transform]="lbl.transform"
|
|
120749
|
+
>
|
|
120750
|
+
{{ lbl.text }}
|
|
120751
|
+
</text>
|
|
120752
|
+
}
|
|
120753
|
+
|
|
120754
|
+
<!-- Zero line -->
|
|
120755
|
+
@if (vm().zeroLine) {
|
|
120756
|
+
<line
|
|
120757
|
+
[attr.x1]="vm().zeroLine!.x1"
|
|
120758
|
+
[attr.y1]="vm().zeroLine!.y1"
|
|
120759
|
+
[attr.x2]="vm().zeroLine!.x2"
|
|
120760
|
+
[attr.y2]="vm().zeroLine!.y2"
|
|
120761
|
+
[attr.stroke]="vm().zeroLine!.stroke"
|
|
120762
|
+
[attr.stroke-width]="vm().zeroLine!.strokeWidth"
|
|
120763
|
+
/>
|
|
120764
|
+
}
|
|
120765
|
+
|
|
120766
|
+
<!-- Category-axis labels -->
|
|
120767
|
+
@for (lbl of vm().categoryLabels; track $index) {
|
|
120768
|
+
<text
|
|
120769
|
+
[attr.x]="lbl.x"
|
|
120770
|
+
[attr.y]="lbl.y"
|
|
120771
|
+
[attr.text-anchor]="lbl.textAnchor"
|
|
120772
|
+
[attr.font-size]="lbl.fontSize"
|
|
120773
|
+
[attr.fill]="lbl.fill"
|
|
120774
|
+
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
120775
|
+
>
|
|
120776
|
+
{{ lbl.text }}
|
|
120777
|
+
</text>
|
|
120778
|
+
}
|
|
120779
|
+
|
|
120780
|
+
<!-- Data primitives (bars, lines, arcs, dots ...); data marks carry
|
|
120781
|
+
data-chart-* hit-testing attributes (inert outside edit mode). -->
|
|
120782
|
+
<g pptx-chart-primitives [primitives]="vm().primitives"></g>
|
|
120783
|
+
|
|
120784
|
+
<!-- Data labels -->
|
|
120785
|
+
@for (dl of vm().dataLabels; track $index) {
|
|
120786
|
+
<text
|
|
120787
|
+
[attr.x]="dl.x"
|
|
120788
|
+
[attr.y]="dl.y"
|
|
120789
|
+
[attr.text-anchor]="dl.textAnchor"
|
|
120790
|
+
[attr.font-size]="dl.fontSize"
|
|
120791
|
+
[attr.fill]="dl.fill"
|
|
120792
|
+
[attr.font-weight]="dl.fontWeight ?? 'normal'"
|
|
120793
|
+
[attr.dominant-baseline]="dl.dominantBaseline ?? 'auto'"
|
|
120794
|
+
>
|
|
120795
|
+
{{ dl.text }}
|
|
120796
|
+
</text>
|
|
120797
|
+
}
|
|
120798
|
+
|
|
120799
|
+
<!-- Legend -->
|
|
120800
|
+
@if (legendItems().length > 0) {
|
|
120801
|
+
@for (item of legendItems(); track $index) {
|
|
120802
|
+
<g [attr.transform]="legendTransform(item)">
|
|
120803
|
+
@if (item.lineSwatch) {
|
|
120804
|
+
<g pptx-chart-primitives [primitives]="item.lineSwatch.primitives"></g>
|
|
120805
|
+
} @else {
|
|
120806
|
+
<rect
|
|
119433
120807
|
x="0"
|
|
119434
|
-
y="
|
|
119435
|
-
[attr.width]="
|
|
119436
|
-
[attr.height]="
|
|
119437
|
-
|
|
120808
|
+
y="-7"
|
|
120809
|
+
[attr.width]="swatchSize"
|
|
120810
|
+
[attr.height]="swatchSize"
|
|
120811
|
+
rx="2"
|
|
120812
|
+
[attr.fill]="item.color"
|
|
119438
120813
|
/>
|
|
119439
|
-
</pattern>
|
|
119440
|
-
}
|
|
119441
|
-
</defs>
|
|
119442
|
-
}
|
|
119443
|
-
|
|
119444
|
-
<!-- Chart-area fill; absent when the deck declares c:chartSpace/a:noFill -->
|
|
119445
|
-
@if (vm().areaFill) {
|
|
119446
|
-
<rect
|
|
119447
|
-
x="0"
|
|
119448
|
-
y="0"
|
|
119449
|
-
[attr.width]="vm().svgWidth"
|
|
119450
|
-
[attr.height]="vm().svgHeight"
|
|
119451
|
-
[attr.rx]="vm().areaRadius"
|
|
119452
|
-
[attr.fill]="vm().areaFill"
|
|
119453
|
-
/>
|
|
119454
|
-
}
|
|
119455
|
-
|
|
119456
|
-
<!-- Chart title (data-chart-part enables in-place editing in edit mode) -->
|
|
119457
|
-
@if (vm().title) {
|
|
119458
|
-
<text
|
|
119459
|
-
[attr.x]="vm().titleX"
|
|
119460
|
-
[attr.y]="vm().titleY"
|
|
119461
|
-
text-anchor="middle"
|
|
119462
|
-
[attr.font-size]="vm().titleStyle?.fontSize ?? 12"
|
|
119463
|
-
[attr.font-weight]="vm().titleStyle?.fontWeight ?? 600"
|
|
119464
|
-
[attr.font-family]="vm().titleStyle?.fontFamily"
|
|
119465
|
-
[attr.fill]="vm().titleStyle?.fill ?? '#1e293b'"
|
|
119466
|
-
data-chart-part="title"
|
|
119467
|
-
>
|
|
119468
|
-
@if (vm().titleRunSpans; as titleRunSpans) {
|
|
119469
|
-
@for (run of titleRunSpans; track $index) {
|
|
119470
|
-
<tspan
|
|
119471
|
-
[attr.font-size]="run.fontSize"
|
|
119472
|
-
[attr.font-weight]="run.fontWeight"
|
|
119473
|
-
[attr.font-style]="run.fontStyle"
|
|
119474
|
-
[attr.font-family]="run.fontFamily"
|
|
119475
|
-
[attr.fill]="run.fill"
|
|
119476
|
-
>
|
|
119477
|
-
{{ run.text }}
|
|
119478
|
-
</tspan>
|
|
119479
120814
|
}
|
|
119480
|
-
} @else {
|
|
119481
|
-
{{ vm().title }}
|
|
119482
|
-
}
|
|
119483
|
-
</text>
|
|
119484
|
-
}
|
|
119485
|
-
|
|
119486
|
-
<!-- Gridlines -->
|
|
119487
|
-
@for (gl of vm().gridlines; track $index) {
|
|
119488
|
-
<line
|
|
119489
|
-
[attr.x1]="gl.x1"
|
|
119490
|
-
[attr.y1]="gl.y1"
|
|
119491
|
-
[attr.x2]="gl.x2"
|
|
119492
|
-
[attr.y2]="gl.y2"
|
|
119493
|
-
[attr.stroke]="gl.stroke"
|
|
119494
|
-
[attr.stroke-width]="gl.strokeWidth"
|
|
119495
|
-
/>
|
|
119496
|
-
}
|
|
119497
|
-
|
|
119498
|
-
<!-- Value-axis labels -->
|
|
119499
|
-
@for (lbl of vm().axisLabels; track $index) {
|
|
119500
|
-
<text
|
|
119501
|
-
[attr.x]="lbl.x"
|
|
119502
|
-
[attr.y]="lbl.y"
|
|
119503
|
-
[attr.text-anchor]="lbl.textAnchor"
|
|
119504
|
-
[attr.font-size]="lbl.fontSize"
|
|
119505
|
-
[attr.fill]="lbl.fill"
|
|
119506
|
-
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
119507
|
-
>
|
|
119508
|
-
{{ lbl.text }}
|
|
119509
|
-
</text>
|
|
119510
|
-
}
|
|
119511
|
-
|
|
119512
|
-
<!-- Secondary value-axis gridlines -->
|
|
119513
|
-
@for (gl of vm().secondaryGridlines ?? []; track $index) {
|
|
119514
|
-
<line
|
|
119515
|
-
[attr.x1]="gl.x1"
|
|
119516
|
-
[attr.y1]="gl.y1"
|
|
119517
|
-
[attr.x2]="gl.x2"
|
|
119518
|
-
[attr.y2]="gl.y2"
|
|
119519
|
-
[attr.stroke]="gl.stroke"
|
|
119520
|
-
[attr.stroke-width]="gl.strokeWidth"
|
|
119521
|
-
[attr.stroke-dasharray]="gl.dashArray"
|
|
119522
|
-
[attr.opacity]="gl.opacity ?? 1"
|
|
119523
|
-
/>
|
|
119524
|
-
}
|
|
119525
|
-
|
|
119526
|
-
<!-- Secondary value-axis labels -->
|
|
119527
|
-
@for (lbl of vm().secondaryAxisLabels ?? []; track $index) {
|
|
119528
|
-
<text
|
|
119529
|
-
[attr.x]="lbl.x"
|
|
119530
|
-
[attr.y]="lbl.y"
|
|
119531
|
-
[attr.text-anchor]="lbl.textAnchor"
|
|
119532
|
-
[attr.font-size]="lbl.fontSize"
|
|
119533
|
-
[attr.fill]="lbl.fill"
|
|
119534
|
-
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
119535
|
-
[attr.opacity]="lbl.opacity ?? 1"
|
|
119536
|
-
[attr.transform]="lbl.transform"
|
|
119537
|
-
>
|
|
119538
|
-
{{ lbl.text }}
|
|
119539
|
-
</text>
|
|
119540
|
-
}
|
|
119541
|
-
|
|
119542
|
-
<!-- Zero line -->
|
|
119543
|
-
@if (vm().zeroLine) {
|
|
119544
|
-
<line
|
|
119545
|
-
[attr.x1]="vm().zeroLine!.x1"
|
|
119546
|
-
[attr.y1]="vm().zeroLine!.y1"
|
|
119547
|
-
[attr.x2]="vm().zeroLine!.x2"
|
|
119548
|
-
[attr.y2]="vm().zeroLine!.y2"
|
|
119549
|
-
[attr.stroke]="vm().zeroLine!.stroke"
|
|
119550
|
-
[attr.stroke-width]="vm().zeroLine!.strokeWidth"
|
|
119551
|
-
/>
|
|
119552
|
-
}
|
|
119553
|
-
|
|
119554
|
-
<!-- Category-axis labels -->
|
|
119555
|
-
@for (lbl of vm().categoryLabels; track $index) {
|
|
119556
|
-
<text
|
|
119557
|
-
[attr.x]="lbl.x"
|
|
119558
|
-
[attr.y]="lbl.y"
|
|
119559
|
-
[attr.text-anchor]="lbl.textAnchor"
|
|
119560
|
-
[attr.font-size]="lbl.fontSize"
|
|
119561
|
-
[attr.fill]="lbl.fill"
|
|
119562
|
-
[attr.dominant-baseline]="lbl.dominantBaseline ?? 'auto'"
|
|
119563
|
-
>
|
|
119564
|
-
{{ lbl.text }}
|
|
119565
|
-
</text>
|
|
119566
|
-
}
|
|
119567
|
-
|
|
119568
|
-
<!-- Data primitives (bars, lines, arcs, dots ...); data marks carry
|
|
119569
|
-
data-chart-* hit-testing attributes (inert outside edit mode). -->
|
|
119570
|
-
<g pptx-chart-primitives [primitives]="vm().primitives"></g>
|
|
119571
|
-
|
|
119572
|
-
<!-- Data labels -->
|
|
119573
|
-
@for (dl of vm().dataLabels; track $index) {
|
|
119574
|
-
<text
|
|
119575
|
-
[attr.x]="dl.x"
|
|
119576
|
-
[attr.y]="dl.y"
|
|
119577
|
-
[attr.text-anchor]="dl.textAnchor"
|
|
119578
|
-
[attr.font-size]="dl.fontSize"
|
|
119579
|
-
[attr.fill]="dl.fill"
|
|
119580
|
-
[attr.font-weight]="dl.fontWeight ?? 'normal'"
|
|
119581
|
-
[attr.dominant-baseline]="dl.dominantBaseline ?? 'auto'"
|
|
119582
|
-
>
|
|
119583
|
-
{{ dl.text }}
|
|
119584
|
-
</text>
|
|
119585
|
-
}
|
|
119586
|
-
|
|
119587
|
-
<!-- Legend -->
|
|
119588
|
-
@if (legendItems().length > 0) {
|
|
119589
|
-
@for (item of legendItems(); track $index) {
|
|
119590
|
-
<g [attr.transform]="legendTransform(item)">
|
|
119591
|
-
<rect
|
|
119592
|
-
x="0"
|
|
119593
|
-
y="-7"
|
|
119594
|
-
[attr.width]="swatchSize"
|
|
119595
|
-
[attr.height]="swatchSize"
|
|
119596
|
-
rx="2"
|
|
119597
|
-
[attr.fill]="item.color"
|
|
119598
|
-
/>
|
|
119599
120815
|
<text
|
|
119600
120816
|
[attr.x]="swatchSize + 3"
|
|
119601
120817
|
y="3"
|
|
@@ -119910,121 +121126,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
119910
121126
|
`, styles: [".pptx-ng-bar-chart-3d-scene{width:100%;height:100%;display:block}\n"] }]
|
|
119911
121127
|
}], ctorParameters: () => [], propDecorators: { element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: true }] }], selectedPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedPart", required: false }] }], textStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "textStyle", required: false }] }], partSelect: [{ type: i0.Output, args: ["partSelect"] }], valueDragPreview: [{ type: i0.Output, args: ["valueDragPreview"] }], valueDragCommit: [{ type: i0.Output, args: ["valueDragCommit"] }], sceneRef: [{ type: i0.ViewChild, args: ['scene', { isSignal: true }] }] } });
|
|
119912
121128
|
|
|
119913
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119914
|
-
// Direct part-editing gate
|
|
119915
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119916
|
-
/**
|
|
119917
|
-
* May this chart's individual parts (title, series, data points) be entered
|
|
119918
|
-
* for direct on-canvas editing?
|
|
119919
|
-
*
|
|
119920
|
-
* Pure so it is unit-testable without a full Angular injection context:
|
|
119921
|
-
* `ChartElementViewComponent`'s constructor runs an `effect()` that needs a
|
|
119922
|
-
* `ChangeDetectionScheduler` this package's TestBed-free suite doesn't
|
|
119923
|
-
* provide. G8: `a:graphicFrameLocks/@noDrilldown` forbids the drill-down,
|
|
119924
|
-
* even when the chart is otherwise selected + editable.
|
|
119925
|
-
*/
|
|
119926
|
-
function chartCanEditParts(editable, isSelected, hasEditor, element) {
|
|
119927
|
-
return editable && isSelected && hasEditor && canDrillDown(element);
|
|
119928
|
-
}
|
|
119929
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119930
|
-
// Value-drag commit gate
|
|
119931
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119932
|
-
/**
|
|
119933
|
-
* The chart data a finished drag should commit, or null when nothing should
|
|
119934
|
-
* be committed (cancelled, or the press never became a drag).
|
|
119935
|
-
*/
|
|
119936
|
-
function chartDragCommitData(session, commit) {
|
|
119937
|
-
if (!commit || !session?.moved) {
|
|
119938
|
-
return null;
|
|
119939
|
-
}
|
|
119940
|
-
return session.lastData;
|
|
119941
|
-
}
|
|
119942
|
-
/**
|
|
119943
|
-
* Same gate as {@link chartDragCommitData}, for a pie/radar/stacked mark drag
|
|
119944
|
-
* ({@link ChartMarkDragState} carries the same `moved`/`lastData` shape as the
|
|
119945
|
-
* cartesian value-drag session, just resolved through a different geometry).
|
|
119946
|
-
*/
|
|
119947
|
-
function chartMarkDragCommitData(session, commit) {
|
|
119948
|
-
if (!commit || !session?.moved) {
|
|
119949
|
-
return null;
|
|
119950
|
-
}
|
|
119951
|
-
return session.lastData;
|
|
119952
|
-
}
|
|
119953
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119954
|
-
// 3D value-drag commit data (bar3D/line3D/area3D/pie3D/surface3D: no drag
|
|
119955
|
-
// state machine of their own, the shared three.js scene reports the
|
|
119956
|
-
// live/final value directly)
|
|
119957
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119958
|
-
/**
|
|
119959
|
-
* The chart data a 3D scene's `onValueDragPreview`/`onValueDragCommit` should
|
|
119960
|
-
* apply, or `null` when the part carries no point index (a series-level part,
|
|
119961
|
-
* or a part shape the 3D scenes never actually emit for a value drag) or there
|
|
119962
|
-
* is no chart data to update against.
|
|
119963
|
-
*
|
|
119964
|
-
* Pure so it is unit-testable without a WebGL context: `BarChart3DRendererComponent`
|
|
119965
|
-
* / `LineChart3DRendererComponent` / `AreaChart3DRendererComponent` /
|
|
119966
|
-
* `PieChart3DRendererComponent` / `SurfaceChart3DRendererComponent` forward
|
|
119967
|
-
* their `valueDragPreview`/`valueDragCommit` outputs here via
|
|
119968
|
-
* `ChartElementViewComponent`.
|
|
119969
|
-
*/
|
|
119970
|
-
function chart3DPointValueUpdate(chartData, part, value) {
|
|
119971
|
-
if (!chartData || part.pointIndex === undefined) {
|
|
119972
|
-
return null;
|
|
119973
|
-
}
|
|
119974
|
-
return withChartPointValue(chartData, part.seriesIndex, part.pointIndex, value);
|
|
119975
|
-
}
|
|
119976
|
-
/**
|
|
119977
|
-
* Commit an on-canvas chart edit through the editor's normal element-update
|
|
119978
|
-
* path (the exact channel the inspector uses: one history snapshot per call).
|
|
119979
|
-
* No-op without an editor or when the element is not on any slide.
|
|
119980
|
-
*
|
|
119981
|
-
* `templateSlideId` is the id of the slide the hosting canvas renders (from
|
|
119982
|
-
* `SLIDE_CONTEXT`); it resolves template (master/layout) chart elements, which
|
|
119983
|
-
* live in the per-slide template store rather than in `slides[].elements`.
|
|
119984
|
-
*/
|
|
119985
|
-
function commitChartElementData(editor, elementId, chartData, templateSlideId) {
|
|
119986
|
-
if (!editor) {
|
|
119987
|
-
return;
|
|
119988
|
-
}
|
|
119989
|
-
const slideIndex = findOwningSlideIndex(editor.slides(), elementId, templateSlideId);
|
|
119990
|
-
if (slideIndex < 0) {
|
|
119991
|
-
return;
|
|
119992
|
-
}
|
|
119993
|
-
editor.updateElement(slideIndex, elementId, { chartData });
|
|
119994
|
-
}
|
|
119995
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119996
|
-
// Interaction stylesheet (shared base rules + Angular's own badge/editor CSS)
|
|
119997
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
119998
|
-
const STYLE_ELEMENT_ID = 'pptx-ng-chart-interaction-styles';
|
|
119999
|
-
/**
|
|
120000
|
-
* Angular-only interaction CSS, injected once into `document.head` alongside
|
|
120001
|
-
* the shared `[data-chart-part]` / selected-mark rules from
|
|
120002
|
-
* `ensureChartInteractionStyles` (`pptx-viewer-shared`). Component styles are
|
|
120003
|
-
* view-encapsulated in Angular, so they cannot reach into the chart
|
|
120004
|
-
* renderer's SVG or style the badge/title-input this component projects
|
|
120005
|
-
* next to it.
|
|
120006
|
-
*/
|
|
120007
|
-
const INTERACTION_CSS = `
|
|
120008
|
-
.pptx-ng-chart-view { position: relative; width: 100%; height: 100%; }
|
|
120009
|
-
.pptx-ng-chart-drag-badge { position: absolute; top: 4px; right: 4px; z-index: 10; border-radius: 4px; background: rgba(37, 99, 235, 0.9); padding: 2px 6px; font-size: 10px; font-weight: 500; color: #fff; pointer-events: none; }
|
|
120010
|
-
.pptx-ng-chart-title-input { position: absolute; left: 50%; top: 2px; z-index: 10; width: 60%; transform: translateX(-50%); border: 1px solid #94a3b8; border-radius: 4px; background: #fff; padding: 2px 4px; text-align: center; font-size: 11px; color: #0f172a; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); }
|
|
120011
|
-
`;
|
|
120012
|
-
/**
|
|
120013
|
-
* Inject the interaction stylesheets for chart part hit targets: the shared
|
|
120014
|
-
* base rules (singleton, shared across all five bindings) plus Angular's own
|
|
120015
|
-
* badge/title-input CSS (singleton, this binding only).
|
|
120016
|
-
*/
|
|
120017
|
-
function ensureChartInteractionStyles() {
|
|
120018
|
-
ensureChartInteractionStyles$1();
|
|
120019
|
-
if (typeof document === 'undefined' || document.getElementById(STYLE_ELEMENT_ID)) {
|
|
120020
|
-
return;
|
|
120021
|
-
}
|
|
120022
|
-
const style = document.createElement('style');
|
|
120023
|
-
style.id = STYLE_ELEMENT_ID;
|
|
120024
|
-
style.textContent = INTERACTION_CSS;
|
|
120025
|
-
document.head.appendChild(style);
|
|
120026
|
-
}
|
|
120027
|
-
|
|
120028
121129
|
/**
|
|
120029
121130
|
* chart-part-selection.service.ts: the bridge between on-canvas chart part
|
|
120030
121131
|
* clicks (`ChartElementViewComponent`) and the chart inspector
|
|
@@ -126160,6 +127261,15 @@ function buildAngularImageRenderView(element) {
|
|
|
126160
127261
|
if (computed.opacity !== undefined) {
|
|
126161
127262
|
imageStyle.opacity = computed.opacity;
|
|
126162
127263
|
}
|
|
127264
|
+
// `a:blipFill/@rotWithShape="0"`: keep the picture's pixels upright/fixed to
|
|
127265
|
+
// the page frame while `frameGeometryMask`'s container still carries the
|
|
127266
|
+
// shape's own rotate/flip. `undefined` (no-op) in the overwhelming majority
|
|
127267
|
+
// of cases; see `getImageFillCounterTransform`.
|
|
127268
|
+
const fillCounterTransform = getImageFillCounterTransform(element, Boolean(imageStyle['transform']));
|
|
127269
|
+
if (fillCounterTransform) {
|
|
127270
|
+
imageStyle['transform'] = fillCounterTransform;
|
|
127271
|
+
imageStyle['transform-origin'] = 'center';
|
|
127272
|
+
}
|
|
126163
127273
|
const frameGeometryMask = buildAngularImageContainerMask(element);
|
|
126164
127274
|
const colorWash = getImageColorWashStyle(isImageLikeElement$1(element) ? element.imageEffects?.colorWash : undefined);
|
|
126165
127275
|
const colorWashStyle = colorWash
|
|
@@ -127952,6 +129062,16 @@ class SlideCanvasComponent {
|
|
|
127952
129062
|
return box ? (this.elementById(box.id) ?? null) : null;
|
|
127953
129063
|
}, /* @ts-ignore */
|
|
127954
129064
|
...(ngDevMode ? [{ debugName: "singleSelectedElement" }] : /* istanbul ignore next */ []));
|
|
129065
|
+
/**
|
|
129066
|
+
* The single selected element when (and only when) it is a chart: gates
|
|
129067
|
+
* the "Chart Elements"/"Chart Styles"/"Chart Filters" quick-action overlay,
|
|
129068
|
+
* which only makes sense for exactly one selected chart.
|
|
129069
|
+
*/
|
|
129070
|
+
this.quickActionsChartElement = computed(() => {
|
|
129071
|
+
const el = this.singleSelectedElement();
|
|
129072
|
+
return el && el.type === 'chart' ? el : null;
|
|
129073
|
+
}, /* @ts-ignore */
|
|
129074
|
+
...(ngDevMode ? [{ debugName: "quickActionsChartElement" }] : /* istanbul ignore next */ []));
|
|
127955
129075
|
/**
|
|
127956
129076
|
* Resize-handle render boxes (stage coords) for the single selection. Empty
|
|
127957
129077
|
* for an element whose authored `a:spLocks/@noResize` pins its size.
|
|
@@ -128909,7 +130029,7 @@ class SlideCanvasComponent {
|
|
|
128909
130029
|
// SmartArt), so template (master/layout) element commits can resolve
|
|
128910
130030
|
// their owning slide; template elements are absent from slides[].elements.
|
|
128911
130031
|
{ provide: SLIDE_CONTEXT, useExisting: forwardRef(() => SlideCanvasComponent) },
|
|
128912
|
-
], viewQueries: [{ propertyName: "textEditor", first: true, predicate: ["textEditor"], descendants: true, isSignal: true }, { propertyName: "listEditor", first: true, predicate: InlineListEditorComponent, descendants: true, isSignal: true }, { propertyName: "stageRef", first: true, predicate: ["stage"], descendants: true, isSignal: true }, { propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n\t#viewport\n\tclass=\"pptx-ng-canvas-viewport\"\n\t[attr.data-pptx-viewport]=\"interactive() ? '' : null\"\n\t(pointerdown)=\"onViewportPointerDown($event)\"\n>\n\t<div class=\"pptx-ng-canvas-wrapper\" [ngStyle]=\"wrapperStyle()\">\n\t\t<div\n\t\t\t#stage\n\t\t\tclass=\"pptx-ng-canvas-stage\"\n\t\t\t[class.is-editable]=\"editable()\"\n\t\t\trole=\"region\"\n\t\t\t[attr.aria-roledescription]=\"interactive() || presenting() ? 'slide' : null\"\n\t\t\t[attr.data-pptx-ai-active]=\"aiActive() ? 'true' : null\"\n\t\t\t[ngStyle]=\"stageStyle()\"\n\t\t\t(pointerdown)=\"onStagePointerDown($event)\"\n\t\t\t(contextmenu)=\"onContextMenu($event)\"\n\t\t\t(dblclick)=\"onDblClick($event)\"\n\t\t>\n\t\t\t<!--\n\t\t\t\tTemplate layer: inherited master/layout elements, rendered BEHIND\n\t\t\t\tthe slide's own elements (lower z-index). Interactive + given the\n\t\t\t\tamber editable affordance only while editTemplateMode is on; when\n\t\t\t\toff they render inertly with no affordance, exactly as core\n\t\t\t\tdelivered them. `marked` keeps the data-pptx-element contract on\n\t\t\t\tthem even while locked: the marker means \"rendered slide element\",\n\t\t\t\tnot \"editable right now\".\n\t\t\t-->\n\t\t\t@for (element of visibleTemplateElements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive() && editTemplateMode()\"\n\t\t\t\t\t[marked]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[editable]=\"editable() && editTemplateMode()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"editTemplateMode()\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@for (element of elements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"visibleTemplateElements().length + i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[presenting]=\"presenting()\"\n\t\t\t\t\t[editable]=\"editable()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"false\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<pptx-activex-controls-overlay\n\t\t\t\t[controls]=\"slide()?.activeXControls ?? []\"\n\t\t\t\t[canvasSize]=\"canvasSize()\"\n\t\t\t/>\n\t\t\t@if (aiHighlights().length > 0 || aiActive()) {\n\t\t\t\t<pptx-ai-focus-highlight-overlay\n\t\t\t\t\t[highlights]=\"aiHighlights()\"\n\t\t\t\t\t[elements]=\"elements()\"\n\t\t\t\t\t[activeSlideIndex]=\"aiActiveSlideIndex()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@if (aiChangeBatch(); as batch) {\n\t\t\t\t<pptx-ai-change-overlay [batch]=\"batch\" [activeSlideIndex]=\"aiActiveSlideIndex()\" />\n\t\t\t}\n\t\t\t@for (box of selectionBoxes(); track box.id) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-selection\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"box.x\"\n\t\t\t\t\t[style.top.px]=\"box.y\"\n\t\t\t\t\t[style.width.px]=\"box.width\"\n\t\t\t\t\t[style.height.px]=\"box.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (h of handleBoxes(); track h.handle) {\n\t\t\t\t@let controlArtwork = selectionControlArtwork(h, h.handle);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"resize\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.resize' | translate: { handle: h.handle }\"\n\t\t\t\t\t[ngStyle]=\"controlArtwork.frame\"\n\t\t\t\t\t[style.cursor]=\"h.cursor\"\n\t\t\t\t\t[style.--pptx-selection-width.px]=\"singleSelected()?.width\"\n\t\t\t\t\t[style.--pptx-selection-height.px]=\"singleSelected()?.height\"\n\t\t\t\t\t(pointerdown)=\"onHandlePointerDown($event, h.handle)\"\n\t\t\t\t\t(keydown)=\"onResizeHandleKeydown($event, h.handle)\"\n\t\t\t\t>\n\t\t\t\t\t<span data-pptx-handle-hit [ngStyle]=\"resizeHitAreaStyle(h.handle)\"></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"controlArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@if (marqueeRect(); as mr) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-marquee\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"mr.x\"\n\t\t\t\t\t[style.top.px]=\"mr.y\"\n\t\t\t\t\t[style.width.px]=\"mr.width\"\n\t\t\t\t\t[style.height.px]=\"mr.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (g of snapGuides(); track $index) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-snap-guide\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : g.start\"\n\t\t\t\t\t[style.top.px]=\"g.axis === 'x' ? g.start : g.pos\"\n\t\t\t\t\t[style.width.px]=\"g.axis === 'x' ? 0 : g.end - g.start\"\n\t\t\t\t\t[style.height.px]=\"g.axis === 'x' ? g.end - g.start : 0\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@if (rotateHandle(); as rh) {\n\t\t\t\t@let rotateArtwork = selectionControlArtwork(rh);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-rotate-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.z-index]=\"editingBox() ? 10003 : null\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"rotate\"\n\t\t\t\t\t[pptxRotateHandleFor]=\"singleSelected()?.id\"\n\t\t\t\t\t[pptxRotateHandleStage]=\"stage\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.rotate' | translate\"\n\t\t\t\t\t[ngStyle]=\"rotateArtwork.frame\"\n\t\t\t\t\t(pointerdown)=\"onRotatePointerDown($event)\"\n\t\t\t\t\t(keydown)=\"onRotateHandleKeydown($event)\"\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"rotateArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tShape-adjustment affordances (amber diamonds), ONE per `a:avLst`\n\t\t\t\tguide. Existence, position and cursor all come from the SHARED\n\t\t\t\t`getShapeAdjustmentHandleDescriptors`, so they appear only for a\n\t\t\t\tgeometry with an adjustable parameter, sit where the other four\n\t\t\t\tbindings put them, and are absent for a shape locked with\n\t\t\t\t`noAdjustHandles`. Dragging one writes `shapeAdjustments`; it does\n\t\t\t\tNOT resize the element.\n\t\t\t-->\n\t\t\t@for (ah of adjustHandles(); track ah.key) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-adjust-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"adjust\"\n\t\t\t\t\t[attr.data-pptx-adjust-key]=\"ah.key\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.adjust' | translate\"\n\t\t\t\t\t[style.left.px]=\"ah.left\"\n\t\t\t\t\t[style.top.px]=\"ah.top\"\n\t\t\t\t\t[style.width.px]=\"ah.size\"\n\t\t\t\t\t[style.height.px]=\"ah.size\"\n\t\t\t\t\t[style.cursor]=\"ah.cursor\"\n\t\t\t\t\t(pointerdown)=\"onAdjustPointerDown($event, ah)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tConnector endpoint authoring: drag an end onto a shape's connection\n\t\t\t\tpoint to bind it (`a:stCxn` / `a:endCxn`), or off one to detach.\n\t\t\t\tCandidate sites are revealed only while an end is in flight so they\n\t\t\t\tnever obscure the deck at rest.\n\t\t\t-->\n\t\t\t@for (site of connectorSiteCandidates(); track site.key) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-connection-site\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-snapped]=\"site.snapped\"\n\t\t\t\t\tdata-pptx-connection-site\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[style.left.px]=\"site.left\"\n\t\t\t\t\t[style.top.px]=\"site.top\"\n\t\t\t\t\t[style.width.px]=\"site.size\"\n\t\t\t\t\t[style.height.px]=\"site.size\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (ep of connectorEndpoints(); track ep.kind) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-connector-endpoint\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.data-pptx-connector-endpoint]=\"ep.kind\"\n\t\t\t\t\t[attr.data-pptx-connector-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.aria-label]=\"\n\t\t\t\t\t\t(ep.kind === 'start'\n\t\t\t\t\t\t\t? 'pptx.canvas.connectorEndpointStart'\n\t\t\t\t\t\t\t: 'pptx.canvas.connectorEndpointEnd'\n\t\t\t\t\t\t) | translate\n\t\t\t\t\t\"\n\t\t\t\t\t[style.left.px]=\"ep.left\"\n\t\t\t\t\t[style.top.px]=\"ep.top\"\n\t\t\t\t\t[style.width.px]=\"ep.size\"\n\t\t\t\t\t[style.height.px]=\"ep.size\"\n\t\t\t\t\t(pointerdown)=\"onConnectorEndpointPointerDown($event, ep.kind)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t@if (editingBox(); as eb) {\n\t\t\t\t@if (inlineListSeed(); as seed) {\n\t\t\t\t\t@for (session of [seed]; track session) {\n\t\t\t\t\t\t<pptx-inline-list-editor\n\t\t\t\t\t\t\t[element]=\"eb.element\"\n\t\t\t\t\t\t\t[seed]=\"session\"\n\t\t\t\t\t\t\t[activationSelection]=\"listActivationSelection()\"\n\t\t\t\t\t\t\t[spellCheck]=\"spellCheck()\"\n\t\t\t\t\t\t\t(textInput)=\"textInput.emit($event)\"\n\t\t\t\t\t\t\t(textCommit)=\"textCommit.emit($event)\"\n\t\t\t\t\t\t\t(textCancel)=\"textCancel.emit()\"\n\t\t\t\t\t\t\t(textFormat)=\"textFormat.emit($event)\"\n\t\t\t\t\t\t\t(listSession)=\"listSession.emit($event)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t} @else {\n\t\t\t\t\t<textarea\n\t\t\t\t\t\t#textEditor\n\t\t\t\t\t\tdata-inline-editor\n\t\t\t\t\t\tclass=\"pptx-ng-text-editor\"\n\t\t\t\t\t\taria-label=\"Edit slide text\"\n\t\t\t\t\t\t[spellcheck]=\"spellCheck()\"\n\t\t\t\t\t\t[style.left.px]=\"eb.x\"\n\t\t\t\t\t\t[style.top.px]=\"eb.y\"\n\t\t\t\t\t\t[style.width.px]=\"eb.width\"\n\t\t\t\t\t\t[style.height.px]=\"eb.height\"\n\t\t\t\t\t\t(pointerdown)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t(input)=\"onTextInput($event, eb.id)\"\n\t\t\t\t\t\t(blur)=\"commitText($event, eb.id)\"\n\t\t\t\t\t\t(keydown)=\"onEditorKeydown($event)\"\n\t\t\t\t\t></textarea>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tView overlays: editor aids only, never on thumbnails/preview/presentation.\n\t\t\t\tAll are pointer-events:none so they never break selection/drag.\n\t\t\t\tNone carry data-pptx-element / aria-roledescription / data-pptx-viewport.\n\t\t\t-->\n\t\t\t@if (interactive() && showGrid()) {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-grid\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t<pattern\n\t\t\t\t\t\t\t[attr.id]=\"gridPatternId\"\n\t\t\t\t\t\t\t[attr.width]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\t[attr.height]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\tpatternUnits=\"userSpaceOnUse\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t[attr.cx]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\t[attr.cy]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\tr=\"0.6\"\n\t\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.55)\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</pattern>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\t\t[attr.fill]=\"'url(#' + gridPatternId + ')'\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t@if (interactive() && showGuides()) {\n\t\t\t\t<!--\n\t\t\t\t\tCenter crosshair: one horizontal line and one vertical line\n\t\t\t\t\tthrough the midpoint of the slide. Static; draggable guides\n\t\t\t\t\tare a follow-up.\n\t\t\t\t-->\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-guides\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<!-- Horizontal center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t\t[attr.y1]=\"canvasSize().height / 2\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height / 2\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t\t<!-- Vertical center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"canvasSize().width / 2\"\n\t\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width / 2\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tUser-created ruler guides.\n\t\t\t\tEach guide has a non-interactive line body and an interactive\n\t\t\t\tdrag handle. Double-click the handle to delete the guide.\n\n\t\t\t\tDeliberately OUTSIDE the showGuides() gate above (which owns the\n\t\t\t\tstatic centre crosshair): a guide the user just dragged off a ruler\n\t\t\t\tstrip must be visible immediately, as it is in Vue and Vanilla.\n\t\t\t\tWhile it lived under that gate, the drag-out gesture silently\n\t\t\t\tproduced an invisible guide unless View > Guides happened to be on.\n\t\t\t-->\n\t\t\t@if (interactive()) {\n\t\t\t\t@for (g of rulerGuidesSvc.rulerGuides(); track g.id) {\n\t\t\t\t\t<!-- Guide line body: pointer-events:none -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-line\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '1px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '1px' : '100%'\"\n\t\t\t\t\t></div>\n\t\t\t\t\t<!-- Drag handle: pointer-events:auto -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-handle\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.cursor]=\"g.axis === 'x' ? 'col-resize' : 'row-resize'\"\n\t\t\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onGuidePointerDown($event, g.id, g.axis)\"\n\t\t\t\t\t\t(dblclick)=\"rulerGuidesSvc.onGuideDoubleClick($event, g.id)\"\n\t\t\t\t\t\t[title]=\"'pptx.canvas.guideTooltip' | translate\"\n\t\t\t\t\t></div>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tLive ink stroke preview: shown while the user is drawing.\n\t\t\t\tpointer-events:none so it never intercepts element gestures.\n\t\t\t\tNo data-pptx-element / aria-roledescription / data-pptx-viewport.\n\n\t\t\t\tRenders `inkDrawing.liveStrokeView()`: the same plain-path /\n\t\t\t\tpressure-circle / tilt-nib decision `InkRendererComponent` makes for\n\t\t\t\ta committed stroke, so a calligraphic lean or pressure-variable\n\t\t\t\twidth shows up while the pointer is still down, not only after\n\t\t\t\trelease.\n\t\t\t-->\n\t\t\t@if (inkDrawing.active() && inkDrawing.liveStrokeView() && drawTool() !== 'select') {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-ink-preview\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\tstyle=\"position: absolute; inset: 0; pointer-events: none; z-index: 70\"\n\t\t\t\t>\n\t\t\t\t\t@if (inkDrawing.liveStrokeView()?.nibMarks; as nibMarks) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (m of nibMarks; track $index) {\n\t\t\t\t\t\t\t\t<ellipse\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"m.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"m.cy\"\n\t\t\t\t\t\t\t\t\t[attr.rx]=\"m.rPerp\"\n\t\t\t\t\t\t\t\t\t[attr.ry]=\"m.rTilt\"\n\t\t\t\t\t\t\t\t\t[attr.transform]=\"'rotate(' + m.rotationDeg + ' ' + m.cx + ' ' + m.cy + ')'\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else if (inkDrawing.liveStrokeView()?.circles; as circles) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (c of circles; track $index) {\n\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"c.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"c.cy\"\n\t\t\t\t\t\t\t\t\t[attr.r]=\"c.r\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t[attr.d]=\"inkDrawing.liveStrokeView()?.d\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t[attr.stroke]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t[attr.stroke-width]=\"inkDrawing.liveStrokeView()?.width\"\n\t\t\t\t\t\t\t[attr.stroke-opacity]=\"inkDrawing.liveStrokeView()?.opacity\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tProjected overlays (collaboration cursors + remote selections).\n\t\t\t\tThey live INSIDE the scaled stage so the stage's CSS\n\t\t\t\ttransform:scale() applies the on-screen scale exactly once and\n\t\t\t\tthey can be authored in raw, unscaled slide coordinates, exactly\n\t\t\t\tlike the React/Vue overlays.\n\t\t\t-->\n\t\t\t<ng-content />\n\t\t</div>\n\n\t\t<!--\n\t\t\tRuler strips: siblings to the scaled stage inside the wrapper div,\n\t\t\tabsolutely positioned at top:0/left:0 within the wrapper's padding area.\n\t\t\tThe wrapper's padding (RULER_THICKNESS px top+left) reserves space for\n\t\t\tthese strips so the stage content starts below/right of them.\n\t\t\tpointer-events:none so they never intercept element gestures.\n\t\t-->\n\t\t@if (interactive() && showRulers()) {\n\t\t\t<!-- Corner square at the intersection of the two ruler strips -->\n\t\t\t<div class=\"pptx-ng-ruler-corner\" aria-hidden=\"true\"></div>\n\n\t\t\t<!--\n\t\t\t\tHorizontal ruler: spans the top padding row of the wrapper.\n\t\t\t\tA guide is dropped on pointer-UP (not down), resolved by the shared\n\t\t\t\trulerDragToGuidePosition, so a stray click on the strip cannot\n\t\t\t\tdrop a guide the user never dragged out - the rule React, Vue,\n\t\t\t\tSvelte and Vanilla all follow.\n\t\t\t-->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-h\"\n\t\t\t\tdata-pptx-ruler=\"h\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t[style.cursor]=\"editable() ? 'row-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('h', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('h', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (hRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.x]=\"highlight.start\"\n\t\t\t\t\t\ty=\"0\"\n\t\t\t\t\t\t[attr.width]=\"highlight.span\"\n\t\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t[attr.y1]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.x2]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.y2]=\"rulerThickness - 0.5\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of hRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"tick.position\"\n\t\t\t\t\t\t[attr.y1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.x2]=\"tick.position\"\n\t\t\t\t\t\t[attr.y2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t[attr.x]=\"tick.position + 2\"\n\t\t\t\t\t\t\t[attr.y]=\"rulerFontSize + 1\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\n\t\t\t<!-- Vertical ruler: spans the left padding column of the wrapper -->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-v\"\n\t\t\t\tdata-pptx-ruler=\"v\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t[style.cursor]=\"editable() ? 'col-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('v', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('v', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (vRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\tx=\"0\"\n\t\t\t\t\t\t[attr.y]=\"highlight.start\"\n\t\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.height]=\"highlight.span\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\t[attr.x1]=\"rulerThickness - 0.5\"\n\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t[attr.x2]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.y2]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of vRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.y1]=\"tick.position\"\n\t\t\t\t\t\t[attr.x2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\t[attr.y2]=\"tick.position\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx=\"2\"\n\t\t\t\t\t\t\t[attr.y]=\"tick.position + rulerFontSize + 2\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\t\t}\n\t</div>\n</div>\n", styles: [".pptx-ng-canvas-stage.is-editable{touch-action:none}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ElementRendererComponent, selector: "pptx-element-renderer", inputs: ["element", "mediaDataUrls", "zIndex", "obstacles", "canvasWidth", "canvasHeight", "interactive", "marked", "exposeElementId", "presenting", "editable", "fieldContext", "slideElements", "editTemplateMode", "parentGroupFill", "editingElementId"], outputs: ["cellCommit", "tableChange"] }, { kind: "component", type: AiFocusHighlightOverlayComponent, selector: "pptx-ai-focus-highlight-overlay", inputs: ["highlights", "elements", "activeSlideIndex"] }, { kind: "component", type: AiChangeOverlayComponent, selector: "pptx-ai-change-overlay", inputs: ["batch", "activeSlideIndex"] }, { kind: "component", type: ActiveXControlsOverlayComponent, selector: "pptx-activex-controls-overlay", inputs: ["controls", "canvasSize"] }, { kind: "component", type: InlineListEditorComponent, selector: "pptx-inline-list-editor", inputs: ["element", "seed", "spellCheck", "activationSelection"], outputs: ["textInput", "textCommit", "textCancel", "textFormat", "listSession"] }, { kind: "directive", type: RotateHandlePlacementDirective, selector: "[pptxRotateHandleFor]", inputs: ["pptxRotateHandleFor", "pptxRotateHandleStage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
130032
|
+
], viewQueries: [{ propertyName: "textEditor", first: true, predicate: ["textEditor"], descendants: true, isSignal: true }, { propertyName: "listEditor", first: true, predicate: InlineListEditorComponent, descendants: true, isSignal: true }, { propertyName: "stageRef", first: true, predicate: ["stage"], descendants: true, isSignal: true }, { propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n\t#viewport\n\tclass=\"pptx-ng-canvas-viewport\"\n\t[attr.data-pptx-viewport]=\"interactive() ? '' : null\"\n\t(pointerdown)=\"onViewportPointerDown($event)\"\n>\n\t<div class=\"pptx-ng-canvas-wrapper\" [ngStyle]=\"wrapperStyle()\">\n\t\t<div\n\t\t\t#stage\n\t\t\tclass=\"pptx-ng-canvas-stage\"\n\t\t\t[class.is-editable]=\"editable()\"\n\t\t\trole=\"region\"\n\t\t\t[attr.aria-roledescription]=\"interactive() || presenting() ? 'slide' : null\"\n\t\t\t[attr.data-pptx-ai-active]=\"aiActive() ? 'true' : null\"\n\t\t\t[ngStyle]=\"stageStyle()\"\n\t\t\t(pointerdown)=\"onStagePointerDown($event)\"\n\t\t\t(contextmenu)=\"onContextMenu($event)\"\n\t\t\t(dblclick)=\"onDblClick($event)\"\n\t\t>\n\t\t\t<!--\n\t\t\t\tTemplate layer: inherited master/layout elements, rendered BEHIND\n\t\t\t\tthe slide's own elements (lower z-index). Interactive + given the\n\t\t\t\tamber editable affordance only while editTemplateMode is on; when\n\t\t\t\toff they render inertly with no affordance, exactly as core\n\t\t\t\tdelivered them. `marked` keeps the data-pptx-element contract on\n\t\t\t\tthem even while locked: the marker means \"rendered slide element\",\n\t\t\t\tnot \"editable right now\".\n\t\t\t-->\n\t\t\t@for (element of visibleTemplateElements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive() && editTemplateMode()\"\n\t\t\t\t\t[marked]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[editable]=\"editable() && editTemplateMode()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"editTemplateMode()\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@for (element of elements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"visibleTemplateElements().length + i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[presenting]=\"presenting()\"\n\t\t\t\t\t[editable]=\"editable()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"false\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<pptx-activex-controls-overlay\n\t\t\t\t[controls]=\"slide()?.activeXControls ?? []\"\n\t\t\t\t[canvasSize]=\"canvasSize()\"\n\t\t\t/>\n\t\t\t@if (aiHighlights().length > 0 || aiActive()) {\n\t\t\t\t<pptx-ai-focus-highlight-overlay\n\t\t\t\t\t[highlights]=\"aiHighlights()\"\n\t\t\t\t\t[elements]=\"elements()\"\n\t\t\t\t\t[activeSlideIndex]=\"aiActiveSlideIndex()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@if (aiChangeBatch(); as batch) {\n\t\t\t\t<pptx-ai-change-overlay [batch]=\"batch\" [activeSlideIndex]=\"aiActiveSlideIndex()\" />\n\t\t\t}\n\t\t\t@for (box of selectionBoxes(); track box.id) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-selection\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"box.x\"\n\t\t\t\t\t[style.top.px]=\"box.y\"\n\t\t\t\t\t[style.width.px]=\"box.width\"\n\t\t\t\t\t[style.height.px]=\"box.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (h of handleBoxes(); track h.handle) {\n\t\t\t\t@let controlArtwork = selectionControlArtwork(h, h.handle);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"resize\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.resize' | translate: { handle: h.handle }\"\n\t\t\t\t\t[ngStyle]=\"controlArtwork.frame\"\n\t\t\t\t\t[style.cursor]=\"h.cursor\"\n\t\t\t\t\t[style.--pptx-selection-width.px]=\"singleSelected()?.width\"\n\t\t\t\t\t[style.--pptx-selection-height.px]=\"singleSelected()?.height\"\n\t\t\t\t\t(pointerdown)=\"onHandlePointerDown($event, h.handle)\"\n\t\t\t\t\t(keydown)=\"onResizeHandleKeydown($event, h.handle)\"\n\t\t\t\t>\n\t\t\t\t\t<span data-pptx-handle-hit [ngStyle]=\"resizeHitAreaStyle(h.handle)\"></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"controlArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@if (marqueeRect(); as mr) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-marquee\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"mr.x\"\n\t\t\t\t\t[style.top.px]=\"mr.y\"\n\t\t\t\t\t[style.width.px]=\"mr.width\"\n\t\t\t\t\t[style.height.px]=\"mr.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (g of snapGuides(); track $index) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-snap-guide\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : g.start\"\n\t\t\t\t\t[style.top.px]=\"g.axis === 'x' ? g.start : g.pos\"\n\t\t\t\t\t[style.width.px]=\"g.axis === 'x' ? 0 : g.end - g.start\"\n\t\t\t\t\t[style.height.px]=\"g.axis === 'x' ? g.end - g.start : 0\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@if (rotateHandle(); as rh) {\n\t\t\t\t@let rotateArtwork = selectionControlArtwork(rh);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-rotate-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.z-index]=\"editingBox() ? 10003 : null\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"rotate\"\n\t\t\t\t\t[pptxRotateHandleFor]=\"singleSelected()?.id\"\n\t\t\t\t\t[pptxRotateHandleStage]=\"stage\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.rotate' | translate\"\n\t\t\t\t\t[ngStyle]=\"rotateArtwork.frame\"\n\t\t\t\t\t(pointerdown)=\"onRotatePointerDown($event)\"\n\t\t\t\t\t(keydown)=\"onRotateHandleKeydown($event)\"\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"rotateArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tShape-adjustment affordances (amber diamonds), ONE per `a:avLst`\n\t\t\t\tguide. Existence, position and cursor all come from the SHARED\n\t\t\t\t`getShapeAdjustmentHandleDescriptors`, so they appear only for a\n\t\t\t\tgeometry with an adjustable parameter, sit where the other four\n\t\t\t\tbindings put them, and are absent for a shape locked with\n\t\t\t\t`noAdjustHandles`. Dragging one writes `shapeAdjustments`; it does\n\t\t\t\tNOT resize the element.\n\t\t\t-->\n\t\t\t@for (ah of adjustHandles(); track ah.key) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-adjust-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"adjust\"\n\t\t\t\t\t[attr.data-pptx-adjust-key]=\"ah.key\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.adjust' | translate\"\n\t\t\t\t\t[style.left.px]=\"ah.left\"\n\t\t\t\t\t[style.top.px]=\"ah.top\"\n\t\t\t\t\t[style.width.px]=\"ah.size\"\n\t\t\t\t\t[style.height.px]=\"ah.size\"\n\t\t\t\t\t[style.cursor]=\"ah.cursor\"\n\t\t\t\t\t(pointerdown)=\"onAdjustPointerDown($event, ah)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tPowerPoint's floating \"Chart Elements\"/\"Chart Styles\"/\"Chart\n\t\t\t\tFilters\" quick-action icons, shown just outside the chart's\n\t\t\t\ttop-right corner when it is the single selected element.\n\t\t\t-->\n\t\t\t@if (editable() && quickActionsChartElement(); as chartEl) {\n\t\t\t\t<pptx-chart-quick-actions-overlay\n\t\t\t\t\t[element]=\"chartEl\"\n\t\t\t\t\t[canEdit]=\"editable()\"\n\t\t\t\t\t[zoom]=\"effectiveScalePublic()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tConnector endpoint authoring: drag an end onto a shape's connection\n\t\t\t\tpoint to bind it (`a:stCxn` / `a:endCxn`), or off one to detach.\n\t\t\t\tCandidate sites are revealed only while an end is in flight so they\n\t\t\t\tnever obscure the deck at rest.\n\t\t\t-->\n\t\t\t@for (site of connectorSiteCandidates(); track site.key) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-connection-site\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-snapped]=\"site.snapped\"\n\t\t\t\t\tdata-pptx-connection-site\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[style.left.px]=\"site.left\"\n\t\t\t\t\t[style.top.px]=\"site.top\"\n\t\t\t\t\t[style.width.px]=\"site.size\"\n\t\t\t\t\t[style.height.px]=\"site.size\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (ep of connectorEndpoints(); track ep.kind) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-connector-endpoint\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.data-pptx-connector-endpoint]=\"ep.kind\"\n\t\t\t\t\t[attr.data-pptx-connector-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.aria-label]=\"\n\t\t\t\t\t\t(ep.kind === 'start'\n\t\t\t\t\t\t\t? 'pptx.canvas.connectorEndpointStart'\n\t\t\t\t\t\t\t: 'pptx.canvas.connectorEndpointEnd'\n\t\t\t\t\t\t) | translate\n\t\t\t\t\t\"\n\t\t\t\t\t[style.left.px]=\"ep.left\"\n\t\t\t\t\t[style.top.px]=\"ep.top\"\n\t\t\t\t\t[style.width.px]=\"ep.size\"\n\t\t\t\t\t[style.height.px]=\"ep.size\"\n\t\t\t\t\t(pointerdown)=\"onConnectorEndpointPointerDown($event, ep.kind)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t@if (editingBox(); as eb) {\n\t\t\t\t@if (inlineListSeed(); as seed) {\n\t\t\t\t\t@for (session of [seed]; track session) {\n\t\t\t\t\t\t<pptx-inline-list-editor\n\t\t\t\t\t\t\t[element]=\"eb.element\"\n\t\t\t\t\t\t\t[seed]=\"session\"\n\t\t\t\t\t\t\t[activationSelection]=\"listActivationSelection()\"\n\t\t\t\t\t\t\t[spellCheck]=\"spellCheck()\"\n\t\t\t\t\t\t\t(textInput)=\"textInput.emit($event)\"\n\t\t\t\t\t\t\t(textCommit)=\"textCommit.emit($event)\"\n\t\t\t\t\t\t\t(textCancel)=\"textCancel.emit()\"\n\t\t\t\t\t\t\t(textFormat)=\"textFormat.emit($event)\"\n\t\t\t\t\t\t\t(listSession)=\"listSession.emit($event)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t} @else {\n\t\t\t\t\t<textarea\n\t\t\t\t\t\t#textEditor\n\t\t\t\t\t\tdata-inline-editor\n\t\t\t\t\t\tclass=\"pptx-ng-text-editor\"\n\t\t\t\t\t\taria-label=\"Edit slide text\"\n\t\t\t\t\t\t[spellcheck]=\"spellCheck()\"\n\t\t\t\t\t\t[style.left.px]=\"eb.x\"\n\t\t\t\t\t\t[style.top.px]=\"eb.y\"\n\t\t\t\t\t\t[style.width.px]=\"eb.width\"\n\t\t\t\t\t\t[style.height.px]=\"eb.height\"\n\t\t\t\t\t\t(pointerdown)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t(input)=\"onTextInput($event, eb.id)\"\n\t\t\t\t\t\t(blur)=\"commitText($event, eb.id)\"\n\t\t\t\t\t\t(keydown)=\"onEditorKeydown($event)\"\n\t\t\t\t\t></textarea>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tView overlays: editor aids only, never on thumbnails/preview/presentation.\n\t\t\t\tAll are pointer-events:none so they never break selection/drag.\n\t\t\t\tNone carry data-pptx-element / aria-roledescription / data-pptx-viewport.\n\t\t\t-->\n\t\t\t@if (interactive() && showGrid()) {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-grid\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t<pattern\n\t\t\t\t\t\t\t[attr.id]=\"gridPatternId\"\n\t\t\t\t\t\t\t[attr.width]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\t[attr.height]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\tpatternUnits=\"userSpaceOnUse\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t[attr.cx]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\t[attr.cy]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\tr=\"0.6\"\n\t\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.55)\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</pattern>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\t\t[attr.fill]=\"'url(#' + gridPatternId + ')'\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t@if (interactive() && showGuides()) {\n\t\t\t\t<!--\n\t\t\t\t\tCenter crosshair: one horizontal line and one vertical line\n\t\t\t\t\tthrough the midpoint of the slide. Static; draggable guides\n\t\t\t\t\tare a follow-up.\n\t\t\t\t-->\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-guides\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<!-- Horizontal center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t\t[attr.y1]=\"canvasSize().height / 2\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height / 2\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t\t<!-- Vertical center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"canvasSize().width / 2\"\n\t\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width / 2\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tUser-created ruler guides.\n\t\t\t\tEach guide has a non-interactive line body and an interactive\n\t\t\t\tdrag handle. Double-click the handle to delete the guide.\n\n\t\t\t\tDeliberately OUTSIDE the showGuides() gate above (which owns the\n\t\t\t\tstatic centre crosshair): a guide the user just dragged off a ruler\n\t\t\t\tstrip must be visible immediately, as it is in Vue and Vanilla.\n\t\t\t\tWhile it lived under that gate, the drag-out gesture silently\n\t\t\t\tproduced an invisible guide unless View > Guides happened to be on.\n\t\t\t-->\n\t\t\t@if (interactive()) {\n\t\t\t\t@for (g of rulerGuidesSvc.rulerGuides(); track g.id) {\n\t\t\t\t\t<!-- Guide line body: pointer-events:none -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-line\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '1px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '1px' : '100%'\"\n\t\t\t\t\t></div>\n\t\t\t\t\t<!-- Drag handle: pointer-events:auto -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-handle\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.cursor]=\"g.axis === 'x' ? 'col-resize' : 'row-resize'\"\n\t\t\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onGuidePointerDown($event, g.id, g.axis)\"\n\t\t\t\t\t\t(dblclick)=\"rulerGuidesSvc.onGuideDoubleClick($event, g.id)\"\n\t\t\t\t\t\t[title]=\"'pptx.canvas.guideTooltip' | translate\"\n\t\t\t\t\t></div>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tLive ink stroke preview: shown while the user is drawing.\n\t\t\t\tpointer-events:none so it never intercepts element gestures.\n\t\t\t\tNo data-pptx-element / aria-roledescription / data-pptx-viewport.\n\n\t\t\t\tRenders `inkDrawing.liveStrokeView()`: the same plain-path /\n\t\t\t\tpressure-circle / tilt-nib decision `InkRendererComponent` makes for\n\t\t\t\ta committed stroke, so a calligraphic lean or pressure-variable\n\t\t\t\twidth shows up while the pointer is still down, not only after\n\t\t\t\trelease.\n\t\t\t-->\n\t\t\t@if (inkDrawing.active() && inkDrawing.liveStrokeView() && drawTool() !== 'select') {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-ink-preview\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\tstyle=\"position: absolute; inset: 0; pointer-events: none; z-index: 70\"\n\t\t\t\t>\n\t\t\t\t\t@if (inkDrawing.liveStrokeView()?.nibMarks; as nibMarks) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (m of nibMarks; track $index) {\n\t\t\t\t\t\t\t\t<ellipse\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"m.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"m.cy\"\n\t\t\t\t\t\t\t\t\t[attr.rx]=\"m.rPerp\"\n\t\t\t\t\t\t\t\t\t[attr.ry]=\"m.rTilt\"\n\t\t\t\t\t\t\t\t\t[attr.transform]=\"'rotate(' + m.rotationDeg + ' ' + m.cx + ' ' + m.cy + ')'\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else if (inkDrawing.liveStrokeView()?.circles; as circles) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (c of circles; track $index) {\n\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"c.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"c.cy\"\n\t\t\t\t\t\t\t\t\t[attr.r]=\"c.r\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t[attr.d]=\"inkDrawing.liveStrokeView()?.d\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t[attr.stroke]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t[attr.stroke-width]=\"inkDrawing.liveStrokeView()?.width\"\n\t\t\t\t\t\t\t[attr.stroke-opacity]=\"inkDrawing.liveStrokeView()?.opacity\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tProjected overlays (collaboration cursors + remote selections).\n\t\t\t\tThey live INSIDE the scaled stage so the stage's CSS\n\t\t\t\ttransform:scale() applies the on-screen scale exactly once and\n\t\t\t\tthey can be authored in raw, unscaled slide coordinates, exactly\n\t\t\t\tlike the React/Vue overlays.\n\t\t\t-->\n\t\t\t<ng-content />\n\t\t</div>\n\n\t\t<!--\n\t\t\tRuler strips: siblings to the scaled stage inside the wrapper div,\n\t\t\tabsolutely positioned at top:0/left:0 within the wrapper's padding area.\n\t\t\tThe wrapper's padding (RULER_THICKNESS px top+left) reserves space for\n\t\t\tthese strips so the stage content starts below/right of them.\n\t\t\tpointer-events:none so they never intercept element gestures.\n\t\t-->\n\t\t@if (interactive() && showRulers()) {\n\t\t\t<!-- Corner square at the intersection of the two ruler strips -->\n\t\t\t<div class=\"pptx-ng-ruler-corner\" aria-hidden=\"true\"></div>\n\n\t\t\t<!--\n\t\t\t\tHorizontal ruler: spans the top padding row of the wrapper.\n\t\t\t\tA guide is dropped on pointer-UP (not down), resolved by the shared\n\t\t\t\trulerDragToGuidePosition, so a stray click on the strip cannot\n\t\t\t\tdrop a guide the user never dragged out - the rule React, Vue,\n\t\t\t\tSvelte and Vanilla all follow.\n\t\t\t-->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-h\"\n\t\t\t\tdata-pptx-ruler=\"h\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t[style.cursor]=\"editable() ? 'row-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('h', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('h', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (hRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.x]=\"highlight.start\"\n\t\t\t\t\t\ty=\"0\"\n\t\t\t\t\t\t[attr.width]=\"highlight.span\"\n\t\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t[attr.y1]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.x2]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.y2]=\"rulerThickness - 0.5\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of hRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"tick.position\"\n\t\t\t\t\t\t[attr.y1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.x2]=\"tick.position\"\n\t\t\t\t\t\t[attr.y2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t[attr.x]=\"tick.position + 2\"\n\t\t\t\t\t\t\t[attr.y]=\"rulerFontSize + 1\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\n\t\t\t<!-- Vertical ruler: spans the left padding column of the wrapper -->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-v\"\n\t\t\t\tdata-pptx-ruler=\"v\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t[style.cursor]=\"editable() ? 'col-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('v', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('v', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (vRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\tx=\"0\"\n\t\t\t\t\t\t[attr.y]=\"highlight.start\"\n\t\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.height]=\"highlight.span\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\t[attr.x1]=\"rulerThickness - 0.5\"\n\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t[attr.x2]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.y2]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of vRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.y1]=\"tick.position\"\n\t\t\t\t\t\t[attr.x2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\t[attr.y2]=\"tick.position\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx=\"2\"\n\t\t\t\t\t\t\t[attr.y]=\"tick.position + rulerFontSize + 2\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\t\t}\n\t</div>\n</div>\n", styles: [".pptx-ng-canvas-stage.is-editable{touch-action:none}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ElementRendererComponent, selector: "pptx-element-renderer", inputs: ["element", "mediaDataUrls", "zIndex", "obstacles", "canvasWidth", "canvasHeight", "interactive", "marked", "exposeElementId", "presenting", "editable", "fieldContext", "slideElements", "editTemplateMode", "parentGroupFill", "editingElementId"], outputs: ["cellCommit", "tableChange"] }, { kind: "component", type: AiFocusHighlightOverlayComponent, selector: "pptx-ai-focus-highlight-overlay", inputs: ["highlights", "elements", "activeSlideIndex"] }, { kind: "component", type: AiChangeOverlayComponent, selector: "pptx-ai-change-overlay", inputs: ["batch", "activeSlideIndex"] }, { kind: "component", type: ActiveXControlsOverlayComponent, selector: "pptx-activex-controls-overlay", inputs: ["controls", "canvasSize"] }, { kind: "component", type: InlineListEditorComponent, selector: "pptx-inline-list-editor", inputs: ["element", "seed", "spellCheck", "activationSelection"], outputs: ["textInput", "textCommit", "textCancel", "textFormat", "listSession"] }, { kind: "directive", type: RotateHandlePlacementDirective, selector: "[pptxRotateHandleFor]", inputs: ["pptxRotateHandleFor", "pptxRotateHandleStage"] }, { kind: "component", type: ChartQuickActionsOverlayComponent, selector: "pptx-chart-quick-actions-overlay", inputs: ["element", "canEdit", "zoom"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
128913
130033
|
}
|
|
128914
130034
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: SlideCanvasComponent, decorators: [{
|
|
128915
130035
|
type: Component,
|
|
@@ -128930,7 +130050,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
128930
130050
|
ActiveXControlsOverlayComponent,
|
|
128931
130051
|
InlineListEditorComponent,
|
|
128932
130052
|
RotateHandlePlacementDirective,
|
|
128933
|
-
], template: "<div\n\t#viewport\n\tclass=\"pptx-ng-canvas-viewport\"\n\t[attr.data-pptx-viewport]=\"interactive() ? '' : null\"\n\t(pointerdown)=\"onViewportPointerDown($event)\"\n>\n\t<div class=\"pptx-ng-canvas-wrapper\" [ngStyle]=\"wrapperStyle()\">\n\t\t<div\n\t\t\t#stage\n\t\t\tclass=\"pptx-ng-canvas-stage\"\n\t\t\t[class.is-editable]=\"editable()\"\n\t\t\trole=\"region\"\n\t\t\t[attr.aria-roledescription]=\"interactive() || presenting() ? 'slide' : null\"\n\t\t\t[attr.data-pptx-ai-active]=\"aiActive() ? 'true' : null\"\n\t\t\t[ngStyle]=\"stageStyle()\"\n\t\t\t(pointerdown)=\"onStagePointerDown($event)\"\n\t\t\t(contextmenu)=\"onContextMenu($event)\"\n\t\t\t(dblclick)=\"onDblClick($event)\"\n\t\t>\n\t\t\t<!--\n\t\t\t\tTemplate layer: inherited master/layout elements, rendered BEHIND\n\t\t\t\tthe slide's own elements (lower z-index). Interactive + given the\n\t\t\t\tamber editable affordance only while editTemplateMode is on; when\n\t\t\t\toff they render inertly with no affordance, exactly as core\n\t\t\t\tdelivered them. `marked` keeps the data-pptx-element contract on\n\t\t\t\tthem even while locked: the marker means \"rendered slide element\",\n\t\t\t\tnot \"editable right now\".\n\t\t\t-->\n\t\t\t@for (element of visibleTemplateElements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive() && editTemplateMode()\"\n\t\t\t\t\t[marked]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[editable]=\"editable() && editTemplateMode()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"editTemplateMode()\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@for (element of elements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"visibleTemplateElements().length + i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[presenting]=\"presenting()\"\n\t\t\t\t\t[editable]=\"editable()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"false\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<pptx-activex-controls-overlay\n\t\t\t\t[controls]=\"slide()?.activeXControls ?? []\"\n\t\t\t\t[canvasSize]=\"canvasSize()\"\n\t\t\t/>\n\t\t\t@if (aiHighlights().length > 0 || aiActive()) {\n\t\t\t\t<pptx-ai-focus-highlight-overlay\n\t\t\t\t\t[highlights]=\"aiHighlights()\"\n\t\t\t\t\t[elements]=\"elements()\"\n\t\t\t\t\t[activeSlideIndex]=\"aiActiveSlideIndex()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@if (aiChangeBatch(); as batch) {\n\t\t\t\t<pptx-ai-change-overlay [batch]=\"batch\" [activeSlideIndex]=\"aiActiveSlideIndex()\" />\n\t\t\t}\n\t\t\t@for (box of selectionBoxes(); track box.id) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-selection\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"box.x\"\n\t\t\t\t\t[style.top.px]=\"box.y\"\n\t\t\t\t\t[style.width.px]=\"box.width\"\n\t\t\t\t\t[style.height.px]=\"box.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (h of handleBoxes(); track h.handle) {\n\t\t\t\t@let controlArtwork = selectionControlArtwork(h, h.handle);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"resize\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.resize' | translate: { handle: h.handle }\"\n\t\t\t\t\t[ngStyle]=\"controlArtwork.frame\"\n\t\t\t\t\t[style.cursor]=\"h.cursor\"\n\t\t\t\t\t[style.--pptx-selection-width.px]=\"singleSelected()?.width\"\n\t\t\t\t\t[style.--pptx-selection-height.px]=\"singleSelected()?.height\"\n\t\t\t\t\t(pointerdown)=\"onHandlePointerDown($event, h.handle)\"\n\t\t\t\t\t(keydown)=\"onResizeHandleKeydown($event, h.handle)\"\n\t\t\t\t>\n\t\t\t\t\t<span data-pptx-handle-hit [ngStyle]=\"resizeHitAreaStyle(h.handle)\"></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"controlArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@if (marqueeRect(); as mr) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-marquee\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"mr.x\"\n\t\t\t\t\t[style.top.px]=\"mr.y\"\n\t\t\t\t\t[style.width.px]=\"mr.width\"\n\t\t\t\t\t[style.height.px]=\"mr.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (g of snapGuides(); track $index) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-snap-guide\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : g.start\"\n\t\t\t\t\t[style.top.px]=\"g.axis === 'x' ? g.start : g.pos\"\n\t\t\t\t\t[style.width.px]=\"g.axis === 'x' ? 0 : g.end - g.start\"\n\t\t\t\t\t[style.height.px]=\"g.axis === 'x' ? g.end - g.start : 0\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@if (rotateHandle(); as rh) {\n\t\t\t\t@let rotateArtwork = selectionControlArtwork(rh);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-rotate-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.z-index]=\"editingBox() ? 10003 : null\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"rotate\"\n\t\t\t\t\t[pptxRotateHandleFor]=\"singleSelected()?.id\"\n\t\t\t\t\t[pptxRotateHandleStage]=\"stage\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.rotate' | translate\"\n\t\t\t\t\t[ngStyle]=\"rotateArtwork.frame\"\n\t\t\t\t\t(pointerdown)=\"onRotatePointerDown($event)\"\n\t\t\t\t\t(keydown)=\"onRotateHandleKeydown($event)\"\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"rotateArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tShape-adjustment affordances (amber diamonds), ONE per `a:avLst`\n\t\t\t\tguide. Existence, position and cursor all come from the SHARED\n\t\t\t\t`getShapeAdjustmentHandleDescriptors`, so they appear only for a\n\t\t\t\tgeometry with an adjustable parameter, sit where the other four\n\t\t\t\tbindings put them, and are absent for a shape locked with\n\t\t\t\t`noAdjustHandles`. Dragging one writes `shapeAdjustments`; it does\n\t\t\t\tNOT resize the element.\n\t\t\t-->\n\t\t\t@for (ah of adjustHandles(); track ah.key) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-adjust-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"adjust\"\n\t\t\t\t\t[attr.data-pptx-adjust-key]=\"ah.key\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.adjust' | translate\"\n\t\t\t\t\t[style.left.px]=\"ah.left\"\n\t\t\t\t\t[style.top.px]=\"ah.top\"\n\t\t\t\t\t[style.width.px]=\"ah.size\"\n\t\t\t\t\t[style.height.px]=\"ah.size\"\n\t\t\t\t\t[style.cursor]=\"ah.cursor\"\n\t\t\t\t\t(pointerdown)=\"onAdjustPointerDown($event, ah)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tConnector endpoint authoring: drag an end onto a shape's connection\n\t\t\t\tpoint to bind it (`a:stCxn` / `a:endCxn`), or off one to detach.\n\t\t\t\tCandidate sites are revealed only while an end is in flight so they\n\t\t\t\tnever obscure the deck at rest.\n\t\t\t-->\n\t\t\t@for (site of connectorSiteCandidates(); track site.key) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-connection-site\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-snapped]=\"site.snapped\"\n\t\t\t\t\tdata-pptx-connection-site\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[style.left.px]=\"site.left\"\n\t\t\t\t\t[style.top.px]=\"site.top\"\n\t\t\t\t\t[style.width.px]=\"site.size\"\n\t\t\t\t\t[style.height.px]=\"site.size\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (ep of connectorEndpoints(); track ep.kind) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-connector-endpoint\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.data-pptx-connector-endpoint]=\"ep.kind\"\n\t\t\t\t\t[attr.data-pptx-connector-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.aria-label]=\"\n\t\t\t\t\t\t(ep.kind === 'start'\n\t\t\t\t\t\t\t? 'pptx.canvas.connectorEndpointStart'\n\t\t\t\t\t\t\t: 'pptx.canvas.connectorEndpointEnd'\n\t\t\t\t\t\t) | translate\n\t\t\t\t\t\"\n\t\t\t\t\t[style.left.px]=\"ep.left\"\n\t\t\t\t\t[style.top.px]=\"ep.top\"\n\t\t\t\t\t[style.width.px]=\"ep.size\"\n\t\t\t\t\t[style.height.px]=\"ep.size\"\n\t\t\t\t\t(pointerdown)=\"onConnectorEndpointPointerDown($event, ep.kind)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t@if (editingBox(); as eb) {\n\t\t\t\t@if (inlineListSeed(); as seed) {\n\t\t\t\t\t@for (session of [seed]; track session) {\n\t\t\t\t\t\t<pptx-inline-list-editor\n\t\t\t\t\t\t\t[element]=\"eb.element\"\n\t\t\t\t\t\t\t[seed]=\"session\"\n\t\t\t\t\t\t\t[activationSelection]=\"listActivationSelection()\"\n\t\t\t\t\t\t\t[spellCheck]=\"spellCheck()\"\n\t\t\t\t\t\t\t(textInput)=\"textInput.emit($event)\"\n\t\t\t\t\t\t\t(textCommit)=\"textCommit.emit($event)\"\n\t\t\t\t\t\t\t(textCancel)=\"textCancel.emit()\"\n\t\t\t\t\t\t\t(textFormat)=\"textFormat.emit($event)\"\n\t\t\t\t\t\t\t(listSession)=\"listSession.emit($event)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t} @else {\n\t\t\t\t\t<textarea\n\t\t\t\t\t\t#textEditor\n\t\t\t\t\t\tdata-inline-editor\n\t\t\t\t\t\tclass=\"pptx-ng-text-editor\"\n\t\t\t\t\t\taria-label=\"Edit slide text\"\n\t\t\t\t\t\t[spellcheck]=\"spellCheck()\"\n\t\t\t\t\t\t[style.left.px]=\"eb.x\"\n\t\t\t\t\t\t[style.top.px]=\"eb.y\"\n\t\t\t\t\t\t[style.width.px]=\"eb.width\"\n\t\t\t\t\t\t[style.height.px]=\"eb.height\"\n\t\t\t\t\t\t(pointerdown)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t(input)=\"onTextInput($event, eb.id)\"\n\t\t\t\t\t\t(blur)=\"commitText($event, eb.id)\"\n\t\t\t\t\t\t(keydown)=\"onEditorKeydown($event)\"\n\t\t\t\t\t></textarea>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tView overlays: editor aids only, never on thumbnails/preview/presentation.\n\t\t\t\tAll are pointer-events:none so they never break selection/drag.\n\t\t\t\tNone carry data-pptx-element / aria-roledescription / data-pptx-viewport.\n\t\t\t-->\n\t\t\t@if (interactive() && showGrid()) {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-grid\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t<pattern\n\t\t\t\t\t\t\t[attr.id]=\"gridPatternId\"\n\t\t\t\t\t\t\t[attr.width]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\t[attr.height]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\tpatternUnits=\"userSpaceOnUse\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t[attr.cx]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\t[attr.cy]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\tr=\"0.6\"\n\t\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.55)\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</pattern>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\t\t[attr.fill]=\"'url(#' + gridPatternId + ')'\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t@if (interactive() && showGuides()) {\n\t\t\t\t<!--\n\t\t\t\t\tCenter crosshair: one horizontal line and one vertical line\n\t\t\t\t\tthrough the midpoint of the slide. Static; draggable guides\n\t\t\t\t\tare a follow-up.\n\t\t\t\t-->\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-guides\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<!-- Horizontal center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t\t[attr.y1]=\"canvasSize().height / 2\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height / 2\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t\t<!-- Vertical center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"canvasSize().width / 2\"\n\t\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width / 2\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tUser-created ruler guides.\n\t\t\t\tEach guide has a non-interactive line body and an interactive\n\t\t\t\tdrag handle. Double-click the handle to delete the guide.\n\n\t\t\t\tDeliberately OUTSIDE the showGuides() gate above (which owns the\n\t\t\t\tstatic centre crosshair): a guide the user just dragged off a ruler\n\t\t\t\tstrip must be visible immediately, as it is in Vue and Vanilla.\n\t\t\t\tWhile it lived under that gate, the drag-out gesture silently\n\t\t\t\tproduced an invisible guide unless View > Guides happened to be on.\n\t\t\t-->\n\t\t\t@if (interactive()) {\n\t\t\t\t@for (g of rulerGuidesSvc.rulerGuides(); track g.id) {\n\t\t\t\t\t<!-- Guide line body: pointer-events:none -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-line\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '1px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '1px' : '100%'\"\n\t\t\t\t\t></div>\n\t\t\t\t\t<!-- Drag handle: pointer-events:auto -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-handle\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.cursor]=\"g.axis === 'x' ? 'col-resize' : 'row-resize'\"\n\t\t\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onGuidePointerDown($event, g.id, g.axis)\"\n\t\t\t\t\t\t(dblclick)=\"rulerGuidesSvc.onGuideDoubleClick($event, g.id)\"\n\t\t\t\t\t\t[title]=\"'pptx.canvas.guideTooltip' | translate\"\n\t\t\t\t\t></div>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tLive ink stroke preview: shown while the user is drawing.\n\t\t\t\tpointer-events:none so it never intercepts element gestures.\n\t\t\t\tNo data-pptx-element / aria-roledescription / data-pptx-viewport.\n\n\t\t\t\tRenders `inkDrawing.liveStrokeView()`: the same plain-path /\n\t\t\t\tpressure-circle / tilt-nib decision `InkRendererComponent` makes for\n\t\t\t\ta committed stroke, so a calligraphic lean or pressure-variable\n\t\t\t\twidth shows up while the pointer is still down, not only after\n\t\t\t\trelease.\n\t\t\t-->\n\t\t\t@if (inkDrawing.active() && inkDrawing.liveStrokeView() && drawTool() !== 'select') {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-ink-preview\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\tstyle=\"position: absolute; inset: 0; pointer-events: none; z-index: 70\"\n\t\t\t\t>\n\t\t\t\t\t@if (inkDrawing.liveStrokeView()?.nibMarks; as nibMarks) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (m of nibMarks; track $index) {\n\t\t\t\t\t\t\t\t<ellipse\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"m.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"m.cy\"\n\t\t\t\t\t\t\t\t\t[attr.rx]=\"m.rPerp\"\n\t\t\t\t\t\t\t\t\t[attr.ry]=\"m.rTilt\"\n\t\t\t\t\t\t\t\t\t[attr.transform]=\"'rotate(' + m.rotationDeg + ' ' + m.cx + ' ' + m.cy + ')'\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else if (inkDrawing.liveStrokeView()?.circles; as circles) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (c of circles; track $index) {\n\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"c.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"c.cy\"\n\t\t\t\t\t\t\t\t\t[attr.r]=\"c.r\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t[attr.d]=\"inkDrawing.liveStrokeView()?.d\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t[attr.stroke]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t[attr.stroke-width]=\"inkDrawing.liveStrokeView()?.width\"\n\t\t\t\t\t\t\t[attr.stroke-opacity]=\"inkDrawing.liveStrokeView()?.opacity\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tProjected overlays (collaboration cursors + remote selections).\n\t\t\t\tThey live INSIDE the scaled stage so the stage's CSS\n\t\t\t\ttransform:scale() applies the on-screen scale exactly once and\n\t\t\t\tthey can be authored in raw, unscaled slide coordinates, exactly\n\t\t\t\tlike the React/Vue overlays.\n\t\t\t-->\n\t\t\t<ng-content />\n\t\t</div>\n\n\t\t<!--\n\t\t\tRuler strips: siblings to the scaled stage inside the wrapper div,\n\t\t\tabsolutely positioned at top:0/left:0 within the wrapper's padding area.\n\t\t\tThe wrapper's padding (RULER_THICKNESS px top+left) reserves space for\n\t\t\tthese strips so the stage content starts below/right of them.\n\t\t\tpointer-events:none so they never intercept element gestures.\n\t\t-->\n\t\t@if (interactive() && showRulers()) {\n\t\t\t<!-- Corner square at the intersection of the two ruler strips -->\n\t\t\t<div class=\"pptx-ng-ruler-corner\" aria-hidden=\"true\"></div>\n\n\t\t\t<!--\n\t\t\t\tHorizontal ruler: spans the top padding row of the wrapper.\n\t\t\t\tA guide is dropped on pointer-UP (not down), resolved by the shared\n\t\t\t\trulerDragToGuidePosition, so a stray click on the strip cannot\n\t\t\t\tdrop a guide the user never dragged out - the rule React, Vue,\n\t\t\t\tSvelte and Vanilla all follow.\n\t\t\t-->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-h\"\n\t\t\t\tdata-pptx-ruler=\"h\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t[style.cursor]=\"editable() ? 'row-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('h', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('h', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (hRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.x]=\"highlight.start\"\n\t\t\t\t\t\ty=\"0\"\n\t\t\t\t\t\t[attr.width]=\"highlight.span\"\n\t\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t[attr.y1]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.x2]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.y2]=\"rulerThickness - 0.5\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of hRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"tick.position\"\n\t\t\t\t\t\t[attr.y1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.x2]=\"tick.position\"\n\t\t\t\t\t\t[attr.y2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t[attr.x]=\"tick.position + 2\"\n\t\t\t\t\t\t\t[attr.y]=\"rulerFontSize + 1\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\n\t\t\t<!-- Vertical ruler: spans the left padding column of the wrapper -->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-v\"\n\t\t\t\tdata-pptx-ruler=\"v\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t[style.cursor]=\"editable() ? 'col-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('v', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('v', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (vRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\tx=\"0\"\n\t\t\t\t\t\t[attr.y]=\"highlight.start\"\n\t\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.height]=\"highlight.span\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\t[attr.x1]=\"rulerThickness - 0.5\"\n\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t[attr.x2]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.y2]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of vRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.y1]=\"tick.position\"\n\t\t\t\t\t\t[attr.x2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\t[attr.y2]=\"tick.position\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx=\"2\"\n\t\t\t\t\t\t\t[attr.y]=\"tick.position + rulerFontSize + 2\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\t\t}\n\t</div>\n</div>\n", styles: [".pptx-ng-canvas-stage.is-editable{touch-action:none}\n"] }]
|
|
130053
|
+
ChartQuickActionsOverlayComponent,
|
|
130054
|
+
], template: "<div\n\t#viewport\n\tclass=\"pptx-ng-canvas-viewport\"\n\t[attr.data-pptx-viewport]=\"interactive() ? '' : null\"\n\t(pointerdown)=\"onViewportPointerDown($event)\"\n>\n\t<div class=\"pptx-ng-canvas-wrapper\" [ngStyle]=\"wrapperStyle()\">\n\t\t<div\n\t\t\t#stage\n\t\t\tclass=\"pptx-ng-canvas-stage\"\n\t\t\t[class.is-editable]=\"editable()\"\n\t\t\trole=\"region\"\n\t\t\t[attr.aria-roledescription]=\"interactive() || presenting() ? 'slide' : null\"\n\t\t\t[attr.data-pptx-ai-active]=\"aiActive() ? 'true' : null\"\n\t\t\t[ngStyle]=\"stageStyle()\"\n\t\t\t(pointerdown)=\"onStagePointerDown($event)\"\n\t\t\t(contextmenu)=\"onContextMenu($event)\"\n\t\t\t(dblclick)=\"onDblClick($event)\"\n\t\t>\n\t\t\t<!--\n\t\t\t\tTemplate layer: inherited master/layout elements, rendered BEHIND\n\t\t\t\tthe slide's own elements (lower z-index). Interactive + given the\n\t\t\t\tamber editable affordance only while editTemplateMode is on; when\n\t\t\t\toff they render inertly with no affordance, exactly as core\n\t\t\t\tdelivered them. `marked` keeps the data-pptx-element contract on\n\t\t\t\tthem even while locked: the marker means \"rendered slide element\",\n\t\t\t\tnot \"editable right now\".\n\t\t\t-->\n\t\t\t@for (element of visibleTemplateElements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive() && editTemplateMode()\"\n\t\t\t\t\t[marked]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[editable]=\"editable() && editTemplateMode()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"editTemplateMode()\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@for (element of elements(); track element.id; let i = $index) {\n\t\t\t\t<pptx-element-renderer\n\t\t\t\t\t[element]=\"element\"\n\t\t\t\t\t[mediaDataUrls]=\"mediaDataUrls()\"\n\t\t\t\t\t[zIndex]=\"visibleTemplateElements().length + i\"\n\t\t\t\t\t[obstacles]=\"connectorObstacles()\"\n\t\t\t\t\t[canvasWidth]=\"canvasSize().width\"\n\t\t\t\t\t[canvasHeight]=\"canvasSize().height\"\n\t\t\t\t\t[interactive]=\"interactive()\"\n\t\t\t\t\t[exposeElementId]=\"exposeElementIds()\"\n\t\t\t\t\t[presenting]=\"presenting()\"\n\t\t\t\t\t[editable]=\"editable()\"\n\t\t\t\t\t[fieldContext]=\"fieldContext()\"\n\t\t\t\t\t[slideElements]=\"allElements()\"\n\t\t\t\t\t[editTemplateMode]=\"false\"\n\t\t\t\t\t[editingElementId]=\"editingId()\"\n\t\t\t\t\t(cellCommit)=\"cellCommit.emit($event)\"\n\t\t\t\t\t(tableChange)=\"tableChange.emit($event)\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<pptx-activex-controls-overlay\n\t\t\t\t[controls]=\"slide()?.activeXControls ?? []\"\n\t\t\t\t[canvasSize]=\"canvasSize()\"\n\t\t\t/>\n\t\t\t@if (aiHighlights().length > 0 || aiActive()) {\n\t\t\t\t<pptx-ai-focus-highlight-overlay\n\t\t\t\t\t[highlights]=\"aiHighlights()\"\n\t\t\t\t\t[elements]=\"elements()\"\n\t\t\t\t\t[activeSlideIndex]=\"aiActiveSlideIndex()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t@if (aiChangeBatch(); as batch) {\n\t\t\t\t<pptx-ai-change-overlay [batch]=\"batch\" [activeSlideIndex]=\"aiActiveSlideIndex()\" />\n\t\t\t}\n\t\t\t@for (box of selectionBoxes(); track box.id) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-selection\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"box.x\"\n\t\t\t\t\t[style.top.px]=\"box.y\"\n\t\t\t\t\t[style.width.px]=\"box.width\"\n\t\t\t\t\t[style.height.px]=\"box.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (h of handleBoxes(); track h.handle) {\n\t\t\t\t@let controlArtwork = selectionControlArtwork(h, h.handle);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"resize\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.resize' | translate: { handle: h.handle }\"\n\t\t\t\t\t[ngStyle]=\"controlArtwork.frame\"\n\t\t\t\t\t[style.cursor]=\"h.cursor\"\n\t\t\t\t\t[style.--pptx-selection-width.px]=\"singleSelected()?.width\"\n\t\t\t\t\t[style.--pptx-selection-height.px]=\"singleSelected()?.height\"\n\t\t\t\t\t(pointerdown)=\"onHandlePointerDown($event, h.handle)\"\n\t\t\t\t\t(keydown)=\"onResizeHandleKeydown($event, h.handle)\"\n\t\t\t\t>\n\t\t\t\t\t<span data-pptx-handle-hit [ngStyle]=\"resizeHitAreaStyle(h.handle)\"></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"controlArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t@if (marqueeRect(); as mr) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-marquee\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"mr.x\"\n\t\t\t\t\t[style.top.px]=\"mr.y\"\n\t\t\t\t\t[style.width.px]=\"mr.width\"\n\t\t\t\t\t[style.height.px]=\"mr.height\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (g of snapGuides(); track $index) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-snap-guide\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : g.start\"\n\t\t\t\t\t[style.top.px]=\"g.axis === 'x' ? g.start : g.pos\"\n\t\t\t\t\t[style.width.px]=\"g.axis === 'x' ? 0 : g.end - g.start\"\n\t\t\t\t\t[style.height.px]=\"g.axis === 'x' ? g.end - g.start : 0\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@if (rotateHandle(); as rh) {\n\t\t\t\t@let rotateArtwork = selectionControlArtwork(rh);\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-rotate-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[style.z-index]=\"editingBox() ? 10003 : null\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"rotate\"\n\t\t\t\t\t[pptxRotateHandleFor]=\"singleSelected()?.id\"\n\t\t\t\t\t[pptxRotateHandleStage]=\"stage\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.rotate' | translate\"\n\t\t\t\t\t[ngStyle]=\"rotateArtwork.frame\"\n\t\t\t\t\t(pointerdown)=\"onRotatePointerDown($event)\"\n\t\t\t\t\t(keydown)=\"onRotateHandleKeydown($event)\"\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclass=\"pptx-ng-control-artwork\"\n\t\t\t\t\t\tdata-pptx-handle-artwork\n\t\t\t\t\t\t[ngStyle]=\"rotateArtwork.artwork\"\n\t\t\t\t\t></span>\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tShape-adjustment affordances (amber diamonds), ONE per `a:avLst`\n\t\t\t\tguide. Existence, position and cursor all come from the SHARED\n\t\t\t\t`getShapeAdjustmentHandleDescriptors`, so they appear only for a\n\t\t\t\tgeometry with an adjustable parameter, sit where the other four\n\t\t\t\tbindings put them, and are absent for a shape locked with\n\t\t\t\t`noAdjustHandles`. Dragging one writes `shapeAdjustments`; it does\n\t\t\t\tNOT resize the element.\n\t\t\t-->\n\t\t\t@for (ah of adjustHandles(); track ah.key) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-adjust-handle\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\tdata-pptx-compact\n\t\t\t\t\tdata-pptx-handle-kind=\"adjust\"\n\t\t\t\t\t[attr.data-pptx-adjust-key]=\"ah.key\"\n\t\t\t\t\t[attr.aria-label]=\"'pptx.selectionOverlay.adjust' | translate\"\n\t\t\t\t\t[style.left.px]=\"ah.left\"\n\t\t\t\t\t[style.top.px]=\"ah.top\"\n\t\t\t\t\t[style.width.px]=\"ah.size\"\n\t\t\t\t\t[style.height.px]=\"ah.size\"\n\t\t\t\t\t[style.cursor]=\"ah.cursor\"\n\t\t\t\t\t(pointerdown)=\"onAdjustPointerDown($event, ah)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tPowerPoint's floating \"Chart Elements\"/\"Chart Styles\"/\"Chart\n\t\t\t\tFilters\" quick-action icons, shown just outside the chart's\n\t\t\t\ttop-right corner when it is the single selected element.\n\t\t\t-->\n\t\t\t@if (editable() && quickActionsChartElement(); as chartEl) {\n\t\t\t\t<pptx-chart-quick-actions-overlay\n\t\t\t\t\t[element]=\"chartEl\"\n\t\t\t\t\t[canEdit]=\"editable()\"\n\t\t\t\t\t[zoom]=\"effectiveScalePublic()\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\t<!--\n\t\t\t\tConnector endpoint authoring: drag an end onto a shape's connection\n\t\t\t\tpoint to bind it (`a:stCxn` / `a:endCxn`), or off one to detach.\n\t\t\t\tCandidate sites are revealed only while an end is in flight so they\n\t\t\t\tnever obscure the deck at rest.\n\t\t\t-->\n\t\t\t@for (site of connectorSiteCandidates(); track site.key) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"pptx-ng-connection-site\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-snapped]=\"site.snapped\"\n\t\t\t\t\tdata-pptx-connection-site\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[style.left.px]=\"site.left\"\n\t\t\t\t\t[style.top.px]=\"site.top\"\n\t\t\t\t\t[style.width.px]=\"site.size\"\n\t\t\t\t\t[style.height.px]=\"site.size\"\n\t\t\t\t></div>\n\t\t\t}\n\t\t\t@for (ep of connectorEndpoints(); track ep.kind) {\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"pptx-ng-connector-endpoint\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t[class.is-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.data-pptx-connector-endpoint]=\"ep.kind\"\n\t\t\t\t\t[attr.data-pptx-connector-attached]=\"ep.attached\"\n\t\t\t\t\t[attr.aria-label]=\"\n\t\t\t\t\t\t(ep.kind === 'start'\n\t\t\t\t\t\t\t? 'pptx.canvas.connectorEndpointStart'\n\t\t\t\t\t\t\t: 'pptx.canvas.connectorEndpointEnd'\n\t\t\t\t\t\t) | translate\n\t\t\t\t\t\"\n\t\t\t\t\t[style.left.px]=\"ep.left\"\n\t\t\t\t\t[style.top.px]=\"ep.top\"\n\t\t\t\t\t[style.width.px]=\"ep.size\"\n\t\t\t\t\t[style.height.px]=\"ep.size\"\n\t\t\t\t\t(pointerdown)=\"onConnectorEndpointPointerDown($event, ep.kind)\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t\t@if (editingBox(); as eb) {\n\t\t\t\t@if (inlineListSeed(); as seed) {\n\t\t\t\t\t@for (session of [seed]; track session) {\n\t\t\t\t\t\t<pptx-inline-list-editor\n\t\t\t\t\t\t\t[element]=\"eb.element\"\n\t\t\t\t\t\t\t[seed]=\"session\"\n\t\t\t\t\t\t\t[activationSelection]=\"listActivationSelection()\"\n\t\t\t\t\t\t\t[spellCheck]=\"spellCheck()\"\n\t\t\t\t\t\t\t(textInput)=\"textInput.emit($event)\"\n\t\t\t\t\t\t\t(textCommit)=\"textCommit.emit($event)\"\n\t\t\t\t\t\t\t(textCancel)=\"textCancel.emit()\"\n\t\t\t\t\t\t\t(textFormat)=\"textFormat.emit($event)\"\n\t\t\t\t\t\t\t(listSession)=\"listSession.emit($event)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t} @else {\n\t\t\t\t\t<textarea\n\t\t\t\t\t\t#textEditor\n\t\t\t\t\t\tdata-inline-editor\n\t\t\t\t\t\tclass=\"pptx-ng-text-editor\"\n\t\t\t\t\t\taria-label=\"Edit slide text\"\n\t\t\t\t\t\t[spellcheck]=\"spellCheck()\"\n\t\t\t\t\t\t[style.left.px]=\"eb.x\"\n\t\t\t\t\t\t[style.top.px]=\"eb.y\"\n\t\t\t\t\t\t[style.width.px]=\"eb.width\"\n\t\t\t\t\t\t[style.height.px]=\"eb.height\"\n\t\t\t\t\t\t(pointerdown)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t(input)=\"onTextInput($event, eb.id)\"\n\t\t\t\t\t\t(blur)=\"commitText($event, eb.id)\"\n\t\t\t\t\t\t(keydown)=\"onEditorKeydown($event)\"\n\t\t\t\t\t></textarea>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tView overlays: editor aids only, never on thumbnails/preview/presentation.\n\t\t\t\tAll are pointer-events:none so they never break selection/drag.\n\t\t\t\tNone carry data-pptx-element / aria-roledescription / data-pptx-viewport.\n\t\t\t-->\n\t\t\t@if (interactive() && showGrid()) {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-grid\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t<pattern\n\t\t\t\t\t\t\t[attr.id]=\"gridPatternId\"\n\t\t\t\t\t\t\t[attr.width]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\t[attr.height]=\"gridSpacingPx()\"\n\t\t\t\t\t\t\tpatternUnits=\"userSpaceOnUse\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t[attr.cx]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\t[attr.cy]=\"gridSpacingPx() / 2\"\n\t\t\t\t\t\t\t\tr=\"0.6\"\n\t\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.55)\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</pattern>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\t\t[attr.fill]=\"'url(#' + gridPatternId + ')'\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t@if (interactive() && showGuides()) {\n\t\t\t\t<!--\n\t\t\t\t\tCenter crosshair: one horizontal line and one vertical line\n\t\t\t\t\tthrough the midpoint of the slide. Static; draggable guides\n\t\t\t\t\tare a follow-up.\n\t\t\t\t-->\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-overlay-guides\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t>\n\t\t\t\t\t<!-- Horizontal center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t\t[attr.y1]=\"canvasSize().height / 2\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height / 2\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t\t<!-- Vertical center guide -->\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"canvasSize().width / 2\"\n\t\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t\t[attr.x2]=\"canvasSize().width / 2\"\n\t\t\t\t\t\t[attr.y2]=\"canvasSize().height\"\n\t\t\t\t\t\tstroke=\"rgba(99,102,241,0.7)\"\n\t\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t\t\tstroke-dasharray=\"6 3\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tUser-created ruler guides.\n\t\t\t\tEach guide has a non-interactive line body and an interactive\n\t\t\t\tdrag handle. Double-click the handle to delete the guide.\n\n\t\t\t\tDeliberately OUTSIDE the showGuides() gate above (which owns the\n\t\t\t\tstatic centre crosshair): a guide the user just dragged off a ruler\n\t\t\t\tstrip must be visible immediately, as it is in Vue and Vanilla.\n\t\t\t\tWhile it lived under that gate, the drag-out gesture silently\n\t\t\t\tproduced an invisible guide unless View > Guides happened to be on.\n\t\t\t-->\n\t\t\t@if (interactive()) {\n\t\t\t\t@for (g of rulerGuidesSvc.rulerGuides(); track g.id) {\n\t\t\t\t\t<!-- Guide line body: pointer-events:none -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-line\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '1px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '1px' : '100%'\"\n\t\t\t\t\t></div>\n\t\t\t\t\t<!-- Drag handle: pointer-events:auto -->\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"pptx-ng-ruler-guide-handle\"\n\t\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\t\t[style.left.px]=\"g.axis === 'x' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.top.px]=\"g.axis === 'y' ? g.pos - 4 : 0\"\n\t\t\t\t\t\t[style.width]=\"g.axis === 'x' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.height]=\"g.axis === 'y' ? '9px' : '100%'\"\n\t\t\t\t\t\t[style.cursor]=\"g.axis === 'x' ? 'col-resize' : 'row-resize'\"\n\t\t\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onGuidePointerDown($event, g.id, g.axis)\"\n\t\t\t\t\t\t(dblclick)=\"rulerGuidesSvc.onGuideDoubleClick($event, g.id)\"\n\t\t\t\t\t\t[title]=\"'pptx.canvas.guideTooltip' | translate\"\n\t\t\t\t\t></div>\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tLive ink stroke preview: shown while the user is drawing.\n\t\t\t\tpointer-events:none so it never intercepts element gestures.\n\t\t\t\tNo data-pptx-element / aria-roledescription / data-pptx-viewport.\n\n\t\t\t\tRenders `inkDrawing.liveStrokeView()`: the same plain-path /\n\t\t\t\tpressure-circle / tilt-nib decision `InkRendererComponent` makes for\n\t\t\t\ta committed stroke, so a calligraphic lean or pressure-variable\n\t\t\t\twidth shows up while the pointer is still down, not only after\n\t\t\t\trelease.\n\t\t\t-->\n\t\t\t@if (inkDrawing.active() && inkDrawing.liveStrokeView() && drawTool() !== 'select') {\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"pptx-ng-ink-preview\"\n\t\t\t\t\tdata-export-ignore=\"true\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t[attr.width]=\"canvasSize().width\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height\"\n\t\t\t\t\tstyle=\"position: absolute; inset: 0; pointer-events: none; z-index: 70\"\n\t\t\t\t>\n\t\t\t\t\t@if (inkDrawing.liveStrokeView()?.nibMarks; as nibMarks) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (m of nibMarks; track $index) {\n\t\t\t\t\t\t\t\t<ellipse\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"m.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"m.cy\"\n\t\t\t\t\t\t\t\t\t[attr.rx]=\"m.rPerp\"\n\t\t\t\t\t\t\t\t\t[attr.ry]=\"m.rTilt\"\n\t\t\t\t\t\t\t\t\t[attr.transform]=\"'rotate(' + m.rotationDeg + ' ' + m.cx + ' ' + m.cy + ')'\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else if (inkDrawing.liveStrokeView()?.circles; as circles) {\n\t\t\t\t\t\t<g [attr.opacity]=\"inkDrawing.liveStrokeView()?.opacity\">\n\t\t\t\t\t\t\t@for (c of circles; track $index) {\n\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t[attr.cx]=\"c.cx\"\n\t\t\t\t\t\t\t\t\t[attr.cy]=\"c.cy\"\n\t\t\t\t\t\t\t\t\t[attr.r]=\"c.r\"\n\t\t\t\t\t\t\t\t\t[attr.fill]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t[attr.d]=\"inkDrawing.liveStrokeView()?.d\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t[attr.stroke]=\"inkDrawing.liveStrokeView()?.color\"\n\t\t\t\t\t\t\t[attr.stroke-width]=\"inkDrawing.liveStrokeView()?.width\"\n\t\t\t\t\t\t\t[attr.stroke-opacity]=\"inkDrawing.liveStrokeView()?.opacity\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t}\n\t\t\t\t</svg>\n\t\t\t}\n\n\t\t\t<!--\n\t\t\t\tProjected overlays (collaboration cursors + remote selections).\n\t\t\t\tThey live INSIDE the scaled stage so the stage's CSS\n\t\t\t\ttransform:scale() applies the on-screen scale exactly once and\n\t\t\t\tthey can be authored in raw, unscaled slide coordinates, exactly\n\t\t\t\tlike the React/Vue overlays.\n\t\t\t-->\n\t\t\t<ng-content />\n\t\t</div>\n\n\t\t<!--\n\t\t\tRuler strips: siblings to the scaled stage inside the wrapper div,\n\t\t\tabsolutely positioned at top:0/left:0 within the wrapper's padding area.\n\t\t\tThe wrapper's padding (RULER_THICKNESS px top+left) reserves space for\n\t\t\tthese strips so the stage content starts below/right of them.\n\t\t\tpointer-events:none so they never intercept element gestures.\n\t\t-->\n\t\t@if (interactive() && showRulers()) {\n\t\t\t<!-- Corner square at the intersection of the two ruler strips -->\n\t\t\t<div class=\"pptx-ng-ruler-corner\" aria-hidden=\"true\"></div>\n\n\t\t\t<!--\n\t\t\t\tHorizontal ruler: spans the top padding row of the wrapper.\n\t\t\t\tA guide is dropped on pointer-UP (not down), resolved by the shared\n\t\t\t\trulerDragToGuidePosition, so a stray click on the strip cannot\n\t\t\t\tdrop a guide the user never dragged out - the rule React, Vue,\n\t\t\t\tSvelte and Vanilla all follow.\n\t\t\t-->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-h\"\n\t\t\t\tdata-pptx-ruler=\"h\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t[style.cursor]=\"editable() ? 'row-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('h', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('h', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (hRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\t[attr.x]=\"highlight.start\"\n\t\t\t\t\t\ty=\"0\"\n\t\t\t\t\t\t[attr.width]=\"highlight.span\"\n\t\t\t\t\t\t[attr.height]=\"rulerThickness\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t[attr.y1]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.x2]=\"canvasSize().width * effectiveScalePublic()\"\n\t\t\t\t\t[attr.y2]=\"rulerThickness - 0.5\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of hRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"tick.position\"\n\t\t\t\t\t\t[attr.y1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.x2]=\"tick.position\"\n\t\t\t\t\t\t[attr.y2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t[attr.x]=\"tick.position + 2\"\n\t\t\t\t\t\t\t[attr.y]=\"rulerFontSize + 1\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\n\t\t\t<!-- Vertical ruler: spans the left padding column of the wrapper -->\n\t\t\t<svg\n\t\t\t\tclass=\"pptx-ng-ruler-v\"\n\t\t\t\tdata-pptx-ruler=\"v\"\n\t\t\t\trole=\"presentation\"\n\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t[style.cursor]=\"editable() ? 'col-resize' : null\"\n\t\t\t\t(pointerdown)=\"rulerGuidesSvc.onRulerPointerDown('v', $event)\"\n\t\t\t\t(pointerup)=\"rulerGuidesSvc.onRulerPointerUp('v', $event)\"\n\t\t\t>\n\t\t\t\t<rect\n\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t[attr.height]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tfill=\"#1e293b\"\n\t\t\t\t/>\n\t\t\t\t@if (vRulerHighlight(); as highlight) {\n\t\t\t\t\t<rect\n\t\t\t\t\t\tx=\"0\"\n\t\t\t\t\t\t[attr.y]=\"highlight.start\"\n\t\t\t\t\t\t[attr.width]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.height]=\"highlight.span\"\n\t\t\t\t\t\tfill=\"rgba(96,165,250,0.25)\"\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t\t<line\n\t\t\t\t\t[attr.x1]=\"rulerThickness - 0.5\"\n\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t[attr.x2]=\"rulerThickness - 0.5\"\n\t\t\t\t\t[attr.y2]=\"canvasSize().height * effectiveScalePublic()\"\n\t\t\t\t\tstroke=\"rgba(255,255,255,0.15)\"\n\t\t\t\t\tstroke-width=\"1\"\n\t\t\t\t/>\n\t\t\t\t@for (tick of vRulerTicks(); track $index) {\n\t\t\t\t\t<line\n\t\t\t\t\t\t[attr.x1]=\"rulerThickness\"\n\t\t\t\t\t\t[attr.y1]=\"tick.position\"\n\t\t\t\t\t\t[attr.x2]=\"rulerThickness - rulerThickness * (tick.isMajor ? 0.6 : 0.3)\"\n\t\t\t\t\t\t[attr.y2]=\"tick.position\"\n\t\t\t\t\t\tstroke=\"rgba(156,163,175,0.7)\"\n\t\t\t\t\t\t[attr.stroke-width]=\"tick.isMajor ? 1 : 0.5\"\n\t\t\t\t\t/>\n\t\t\t\t\t@if (tick.label) {\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx=\"2\"\n\t\t\t\t\t\t\t[attr.y]=\"tick.position + rulerFontSize + 2\"\n\t\t\t\t\t\t\t[attr.font-size]=\"rulerFontSize\"\n\t\t\t\t\t\t\tfill=\"rgba(156,163,175,0.9)\"\n\t\t\t\t\t\t\tstyle=\"font-family: system-ui, sans-serif\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{{ tick.label }}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</svg>\n\t\t}\n\t</div>\n</div>\n", styles: [".pptx-ng-canvas-stage.is-editable{touch-action:none}\n"] }]
|
|
128934
130055
|
}], ctorParameters: () => [], propDecorators: { slide: [{ type: i0.Input, args: [{ isSignal: true, alias: "slide", required: false }] }], canvasSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasSize", required: true }] }], mediaDataUrls: [{ type: i0.Input, args: [{ isSignal: true, alias: "mediaDataUrls", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], fitPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitPadding", required: false }] }], maxFitScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFitScale", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showRulers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRulers", required: false }] }], rulerUnit: [{ type: i0.Input, args: [{ isSignal: true, alias: "rulerUnit", required: false }] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], snapToGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGrid", required: false }] }], gridSpacing: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridSpacing", required: false }] }], snapToShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToShape", required: false }] }], guideCommand: [{ type: i0.Input, args: [{ isSignal: true, alias: "guideCommand", required: false }] }], spellCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "spellCheck", required: false }] }], snapToGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapToGuides", required: false }] }], autoFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFit", required: false }] }], transparentBackground: [{ type: i0.Input, args: [{ isSignal: true, alias: "transparentBackground", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], exposeElementIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "exposeElementIds", required: false }] }], presenting: [{ type: i0.Input, args: [{ isSignal: true, alias: "presenting", required: false }] }], selectedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIds", required: false }] }], editingId: [{ type: i0.Input, args: [{ isSignal: true, alias: "editingId", required: false }] }], editTemplateMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editTemplateMode", required: false }] }], templateElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateElements", required: false }] }], aiHighlights: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiHighlights", required: false }] }], aiActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiActive", required: false }] }], aiActiveSlideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiActiveSlideIndex", required: false }] }], aiChangeBatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiChangeBatch", required: false }] }], aiPickMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiPickMode", required: false }] }], drawTool: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawTool", required: false }] }], drawColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawColor", required: false }] }], drawWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawWidth", required: false }] }], elementSelect: [{ type: i0.Output, args: ["elementSelect"] }], backgroundClick: [{ type: i0.Output, args: ["backgroundClick"] }], transformStart: [{ type: i0.Output, args: ["transformStart"] }], transformUpdate: [{ type: i0.Output, args: ["transformUpdate"] }], transformEnd: [{ type: i0.Output, args: ["transformEnd"] }], adjustUpdate: [{ type: i0.Output, args: ["adjustUpdate"] }], connectorEndpointUpdate: [{ type: i0.Output, args: ["connectorEndpointUpdate"] }], contextMenu: [{ type: i0.Output, args: ["contextMenu"] }], textEditStart: [{ type: i0.Output, args: ["textEditStart"] }], textCommit: [{ type: i0.Output, args: ["textCommit"] }], textInput: [{ type: i0.Output, args: ["textInput"] }], textCancel: [{ type: i0.Output, args: ["textCancel"] }], textFormat: [{ type: i0.Output, args: ["textFormat"] }], listSession: [{ type: i0.Output, args: ["listSession"] }], rotateUpdate: [{ type: i0.Output, args: ["rotateUpdate"] }], marqueeSelect: [{ type: i0.Output, args: ["marqueeSelect"] }], inkStrokeComplete: [{ type: i0.Output, args: ["inkStrokeComplete"] }], eraserHit: [{ type: i0.Output, args: ["eraserHit"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], tableChange: [{ type: i0.Output, args: ["tableChange"] }], textEditor: [{ type: i0.ViewChild, args: ['textEditor', { isSignal: true }] }], listEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => InlineListEditorComponent), { isSignal: true }] }], stageRef: [{ type: i0.ViewChild, args: ['stage', { isSignal: true }] }], viewportRef: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], onPointerMove: [{
|
|
128935
130056
|
type: HostListener,
|
|
128936
130057
|
args: ['document:pointermove', ['$event']]
|
|
@@ -132165,31 +133286,39 @@ async function recordWebm(canvases, opts = {}) {
|
|
|
132165
133286
|
recorder.onerror = (e) => reject(e);
|
|
132166
133287
|
});
|
|
132167
133288
|
recorder.start();
|
|
132168
|
-
|
|
132169
|
-
|
|
132170
|
-
|
|
132171
|
-
recorder.stop();
|
|
132172
|
-
throw new DOMException('Export cancelled', 'AbortError');
|
|
132173
|
-
}
|
|
132174
|
-
onProgress?.(i, canvases.length);
|
|
132175
|
-
const duration = slideTimingsMs?.[i] ?? slideDurationMs;
|
|
132176
|
-
const framesNeeded = segmentFrameCount(duration, fps);
|
|
132177
|
-
ctx.clearRect(0, 0, recordingCanvas.width, recordingCanvas.height);
|
|
132178
|
-
ctx.drawImage(canvases[i], 0, 0);
|
|
132179
|
-
for (let f = 0; f < framesNeeded; f++) {
|
|
133289
|
+
try {
|
|
133290
|
+
const frameIntervalMs = fpsToFrameIntervalMs(fps);
|
|
133291
|
+
for (let i = 0; i < canvases.length; i++) {
|
|
132180
133292
|
if (signal?.aborted) {
|
|
132181
133293
|
recorder.stop();
|
|
132182
133294
|
throw new DOMException('Export cancelled', 'AbortError');
|
|
132183
133295
|
}
|
|
133296
|
+
onProgress?.(i, canvases.length);
|
|
133297
|
+
const duration = slideTimingsMs?.[i] ?? slideDurationMs;
|
|
133298
|
+
const framesNeeded = segmentFrameCount(duration, fps);
|
|
133299
|
+
ctx.clearRect(0, 0, recordingCanvas.width, recordingCanvas.height);
|
|
132184
133300
|
ctx.drawImage(canvases[i], 0, 0);
|
|
132185
|
-
|
|
132186
|
-
|
|
132187
|
-
|
|
133301
|
+
for (let f = 0; f < framesNeeded; f++) {
|
|
133302
|
+
if (signal?.aborted) {
|
|
133303
|
+
recorder.stop();
|
|
133304
|
+
throw new DOMException('Export cancelled', 'AbortError');
|
|
133305
|
+
}
|
|
133306
|
+
ctx.drawImage(canvases[i], 0, 0);
|
|
133307
|
+
await new Promise((resolve) => {
|
|
133308
|
+
setTimeout(resolve, frameIntervalMs);
|
|
133309
|
+
});
|
|
133310
|
+
}
|
|
132188
133311
|
}
|
|
133312
|
+
recorder.stop();
|
|
133313
|
+
await recorderDone;
|
|
133314
|
+
return new Blob(chunks, { type: 'video/webm' });
|
|
133315
|
+
}
|
|
133316
|
+
finally {
|
|
133317
|
+
// `recorder.stop()` only stops the recorder; the capture stream itself
|
|
133318
|
+
// keeps compositing frames from `recordingCanvas` indefinitely otherwise
|
|
133319
|
+
// - a real leak on every export, not just an abort path.
|
|
133320
|
+
stopCaptureStream(stream);
|
|
132189
133321
|
}
|
|
132190
|
-
recorder.stop();
|
|
132191
|
-
await recorderDone;
|
|
132192
|
-
return new Blob(chunks, { type: 'video/webm' });
|
|
132193
133322
|
}
|
|
132194
133323
|
|
|
132195
133324
|
/**
|
|
@@ -147614,7 +148743,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
147614
148743
|
}], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }] } });
|
|
147615
148744
|
|
|
147616
148745
|
// Generated by scripts/inline-shared.mjs from package.json. Do not edit.
|
|
147617
|
-
const PPTX_ANGULAR_VIEWER_VERSION = "3.19.
|
|
148746
|
+
const PPTX_ANGULAR_VIEWER_VERSION = "3.19.2";
|
|
147618
148747
|
|
|
147619
148748
|
/**
|
|
147620
148749
|
* account-page.component.ts: File > Account content.
|
|
@@ -185879,4 +187008,4 @@ function cn(...values) {
|
|
|
185879
187008
|
*/
|
|
185880
187009
|
|
|
185881
187010
|
export { CollaborationCursorsComponent as $, AFTER_ANIMATION_VALUES as A, AnimationAuthorPanelComponent as B, AnimationPanelComponent as C, AnimationPlaybackService as D, AutosaveRecoveryDialogComponent as E, AutosaveService as F, BroadcastDialogComponent as G, CHART_EDITOR_STYLES as H, CINEMATIC_TRANSITION_KEYFRAMES as I, CURSOR_PALETTE as J, CanvasFitService as K, ChartAxisOptionsComponent as L, ChartAxisStyleOptionsComponent as M, ChartComboTypeOptionsComponent as N, ChartDataEditorComponent as O, ChartDataLabelOptionsComponent as P, ChartDatapointMarkerOptionsComponent as Q, ChartDatapointOptionsComponent as R, ChartDisplayOptionsComponent as S, ChartElementViewComponent as T, ChartErrorBarOptionsComponent as U, ChartMarkerOptionsComponent as V, ChartPartSelectionService as W, ChartPrimitivesComponent as X, ChartRendererComponent as Y, ChartTrendlineOptionsComponent as Z, ChartTypeSelectorComponent as _, ALIGN_OPTIONS as a, INSTANT as a$, CollaborationService as a0, ColorChangedImageComponent as a1, CommentMarkersOverlayComponent as a2, CommentsPanelComponent as a3, CommentsService as a4, ComparePanelComponent as a5, ConnectorRendererComponent as a6, ConnectorTextOverlayComponent as a7, CustomShowsComponent as a8, DEFAULT_BOUNDS as a9, EditorContextMenuComponent as aA, EditorHistory as aB, EditorStateService as aC, EditorToolbarComponent as aD, EffectsPanelComponent as aE, ElementRendererComponent as aF, EmbeddedFontsService as aG, EncryptedFileDialogComponent as aH, EquationEditorDialogComponent as aI, EquationRendererComponent as aJ, EquationTemplateGalleryComponent as aK, ExportProgressModalComponent as aL, ExportService as aM, FieldContextService as aN, FindBarComponent as aO, FindReplaceBarComponent as aP, FollowModeBarComponent as aQ, FontEmbeddingListComponent as aR, FontEmbeddingPanelComponent as aS, GALLERY_THEME_PRESETS as aT, GOOGLE_WEBFONTS_LINK_ID as aU, GRIDLINE_COLOR$1 as aV, GoogleWebfontsService as aW, GradientPickerComponent as aX, HANDOUT_OPTIONS as aY, HeaderFooterDialogComponent as aZ, HyperlinkDialogComponent as a_, DEFAULT_BROADCAST_SERVER_URL as aa, DEFAULT_CANVAS_HEIGHT as ab, DEFAULT_CANVAS_WIDTH as ac, DEFAULT_COLOR_SCHEME as ad, DEFAULT_FILL_COLOR$1 as ae, DEFAULT_LAYOUT as af, DEFAULT_MORPH_DURATION_MS as ag, DEFAULT_PALETTE$1 as ah, DEFAULT_PATTERN_FILL_PRESET as ai, DEFAULT_PRINT_SETTINGS as aj, DEFAULT_SLIDE_BACKGROUND as ak, DEFAULT_STROKE_COLOR as al, DEFAULT_STYLE as am, DEFAULT_TABLE_ROW_HEIGHT as an, DEFAULT_TEXT_COLOR$2 as ao, DEFAULT_VIEWER_PROFILE as ap, DIRECTIONAL_PRESETS as aq, DIRECTION_OPTIONS as ar, DocumentPropertiesCardComponent as as, EASE as at, EFFECT_SOUND_CATALOGUE as au, EMBEDDED_FONTS_STYLE_ID as av, EMPHASIS_PRESETS as aw, ENTRANCE_PRESETS as ax, TEMPLATES as ay, EXIT_PRESETS as az, ANIMATION_PRESET_CATEGORIES as b, REPEAT_MODE_OPTIONS as b$, ImagePropertiesPanelComponent as b0, InkDrawingService as b1, InkRendererComponent as b2, InsertSmartArtDialogComponent as b3, InspectorPaneHeaderComponent as b4, InspectorPanelComponent as b5, IsMobileService as b6, KeepAnnotationsDialogComponent as b7, LOCALE_CATALOG as b8, LONG_PRESS_DURATION_MS as b9, POWER_POINT_VIEWER_PROVIDERS as bA, PPTX_OPEN_ACCEPT as bB, PRESENTATION_OPEN_EXTENSIONS as bC, PRESENTER_CHANNEL_NAME as bD, PRESENTER_MSG_ORIGIN as bE, PRESENTER_TIMER_SEGMENT_MS as bF, PX_PER_CM as bG, PX_PER_INCH as bH, PasswordProtectionDialogComponent as bI, PasswordStrengthMeterComponent as bJ, PowerPointViewerComponent as bK, PresentToolbarAutoHide as bL, PresentationAnnotationOverlayComponent as bM, PresentationAnnotationsService as bN, PresentationOverlayComponent as bO, PresentationPropertiesPanelComponent as bP, PresentationSettingsCardComponent as bQ, PresentationSubtitleBarComponent as bR, PresentationToolbarComponent as bS, PresentationTransitionOverlayComponent as bT, PresenterViewComponent as bU, PresenterWindowService as bV, PrintDialogComponent as bW, PrintService as bX, PrintSettingsPanelComponent as bY, PropertiesDialogComponent as bZ, RANDOM_ELIGIBLE_TYPES as b_, LONG_PRESS_MOVE_TOLERANCE_PX as ba, LoadContentService as bb, LocalPresencePublisher as bc, MAX_ZOOM_SCALE as bd, MIN_ZOOM_SCALE as be, MOTION_PATH_COLUMNS as bf, MediaPreviewComponent as bg, MediaPropertiesPanelComponent as bh, MediaRendererComponent as bi, MediaTrimTimelineComponent as bj, MobileBottomBarComponent as bk, MobileMenuSheetComponent as bl, MobilePresenterViewComponent as bm, MobileSheetComponent as bn, MobileSlidesSheetComponent as bo, MobileToolbarComponent as bp, ModalDialogComponent as bq, Model3DRendererComponent as br, NotesHandoutCardComponent as bs, NotesPanelComponent as bt, NotesToolbarComponent as bu, OleRendererComponent as bv, OutlineViewOverlayComponent as bw, P14_TRANSITION_KEYFRAMES as bx, P14_TRANSITION_KEYFRAMES_2 as by, P14_TRANSITION_KEYFRAMES_ALL as bz, AUDIENCE_HASH as c, SlideSorterOverlayComponent as c$, RESIZE_HANDLES as c0, RULER_FONT_SIZE as c1, RULER_THICKNESS as c2, ReadingViewOverlayComponent as c3, RemoteSelectionOverlayComponent as c4, RibbonAnimationGalleryComponent as c5, RibbonAnimationsSectionComponent as c6, RibbonArrangeSectionComponent as c7, RibbonColorPopoverComponent as c8, RibbonComponent as c9, DEFAULT_PALETTE as cA, PALETTES$1 as cB, SMART_ART_COLOR_SCHEMES as cC, SMART_ART_STYLE_OPTIONS as cD, SUB_ITEM_LABEL as cE, SVG_WARP_PRESETS as cF, SWIPE_MAX_VERTICAL_PX as cG, SWIPE_THRESHOLD_PX as cH, SelectionPaneComponent as cI, SetUpSlideShowDialogComponent as cJ, SettingsAppearanceTabComponent as cK, SettingsDialogComponent as cL, SettingsLanguageTabComponent as cM, ShareDialogComponent as cN, ShortcutPanelComponent as cO, ShowOptionsFieldsetComponent as cP, ShowSlidesFieldsetComponent as cQ, SignatureStrippedDialogComponent as cR, SignaturesPanelComponent as cS, SignaturesService as cT, SlideBackgroundCardComponent as cU, SlideCanvasComponent as cV, SlideDefaultInspectorComponent as cW, SlideDiffChangesComponent as cX, SlideDiffRowComponent as cY, SlideDiffThumbnailsComponent as cZ, SlideSizeCardComponent as c_, RibbonDesignSectionComponent as ca, RibbonDrawSectionComponent as cb, RibbonDrawingGroupComponent as cc, RibbonEditingSectionComponent as cd, RibbonFileSectionComponent as ce, RibbonFontControlsComponent as cf, RibbonHomeSectionComponent as cg, RibbonHyperlinkButtonComponent as ch, RibbonInsertFieldsComponent as ci, RibbonInsertSectionComponent as cj, RibbonMotionPathGalleryComponent as ck, RibbonParagraphControlsComponent as cl, RibbonPrimaryRowComponent as cm, RibbonReviewSectionComponent as cn, RibbonShapeExtrasComponent as co, RibbonSlideshowSectionComponent as cp, RibbonTransitionsSectionComponent as cq, RibbonViewSectionComponent as cr, RulerGuidesService as cs, SEQUENCE_OPTIONS as ct, SEVERITY_GROUPS as cu, SEVERITY_LABELS as cv, DEFAULT_TRANSITION_DURATION_MS$1 as cw, SHORTCUT_REFERENCE_ITEMS as cx, SLIDE_TRANSITION_KEYFRAMES as cy, SLIDE_TRANSITION_KEYFRAMES_CSS as cz, AUDIENCE_NONCE_KEY as d, ZoomNavigationService as d$, SlideThemeOverridePanelComponent as d0, SlideTransitionCardComponent as d1, SlidesPanelComponent as d2, SmartArt3DRendererComponent as d3, SmartArt3DService as d4, SmartArtPreviewComponent as d5, SmartArtPropertiesComponent as d6, SmartArtRendererComponent as d7, StatusBarComponent as d8, TABLE_STRUCTURE_TOGGLES as d9, TransitionPreviewComponent as dA, VALIGN_OPTIONS as dB, VIEWER_THEME as dC, VersionHistoryPanelComponent as dD, ViewerCanvasEditingService as dE, ViewerCollabCursorService as dF, ViewerCollaborationSessionService as dG, ViewerCompareService as dH, ViewerCustomShowsService as dI, ViewerDialogsService as dJ, ViewerDocumentPropertiesService as dK, ViewerExportService as dL, ViewerExtraDialogsComponent as dM, ViewerFileIOService as dN, ViewerFindReplaceService as dO, ViewerFormatPainterService as dP, ViewerInspectorPanelService as dQ, ViewerKeyboardService as dR, ViewerMobileSheetService as dS, ViewerPresentationModeService as dT, ViewerThemeGalleryService as dU, ViewerTouchGesturesService as dV, ViewerZoomService as dW, WEBM_MIME_CANDIDATES as dX, WHEEL_SPOKE_COUNTS as dY, WriteBackScheduler as dZ, ZERO_LINE_COLOR as d_, TEXT_3D_BOTTOM_BEVEL_KEYS as da, TEXT_3D_TOP_BEVEL_KEYS as db, TEXT_DIRECTION_OPTIONS$1 as dc, THEME_CATALOG as dd, TIMING_CURVE_OPTIONS as de, TRANSITION_SPEED_DURATION_MS as df, TRIGGER_OPTIONS as dg, TYPE_LABELS as dh, TableCellAdvancedFillComponent as di, TableCellFormattingComponent as dj, TableDataEditorComponent as dk, TablePropertiesComponent as dl, TableRendererComponent as dm, TableResizeOverlayComponent as dn, TableSelectionService as dp, TagsCardComponent as dq, Text3DBevelSectionComponent as dr, Text3DPanelComponent as ds, TextAdvancedPanelComponent as dt, ThemeEditorFieldsComponent as du, ThemeGalleryComponent as dv, ThemeSelectorCardComponent as dw, TitleBarComponent as dx, TitleBarSearchComponent as dy, TransitionDirectionPickerComponent as dz, AVATAR_COLOR_SWATCHES as e, buildModel3DViewModel as e$, ZoomRendererComponent as e0, ZoomTargetService as e1, addCategory as e2, addCommentToList as e3, addGradientStopPatch as e4, addItem as e5, addSeries as e6, addSubItem as e7, advanceStep as e8, affordanceElements as e9, buildCellParagraphs as eA, buildChartViewModel as eB, buildChatLogExport as eC, buildChatLogMarkdown as eD, buildChromeStyle as eE, buildClearHyperlinkPatch as eF, buildClickGroups as eG, buildColStyles as eH, buildCollaborationConfig as eI, buildComboViewModel as eJ, buildCssGradientFromShapeStyle as eK, buildDuotoneFilter as eL, buildDuotoneFilterId as eM, buildEmbeddedFontStyles as eN, buildEquationElement as eO, buildEquationSegment as eP, buildFallbackViewModel as eQ, buildFontFaceRule as eR, buildGradientFillCss as eS, buildGridlinesAndLabels as eT, buildHyperlinkPatch as eU, buildInkContainerStyle as eV, buildInkStrokes as eW, buildLegend as eX, buildLiveInkStrokeView as eY, buildMarkTooltip as eZ, buildModel3DContainerStyle as e_, aiToggleVisible as ea, alignPatch as eb, animationFor as ec, animationPresetLabelKey as ed, annotationMapToInkInserts as ee, applyAcceptedDiff as ef, applyAnimationPreset as eg, applyFindReplacements as eh, applyFormatToElement as ei, applyMove as ej, applyResize as ek, asMediaElement as el, assignUserColor as em, attachShowVisibilityPause as en, attachTouchGestures as eo, axisTickValues as ep, beginDrawingNodeEdit as eq, beginNodeEdit as er, bevelSizePatch as es, boolFromEvent as et, bringForward as eu, bringToFront as ev, buildBarActions as ew, buildBroadcastConfig as ex, buildBroadcastViewerUrl as ey, buildCategoryLabels as ez, AXIS_LABEL_COLOR as f, computeErrorBarPrimitives as f$, buildOleActionModel as f0, buildOleInfoRows as f1, buildPatternFillCss as f2, buildPieViewModel as f3, buildPrintHtmlDocument as f4, buildPropertiesPatch as f5, buildRadarViewModel as f6, buildRegionMapViewModel as f7, buildSaveSlides as f8, buildShareUrl as f9, changeIcon as fA, characterSpacingPatch as fB, chartPreserveAspectRatio as fC, checkFontAvailable as fD, clampCursorPosition as fE, clampGifDimensions as fF, clampIndex as fG, clampNotesFontSize as fH, clampScale as fI, clampStep as fJ, clearAllLocalViewerData as fK, clearAudienceContent as fL, cn as fM, collectAccessibilityIssues as fN, collectElementText as fO, collectSlideText as fP, collectStoredChats as fQ, collectUsedFontFamilies as fR, columnWidthStyle as fS, commitNodeText as fT, computeAlign as fU, computeAxisTitlePrimitives as fV, computeBarRects as fW, computeBubbleRadius as fX, computeCornerHandle as fY, computeDistribute as fZ, computeDrawingViewBox as f_, buildSmartArtInsertElement as fa, buildSmartArtNodes as fb, buildStockViewModel as fc, buildSurfaceViewModel as fd, buildTableViewModel as fe, buildTreemapViewModel as ff, buildTrimFragment as fg, buildWaterfallViewModel as fh, buildZeroLine as fi, buildZoomContainerStyle as fj, buildZoomViewModel as fk, bulletIndentPx as fl, canAddTopLevelNode as fm, canEditSmartArtNodes as fn, canGroupSelection as fo, canRemoveTopLevelNode as fp, canSetStrokeWidth as fq, canStartBroadcast as fr, canStartShare as fs, canUngroupSelection as ft, canUseClipboard as fu, captionDisplayText as fv, cellRunStyle as fw, cellStyleToStyleMap as fx, cellTdStyle as fy, changeCountLabel as fz, AccessibilityPanelComponent as g, encodeGif as g$, computeFocusTargets as g0, computeGridSpacingPx as g1, computeHandleBoxes as g2, computeHandoutLayout as g3, computeIsMobile as g4, computeIsTablet as g5, computeLinePoints as g6, computeLinearRegression as g7, computePageCount as g8, computePieLayout as g9, createWebsocketBundle as gA, cssObjectToStyleMap as gB, currentColorScheme as gC, currentLayout as gD, currentStyle as gE, defaultCssVars as gF, defaultRadius as gG, defaultThemeColors as gH, deleteElementsByIds as gI, deleteVersion as gJ, demoteNode as gK, deriveModel3DBlobUrl as gL, derivePresenceList as gM, describeSmartArtBounds as gN, disableGlowPatch as gO, disableInnerShadowPatch as gP, disableOuterShadowPatch as gQ, disableReflectionPatch as gR, disableSoftEdgePatch as gS, duplicateElementById as gT, durationOf as gU, effectsStateOf as gV, enableGlowPatch as gW, enableInnerShadowPatch as gX, enableOuterShadowPatch as gY, enableReflectionPatch as gZ, enableSoftEdgePatch as g_, computePieSlicePath as ga, computePieSlices as gb, computePlotLayout as gc, computeRSquared as gd, computeRadarPoints as ge, computeResizeHandleBoxes as gf, computeRotateHandleBox as gg, computeScatterDots as gh, computeScatterXDomain as gi, computeSelectionBoxes as gj, computeSingleSelected as gk, computeSlideIndices as gl, computeSnap as gm, computeStackedBarRects as gn, computeStackedValueRange as go, computeTrendlinePrimitives as gp, computeValueRange as gq, convertOmmlToMathMl as gr, copyFormatFromElement as gs, countAccessibilityIssues as gt, countAnnotationStrokes as gu, createAngularAiBridge as gv, createCustomShow as gw, createImageElementFromFile as gx, createSwipeDismissDrag as gy, createWebrtcBundle as gz, AccessibilityService as h, gradientStateFromStyle as h$, endShowMediaCleanup as h0, estimatePageCount as h1, exitPresentationFullscreen as h2, exportAiChatLogs as h3, extractPathPoints as h4, eyedropperAvailable as h5, fillColorOf$1 as h6, findInSlides as h7, findOwningSlideIndex as h8, findSlideIndexByElementId as h9, getEffectSoundState as hA, getImageSrc as hB, getLocalStorageUsageSummary as hC, getOleAriaLabel as hD, getOleBadgeLabel as hE, getOleDisplayName as hF, getOleDownloadFileName as hG, getOleTypeColor as hH, getOleTypeLabel as hI, getP14TransitionAnimations as hJ, getPasswordStrength as hK, getPatternSvg as hL, getPlaceholderStyle as hM, getVersions as hN, getResolvedShapeClipPath as hO, getResolvedShapeClipPathFor as hP, getSessionTabId as hQ, getShapeFillStrokeStyle as hR, getSlideBackgroundStyle as hS, getSlideTransitionAnimations as hT, getSmartArtNodeBounds as hU, getSpeechRecognitionCtor as hV, getTextBlockStyle as hW, getTextWarp as hX, getTouchDistance as hY, getWarpCategory as hZ, getWarpPath as h_, firstVisibleIndex as ha, fitPolynomial as hb, fitZoom as hc, focusTargetChips as hd, fontMimeForFormat as he, fontSizeOf as hf, forgetSessionDeck as hg, formatAxisValue as hh, formatBytes as hi, formatCursorLabel as hj, formatElapsed as hk, formatFileSize as hl, formatInlineListStyle as hm, formatPropertyDate as hn, formatTime as ho, fpsToFrameIntervalMs as hp, generateBroadcastRoomId as hq, generateCommentId as hr, generateCustomShowId as hs, generatePressureCircles as ht, generateTicks as hu, getCinematicTransitionAnimations as hv, getClrChangeParams as hw, getContainerStyle as hx, getDuotoneFilterDef as hy, getEffectSoundAsset as hz, AccessibilityTextPanelComponent as i, narrowToRect as i$, gradientStateOf as i0, gradientStatePatch as i1, gradientStopColorCommitPatch as i2, gridColumns as i3, groupIssuesBySeverity as i4, hasAnimation as i5, hasCopyableFormat as i6, hasExistingLink as i7, hasExitedFullscreen as i8, hasGradientFill as i9, isValidRoomId as iA, isViewportBackgroundPressTarget as iB, isZoomActivationKey as iC, issueTrackKey as iD, issueTypeLabel as iE, keyToLabel as iF, lastVisibleIndex as iG, latexToMathml as iH, layoutConnectorPaints as iI, layoutNodeLabels as iJ, linePointsToSvgString as iK, lineSpacingPatch as iL, loadAudienceContent as iM, loadSessionDeck as iN, mediaFallbackFor as iO, mediaSurfaceFor as iP, mergeCaptionResults as iQ, mergeDown as iR, mergeRight as iS, mergeSelection as iT, mergeTablesDirective as iU, moveElementBy as iV, moveNodeDown as iW, moveNodeUp as iX, msToFrameDelayCs as iY, narrowToCircle as iZ, narrowToPolygon as i_, hasPressureVariation as ia, hasVisibleSlideAfter as ib, headerLabel as ic, imageDimensions as id, inkViewBox as ie, insertTableElementColumn as ig, insertTableElementRow as ih, interpolateWidth as ii, isAudienceTab as ij, isBold as ik, isBrowserOpenableMime as il, isChildNode as im, isElementInteractive as io, isInjectableUrl as ip, isItalic as iq, isLegacyBinaryPresentation as ir, isPpactionUrl as is, isPresenterMessage as it, isSigned as iu, isSupportedPresentationFile as iv, isTextElement as iw, isTwoTableFocus as ix, isUnderline as iy, isUrlSafe as iz, AccountPageComponent as j, removeNode as j$, newChartElement as j0, newEquationElement as j1, newPresetShapeElement as j2, newShapeElement as j3, newSmartArtElement as j4, newTableElement as j5, newTextElement as j6, nextVisibleIndex as j7, nodeBold as j8, nodeEditBox as j9, planGifFrames as jA, planVideoSegments as jB, pointFromPointerEvent as jC, pointsToSvgPathD as jD, presenceToCursors as jE, presentationBaseName as jF, presentationStageStyle as jG, presenterTimerProgress as jH, presetByLayout as jI, presetsForCategory as jJ, pressuresToWidths as jK, prevVisibleIndex as jL, projectDrawingShapes as jM, promoteNode as jN, provideViewerTheme as jO, radarAngle as jP, radarRingPoints as jQ, readAsDataUrl as jR, recordWebm as jS, registerCrossSlideAudio as jT, rememberSessionDeck as jU, removeAnimation as jV, removeCategory as jW, removeTableElementColumn as jX, removeCommentFromList as jY, removeElementAnimation as jZ, removeGradientStopPatch as j_, nodeFillColor as ja, nodeFontColor as jb, nodeIdFromKey as jc, nodeItalic as jd, nodeStyle as je, normalizeFontFormat as jf, normalizeSlidesPerPage as jg, normalizeValue as jh, numFromEvent as ji, ommlToMathml as jj, ooxmlDashToCssBorderStyle as jk, openNativeEyeDropper as jl, overallStatus as jm, paletteColor as jn, parseAudienceNonce as jo, parseNodeTextarea as jp, partitionSlides as jq, patchChartData as jr, patchChartStyle as js, patchTableData as jt, patchTextStyle as ju, patternPresetOptions as jv, pendingElementStyles as jw, pickColorByClickFallback as jx, pickFile as jy, pickSupportedMimeType as jz, ActionSettingsPanelComponent as k, setAxisLogScale as k$, removeTableElementRow as k0, removeSeries as k1, renderToCanvas as k2, reorderAnimationDown as k3, reorderAnimationUp as k4, replaceInSlides as k5, replaceMatch as k6, requestPresentationFullscreen as k7, resizeElement as k8, resolveCaptionTracks as k9, rulerHighlight as kA, rulerStripTicks as kB, sampleColorFromSlide as kC, sanitizeColor as kD, sanitizeSlideIndex as kE, sanitizeUserName as kF, saveViewerProfile as kG, savedPresentationFileName as kH, scanAvailableFonts as kI, searchSlides as kJ, seedBroadcastFields as kK, seedHyperlinkDraft as kL, seedPropertiesDraft as kM, seedShareFields as kN, segmentFrameCount as kO, selectValue$3 as kP, sendBackward as kQ, sendToBack as kR, sequentialColorScale as kS, serializeWriteBack as kT, seriesColor as kU, setAfterAnimation as kV, setAfterAnimationColor as kW, setAnimationEmphasis as kX, setAnimationEntrance as kY, setAnimationExit as kZ, setAxis as k_, resolveChartKind as ka, resolveDirection as kb, resolveDirection8 as kc, resolveFontVariant as kd, resolveHyperlinkHref as ke, resolveInteractiveElementId as kf, resolveMediaSrc as kg, resolveOleType as kh, resolveOrientation as ki, resolveParagraphBullet as kj, resolvePresenterNotes as kk, resolveProfileInitial as kl, resolveRegionCode as km, resolveRibbonCanGroup as kn, resolveSlideAutoAdvanceMs as ko, resolveSlideTransition as kp, resolvePalette as kq, resolveThemeCatalogEntry as kr, resolveTransitionDuration as ks, resolveTransitionDurationMs as kt, resolveWheelSpokeCount as ku, restoreSessionDeck as kv, revealedElementStyles as kw, routeOrthogonalConnector as kx, rowStyle as ky, rulerDragToGuidePosition as kz, AdvancedChartEditorComponent as l, textAdvancedStateFromStyle as l$, setAxisTitleStyle as l0, setCategoryLabel as l1, setCellText as l2, setColorScheme as l3, setDataLabels as l4, setDataPointExplosion as l5, setDataPointFill as l6, setDataPointLabel as l7, setDataPointMarker as l8, setDelay as l9, sheetAfterNavigate as lA, shouldBlockClickAdvance as lB, shouldUseSvgWarp as lC, showDirectionPicker as lD, showsTemplateAffordance as lE, signatureCountLabel as lF, signatureKey as lG, signatureTimestamp as lH, signerName as lI, statusLabel as lJ, slideNumberOf as lK, slidesWithReappliedLayout as lL, smartArtNodes as lM, paletteColour as lN, snapToGridStep as lO, splitCursorCell as lP, splitMergedCell as lQ, statusKind as lR, statusLabel$1 as lS, storeAudienceContent as lT, stringFromEvent$5 as lU, strokeColorOf as lV, strokeToInkElement as lW, strokeWidthOf as lX, styleShadowFilter as lY, surfaceColor as lZ, textAdvancedPatch as l_, setDirection as la, setDuration as lb, setEffectSound as lc, setEffectStockSound as ld, setElementPosition as le, setGridlineStyle as lf, setLayout as lg, setLegend as lh, setNodeStyle as li, setNodeText as lj, setRepeatCount as lk, setRepeatMode as ll, setSequence as lm, setSeriesChartType as ln, setSeriesColor as lo, setSeriesErrorBars as lp, setSeriesMarker as lq, setSeriesName as lr, setSeriesTrendline as ls, setSeriesValue as lt, setStyle as lu, setTimingCurve as lv, setTitle as lw, setTrigger as lx, setTriggerShapeId as ly, shapeStylePatch$1 as lz, AiChangeOverlayComponent as m, textAdvancedStateOf as m0, textColorOf as m1, textDirectionPatch as m2, textFontSizePatch as m3, textStyleOf as m4, textStylePatch as m5, themeStyle as m6, themeToCssVars as m7, thumbnailHeight as m8, thumbnailZoom as m9, zoomTargetSlideIndex as mA, toggleCommentResolvedInList as ma, toggleNodeBold as mb, toggleNodeItalic as mc, toggleSheet as md, topLevelNodeCount as me, transformSelectedTextCase as mf, translationsEn as mg, updateElementById as mh, updateGlowPatch as mi, updateGradientStopPatch as mj, updateInnerShadowPatch as mk, updateOuterShadowPatch as ml, updateReflectionPatch as mm, vAlignPatch as mn, validatePassword as mo, validatePrintSettings as mp, validateRoomId as mq, valueToY as mr, vermilionDarkColors as ms, vermilionDarkTheme as mt, vermilionLightColors as mu, vermilionLightTheme as mv, vermilionRadius as mw, waypointsToPathD as mx, withManualLayouts as my, worstStatus as mz, AiChatPanelComponent as n, AiChatService as o, AiComposerComponent as p, AiFocusBarComponent as q, AiFocusHighlightOverlayComponent as r, AiHistoryMenuComponent as s, toChatSummary as t, AiHistoryService as u, AiMessageListComponent as v, AiPanelStore as w, AiProposalCardComponent as x, AiSettingsSectionComponent as y, AiToolCallCardComponent as z };
|
|
185882
|
-
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-
|
|
187011
|
+
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-DP0uS-ib.mjs.map
|