pptx-angular-viewer 2.18.7 → 2.19.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 +13 -0
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-IYWfGg1_.mjs → pptx-angular-viewer-chat-history-idb-BwJthigo.mjs} +2 -2
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-IYWfGg1_.mjs.map → pptx-angular-viewer-chat-history-idb-BwJthigo.mjs.map} +1 -1
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-CgQt2SkW.mjs → pptx-angular-viewer-pptx-angular-viewer-BLdLZTxr.mjs} +1250 -803
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-CgQt2SkW.mjs.map → pptx-angular-viewer-pptx-angular-viewer-BLdLZTxr.mjs.map} +1 -1
- package/fesm2022/pptx-angular-viewer.mjs +1 -1
- package/package.json +1 -1
- package/types/pptx-angular-viewer.d.ts +177 -94
- package/types/pptx-angular-viewer.d.ts.map +1 -1
|
@@ -2,7 +2,7 @@ import { NgStyle, NgTemplateOutlet, NgClass } from '@angular/common';
|
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
3
|
import { InjectionToken, input, output, inject, computed, ChangeDetectionStrategy, Component, signal, Injectable, DestroyRef, viewChild, effect, Injector, ElementRef, HostListener, afterNextRender, Renderer2, ViewEncapsulation, forwardRef, untracked } from '@angular/core';
|
|
4
4
|
import { TranslateService, TranslatePipe, translate } from '@ngx-translate/core';
|
|
5
|
-
import { THEME_PRESETS, guideEmuToPx, getShapeClipPath, getAdjustmentAwareClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, ooxmlGradientAngleToCssDegrees, hasShapeProperties, evaluatePresetShape, getShapeType, isImageLikeElement, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$1, getElementTextContent, getRoundRectRadiusPx as getRoundRectRadiusPx$1, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, rebuildTableStructureInRawXml, updateCellTextInRawXml, deepCloneData, cloneTemplateElementsBySlideId, cloneSlide, DEFAULT_SCHEME_COLOR_MAP as DEFAULT_SCHEME_COLOR_MAP$1, formatAutoNumberMarker, getLinkedTextBoxSegments, getSubstituteFontFamily, parsePanoseString, deriveSlideTitle, svgPathToPolygons, THEME_COLOR_SCHEME_KEYS, createBuiltinVariables, resolveCoordinate, lookupPresetShape, normalizeStShapeType, evaluateGuides, PptxHandler, deobfuscateFont, detectFontFormat, createEditorId, makeStoreAwareId, reassignDescendantIds, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, detectFileFormat, serializePptxToJson, PPTX_JSON_MIME_TYPE, PptxMarkdownConverter, ThemePresets, formatCommentTimestamp as formatCommentTimestamp$1, EncryptedFileError, decodeXmlEntities, parseSignatureXml, groupElements, ungroupElements, cloneElement, updateSmartArtNodeText, setSmartArtNodeStyle, isInkElement, parseDataUrlToBytes, isZoomElement, SvgExporter, applyThemeToData, pptxActionToElementAction, elementActionToPptxAction, setChartTitle, setChartLegend, setChartDataLabels, setChartAxis, setChartSeriesTrendline, setChartSeriesErrorBars, setChartDataPointLabel, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointMarker, setChartDataPointExplosion, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild,
|
|
5
|
+
import { THEME_PRESETS, guideEmuToPx, getShapeClipPath, getAdjustmentAwareClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, ooxmlGradientAngleToCssDegrees, hasShapeProperties, evaluatePresetShape, getShapeType, isImageLikeElement, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$1, getElementTextContent, getRoundRectRadiusPx as getRoundRectRadiusPx$1, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, hasTextProperties, stripXmlOrderSuffix, orderedXmlKey, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, rebuildTableStructureInRawXml, updateCellTextInRawXml, deepCloneData, cloneTemplateElementsBySlideId, cloneSlide, DEFAULT_SCHEME_COLOR_MAP as DEFAULT_SCHEME_COLOR_MAP$1, formatAutoNumberMarker, getLinkedTextBoxSegments, getSubstituteFontFamily, parsePanoseString, deriveSlideTitle, svgPathToPolygons, THEME_COLOR_SCHEME_KEYS, createBuiltinVariables, resolveCoordinate, lookupPresetShape, normalizeStShapeType, evaluateGuides, PptxHandler, addSmartArtNode, removeSmartArtNode, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode, deobfuscateFont, detectFontFormat, createEditorId, makeStoreAwareId, reassignDescendantIds, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, detectFileFormat, serializePptxToJson, PPTX_JSON_MIME_TYPE, PptxMarkdownConverter, ThemePresets, formatCommentTimestamp as formatCommentTimestamp$1, EncryptedFileError, decodeXmlEntities, parseSignatureXml, groupElements, ungroupElements, cloneElement, updateSmartArtNodeText, setSmartArtNodeStyle, isInkElement, parseDataUrlToBytes, isZoomElement, SvgExporter, applyThemeToData, pptxActionToElementAction, elementActionToPptxAction, setChartTitle, setChartLegend, setChartDataLabels, setChartAxis, setChartSeriesTrendline, setChartSeriesErrorBars, setChartDataPointLabel, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointMarker, setChartDataPointExplosion, chartDataChangeType, getOleObjectTypeLabel, addSmartArtNodeAsChild, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, TRANSITION_VALID_DIRECTIONS } from 'pptx-viewer-core';
|
|
6
6
|
import DOMPurify from 'dompurify';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import * as mcp from 'pptx-viewer-mcp';
|
|
@@ -544,7 +544,7 @@ const DEFAULT_CANVAS_HEIGHT = 720;
|
|
|
544
544
|
/** Fallback text colour. */
|
|
545
545
|
const DEFAULT_TEXT_COLOR$1 = '#111827';
|
|
546
546
|
/** Fallback shape fill colour. */
|
|
547
|
-
const DEFAULT_FILL_COLOR = '#3b82f6';
|
|
547
|
+
const DEFAULT_FILL_COLOR$1 = '#3b82f6';
|
|
548
548
|
/** Fallback shape stroke colour. */
|
|
549
549
|
const DEFAULT_STROKE_COLOR = '#1f2937';
|
|
550
550
|
/** Colour PowerPoint paints hyperlinked text in when the theme declares none. */
|
|
@@ -915,7 +915,7 @@ function sanitizeGradientStops(stops) {
|
|
|
915
915
|
typeof stop?.position === 'number' &&
|
|
916
916
|
Number.isFinite(stop.position))
|
|
917
917
|
.map((stop) => ({
|
|
918
|
-
color: normalizeHexColor$1(String(stop.color), DEFAULT_FILL_COLOR),
|
|
918
|
+
color: normalizeHexColor$1(String(stop.color), DEFAULT_FILL_COLOR$1),
|
|
919
919
|
position: Math.max(0, Math.min(100, stop.position)),
|
|
920
920
|
opacity: typeof stop.opacity === 'number' && Number.isFinite(stop.opacity)
|
|
921
921
|
? clampUnitInterval$1(stop.opacity)
|
|
@@ -8735,6 +8735,25 @@ function renderWinLossSparkline(data) {
|
|
|
8735
8735
|
].join('');
|
|
8736
8736
|
}
|
|
8737
8737
|
|
|
8738
|
+
// Horizontal centre-to-centre spacing between legend swatches in a row, and
|
|
8739
|
+
// vertical spacing between legend swatches stacked in a side column.
|
|
8740
|
+
const LEGEND_ITEM_WIDTH = 80, LEGEND_ITEM_HEIGHT = 14;
|
|
8741
|
+
/**
|
|
8742
|
+
* Compute the positioned legend entries for a chart view model.
|
|
8743
|
+
*
|
|
8744
|
+
* @param vm - The chart view model (`vm.legend` / `vm.legendX` / `vm.legendY`
|
|
8745
|
+
* / `vm.legendAnchor`, as produced by `buildChartViewModel`).
|
|
8746
|
+
*/
|
|
8747
|
+
function computeChartLegendLayout(vm) {
|
|
8748
|
+
const vertical = vm.legendAnchor === 'start';
|
|
8749
|
+
return vm.legend.map((entry, i) => {
|
|
8750
|
+
const x = vertical
|
|
8751
|
+
? vm.legendX
|
|
8752
|
+
: vm.legendX - (vm.legend.length * LEGEND_ITEM_WIDTH) / 2 + i * LEGEND_ITEM_WIDTH, y = vertical ? vm.legendY + i * LEGEND_ITEM_HEIGHT : vm.legendY;
|
|
8753
|
+
return { x, y, color: entry.color, label: entry.label };
|
|
8754
|
+
});
|
|
8755
|
+
}
|
|
8756
|
+
|
|
8738
8757
|
const CHART_TYPE_OPTIONS = [
|
|
8739
8758
|
{ value: 'bar', label: 'Bar', labelKey: 'pptx.chart.typeBar' },
|
|
8740
8759
|
{ value: 'line', label: 'Line', labelKey: 'pptx.chart.typeLine' },
|
|
@@ -9849,11 +9868,8 @@ function blendToWhite(color) {
|
|
|
9849
9868
|
if (!match) {
|
|
9850
9869
|
return color;
|
|
9851
9870
|
}
|
|
9852
|
-
|
|
9853
|
-
const mix = (channel) => Math.round(channel + (255 - channel) * 0.5);
|
|
9854
|
-
const r = mix((value >> 16) & 0xff);
|
|
9855
|
-
const g = mix((value >> 8) & 0xff);
|
|
9856
|
-
const b = mix(value & 0xff);
|
|
9871
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
9872
|
+
const value = Number.parseInt(match[1], 16), mix = (channel) => Math.round(channel + (255 - channel) * 0.5), r = mix((value >> 16) & 0xff), g = mix((value >> 8) & 0xff), b = mix(value & 0xff);
|
|
9857
9873
|
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0').toUpperCase()}`;
|
|
9858
9874
|
}
|
|
9859
9875
|
/**
|
|
@@ -9868,8 +9884,7 @@ function invertNegativeFill(series, pointIndex, value, baseFill) {
|
|
|
9868
9884
|
if (value >= 0) {
|
|
9869
9885
|
return baseFill;
|
|
9870
9886
|
}
|
|
9871
|
-
const point = series.dataPoints?.find((p) => p.idx === pointIndex);
|
|
9872
|
-
const invert = point?.invertIfNegative ?? series.invertIfNegative ?? false;
|
|
9887
|
+
const point = series.dataPoints?.find((p) => p.idx === pointIndex), invert = point?.invertIfNegative ?? series.invertIfNegative ?? false;
|
|
9873
9888
|
return invert ? blendToWhite(baseFill) : baseFill;
|
|
9874
9889
|
}
|
|
9875
9890
|
/**
|
|
@@ -9878,40 +9893,25 @@ function invertNegativeFill(series, pointIndex, value, baseFill) {
|
|
|
9878
9893
|
* labels. Mirrors React's `renderDefaultBarChart` / `renderStackedBarChart`.
|
|
9879
9894
|
*/
|
|
9880
9895
|
function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, grouping, sourceIndices) {
|
|
9881
|
-
const primitives = []
|
|
9882
|
-
const dataLabels = [];
|
|
9883
|
-
const series = chartData.series;
|
|
9884
|
-
const palette = chartData.colorPalette;
|
|
9885
|
-
const showLabels = chartData.style?.hasDataLabels;
|
|
9896
|
+
const primitives = [], dataLabels = [], series = chartData.series, palette = chartData.colorPalette, showLabels = chartData.style?.hasDataLabels,
|
|
9886
9897
|
// Single-series bar/column with c:varyColors=1 gives every category a distinct
|
|
9887
9898
|
// palette colour (a per-point c:dPt fill still wins). Multi-series charts keep
|
|
9888
9899
|
// their per-series colours (varyColors has no cross-series meaning there).
|
|
9889
|
-
|
|
9900
|
+
varyColorsSingle = chartData.varyColors === true && series.length === 1;
|
|
9890
9901
|
if (grouping === 'clustered') {
|
|
9891
|
-
const seriesCount = Math.max(series.length, 1)
|
|
9892
|
-
const barGroupWidth = layout.plotWidth / Math.max(catCount, 1);
|
|
9902
|
+
const seriesCount = Math.max(series.length, 1), barGroupWidth = layout.plotWidth / Math.max(catCount, 1),
|
|
9893
9903
|
// Honour c:gapWidth (gap between clusters, % of a bar width) when parsed;
|
|
9894
9904
|
// otherwise keep the legacy 0.7-of-group heuristic byte-for-byte.
|
|
9895
|
-
|
|
9905
|
+
singleBarWidth = chartData.barGapWidth !== undefined
|
|
9896
9906
|
? barGroupWidth / (seriesCount + Math.max(chartData.barGapWidth, 0) / 100)
|
|
9897
|
-
: (barGroupWidth * 0.7) / seriesCount
|
|
9907
|
+
: (barGroupWidth * 0.7) / seriesCount,
|
|
9898
9908
|
// Honour c:overlap (% overlap between adjacent series). overlap=0 reproduces
|
|
9899
9909
|
// the original side-by-side layout exactly.
|
|
9900
|
-
|
|
9901
|
-
const step = singleBarWidth * (1 - overlap / 100);
|
|
9902
|
-
const clusterWidth = singleBarWidth + step * (seriesCount - 1);
|
|
9903
|
-
const groupOffset = (barGroupWidth - clusterWidth) / 2;
|
|
9910
|
+
overlap = chartData.barOverlap ?? 0, step = singleBarWidth * (1 - overlap / 100), clusterWidth = singleBarWidth + step * (seriesCount - 1), groupOffset = (barGroupWidth - clusterWidth) / 2;
|
|
9904
9911
|
for (let displayIndex = 0; displayIndex < catCount; displayIndex++) {
|
|
9905
9912
|
const sourceIndex = sourceIndices[displayIndex] ?? displayIndex;
|
|
9906
9913
|
for (let si = 0; si < series.length; si++) {
|
|
9907
|
-
const val = series[si].values[sourceIndex] ?? 0
|
|
9908
|
-
const x = layout.plotLeft + barGroupWidth * displayIndex + groupOffset + step * si;
|
|
9909
|
-
const activeRange = secondaryIdx.has(si) && secondaryRange ? secondaryRange : primaryRange;
|
|
9910
|
-
const zeroY = valueToY(0, activeRange, layout.plotTop, layout.plotBottom);
|
|
9911
|
-
const valY = valueToY(val, activeRange, layout.plotTop, layout.plotBottom);
|
|
9912
|
-
const y = Math.min(zeroY, valY);
|
|
9913
|
-
const h = Math.max(Math.abs(zeroY - valY), 1);
|
|
9914
|
-
const baseFill = varyColorsSingle
|
|
9914
|
+
const val = series[si].values[sourceIndex] ?? 0, x = layout.plotLeft + barGroupWidth * displayIndex + groupOffset + step * si, activeRange = secondaryIdx.has(si) && secondaryRange ? secondaryRange : primaryRange, zeroY = valueToY(0, activeRange, layout.plotTop, layout.plotBottom), valY = valueToY(val, activeRange, layout.plotTop, layout.plotBottom), y = Math.min(zeroY, valY), h = Math.max(Math.abs(zeroY - valY), 1), baseFill = varyColorsSingle
|
|
9915
9915
|
? resolveVaryColorFill(series[si], sourceIndex, paletteColor(sourceIndex, palette))
|
|
9916
9916
|
: (resolveDataPointFill(series[si], sourceIndex, paletteColor(si, palette)) ??
|
|
9917
9917
|
seriesColor(series[si], si, palette));
|
|
@@ -9924,6 +9924,7 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
9924
9924
|
fill: invertNegativeFill(series[si], sourceIndex, val, baseFill),
|
|
9925
9925
|
rx: 1,
|
|
9926
9926
|
part: { role: 'dataPoint', seriesIndex: si, pointIndex: sourceIndex },
|
|
9927
|
+
title: buildMarkTooltip(series[si].name, chartData.categories[sourceIndex], val, series[si].numberFormat),
|
|
9927
9928
|
});
|
|
9928
9929
|
if (showLabels) {
|
|
9929
9930
|
// c:showVal / c:showCatName / c:showPercent decide what the label
|
|
@@ -9958,19 +9959,19 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
9958
9959
|
const displaySeries = series.map((entry) => ({
|
|
9959
9960
|
...entry,
|
|
9960
9961
|
values: sourceIndices.map((sourceIndex) => entry.values[sourceIndex] ?? 0),
|
|
9961
|
-
}));
|
|
9962
|
-
const rects = computeStackedBarRects(displaySeries, catCount, layout, primaryRange, palette);
|
|
9962
|
+
})), rects = computeStackedBarRects(displaySeries, catCount, layout, primaryRange, palette);
|
|
9963
9963
|
for (const r of rects) {
|
|
9964
|
-
let fill = r.fill;
|
|
9965
|
-
let part;
|
|
9964
|
+
let fill = r.fill, part, title;
|
|
9966
9965
|
if (r.seriesIndex !== undefined && r.pointIndex !== undefined) {
|
|
9967
9966
|
const sourcePointIndex = sourceIndices[r.pointIndex] ?? r.pointIndex;
|
|
9968
9967
|
fill = resolveDataPointFill(series[r.seriesIndex], sourcePointIndex, r.fill) ?? r.fill;
|
|
9968
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
9969
9969
|
const value = series[r.seriesIndex].values[sourcePointIndex] ?? 0;
|
|
9970
9970
|
fill = invertNegativeFill(series[r.seriesIndex], sourcePointIndex, value, fill);
|
|
9971
9971
|
part = { role: 'dataPoint', seriesIndex: r.seriesIndex, pointIndex: sourcePointIndex };
|
|
9972
|
+
title = buildMarkTooltip(series[r.seriesIndex].name, chartData.categories[sourcePointIndex], value, series[r.seriesIndex].numberFormat);
|
|
9972
9973
|
}
|
|
9973
|
-
primitives.push({ kind: 'rect', x: r.x, y: r.y, w: r.w, h: r.h, fill, rx: 1, part });
|
|
9974
|
+
primitives.push({ kind: 'rect', x: r.x, y: r.y, w: r.w, h: r.h, fill, rx: 1, part, title });
|
|
9974
9975
|
}
|
|
9975
9976
|
if (showLabels) {
|
|
9976
9977
|
pushClusteredStackedLabels(chartData, series, sourceIndices, catCount, layout, primaryRange, dataLabels);
|
|
@@ -9978,31 +9979,16 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
9978
9979
|
return { primitives, dataLabels };
|
|
9979
9980
|
}
|
|
9980
9981
|
// percentStacked: normalise each category to 100% with in-bar percent labels.
|
|
9981
|
-
|
|
9982
|
-
const barW = barGroupWidth * 0.6
|
|
9983
|
-
const barOffset = (barGroupWidth - barW) / 2;
|
|
9984
|
-
const displaySeries = series.map((entry) => ({
|
|
9982
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
9983
|
+
const barGroupWidth = layout.plotWidth / Math.max(catCount, 1), barW = barGroupWidth * 0.6, barOffset = (barGroupWidth - barW) / 2, displaySeries = series.map((entry) => ({
|
|
9985
9984
|
...entry,
|
|
9986
9985
|
values: sourceIndices.map((sourceIndex) => entry.values[sourceIndex] ?? 0),
|
|
9987
|
-
}));
|
|
9988
|
-
const totals = categoryTotals$1(displaySeries, catCount);
|
|
9986
|
+
})), totals = categoryTotals$1(displaySeries, catCount);
|
|
9989
9987
|
for (let ci = 0; ci < catCount; ci++) {
|
|
9990
|
-
let posRunning = 0;
|
|
9991
|
-
let negRunning = 0;
|
|
9988
|
+
let posRunning = 0, negRunning = 0;
|
|
9992
9989
|
const catTotal = totals[ci] || 1;
|
|
9993
9990
|
for (let si = 0; si < series.length; si++) {
|
|
9994
|
-
const sourceIndex = sourceIndices[ci] ?? ci
|
|
9995
|
-
const rawVal = series[si].values[sourceIndex] ?? 0;
|
|
9996
|
-
const val = catTotal > 0 ? (rawVal / catTotal) * 100 : 0;
|
|
9997
|
-
const isNeg = val < 0;
|
|
9998
|
-
const base = isNeg ? negRunning : posRunning;
|
|
9999
|
-
const top = base + val;
|
|
10000
|
-
const x = layout.plotLeft + barGroupWidth * ci + barOffset;
|
|
10001
|
-
const baseY = valueToY(base, primaryRange, layout.plotTop, layout.plotBottom);
|
|
10002
|
-
const topY = valueToY(top, primaryRange, layout.plotTop, layout.plotBottom);
|
|
10003
|
-
const y = Math.min(baseY, topY);
|
|
10004
|
-
const h = Math.max(Math.abs(baseY - topY), 0.5);
|
|
10005
|
-
const pctBaseFill = resolveDataPointFill(series[si], sourceIndex, paletteColor(si, palette)) ??
|
|
9991
|
+
const sourceIndex = sourceIndices[ci] ?? ci, rawVal = series[si].values[sourceIndex] ?? 0, val = catTotal > 0 ? (rawVal / catTotal) * 100 : 0, isNeg = val < 0, base = isNeg ? negRunning : posRunning, top = base + val, x = layout.plotLeft + barGroupWidth * ci + barOffset, baseY = valueToY(base, primaryRange, layout.plotTop, layout.plotBottom), topY = valueToY(top, primaryRange, layout.plotTop, layout.plotBottom), y = Math.min(baseY, topY), h = Math.max(Math.abs(baseY - topY), 0.5), pctBaseFill = resolveDataPointFill(series[si], sourceIndex, paletteColor(si, palette)) ??
|
|
10006
9992
|
seriesColor(series[si], si, palette);
|
|
10007
9993
|
primitives.push({
|
|
10008
9994
|
kind: 'rect',
|
|
@@ -10012,6 +9998,7 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
10012
9998
|
h,
|
|
10013
9999
|
fill: invertNegativeFill(series[si], sourceIndex, rawVal, pctBaseFill),
|
|
10014
10000
|
part: { role: 'dataPoint', seriesIndex: si, pointIndex: sourceIndex },
|
|
10001
|
+
title: buildMarkTooltip(series[si].name, chartData.categories[sourceIndex], rawVal, series[si].numberFormat),
|
|
10015
10002
|
});
|
|
10016
10003
|
if (showLabels && Math.abs(val) > 0) {
|
|
10017
10004
|
dataLabels.push({
|
|
@@ -10042,23 +10029,15 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
10042
10029
|
* reproduces that exact output for byte-identity.
|
|
10043
10030
|
*/
|
|
10044
10031
|
function pushClusteredStackedLabels(chartData, series, sourceIndices, catCount, layout, range, dataLabels) {
|
|
10045
|
-
const barGroupWidth = layout.plotWidth / catCount;
|
|
10046
|
-
const seriesCount = Math.max(series.length, 1);
|
|
10047
|
-
const singleBarWidth = (barGroupWidth * 0.7) / seriesCount;
|
|
10048
|
-
const groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
|
|
10032
|
+
const barGroupWidth = layout.plotWidth / catCount, seriesCount = Math.max(series.length, 1), singleBarWidth = (barGroupWidth * 0.7) / seriesCount, groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
|
|
10049
10033
|
for (let ci = 0; ci < catCount; ci++) {
|
|
10050
10034
|
const sourceIndex = sourceIndices[ci] ?? ci;
|
|
10051
10035
|
for (let si = 0; si < series.length; si++) {
|
|
10052
|
-
const val = series[si].values[sourceIndex] ?? 0
|
|
10053
|
-
const x = layout.plotLeft +
|
|
10036
|
+
const val = series[si].values[sourceIndex] ?? 0, x = layout.plotLeft +
|
|
10054
10037
|
barGroupWidth * ci +
|
|
10055
10038
|
groupOffset +
|
|
10056
10039
|
singleBarWidth * si +
|
|
10057
|
-
singleBarWidth / 2
|
|
10058
|
-
const zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
10059
|
-
const valY = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
10060
|
-
const labelY = val >= 0 ? Math.min(zeroY, valY) - 4 : Math.max(zeroY, valY) + 10;
|
|
10061
|
-
const text = buildDataLabelText({
|
|
10040
|
+
singleBarWidth / 2, zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom), valY = valueToY(val, range, layout.plotTop, layout.plotBottom), labelY = val >= 0 ? Math.min(zeroY, valY) - 4 : Math.max(zeroY, valY) + 10, text = buildDataLabelText({
|
|
10062
10041
|
chartData,
|
|
10063
10042
|
series: series[si],
|
|
10064
10043
|
pointIndex: sourceIndex,
|
|
@@ -10320,6 +10299,16 @@ function buildMarkerPrimitive(input) {
|
|
|
10320
10299
|
}
|
|
10321
10300
|
}
|
|
10322
10301
|
|
|
10302
|
+
/**
|
|
10303
|
+
* Hover-tooltip text for an XY (scatter/bubble) point: `"<series>: (x, y)"`.
|
|
10304
|
+
* Neither kind has a category label to hang the tooltip off, unlike
|
|
10305
|
+
* `buildMarkTooltip`'s bar/line/area/pie/radar marks, so this formats the raw
|
|
10306
|
+
* coordinate pair instead.
|
|
10307
|
+
*/
|
|
10308
|
+
function xyMarkTooltip(seriesName, xVal, yVal, numberFormat) {
|
|
10309
|
+
const y = formatAxisValue(yVal, numberFormat), coords = xVal !== undefined ? `(${formatAxisValue(xVal)}, ${y})` : y;
|
|
10310
|
+
return seriesName.length > 0 ? `${seriesName}: ${coords}` : coords;
|
|
10311
|
+
}
|
|
10323
10312
|
/**
|
|
10324
10313
|
* Build the marker for a data point (honouring `marker.symbol`/`size`) and push
|
|
10325
10314
|
* it, unless the symbol is `none`. Shared by the line / area / scatter builders.
|
|
@@ -10329,9 +10318,8 @@ function buildMarkerPrimitive(input) {
|
|
|
10329
10318
|
* fewer points than the series declares and the override must still land on the
|
|
10330
10319
|
* point the author picked.
|
|
10331
10320
|
*/
|
|
10332
|
-
function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part, opacity) {
|
|
10333
|
-
const marker = resolveDataPointMarker(series, pointIndex)
|
|
10334
|
-
const m = buildMarkerPrimitive({
|
|
10321
|
+
function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part, opacity, title) {
|
|
10322
|
+
const marker = resolveDataPointMarker(series, pointIndex), m = buildMarkerPrimitive({
|
|
10335
10323
|
symbol: marker.symbol,
|
|
10336
10324
|
size: marker.size,
|
|
10337
10325
|
cx,
|
|
@@ -10348,32 +10336,236 @@ function pushMarker(out, series, pointIndex, cx, cy, fill, defaultRadius, part,
|
|
|
10348
10336
|
if (opacity !== undefined) {
|
|
10349
10337
|
m.opacity = opacity;
|
|
10350
10338
|
}
|
|
10339
|
+
if (title !== undefined) {
|
|
10340
|
+
m.title = title;
|
|
10341
|
+
}
|
|
10351
10342
|
out.push(m);
|
|
10352
10343
|
}
|
|
10353
|
-
/**
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10344
|
+
/**
|
|
10345
|
+
* X values a scatter / bubble series is plotted against.
|
|
10346
|
+
*
|
|
10347
|
+
* The series' own `c:xVal` wins. Only when it has none does the chart-level
|
|
10348
|
+
* category list stand in, which is all the engine used to have and is why every
|
|
10349
|
+
* series in a multi-series scatter was plotted against series 1's x axis.
|
|
10350
|
+
*/
|
|
10351
|
+
function seriesXValues(chartData, series) {
|
|
10352
|
+
if (series.xValues && series.xValues.length > 0) {
|
|
10353
|
+
return series.xValues;
|
|
10354
|
+
}
|
|
10355
|
+
const fromCategories = chartData.categories.map(Number);
|
|
10356
|
+
return fromCategories.length > 0 ? fromCategories : undefined;
|
|
10357
|
+
}
|
|
10358
|
+
/**
|
|
10359
|
+
* Whether `c:scatterStyle` joins the points with a line.
|
|
10360
|
+
*
|
|
10361
|
+
* `lineMarker` is what PowerPoint writes for essentially every scatter chart,
|
|
10362
|
+
* including the marker-only ones: it expresses "no line" as an `a:ln/a:noFill`
|
|
10363
|
+
* on the series, not by switching the style to `marker`. Both have to be
|
|
10364
|
+
* checked, and the series flag has to win, or a "Scatter with Straight Lines"
|
|
10365
|
+
* deck loses its lines and a marker-only deck grows some.
|
|
10366
|
+
*/
|
|
10367
|
+
function scatterDrawsLine(chartData, series) {
|
|
10368
|
+
if (series.lineNoFill === true) {
|
|
10369
|
+
return false;
|
|
10370
|
+
}
|
|
10371
|
+
const style = chartData.scatterStyle;
|
|
10372
|
+
return (style === 'line' || style === 'lineMarker' || style === 'smooth' || style === 'smoothMarker');
|
|
10373
|
+
}
|
|
10374
|
+
/** Build scatter-chart primitives, honouring `c:scatterStyle` connecting lines. */
|
|
10375
|
+
function buildScatter(chartData, layout, range) {
|
|
10376
|
+
const primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i)), maxXIndex = Math.max(1, ...allIndices), perSeriesX = chartData.series.map((series) => seriesXValues(chartData, series)), xDomain = computeScatterXDomain(perSeriesX), smoothStyle = chartData.scatterStyle === 'smooth' || chartData.scatterStyle === 'smoothMarker';
|
|
10377
|
+
for (let si = 0; si < chartData.series.length; si++) {
|
|
10378
|
+
const series = chartData.series[si], c = seriesColor(series, si, chartData.colorPalette), dots = computeScatterDots(series.values, maxXIndex, layout, range, perSeriesX[si], xDomain);
|
|
10379
|
+
// The connecting line goes down FIRST so the markers sit on top of it.
|
|
10380
|
+
if (scatterDrawsLine(chartData, series) && dots.length >= 2) {
|
|
10381
|
+
const points = dots.map((dot) => ({ x: dot.cx, y: dot.cy })), seriesPart = { role: 'series', seriesIndex: si };
|
|
10382
|
+
primitives.push((series.smooth ?? smoothStyle)
|
|
10383
|
+
? {
|
|
10384
|
+
kind: 'path',
|
|
10385
|
+
d: smoothLinePath(points),
|
|
10386
|
+
stroke: c,
|
|
10387
|
+
strokeWidth: 2.4,
|
|
10388
|
+
fill: 'none',
|
|
10389
|
+
part: seriesPart,
|
|
10390
|
+
}
|
|
10391
|
+
: {
|
|
10392
|
+
kind: 'polyline',
|
|
10393
|
+
points: linePointsToSvgString(points),
|
|
10394
|
+
stroke: c,
|
|
10395
|
+
strokeWidth: 2.4,
|
|
10396
|
+
fill: 'none',
|
|
10397
|
+
part: seriesPart,
|
|
10398
|
+
});
|
|
10399
|
+
}
|
|
10400
|
+
dots.forEach((dot, vi) => {
|
|
10401
|
+
const part = { role: 'dataPoint', seriesIndex: si, pointIndex: vi };
|
|
10402
|
+
pushMarker(primitives, series, vi, dot.cx, dot.cy, resolveDataPointFill(series, vi, c) ?? c, 4, part, 0.85, xyMarkTooltip(series.name, perSeriesX[si]?.[vi], series.values[vi] ?? 0, series.numberFormat));
|
|
10403
|
+
});
|
|
10404
|
+
if (showLabels) {
|
|
10405
|
+
series.values.forEach((val, vi) => {
|
|
10406
|
+
const dot = dots[vi], text = buildDataLabelText({ chartData, series, pointIndex: vi, value: val });
|
|
10407
|
+
if (!dot || text === undefined) {
|
|
10408
|
+
return;
|
|
10409
|
+
}
|
|
10410
|
+
dataLabels.push({
|
|
10411
|
+
kind: 'text',
|
|
10412
|
+
x: dot.cx,
|
|
10413
|
+
y: dot.cy - 6,
|
|
10414
|
+
text,
|
|
10415
|
+
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10416
|
+
fill: '#334155',
|
|
10417
|
+
textAnchor: 'middle',
|
|
10418
|
+
});
|
|
10419
|
+
});
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
return { primitives, dataLabels };
|
|
10423
|
+
}
|
|
10424
|
+
/**
|
|
10425
|
+
* Build bubble-chart primitives.
|
|
10426
|
+
*
|
|
10427
|
+
* Every series carries its own `c:xVal`, `c:yVal` AND `c:bubbleSize`, so each
|
|
10428
|
+
* one is a complete bubble series. The engine used to read sizes off "the third
|
|
10429
|
+
* series", which drew equal-sized dots for the ordinary one-series bubble chart
|
|
10430
|
+
* and silently deleted every series past the second from a three-series one.
|
|
10431
|
+
*/
|
|
10432
|
+
function buildBubbles(chartData, layout, range) {
|
|
10433
|
+
const primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i)), maxXIndex = Math.max(1, ...allIndices), perSeriesX = chartData.series.map((series) => seriesXValues(chartData, series)), xDomain = computeScatterXDomain(perSeriesX),
|
|
10434
|
+
// One size scale for the whole chart, so bubbles stay comparable across series.
|
|
10435
|
+
allSizes = chartData.series.flatMap((series) => series.bubbleSizes ?? []), maxBubble = allSizes.length > 0 ? Math.max(1, ...allSizes.map(Math.abs)) : 1, medianRadius = Math.min(layout.plotWidth, layout.plotHeight) * 0.04;
|
|
10358
10436
|
for (let si = 0; si < chartData.series.length; si++) {
|
|
10359
|
-
const series = chartData.series[si];
|
|
10437
|
+
const series = chartData.series[si], c = seriesColor(series, si, chartData.colorPalette), dots = computeScatterDots(series.values, maxXIndex, layout, range, perSeriesX[si], xDomain);
|
|
10438
|
+
dots.forEach((dot, vi) => {
|
|
10439
|
+
const r = computeBubbleRadius(series.bubbleSizes?.[vi], maxBubble, medianRadius), size = series.bubbleSizes?.[vi], label = size !== undefined
|
|
10440
|
+
? `${xyMarkTooltip(series.name, perSeriesX[si]?.[vi], series.values[vi] ?? 0, series.numberFormat)}, size ${formatAxisValue(size)}`
|
|
10441
|
+
: xyMarkTooltip(series.name, perSeriesX[si]?.[vi], series.values[vi] ?? 0, series.numberFormat);
|
|
10442
|
+
primitives.push({
|
|
10443
|
+
kind: 'circle',
|
|
10444
|
+
cx: dot.cx,
|
|
10445
|
+
cy: dot.cy,
|
|
10446
|
+
r,
|
|
10447
|
+
fill: resolveDataPointFill(series, vi, c) ?? c,
|
|
10448
|
+
opacity: 0.6,
|
|
10449
|
+
part: { role: 'dataPoint', seriesIndex: si, pointIndex: vi },
|
|
10450
|
+
title: label,
|
|
10451
|
+
});
|
|
10452
|
+
});
|
|
10453
|
+
if (showLabels) {
|
|
10454
|
+
series.values.forEach((val, vi) => {
|
|
10455
|
+
const dot = dots[vi], text = buildDataLabelText({ chartData, series, pointIndex: vi, value: val });
|
|
10456
|
+
if (!dot || text === undefined) {
|
|
10457
|
+
return;
|
|
10458
|
+
}
|
|
10459
|
+
dataLabels.push({
|
|
10460
|
+
kind: 'text',
|
|
10461
|
+
x: dot.cx,
|
|
10462
|
+
y: dot.cy - 10,
|
|
10463
|
+
text,
|
|
10464
|
+
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10465
|
+
fill: '#334155',
|
|
10466
|
+
textAnchor: 'middle',
|
|
10467
|
+
});
|
|
10468
|
+
});
|
|
10469
|
+
}
|
|
10470
|
+
}
|
|
10471
|
+
return { primitives, dataLabels };
|
|
10472
|
+
}
|
|
10473
|
+
|
|
10474
|
+
/**
|
|
10475
|
+
* chart-stacked-series.ts: running-sum plot geometry for stacked and
|
|
10476
|
+
* percentStacked LINE / AREA charts.
|
|
10477
|
+
*
|
|
10478
|
+
* Bar/column stacking (`computeStackedBarRects` in chart-view-model.ts) draws
|
|
10479
|
+
* one rect per segment stacked on a shared baseline. Line/area stacking is
|
|
10480
|
+
* different: each series is plotted at its cumulative height, so the
|
|
10481
|
+
* top-most series traces the category total (line) or the topmost band edge
|
|
10482
|
+
* (area). This module owns that running-sum math, shared by `buildLines` and
|
|
10483
|
+
* `buildAreas` in chart-cartesian-line-area.ts so the two kinds cannot drift.
|
|
10484
|
+
*
|
|
10485
|
+
* @module chart-stacked-series
|
|
10486
|
+
*/
|
|
10487
|
+
/**
|
|
10488
|
+
* Per-category sum of absolute values across all series, used to normalise
|
|
10489
|
+
* percentStacked shares. Mirrors `categoryTotals` in chart-cartesian-bars.ts
|
|
10490
|
+
* (bar's percentStacked normalisation) so bar/line/area agree on what "100%"
|
|
10491
|
+
* means for a category.
|
|
10492
|
+
*/
|
|
10493
|
+
function categoryAbsTotals(seriesValues, catCount) {
|
|
10494
|
+
return Array.from({ length: catCount }, (_, ci) => seriesValues.reduce((sum, values) => sum + Math.abs(values[ci] ?? 0), 0));
|
|
10495
|
+
}
|
|
10496
|
+
/**
|
|
10497
|
+
* Build running-sum stacked/percentStacked plot values for a set of line or
|
|
10498
|
+
* area series, one entry per series in `seriesValues` order (series 0 sits at
|
|
10499
|
+
* the bottom of the stack, exactly like stacked bar segments). Positive and
|
|
10500
|
+
* negative values accumulate on separate running sums, so a chart mixing
|
|
10501
|
+
* signs stacks above and below zero independently instead of the values
|
|
10502
|
+
* cancelling out.
|
|
10503
|
+
*/
|
|
10504
|
+
function computeStackedSeriesPlots(seriesValues, catCount, percent) {
|
|
10505
|
+
const totals = percent ? categoryAbsTotals(seriesValues, catCount) : undefined, posRunning = new Array(catCount).fill(0), negRunning = new Array(catCount).fill(0);
|
|
10506
|
+
return seriesValues.map((values) => {
|
|
10507
|
+
const cumulative = [], base = [], own = [];
|
|
10508
|
+
for (let ci = 0; ci < catCount; ci++) {
|
|
10509
|
+
const raw = values[ci] ?? 0, total = totals?.[ci] ?? 0, val = percent ? (total > 0 ? (raw / total) * 100 : 0) : raw, isNeg = val < 0, from = isNeg ? negRunning[ci] : posRunning[ci], to = from + val;
|
|
10510
|
+
base.push(from);
|
|
10511
|
+
cumulative.push(to);
|
|
10512
|
+
own.push(val);
|
|
10513
|
+
if (isNeg) {
|
|
10514
|
+
negRunning[ci] = to;
|
|
10515
|
+
}
|
|
10516
|
+
else {
|
|
10517
|
+
posRunning[ci] = to;
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10520
|
+
return { cumulative, base, own };
|
|
10521
|
+
});
|
|
10522
|
+
}
|
|
10523
|
+
|
|
10524
|
+
/**
|
|
10525
|
+
* Build line-chart primitives, honouring a secondary value range per series
|
|
10526
|
+
* (clustered only: `stacking !== 'clustered'` always plots against
|
|
10527
|
+
* `primaryRange`, matching `isStacked` disabling the secondary-axis split in
|
|
10528
|
+
* chart-cartesian.ts).
|
|
10529
|
+
*
|
|
10530
|
+
* Stacked/percentStacked plots each series at its running-sum height (each
|
|
10531
|
+
* line sits on top of the ones below it, so the top-most line traces the
|
|
10532
|
+
* category total), computed from every series' own blank-resolved values via
|
|
10533
|
+
* `computeStackedSeriesPlots`. Data labels and marker tooltips still read the
|
|
10534
|
+
* series' own value (or percent share), not the cumulative height it is
|
|
10535
|
+
* plotted at, mirroring stacked bar's label convention.
|
|
10536
|
+
*/
|
|
10537
|
+
function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, sourceIndices, xPositions, stacking = 'clustered') {
|
|
10538
|
+
const primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, isStackedMode = stacking !== 'clustered', isPercent = stacking === 'percentStacked',
|
|
10539
|
+
// Resolve every series' own (blank-handled) values first: stacking needs
|
|
10540
|
+
// all of them at once to compute the running sums.
|
|
10541
|
+
resolved = chartData.series.map((series) => {
|
|
10360
10542
|
if (series.values.length === 0) {
|
|
10361
|
-
|
|
10543
|
+
return undefined;
|
|
10362
10544
|
}
|
|
10363
|
-
const
|
|
10364
|
-
const rawValues = sourceIndices.map((sourceIndex) => series.values[sourceIndex] ?? 0);
|
|
10365
|
-
const displayBlanks = sourceIndices.map((sourceIndex) => series.blanks?.[sourceIndex] ?? false);
|
|
10545
|
+
const rawValues = sourceIndices.map((sourceIndex) => series.values[sourceIndex] ?? 0), displayBlanks = sourceIndices.map((sourceIndex) => series.blanks?.[sourceIndex] ?? false),
|
|
10366
10546
|
// Honour c:dispBlanksAs: gap breaks the line at blanks, span interpolates,
|
|
10367
10547
|
// zero/unset keep the placeholder 0 (existing behaviour).
|
|
10368
|
-
|
|
10369
|
-
|
|
10548
|
+
{ values: displayValues, visible } = resolveBlankDisplay(rawValues, displayBlanks, chartData.chartChrome?.dispBlanksAs);
|
|
10549
|
+
return { displayValues, visible };
|
|
10550
|
+
}), stackedPlots = isStackedMode
|
|
10551
|
+
? computeStackedSeriesPlots(resolved.map((entry) => entry?.displayValues ?? []), catCount, isPercent)
|
|
10552
|
+
: undefined;
|
|
10553
|
+
for (let si = 0; si < chartData.series.length; si++) {
|
|
10554
|
+
const series = chartData.series[si], entry = resolved[si];
|
|
10555
|
+
if (!entry) {
|
|
10556
|
+
continue;
|
|
10557
|
+
}
|
|
10558
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
10559
|
+
const { displayValues, visible } = entry, activeRange = isStackedMode
|
|
10560
|
+
? primaryRange
|
|
10561
|
+
: secondaryIdx.has(si) && secondaryRange
|
|
10562
|
+
? secondaryRange
|
|
10563
|
+
: primaryRange, plotValues = stackedPlots ? stackedPlots[si].cumulative : displayValues, pts = computeLinePoints(plotValues, catCount, layout, activeRange).map((point, index) => ({
|
|
10370
10564
|
...point,
|
|
10371
10565
|
x: xPositions?.[index] ?? point.x,
|
|
10372
|
-
}))
|
|
10373
|
-
const c = seriesColor(series, si, chartData.colorPalette);
|
|
10566
|
+
})), c = seriesColor(series, si, chartData.colorPalette),
|
|
10374
10567
|
// c:smooth draws a bezier path through the points; otherwise a polyline.
|
|
10375
|
-
|
|
10376
|
-
const allVisible = visible.every(Boolean);
|
|
10568
|
+
seriesPart = { role: 'series', seriesIndex: si }, allVisible = visible.every(Boolean);
|
|
10377
10569
|
if (allVisible) {
|
|
10378
10570
|
primitives.push(series.smooth
|
|
10379
10571
|
? {
|
|
@@ -10413,20 +10605,42 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
|
|
|
10413
10605
|
if (!visible[displayIndex]) {
|
|
10414
10606
|
return;
|
|
10415
10607
|
}
|
|
10416
|
-
const idx = sourceIndices[displayIndex] ?? displayIndex;
|
|
10417
|
-
|
|
10418
|
-
pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2.5, part);
|
|
10608
|
+
const idx = sourceIndices[displayIndex] ?? displayIndex, part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
|
|
10609
|
+
pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2.5, part, undefined, buildMarkTooltip(series.name, chartData.categories[idx], series.values[idx] ?? 0, series.numberFormat));
|
|
10419
10610
|
});
|
|
10420
10611
|
if (showLabels) {
|
|
10421
|
-
|
|
10612
|
+
// percentStacked reads like the bar case: a plain rounded percent, not
|
|
10613
|
+
// routed through c:dLbls content options (showValue/showCategory/...),
|
|
10614
|
+
// since the plotted quantity is a derived share, not the raw datum.
|
|
10615
|
+
const labelValues = stackedPlots ? stackedPlots[si].own : displayValues;
|
|
10616
|
+
labelValues.forEach((val, displayIndex) => {
|
|
10422
10617
|
const pt = pts[displayIndex];
|
|
10618
|
+
if (!pt || !visible[displayIndex]) {
|
|
10619
|
+
return;
|
|
10620
|
+
}
|
|
10621
|
+
if (isPercent) {
|
|
10622
|
+
if (val === 0) {
|
|
10623
|
+
return;
|
|
10624
|
+
}
|
|
10625
|
+
dataLabels.push({
|
|
10626
|
+
kind: 'text',
|
|
10627
|
+
x: pt.x,
|
|
10628
|
+
y: pt.y - 7,
|
|
10629
|
+
text: `${Math.round(val)}%`,
|
|
10630
|
+
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10631
|
+
fill: '#334155',
|
|
10632
|
+
textAnchor: 'middle',
|
|
10633
|
+
});
|
|
10634
|
+
return;
|
|
10635
|
+
}
|
|
10636
|
+
// eslint-disable-next-line one-var -- an early return sits between this const and the previous one
|
|
10423
10637
|
const text = buildDataLabelText({
|
|
10424
10638
|
chartData,
|
|
10425
10639
|
series,
|
|
10426
10640
|
pointIndex: sourceIndices[displayIndex] ?? displayIndex,
|
|
10427
10641
|
value: val,
|
|
10428
10642
|
});
|
|
10429
|
-
if (
|
|
10643
|
+
if (text === undefined) {
|
|
10430
10644
|
return;
|
|
10431
10645
|
}
|
|
10432
10646
|
dataLabels.push({
|
|
@@ -10443,27 +10657,52 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
|
|
|
10443
10657
|
}
|
|
10444
10658
|
return { primitives, dataLabels };
|
|
10445
10659
|
}
|
|
10446
|
-
/**
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10660
|
+
/**
|
|
10661
|
+
* Build area-chart primitives (fill polygon + outline).
|
|
10662
|
+
*
|
|
10663
|
+
* Stacked/percentStacked fills the band between this series' running-sum top
|
|
10664
|
+
* and the previous series' top (its own base) instead of down to the zero
|
|
10665
|
+
* baseline, so layers stack visually like a PowerPoint stacked area chart
|
|
10666
|
+
* rather than each series washing over the ones below it. Data labels and
|
|
10667
|
+
* marker tooltips read the series' own value (or percent share), matching
|
|
10668
|
+
* `buildLines` and stacked bar's label convention.
|
|
10669
|
+
*/
|
|
10670
|
+
function buildAreas(chartData, catCount, layout, range, sourceIndices, xPositions, stacking = 'clustered') {
|
|
10671
|
+
const primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, isStackedMode = stacking !== 'clustered', isPercent = stacking === 'percentStacked', baselineY = valueToY(0, range, layout.plotTop, layout.plotBottom), allDisplayValues = chartData.series.map((series) => series.values.length === 0
|
|
10672
|
+
? undefined
|
|
10673
|
+
: sourceIndices.map((sourceIndex) => series.values[sourceIndex] ?? 0)), stackedPlots = isStackedMode
|
|
10674
|
+
? computeStackedSeriesPlots(allDisplayValues.map((values) => values ?? []), catCount, isPercent)
|
|
10675
|
+
: undefined;
|
|
10452
10676
|
for (let si = 0; si < chartData.series.length; si++) {
|
|
10453
|
-
const series = chartData.series[si];
|
|
10454
|
-
if (
|
|
10677
|
+
const series = chartData.series[si], displayValues = allDisplayValues[si];
|
|
10678
|
+
if (!displayValues) {
|
|
10455
10679
|
continue;
|
|
10456
10680
|
}
|
|
10457
|
-
|
|
10458
|
-
const pts = computeLinePoints(
|
|
10681
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
10682
|
+
const plot = stackedPlots?.[si], topValues = plot ? plot.cumulative : displayValues, pts = computeLinePoints(topValues, catCount, layout, range).map((point, index) => ({
|
|
10459
10683
|
...point,
|
|
10460
10684
|
x: xPositions?.[index] ?? point.x,
|
|
10461
|
-
}));
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10685
|
+
})), c = seriesColor(series, si, chartData.colorPalette), lineStr = linePointsToSvgString(pts), firstPt = pts[0], lastPt = pts[pts.length - 1];
|
|
10686
|
+
if (plot) {
|
|
10687
|
+
// Stacked band: fill between this series' cumulative top and its own
|
|
10688
|
+
// base (the previous series' top), like a stream-graph layer, at full
|
|
10689
|
+
// opacity so adjacent bands read as distinct rather than washed.
|
|
10690
|
+
const baseStr = linePointsToSvgString([
|
|
10691
|
+
...computeLinePoints(plot.base, catCount, layout, range).map((point, index) => ({
|
|
10692
|
+
...point,
|
|
10693
|
+
x: xPositions?.[index] ?? point.x,
|
|
10694
|
+
})),
|
|
10695
|
+
].reverse());
|
|
10696
|
+
primitives.push({
|
|
10697
|
+
kind: 'polyline',
|
|
10698
|
+
points: `${lineStr} ${baseStr}`,
|
|
10699
|
+
stroke: 'none',
|
|
10700
|
+
strokeWidth: 0,
|
|
10701
|
+
fill: c,
|
|
10702
|
+
part: { role: 'series', seriesIndex: si },
|
|
10703
|
+
});
|
|
10704
|
+
}
|
|
10705
|
+
else if (firstPt && lastPt) {
|
|
10467
10706
|
primitives.push({
|
|
10468
10707
|
kind: 'polyline',
|
|
10469
10708
|
points: `${firstPt.x.toFixed(2)},${baselineY.toFixed(2)} ${lineStr} ${lastPt.x.toFixed(2)},${baselineY.toFixed(2)}`,
|
|
@@ -10483,20 +10722,39 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
|
|
|
10483
10722
|
part: { role: 'series', seriesIndex: si },
|
|
10484
10723
|
});
|
|
10485
10724
|
pts.forEach((pt, displayIndex) => {
|
|
10486
|
-
const idx = sourceIndices[displayIndex] ?? displayIndex;
|
|
10487
|
-
|
|
10488
|
-
pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2, part);
|
|
10725
|
+
const idx = sourceIndices[displayIndex] ?? displayIndex, part = { role: 'dataPoint', seriesIndex: si, pointIndex: idx };
|
|
10726
|
+
pushMarker(primitives, series, idx, pt.x, pt.y, resolveDataPointFill(series, idx, c) ?? c, 2, part, undefined, buildMarkTooltip(series.name, chartData.categories[idx], series.values[idx] ?? 0, series.numberFormat));
|
|
10489
10727
|
});
|
|
10490
10728
|
if (showLabels) {
|
|
10491
|
-
|
|
10729
|
+
const labelValues = plot ? plot.own : displayValues;
|
|
10730
|
+
labelValues.forEach((val, displayIndex) => {
|
|
10492
10731
|
const pt = pts[displayIndex];
|
|
10732
|
+
if (!pt) {
|
|
10733
|
+
return;
|
|
10734
|
+
}
|
|
10735
|
+
if (isPercent) {
|
|
10736
|
+
if (val === 0) {
|
|
10737
|
+
return;
|
|
10738
|
+
}
|
|
10739
|
+
dataLabels.push({
|
|
10740
|
+
kind: 'text',
|
|
10741
|
+
x: pt.x,
|
|
10742
|
+
y: pt.y - 6,
|
|
10743
|
+
text: `${Math.round(val)}%`,
|
|
10744
|
+
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10745
|
+
fill: '#334155',
|
|
10746
|
+
textAnchor: 'middle',
|
|
10747
|
+
});
|
|
10748
|
+
return;
|
|
10749
|
+
}
|
|
10750
|
+
// eslint-disable-next-line one-var -- an early return sits between this const and the previous one
|
|
10493
10751
|
const text = buildDataLabelText({
|
|
10494
10752
|
chartData,
|
|
10495
10753
|
series,
|
|
10496
10754
|
pointIndex: sourceIndices[displayIndex] ?? displayIndex,
|
|
10497
10755
|
value: val,
|
|
10498
10756
|
});
|
|
10499
|
-
if (
|
|
10757
|
+
if (text === undefined) {
|
|
10500
10758
|
return;
|
|
10501
10759
|
}
|
|
10502
10760
|
dataLabels.push({
|
|
@@ -10513,154 +10771,6 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
|
|
|
10513
10771
|
}
|
|
10514
10772
|
return { primitives, dataLabels };
|
|
10515
10773
|
}
|
|
10516
|
-
/**
|
|
10517
|
-
* X values a scatter / bubble series is plotted against.
|
|
10518
|
-
*
|
|
10519
|
-
* The series' own `c:xVal` wins. Only when it has none does the chart-level
|
|
10520
|
-
* category list stand in, which is all the engine used to have and is why every
|
|
10521
|
-
* series in a multi-series scatter was plotted against series 1's x axis.
|
|
10522
|
-
*/
|
|
10523
|
-
function seriesXValues(chartData, series) {
|
|
10524
|
-
if (series.xValues && series.xValues.length > 0) {
|
|
10525
|
-
return series.xValues;
|
|
10526
|
-
}
|
|
10527
|
-
const fromCategories = chartData.categories.map(Number);
|
|
10528
|
-
return fromCategories.length > 0 ? fromCategories : undefined;
|
|
10529
|
-
}
|
|
10530
|
-
/**
|
|
10531
|
-
* Whether `c:scatterStyle` joins the points with a line.
|
|
10532
|
-
*
|
|
10533
|
-
* `lineMarker` is what PowerPoint writes for essentially every scatter chart,
|
|
10534
|
-
* including the marker-only ones: it expresses "no line" as an `a:ln/a:noFill`
|
|
10535
|
-
* on the series, not by switching the style to `marker`. Both have to be
|
|
10536
|
-
* checked, and the series flag has to win, or a "Scatter with Straight Lines"
|
|
10537
|
-
* deck loses its lines and a marker-only deck grows some.
|
|
10538
|
-
*/
|
|
10539
|
-
function scatterDrawsLine(chartData, series) {
|
|
10540
|
-
if (series.lineNoFill === true) {
|
|
10541
|
-
return false;
|
|
10542
|
-
}
|
|
10543
|
-
const style = chartData.scatterStyle;
|
|
10544
|
-
return (style === 'line' || style === 'lineMarker' || style === 'smooth' || style === 'smoothMarker');
|
|
10545
|
-
}
|
|
10546
|
-
/** Build scatter-chart primitives, honouring `c:scatterStyle` connecting lines. */
|
|
10547
|
-
function buildScatter(chartData, layout, range) {
|
|
10548
|
-
const primitives = [];
|
|
10549
|
-
const dataLabels = [];
|
|
10550
|
-
const showLabels = chartData.style?.hasDataLabels;
|
|
10551
|
-
const allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i));
|
|
10552
|
-
const maxXIndex = Math.max(1, ...allIndices);
|
|
10553
|
-
const perSeriesX = chartData.series.map((series) => seriesXValues(chartData, series));
|
|
10554
|
-
const xDomain = computeScatterXDomain(perSeriesX);
|
|
10555
|
-
const smoothStyle = chartData.scatterStyle === 'smooth' || chartData.scatterStyle === 'smoothMarker';
|
|
10556
|
-
for (let si = 0; si < chartData.series.length; si++) {
|
|
10557
|
-
const series = chartData.series[si];
|
|
10558
|
-
const c = seriesColor(series, si, chartData.colorPalette);
|
|
10559
|
-
const dots = computeScatterDots(series.values, maxXIndex, layout, range, perSeriesX[si], xDomain);
|
|
10560
|
-
// The connecting line goes down FIRST so the markers sit on top of it.
|
|
10561
|
-
if (scatterDrawsLine(chartData, series) && dots.length >= 2) {
|
|
10562
|
-
const points = dots.map((dot) => ({ x: dot.cx, y: dot.cy }));
|
|
10563
|
-
const seriesPart = { role: 'series', seriesIndex: si };
|
|
10564
|
-
primitives.push((series.smooth ?? smoothStyle)
|
|
10565
|
-
? {
|
|
10566
|
-
kind: 'path',
|
|
10567
|
-
d: smoothLinePath(points),
|
|
10568
|
-
stroke: c,
|
|
10569
|
-
strokeWidth: 2.4,
|
|
10570
|
-
fill: 'none',
|
|
10571
|
-
part: seriesPart,
|
|
10572
|
-
}
|
|
10573
|
-
: {
|
|
10574
|
-
kind: 'polyline',
|
|
10575
|
-
points: linePointsToSvgString(points),
|
|
10576
|
-
stroke: c,
|
|
10577
|
-
strokeWidth: 2.4,
|
|
10578
|
-
fill: 'none',
|
|
10579
|
-
part: seriesPart,
|
|
10580
|
-
});
|
|
10581
|
-
}
|
|
10582
|
-
dots.forEach((dot, vi) => {
|
|
10583
|
-
const part = { role: 'dataPoint', seriesIndex: si, pointIndex: vi };
|
|
10584
|
-
pushMarker(primitives, series, vi, dot.cx, dot.cy, resolveDataPointFill(series, vi, c) ?? c, 4, part, 0.85);
|
|
10585
|
-
});
|
|
10586
|
-
if (showLabels) {
|
|
10587
|
-
series.values.forEach((val, vi) => {
|
|
10588
|
-
const dot = dots[vi];
|
|
10589
|
-
const text = buildDataLabelText({ chartData, series, pointIndex: vi, value: val });
|
|
10590
|
-
if (!dot || text === undefined) {
|
|
10591
|
-
return;
|
|
10592
|
-
}
|
|
10593
|
-
dataLabels.push({
|
|
10594
|
-
kind: 'text',
|
|
10595
|
-
x: dot.cx,
|
|
10596
|
-
y: dot.cy - 6,
|
|
10597
|
-
text,
|
|
10598
|
-
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10599
|
-
fill: '#334155',
|
|
10600
|
-
textAnchor: 'middle',
|
|
10601
|
-
});
|
|
10602
|
-
});
|
|
10603
|
-
}
|
|
10604
|
-
}
|
|
10605
|
-
return { primitives, dataLabels };
|
|
10606
|
-
}
|
|
10607
|
-
/**
|
|
10608
|
-
* Build bubble-chart primitives.
|
|
10609
|
-
*
|
|
10610
|
-
* Every series carries its own `c:xVal`, `c:yVal` AND `c:bubbleSize`, so each
|
|
10611
|
-
* one is a complete bubble series. The engine used to read sizes off "the third
|
|
10612
|
-
* series", which drew equal-sized dots for the ordinary one-series bubble chart
|
|
10613
|
-
* and silently deleted every series past the second from a three-series one.
|
|
10614
|
-
*/
|
|
10615
|
-
function buildBubbles(chartData, layout, range) {
|
|
10616
|
-
const primitives = [];
|
|
10617
|
-
const dataLabels = [];
|
|
10618
|
-
const showLabels = chartData.style?.hasDataLabels;
|
|
10619
|
-
const allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i));
|
|
10620
|
-
const maxXIndex = Math.max(1, ...allIndices);
|
|
10621
|
-
const perSeriesX = chartData.series.map((series) => seriesXValues(chartData, series));
|
|
10622
|
-
const xDomain = computeScatterXDomain(perSeriesX);
|
|
10623
|
-
// One size scale for the whole chart, so bubbles stay comparable across series.
|
|
10624
|
-
const allSizes = chartData.series.flatMap((series) => series.bubbleSizes ?? []);
|
|
10625
|
-
const maxBubble = allSizes.length > 0 ? Math.max(1, ...allSizes.map(Math.abs)) : 1;
|
|
10626
|
-
const medianRadius = Math.min(layout.plotWidth, layout.plotHeight) * 0.04;
|
|
10627
|
-
for (let si = 0; si < chartData.series.length; si++) {
|
|
10628
|
-
const series = chartData.series[si];
|
|
10629
|
-
const c = seriesColor(series, si, chartData.colorPalette);
|
|
10630
|
-
const dots = computeScatterDots(series.values, maxXIndex, layout, range, perSeriesX[si], xDomain);
|
|
10631
|
-
dots.forEach((dot, vi) => {
|
|
10632
|
-
const r = computeBubbleRadius(series.bubbleSizes?.[vi], maxBubble, medianRadius);
|
|
10633
|
-
primitives.push({
|
|
10634
|
-
kind: 'circle',
|
|
10635
|
-
cx: dot.cx,
|
|
10636
|
-
cy: dot.cy,
|
|
10637
|
-
r,
|
|
10638
|
-
fill: resolveDataPointFill(series, vi, c) ?? c,
|
|
10639
|
-
opacity: 0.6,
|
|
10640
|
-
part: { role: 'dataPoint', seriesIndex: si, pointIndex: vi },
|
|
10641
|
-
});
|
|
10642
|
-
});
|
|
10643
|
-
if (showLabels) {
|
|
10644
|
-
series.values.forEach((val, vi) => {
|
|
10645
|
-
const dot = dots[vi];
|
|
10646
|
-
const text = buildDataLabelText({ chartData, series, pointIndex: vi, value: val });
|
|
10647
|
-
if (!dot || text === undefined) {
|
|
10648
|
-
return;
|
|
10649
|
-
}
|
|
10650
|
-
dataLabels.push({
|
|
10651
|
-
kind: 'text',
|
|
10652
|
-
x: dot.cx,
|
|
10653
|
-
y: dot.cy - 10,
|
|
10654
|
-
text,
|
|
10655
|
-
fontSize: DEFAULT_CHART_DATA_LABEL_PX,
|
|
10656
|
-
fill: '#334155',
|
|
10657
|
-
textAnchor: 'middle',
|
|
10658
|
-
});
|
|
10659
|
-
});
|
|
10660
|
-
}
|
|
10661
|
-
}
|
|
10662
|
-
return { primitives, dataLabels };
|
|
10663
|
-
}
|
|
10664
10774
|
|
|
10665
10775
|
/**
|
|
10666
10776
|
* chart-helper-lines.ts: shared drop-line / hi-low-line / up-down-bar primitives.
|
|
@@ -11211,10 +11321,7 @@ function valueToX(val, range, leftX, rightX) {
|
|
|
11211
11321
|
const usable = rightX - leftX;
|
|
11212
11322
|
let ratio;
|
|
11213
11323
|
if (range.logScale && range.logBase) {
|
|
11214
|
-
const base = range.logBase;
|
|
11215
|
-
const clampedVal = Math.max(val, range.min);
|
|
11216
|
-
const logVal = Math.log(clampedVal) / Math.log(base);
|
|
11217
|
-
const logMin = Math.log(range.min) / Math.log(base);
|
|
11324
|
+
const base = range.logBase, clampedVal = Math.max(val, range.min), logVal = Math.log(clampedVal) / Math.log(base), logMin = Math.log(range.min) / Math.log(base);
|
|
11218
11325
|
ratio = (logVal - logMin) / range.span;
|
|
11219
11326
|
}
|
|
11220
11327
|
else {
|
|
@@ -11224,8 +11331,7 @@ function valueToX(val, range, leftX, rightX) {
|
|
|
11224
11331
|
}
|
|
11225
11332
|
/** Vertical value gridlines + bottom tick labels (the transposed value axis). */
|
|
11226
11333
|
function buildTransposedValueAxis(range, layout) {
|
|
11227
|
-
const gridlines = [];
|
|
11228
|
-
const axisLabels = [];
|
|
11334
|
+
const gridlines = [], axisLabels = [];
|
|
11229
11335
|
for (const val of axisTickValues(range)) {
|
|
11230
11336
|
const x = valueToX(val, range, layout.plotLeft, layout.plotRight);
|
|
11231
11337
|
gridlines.push({
|
|
@@ -11251,8 +11357,7 @@ function buildTransposedValueAxis(range, layout) {
|
|
|
11251
11357
|
}
|
|
11252
11358
|
/** Left-anchored category labels, one per band, centred on the band. */
|
|
11253
11359
|
function buildSideCategoryLabels(categoryLabels, layout) {
|
|
11254
|
-
const catCount = Math.max(categoryLabels.length, 1);
|
|
11255
|
-
const band = layout.plotHeight / catCount;
|
|
11360
|
+
const catCount = Math.max(categoryLabels.length, 1), band = layout.plotHeight / catCount;
|
|
11256
11361
|
return categoryLabels.map((label, i) => ({
|
|
11257
11362
|
kind: 'text',
|
|
11258
11363
|
x: layout.plotLeft - 4,
|
|
@@ -11282,20 +11387,11 @@ function barFill(chartData, series, seriesIndex, pointIndex) {
|
|
|
11282
11387
|
* clustered / stacked / percentStacked geometry with the axes transposed.
|
|
11283
11388
|
*/
|
|
11284
11389
|
function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
11285
|
-
const layout = computePlotLayout(element.width, element.height, chartData, true)
|
|
11286
|
-
const catCount = Math.max(categoryLabels.length, 1);
|
|
11287
|
-
const series = chartData.series;
|
|
11288
|
-
const grouping = chartData.grouping ?? 'clustered';
|
|
11289
|
-
const isStacked = grouping === 'stacked' || grouping === 'percentStacked';
|
|
11290
|
-
const isPercent = grouping === 'percentStacked';
|
|
11291
|
-
const range = isPercent
|
|
11390
|
+
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', range = isPercent
|
|
11292
11391
|
? { min: 0, max: 100, span: 100 }
|
|
11293
11392
|
: isStacked
|
|
11294
11393
|
? computeStackedValueRange(series, catCount)
|
|
11295
|
-
: computeValueRangeForChart(series, chartData.axes)
|
|
11296
|
-
const { gridlines, axisLabels } = buildTransposedValueAxis(range, layout);
|
|
11297
|
-
const zeroX = valueToX(0, range, layout.plotLeft, layout.plotRight);
|
|
11298
|
-
const zeroLine = range.min < 0 && range.max > 0
|
|
11394
|
+
: computeValueRangeForChart(series, chartData.axes), { gridlines, axisLabels } = buildTransposedValueAxis(range, layout), zeroX = valueToX(0, range, layout.plotLeft, layout.plotRight), zeroLine = range.min < 0 && range.max > 0
|
|
11299
11395
|
? {
|
|
11300
11396
|
kind: 'line',
|
|
11301
11397
|
x1: zeroX,
|
|
@@ -11305,27 +11401,14 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
11305
11401
|
stroke: ZERO_LINE_COLOR,
|
|
11306
11402
|
strokeWidth: 1,
|
|
11307
11403
|
}
|
|
11308
|
-
: undefined;
|
|
11309
|
-
const primitives = [];
|
|
11310
|
-
const dataLabels = [];
|
|
11311
|
-
const showLabels = chartData.style?.hasDataLabels;
|
|
11312
|
-
const band = layout.plotHeight / catCount;
|
|
11404
|
+
: undefined, primitives = [], dataLabels = [], showLabels = chartData.style?.hasDataLabels, band = layout.plotHeight / catCount;
|
|
11313
11405
|
if (!isStacked) {
|
|
11314
|
-
const seriesCount = Math.max(series.length, 1)
|
|
11315
|
-
const singleBarHeight = chartData.barGapWidth !== undefined
|
|
11406
|
+
const seriesCount = Math.max(series.length, 1), singleBarHeight = chartData.barGapWidth !== undefined
|
|
11316
11407
|
? band / (seriesCount + Math.max(chartData.barGapWidth, 0) / 100)
|
|
11317
|
-
: (band * 0.7) / seriesCount;
|
|
11318
|
-
const overlap = chartData.barOverlap ?? 0;
|
|
11319
|
-
const step = singleBarHeight * (1 - overlap / 100);
|
|
11320
|
-
const clusterHeight = singleBarHeight + step * (seriesCount - 1);
|
|
11321
|
-
const groupOffset = (band - clusterHeight) / 2;
|
|
11408
|
+
: (band * 0.7) / seriesCount, overlap = chartData.barOverlap ?? 0, step = singleBarHeight * (1 - overlap / 100), clusterHeight = singleBarHeight + step * (seriesCount - 1), groupOffset = (band - clusterHeight) / 2;
|
|
11322
11409
|
for (let ci = 0; ci < catCount; ci++) {
|
|
11323
11410
|
for (let si = 0; si < series.length; si++) {
|
|
11324
|
-
const val = series[si].values[ci] ?? 0;
|
|
11325
|
-
const y = layout.plotTop + band * ci + groupOffset + step * si;
|
|
11326
|
-
const valX = valueToX(val, range, layout.plotLeft, layout.plotRight);
|
|
11327
|
-
const x = Math.min(zeroX, valX);
|
|
11328
|
-
const w = Math.max(Math.abs(valX - zeroX), 1);
|
|
11411
|
+
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);
|
|
11329
11412
|
primitives.push({
|
|
11330
11413
|
kind: 'rect',
|
|
11331
11414
|
x,
|
|
@@ -11335,6 +11418,7 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
11335
11418
|
fill: barFill(chartData, series[si], si, ci),
|
|
11336
11419
|
rx: 1,
|
|
11337
11420
|
part: { role: 'dataPoint', seriesIndex: si, pointIndex: ci },
|
|
11421
|
+
title: buildMarkTooltip(series[si].name, categoryLabels[ci], val, series[si].numberFormat),
|
|
11338
11422
|
});
|
|
11339
11423
|
if (showLabels) {
|
|
11340
11424
|
dataLabels.push({
|
|
@@ -11352,27 +11436,17 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
11352
11436
|
}
|
|
11353
11437
|
}
|
|
11354
11438
|
else {
|
|
11355
|
-
const totals = isPercent ? categoryTotals(series, catCount) : [];
|
|
11356
|
-
const barH = band * (isPercent ? 0.6 : 0.7);
|
|
11357
|
-
const barOffset = (band - barH) / 2;
|
|
11439
|
+
const totals = isPercent ? categoryTotals(series, catCount) : [], barH = band * (isPercent ? 0.6 : 0.7), barOffset = (band - barH) / 2;
|
|
11358
11440
|
for (let ci = 0; ci < catCount; ci++) {
|
|
11359
|
-
let posRunning = 0;
|
|
11360
|
-
let negRunning = 0;
|
|
11441
|
+
let posRunning = 0, negRunning = 0;
|
|
11361
11442
|
const catTotal = isPercent ? totals[ci] || 1 : 1;
|
|
11362
11443
|
for (let si = 0; si < series.length; si++) {
|
|
11363
|
-
const rawVal = series[si].values[ci] ?? 0;
|
|
11364
|
-
const val = isPercent ? (rawVal / catTotal) * 100 : rawVal;
|
|
11444
|
+
const rawVal = series[si].values[ci] ?? 0, val = isPercent ? (rawVal / catTotal) * 100 : rawVal;
|
|
11365
11445
|
if (val === 0) {
|
|
11366
11446
|
continue;
|
|
11367
11447
|
}
|
|
11368
|
-
|
|
11369
|
-
const base = isNeg ? negRunning : posRunning;
|
|
11370
|
-
const top = base + val;
|
|
11371
|
-
const y = layout.plotTop + band * ci + barOffset;
|
|
11372
|
-
const baseX = valueToX(base, range, layout.plotLeft, layout.plotRight);
|
|
11373
|
-
const topX = valueToX(top, range, layout.plotLeft, layout.plotRight);
|
|
11374
|
-
const x = Math.min(baseX, topX);
|
|
11375
|
-
const w = Math.max(Math.abs(topX - baseX), 0.5);
|
|
11448
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
11449
|
+
const isNeg = val < 0, base = isNeg ? negRunning : posRunning, top = base + val, y = layout.plotTop + band * ci + barOffset, baseX = valueToX(base, range, layout.plotLeft, layout.plotRight), topX = valueToX(top, range, layout.plotLeft, layout.plotRight), x = Math.min(baseX, topX), w = Math.max(Math.abs(topX - baseX), 0.5);
|
|
11376
11450
|
primitives.push({
|
|
11377
11451
|
kind: 'rect',
|
|
11378
11452
|
x,
|
|
@@ -11382,6 +11456,7 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
11382
11456
|
fill: barFill(chartData, series[si], si, ci),
|
|
11383
11457
|
rx: 1,
|
|
11384
11458
|
part: { role: 'dataPoint', seriesIndex: si, pointIndex: ci },
|
|
11459
|
+
title: buildMarkTooltip(series[si].name, categoryLabels[ci], rawVal, series[si].numberFormat),
|
|
11385
11460
|
});
|
|
11386
11461
|
if (showLabels && Math.abs(val) > 0) {
|
|
11387
11462
|
dataLabels.push({
|
|
@@ -11407,8 +11482,8 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
11407
11482
|
}
|
|
11408
11483
|
}
|
|
11409
11484
|
}
|
|
11410
|
-
|
|
11411
|
-
const { legend, legendX, legendY, legendAnchor } = buildLegend(series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
|
|
11485
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
11486
|
+
const legendPos = chartData.style?.legendPosition ?? 'b', { legend, legendX, legendY, legendAnchor } = buildLegend(series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
|
|
11412
11487
|
return {
|
|
11413
11488
|
svgWidth: layout.svgWidth,
|
|
11414
11489
|
svgHeight: layout.svgHeight,
|
|
@@ -12188,46 +12263,71 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
12188
12263
|
return buildHorizontalBarViewModel(element, chartData, categoryLabels);
|
|
12189
12264
|
}
|
|
12190
12265
|
const seriesCount = chartData.series.length;
|
|
12266
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12191
12267
|
const layoutOpts = computeLayoutOptions(chartData.axes, chartData.dataTable, seriesCount);
|
|
12268
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12192
12269
|
const layout = computePlotLayout(element.width, element.height, chartData, true, layoutOpts);
|
|
12270
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12193
12271
|
const catCount = Math.max(categoryLabels.length, 1);
|
|
12194
|
-
|
|
12272
|
+
// Stacking applies to bar, line and area (`GROUPING_SUPPORTED_TYPES` in
|
|
12273
|
+
// chart-editor-options.ts, and lineChart/areaChart both parse a real
|
|
12274
|
+
// `c:grouping`): a stacked or percentStacked line/area chart must render
|
|
12275
|
+
// running-sum geometry and go static (no value-drag) the same way stacked
|
|
12276
|
+
// bar segments do, not silently fall back to clustered/draggable.
|
|
12277
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12278
|
+
const isStacked = (kind === 'bar' || kind === 'line' || kind === 'area') &&
|
|
12279
|
+
(chartData.grouping === 'stacked' || chartData.grouping === 'percentStacked');
|
|
12280
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12195
12281
|
const isPercent = isStacked && chartData.grouping === 'percentStacked';
|
|
12196
12282
|
// Split series across primary/secondary value axes (clustered cartesian only).
|
|
12283
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12197
12284
|
const { secondary } = splitSeriesByAxis(chartData.series, chartData.axes);
|
|
12285
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12198
12286
|
const secondaryIdx = new Set(secondary.map((e) => e.index));
|
|
12287
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12199
12288
|
const useSecondary = !isStacked && secondaryIdx.size > 0;
|
|
12289
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12200
12290
|
const primaryPlotSeries = useSecondary
|
|
12201
12291
|
? chartData.series.filter((_s, i) => !secondaryIdx.has(i))
|
|
12202
12292
|
: chartData.series;
|
|
12293
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12203
12294
|
const secondaryPlotSeries = useSecondary
|
|
12204
12295
|
? chartData.series.filter((_s, i) => secondaryIdx.has(i))
|
|
12205
12296
|
: [];
|
|
12297
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12206
12298
|
const primaryAxis = chartData.axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos !== 'r');
|
|
12299
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12207
12300
|
const primaryRange = isStacked
|
|
12208
12301
|
? {
|
|
12209
12302
|
...stackedRange(chartData, catCount, isPercent),
|
|
12210
12303
|
...(primaryAxis?.orientation === 'maxMin' ? { reverseOrder: true } : {}),
|
|
12211
12304
|
}
|
|
12212
12305
|
: computeValueRangeForChart(primaryPlotSeries.length > 0 ? primaryPlotSeries : chartData.series, chartData.axes);
|
|
12306
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12213
12307
|
const secondaryRange = useSecondary && secondaryPlotSeries.length > 0
|
|
12214
12308
|
? computeValueRangeForAxis(secondaryPlotSeries, chartData.axes?.find((axis) => axis.axisType === 'valAx' && axis.axPos === 'r'))
|
|
12215
12309
|
: undefined;
|
|
12310
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12216
12311
|
const axisRes = buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, catCount);
|
|
12312
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12217
12313
|
const zeroLine = primaryRange.logScale ? undefined : buildZeroLine(primaryRange, layout);
|
|
12314
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12218
12315
|
const horizontalAxis = buildCartesianHorizontalAxis(chartData, categoryLabels, layout, kind, primaryRange, secondaryRange);
|
|
12316
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12219
12317
|
const { catAxisStyle, sourceIndices, displayChartData } = horizontalAxis;
|
|
12318
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12220
12319
|
const legendPos = chartData.style?.legendPosition ?? 'b';
|
|
12320
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12221
12321
|
const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
|
|
12222
12322
|
let plot;
|
|
12223
12323
|
if (kind === 'bar') {
|
|
12224
12324
|
plot = buildBars(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, isStacked ? (isPercent ? 'percentStacked' : 'stacked') : 'clustered', sourceIndices);
|
|
12225
12325
|
}
|
|
12226
12326
|
else if (kind === 'line') {
|
|
12227
|
-
plot = buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, sourceIndices, horizontalAxis.xPositions);
|
|
12327
|
+
plot = buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, sourceIndices, horizontalAxis.xPositions, isStacked ? (isPercent ? 'percentStacked' : 'stacked') : 'clustered');
|
|
12228
12328
|
}
|
|
12229
12329
|
else if (kind === 'area') {
|
|
12230
|
-
plot = buildAreas(chartData, catCount, layout, primaryRange, sourceIndices, horizontalAxis.xPositions);
|
|
12330
|
+
plot = buildAreas(chartData, catCount, layout, primaryRange, sourceIndices, horizontalAxis.xPositions, isStacked ? (isPercent ? 'percentStacked' : 'stacked') : 'clustered');
|
|
12231
12331
|
}
|
|
12232
12332
|
else if (kind === 'scatter') {
|
|
12233
12333
|
plot = buildScatter(chartData, layout, primaryRange);
|
|
@@ -12237,29 +12337,35 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
12237
12337
|
}
|
|
12238
12338
|
// Drop lines / hi-low lines / up-down bars (line + area kinds). Drawn behind
|
|
12239
12339
|
// the series marks so the data stays legible on top.
|
|
12340
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12240
12341
|
const helperLines = kind === 'line' || kind === 'area'
|
|
12241
12342
|
? computeHelperLinePrimitives(chartData, layout, primaryRange, catCount, {
|
|
12242
12343
|
mode: 'line',
|
|
12243
12344
|
xPositions: horizontalAxis.xPositions,
|
|
12244
12345
|
})
|
|
12245
12346
|
: [];
|
|
12347
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12246
12348
|
const primitives = [
|
|
12247
12349
|
...helperLines,
|
|
12248
12350
|
...plot.primitives,
|
|
12249
12351
|
...horizontalAxis.tickMarks,
|
|
12250
12352
|
];
|
|
12251
12353
|
// Overlays (depth): regression trendlines, error bars, axis titles, data table.
|
|
12354
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12252
12355
|
const overlays = [
|
|
12253
12356
|
...computeTrendlinePrimitives(displayChartData, catCount, layout, primaryRange, catAxisStyle, chartData.colorPalette),
|
|
12254
12357
|
...computeErrorBarPrimitives(displayChartData, catCount, layout, primaryRange, catAxisStyle),
|
|
12255
12358
|
...computeAxisTitlePrimitives(chartData, layout),
|
|
12256
12359
|
];
|
|
12360
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12257
12361
|
const dataTablePrims = computeDataTablePrimitives(displayChartData, layout, chartData.colorPalette);
|
|
12258
12362
|
primitives.push(...overlays, ...dataTablePrims);
|
|
12363
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12259
12364
|
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : undefined;
|
|
12260
12365
|
// Vertical drag-to-value only has a single-value meaning for un-stacked marks:
|
|
12261
12366
|
// stacked/percentStacked bar segments sit on running sums, so dragging one
|
|
12262
12367
|
// would not track the pointer.
|
|
12368
|
+
// eslint-disable-next-line one-var -- pre-existing, unrelated to this change
|
|
12263
12369
|
const valueDrag = isStacked
|
|
12264
12370
|
? undefined
|
|
12265
12371
|
: {
|
|
@@ -15116,6 +15222,10 @@ function scalePathD(d, scale, dx, dy) {
|
|
|
15116
15222
|
*
|
|
15117
15223
|
* @module chart-view-model
|
|
15118
15224
|
*/
|
|
15225
|
+
/* eslint-disable one-var -- this module predates the rule and combining every
|
|
15226
|
+
sibling `const`/`let` in a function into one comma-list (oxlint's own
|
|
15227
|
+
`--fix` cannot do this safely once a non-declaration statement sits between
|
|
15228
|
+
them) would churn geometry code far beyond this change's scope. */
|
|
15119
15229
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
15120
15230
|
// Palette
|
|
15121
15231
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -15147,8 +15257,7 @@ function seriesColor(series, index, colorPalette) {
|
|
|
15147
15257
|
* mirrors `computeValueRange` in `chart-helpers.ts`.
|
|
15148
15258
|
*/
|
|
15149
15259
|
function computeValueRange(series) {
|
|
15150
|
-
let dataMin = Number.POSITIVE_INFINITY;
|
|
15151
|
-
let dataMax = Number.NEGATIVE_INFINITY;
|
|
15260
|
+
let dataMin = Number.POSITIVE_INFINITY, dataMax = Number.NEGATIVE_INFINITY;
|
|
15152
15261
|
for (const item of series) {
|
|
15153
15262
|
for (const value of item.values) {
|
|
15154
15263
|
if (value < dataMin) {
|
|
@@ -15170,11 +15279,9 @@ function computeValueRange(series) {
|
|
|
15170
15279
|
* scale as any other value axis.
|
|
15171
15280
|
*/
|
|
15172
15281
|
function computeStackedValueRange(series, catCount) {
|
|
15173
|
-
let maxSum = 0;
|
|
15174
|
-
let minSum = 0;
|
|
15282
|
+
let maxSum = 0, minSum = 0;
|
|
15175
15283
|
for (let ci = 0; ci < catCount; ci++) {
|
|
15176
|
-
let pos = 0;
|
|
15177
|
-
let neg = 0;
|
|
15284
|
+
let pos = 0, neg = 0;
|
|
15178
15285
|
for (const s of series) {
|
|
15179
15286
|
const v = s.values[ci] ?? 0;
|
|
15180
15287
|
if (v >= 0) {
|
|
@@ -15200,10 +15307,7 @@ function valueToY(val, range, topY, bottomY) {
|
|
|
15200
15307
|
const usable = bottomY - topY;
|
|
15201
15308
|
let ratio;
|
|
15202
15309
|
if (range.logScale && range.logBase) {
|
|
15203
|
-
const base = range.logBase;
|
|
15204
|
-
const clampedVal = Math.max(val, range.min);
|
|
15205
|
-
const logVal = Math.log(clampedVal) / Math.log(base);
|
|
15206
|
-
const logMin = Math.log(range.min) / Math.log(base);
|
|
15310
|
+
const base = range.logBase, clampedVal = Math.max(val, range.min), logVal = Math.log(clampedVal) / Math.log(base), logMin = Math.log(range.min) / Math.log(base);
|
|
15207
15311
|
ratio = (logVal - logMin) / range.span;
|
|
15208
15312
|
}
|
|
15209
15313
|
else {
|
|
@@ -15235,6 +15339,23 @@ function formatAxisValue(val, formatCode) {
|
|
|
15235
15339
|
}
|
|
15236
15340
|
return val.toFixed(1);
|
|
15237
15341
|
}
|
|
15342
|
+
/**
|
|
15343
|
+
* Build the hover-tooltip text for a plain data mark (bar / line / area /
|
|
15344
|
+
* scatter / bubble / pie / radar point), projected as each primitive's `title`
|
|
15345
|
+
* field (see the doc comment on `SvgPath.title`).
|
|
15346
|
+
*
|
|
15347
|
+
* Mirrors the region map's own `"<name>: <value>"` tooltip (chart-waterfall-map.ts):
|
|
15348
|
+
* join whichever of the series name and category/point label are known, then
|
|
15349
|
+
* append the formatted value. Either label may be absent (a scatter/bubble
|
|
15350
|
+
* point has no category; an un-named series has no name); the result degrades
|
|
15351
|
+
* to just the value when neither is.
|
|
15352
|
+
*/
|
|
15353
|
+
function buildMarkTooltip(seriesName, categoryLabel, value, numberFormat) {
|
|
15354
|
+
const label = [seriesName, categoryLabel]
|
|
15355
|
+
.filter((part) => Boolean(part && part.length > 0))
|
|
15356
|
+
.join(', '), formatted = formatAxisValue(value, numberFormat);
|
|
15357
|
+
return label.length > 0 ? `${label}: ${formatted}` : formatted;
|
|
15358
|
+
}
|
|
15238
15359
|
/**
|
|
15239
15360
|
* Compute the plot layout for a chart element.
|
|
15240
15361
|
* Mirrors `computeLayout` from chart-layout.ts (React). When `options` is omitted
|
|
@@ -15256,9 +15377,7 @@ function formatAxisValue(val, formatCode) {
|
|
|
15256
15377
|
* @returns Pixels to reserve below the plot area.
|
|
15257
15378
|
*/
|
|
15258
15379
|
function categoryAxisBand(chartData) {
|
|
15259
|
-
const axis = chartData.axes?.find((candidate) => candidate.axisType === 'catAx' || candidate.axisType === 'dateAx');
|
|
15260
|
-
const fontPx = axis?.fontSize !== undefined ? chartFontPx(axis.fontSize) : DEFAULT_CHART_TEXT_PX;
|
|
15261
|
-
const offset = 4 + 8 * ((axis?.labelOffset ?? 100) / 100);
|
|
15380
|
+
const axis = chartData.axes?.find((candidate) => candidate.axisType === 'catAx' || candidate.axisType === 'dateAx'), fontPx = axis?.fontSize !== undefined ? chartFontPx(axis.fontSize) : DEFAULT_CHART_TEXT_PX, offset = 4 + 8 * ((axis?.labelOffset ?? 100) / 100);
|
|
15262
15381
|
return Math.max(24, offset + fontPx * 1.2);
|
|
15263
15382
|
}
|
|
15264
15383
|
function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, options) {
|
|
@@ -15266,14 +15385,9 @@ function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, opti
|
|
|
15266
15385
|
// it with `preserveAspectRatio="none"`, so ANY minimum here (historically
|
|
15267
15386
|
// 320x180) makes the chart scale non-uniformly inside its host (issue #132:
|
|
15268
15387
|
// a 475x174 frame got a 475x180 viewBox, squeezing y by 0.967).
|
|
15269
|
-
const svgWidth = Math.max(1, elementWidth);
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
let plotTop = 8;
|
|
15273
|
-
let plotRight = svgWidth - 8;
|
|
15274
|
-
let plotBottom = svgHeight - (hasAxes ? categoryAxisBand(chartData) : 8);
|
|
15275
|
-
const style = chartData.style;
|
|
15276
|
-
const legendPos = style?.legendPosition ?? 'b';
|
|
15388
|
+
const svgWidth = Math.max(1, elementWidth), svgHeight = Math.max(1, elementHeight);
|
|
15389
|
+
let plotLeft = hasAxes ? 48 : 8, plotTop = 8, plotRight = svgWidth - 8, plotBottom = svgHeight - (hasAxes ? categoryAxisBand(chartData) : 8);
|
|
15390
|
+
const style = chartData.style, legendPos = style?.legendPosition ?? 'b';
|
|
15277
15391
|
if (style?.hasTitle) {
|
|
15278
15392
|
plotTop += 20;
|
|
15279
15393
|
}
|
|
@@ -15304,8 +15418,7 @@ function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, opti
|
|
|
15304
15418
|
const rowCount = options.dataTableRowCount ?? 1;
|
|
15305
15419
|
plotBottom -= 14 + rowCount * 14;
|
|
15306
15420
|
}
|
|
15307
|
-
const plotWidth = Math.max(plotRight - plotLeft, 1);
|
|
15308
|
-
const plotHeight = Math.max(plotBottom - plotTop, 1);
|
|
15421
|
+
const plotWidth = Math.max(plotRight - plotLeft, 1), plotHeight = Math.max(plotBottom - plotTop, 1);
|
|
15309
15422
|
return {
|
|
15310
15423
|
svgWidth,
|
|
15311
15424
|
svgHeight,
|
|
@@ -15341,8 +15454,7 @@ function axisTickValues(range) {
|
|
|
15341
15454
|
return Array.from({ length: TICK_COUNT + 1 }, (_unused, index) => range.min + (range.span / TICK_COUNT) * index);
|
|
15342
15455
|
}
|
|
15343
15456
|
function buildGridlinesAndLabels(range, layout) {
|
|
15344
|
-
const gridlines = [];
|
|
15345
|
-
const axisLabels = [];
|
|
15457
|
+
const gridlines = [], axisLabels = [];
|
|
15346
15458
|
for (const val of axisTickValues(range)) {
|
|
15347
15459
|
const y = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
15348
15460
|
gridlines.push({
|
|
@@ -15406,9 +15518,7 @@ function buildLegend(series, colorPalette, svgWidth, legendPos, svgHeight, plotT
|
|
|
15406
15518
|
color: seriesColor(s, i, colorPalette),
|
|
15407
15519
|
label: s.name,
|
|
15408
15520
|
}));
|
|
15409
|
-
let legendX = svgWidth / 2;
|
|
15410
|
-
let legendY = svgHeight - 8;
|
|
15411
|
-
let legendAnchor = 'middle';
|
|
15521
|
+
let legendX = svgWidth / 2, legendY = svgHeight - 8, legendAnchor = 'middle';
|
|
15412
15522
|
if (legendPos === 'r') {
|
|
15413
15523
|
legendX = svgWidth - 75;
|
|
15414
15524
|
legendY = plotTop;
|
|
@@ -15425,19 +15535,10 @@ function buildLegend(series, colorPalette, svgWidth, legendPos, svgHeight, plotT
|
|
|
15425
15535
|
return { legend, legendX, legendY, legendAnchor };
|
|
15426
15536
|
}
|
|
15427
15537
|
function computeBarRects(series, catCount, layout, range, colorPalette) {
|
|
15428
|
-
const rects = [];
|
|
15429
|
-
const seriesCount = Math.max(series.length, 1);
|
|
15430
|
-
const barGroupWidth = layout.plotWidth / Math.max(catCount, 1);
|
|
15431
|
-
const singleBarWidth = (barGroupWidth * 0.7) / seriesCount;
|
|
15432
|
-
const groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
|
|
15538
|
+
const rects = [], seriesCount = Math.max(series.length, 1), barGroupWidth = layout.plotWidth / Math.max(catCount, 1), singleBarWidth = (barGroupWidth * 0.7) / seriesCount, groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
|
|
15433
15539
|
for (let ci = 0; ci < catCount; ci++) {
|
|
15434
15540
|
for (let si = 0; si < series.length; si++) {
|
|
15435
|
-
const val = series[si].values[ci] ?? 0;
|
|
15436
|
-
const x = layout.plotLeft + barGroupWidth * ci + groupOffset + singleBarWidth * si;
|
|
15437
|
-
const zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
15438
|
-
const valY = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
15439
|
-
const y = Math.min(zeroY, valY);
|
|
15440
|
-
const h = Math.max(Math.abs(zeroY - valY), 1);
|
|
15541
|
+
const val = series[si].values[ci] ?? 0, x = layout.plotLeft + barGroupWidth * ci + groupOffset + singleBarWidth * si, zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom), valY = valueToY(val, range, layout.plotTop, layout.plotBottom), y = Math.min(zeroY, valY), h = Math.max(Math.abs(zeroY - valY), 1);
|
|
15441
15542
|
rects.push({
|
|
15442
15543
|
x,
|
|
15443
15544
|
y,
|
|
@@ -15450,20 +15551,15 @@ function computeBarRects(series, catCount, layout, range, colorPalette) {
|
|
|
15450
15551
|
return rects;
|
|
15451
15552
|
}
|
|
15452
15553
|
function computeStackedBarRects(series, catCount, layout, range, colorPalette) {
|
|
15453
|
-
const rects = [];
|
|
15454
|
-
const barW = (layout.plotWidth / Math.max(catCount, 1)) * 0.7;
|
|
15455
|
-
const barOffset = (layout.plotWidth / Math.max(catCount, 1) - barW) / 2;
|
|
15456
|
-
const zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
15554
|
+
const rects = [], barW = (layout.plotWidth / Math.max(catCount, 1)) * 0.7, barOffset = (layout.plotWidth / Math.max(catCount, 1) - barW) / 2, zeroY = valueToY(0, range, layout.plotTop, layout.plotBottom);
|
|
15457
15555
|
for (let ci = 0; ci < catCount; ci++) {
|
|
15458
|
-
let posTop = zeroY;
|
|
15459
|
-
let negBottom = zeroY;
|
|
15556
|
+
let posTop = zeroY, negBottom = zeroY;
|
|
15460
15557
|
for (let si = 0; si < series.length; si++) {
|
|
15461
15558
|
const val = series[si].values[ci] ?? 0;
|
|
15462
15559
|
if (val === 0) {
|
|
15463
15560
|
continue;
|
|
15464
15561
|
}
|
|
15465
|
-
const x = layout.plotLeft + (layout.plotWidth / Math.max(catCount, 1)) * ci + barOffset
|
|
15466
|
-
const h = Math.max(Math.abs(valueToY(val, range, layout.plotTop, layout.plotBottom) -
|
|
15562
|
+
const x = layout.plotLeft + (layout.plotWidth / Math.max(catCount, 1)) * ci + barOffset, h = Math.max(Math.abs(valueToY(val, range, layout.plotTop, layout.plotBottom) -
|
|
15467
15563
|
valueToY(0, range, layout.plotTop, layout.plotBottom)), 1);
|
|
15468
15564
|
if (val > 0) {
|
|
15469
15565
|
const y = posTop - h;
|
|
@@ -15498,9 +15594,7 @@ function computeStackedBarRects(series, catCount, layout, range, colorPalette) {
|
|
|
15498
15594
|
function computeLinePoints(values, catCount, layout, range) {
|
|
15499
15595
|
const n = Math.max(catCount, 2);
|
|
15500
15596
|
return values.map((val, i) => {
|
|
15501
|
-
const nx = n > 1 ? i / (n - 1) : 0;
|
|
15502
|
-
const x = layout.plotLeft + layout.plotWidth * nx;
|
|
15503
|
-
const y = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
15597
|
+
const nx = n > 1 ? i / (n - 1) : 0, x = layout.plotLeft + layout.plotWidth * nx, y = valueToY(val, range, layout.plotTop, layout.plotBottom);
|
|
15504
15598
|
return { x, y };
|
|
15505
15599
|
});
|
|
15506
15600
|
}
|
|
@@ -15508,55 +15602,37 @@ function linePointsToSvgString(points) {
|
|
|
15508
15602
|
return points.map((p) => `${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(' ');
|
|
15509
15603
|
}
|
|
15510
15604
|
function computePieSlicePath(cx, cy, outerR, innerR, startAngle, endAngle) {
|
|
15511
|
-
const largeArc = endAngle - startAngle > Math.PI ? 1 : 0;
|
|
15512
|
-
const x1 = cx + outerR * Math.cos(startAngle);
|
|
15513
|
-
const y1 = cy + outerR * Math.sin(startAngle);
|
|
15514
|
-
const x2 = cx + outerR * Math.cos(endAngle);
|
|
15515
|
-
const y2 = cy + outerR * Math.sin(endAngle);
|
|
15605
|
+
const largeArc = endAngle - startAngle > Math.PI ? 1 : 0, x1 = cx + outerR * Math.cos(startAngle), y1 = cy + outerR * Math.sin(startAngle), x2 = cx + outerR * Math.cos(endAngle), y2 = cy + outerR * Math.sin(endAngle);
|
|
15516
15606
|
let d;
|
|
15517
15607
|
if (innerR > 0) {
|
|
15518
|
-
const ix1 = cx + innerR * Math.cos(startAngle);
|
|
15519
|
-
const iy1 = cy + innerR * Math.sin(startAngle);
|
|
15520
|
-
const ix2 = cx + innerR * Math.cos(endAngle);
|
|
15521
|
-
const iy2 = cy + innerR * Math.sin(endAngle);
|
|
15608
|
+
const ix1 = cx + innerR * Math.cos(startAngle), iy1 = cy + innerR * Math.sin(startAngle), ix2 = cx + innerR * Math.cos(endAngle), iy2 = cy + innerR * Math.sin(endAngle);
|
|
15522
15609
|
d = `M${x1},${y1} A${outerR},${outerR} 0 ${largeArc} 1 ${x2},${y2} L${ix2},${iy2} A${innerR},${innerR} 0 ${largeArc} 0 ${ix1},${iy1} Z`;
|
|
15523
15610
|
}
|
|
15524
15611
|
else {
|
|
15525
15612
|
d = `M${cx},${cy} L${x1},${y1} A${outerR},${outerR} 0 ${largeArc} 1 ${x2},${y2} Z`;
|
|
15526
15613
|
}
|
|
15527
|
-
const midAngle = (startAngle + endAngle) / 2;
|
|
15528
|
-
const labelR = outerR * 0.7;
|
|
15529
|
-
const labelX = cx + labelR * Math.cos(midAngle);
|
|
15530
|
-
const labelY = cy + labelR * Math.sin(midAngle);
|
|
15614
|
+
const midAngle = (startAngle + endAngle) / 2, labelR = outerR * 0.7, labelX = cx + labelR * Math.cos(midAngle), labelY = cy + labelR * Math.sin(midAngle);
|
|
15531
15615
|
return { d, midAngle, labelX, labelY };
|
|
15532
15616
|
}
|
|
15533
15617
|
function computePieLayout(elementWidth, elementHeight, chartData, isDoughnut) {
|
|
15534
|
-
const size = Math.min(Math.max(elementWidth, 1), Math.max(elementHeight, 1))
|
|
15535
|
-
const titleOffset = chartData.style?.hasTitle ? 20 : 0;
|
|
15536
|
-
const legendOffset = chartData.style?.hasLegend ? 20 : 0;
|
|
15537
|
-
const cx = size / 2;
|
|
15538
|
-
const cy = titleOffset + (size - titleOffset - legendOffset) / 2;
|
|
15539
|
-
const outerR = Math.max((size - titleOffset - legendOffset) * 0.42, 0);
|
|
15618
|
+
const size = Math.min(Math.max(elementWidth, 1), Math.max(elementHeight, 1)), titleOffset = chartData.style?.hasTitle ? 20 : 0, legendOffset = chartData.style?.hasLegend ? 20 : 0, cx = size / 2, cy = titleOffset + (size - titleOffset - legendOffset) / 2, outerR = Math.max((size - titleOffset - legendOffset) * 0.42, 0),
|
|
15540
15619
|
// Honour c:holeSize (10-90% of the outer diameter) when parsed; otherwise
|
|
15541
15620
|
// keep the legacy 0.55 ratio byte-for-byte.
|
|
15542
|
-
|
|
15621
|
+
holeRatio = isDoughnut && chartData.doughnutHoleSize !== undefined
|
|
15543
15622
|
? Math.min(Math.max(chartData.doughnutHoleSize, 10), 90) / 100
|
|
15544
|
-
: 0.55;
|
|
15545
|
-
const innerR = isDoughnut ? outerR * holeRatio : 0;
|
|
15623
|
+
: 0.55, innerR = isDoughnut ? outerR * holeRatio : 0;
|
|
15546
15624
|
return { cx, cy, outerR, innerR, size };
|
|
15547
15625
|
}
|
|
15548
15626
|
function computePieSlices(values, cx, cy, outerR, innerR, options) {
|
|
15549
15627
|
const total = values.reduce((s, v) => s + Math.abs(v), 0) || 1;
|
|
15550
15628
|
let cumAngle = options?.startAngle ?? -Math.PI / 2;
|
|
15551
15629
|
return values.map((val, i) => {
|
|
15552
|
-
const sliceAngle = (Math.abs(val) / total) * Math.PI * 2;
|
|
15553
|
-
const startAngle = cumAngle;
|
|
15630
|
+
const sliceAngle = (Math.abs(val) / total) * Math.PI * 2, startAngle = cumAngle;
|
|
15554
15631
|
cumAngle += sliceAngle;
|
|
15555
15632
|
// A c:explosion pulls the slice outward along its bisector.
|
|
15556
15633
|
const explosion = options?.explosions?.[i] ?? 0;
|
|
15557
15634
|
if (explosion > 0) {
|
|
15558
|
-
const mid = (startAngle + cumAngle) / 2;
|
|
15559
|
-
const offset = outerR * (explosion / 100);
|
|
15635
|
+
const mid = (startAngle + cumAngle) / 2, offset = outerR * (explosion / 100);
|
|
15560
15636
|
return computePieSlicePath(cx + Math.cos(mid) * offset, cy + Math.sin(mid) * offset, outerR, innerR, startAngle, cumAngle);
|
|
15561
15637
|
}
|
|
15562
15638
|
return computePieSlicePath(cx, cy, outerR, innerR, startAngle, cumAngle);
|
|
@@ -15583,9 +15659,7 @@ function computeScatterXDomain(seriesXValues) {
|
|
|
15583
15659
|
return { min, span: Math.max(Math.max(...finite) - min, 1) };
|
|
15584
15660
|
}
|
|
15585
15661
|
function computeScatterDots(values, maxXIndex, layout, range, xValues, xDomain) {
|
|
15586
|
-
const finiteX = xValues?.slice(0, values.length).filter(Number.isFinite)
|
|
15587
|
-
const minX = xDomain ? xDomain.min : finiteX?.length ? Math.min(...finiteX) : 0;
|
|
15588
|
-
const spanX = xDomain
|
|
15662
|
+
const finiteX = xValues?.slice(0, values.length).filter(Number.isFinite), minX = xDomain ? xDomain.min : finiteX?.length ? Math.min(...finiteX) : 0, spanX = xDomain
|
|
15589
15663
|
? xDomain.span
|
|
15590
15664
|
: finiteX?.length
|
|
15591
15665
|
? Math.max(Math.max(...finiteX) - minX, 1)
|
|
@@ -15625,8 +15699,7 @@ function radarAngle(index, catCount) {
|
|
|
15625
15699
|
function computeRadarPoints(values, maxVal, radius, cx, cy, catCount) {
|
|
15626
15700
|
const denom = maxVal > 0 ? maxVal : 1;
|
|
15627
15701
|
return values.slice(0, Math.max(catCount, 1)).map((val, i) => {
|
|
15628
|
-
const angle = radarAngle(i, catCount);
|
|
15629
|
-
const r = (Math.abs(val) / denom) * radius;
|
|
15702
|
+
const angle = radarAngle(i, catCount), r = (Math.abs(val) / denom) * radius;
|
|
15630
15703
|
return { x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) };
|
|
15631
15704
|
});
|
|
15632
15705
|
}
|
|
@@ -15710,18 +15783,15 @@ function buildChartViewModel(element) {
|
|
|
15710
15783
|
if (element.type !== 'chart') {
|
|
15711
15784
|
return buildFallbackViewModel(element.width, element.height, 'Chart');
|
|
15712
15785
|
}
|
|
15713
|
-
const chartEl = element;
|
|
15714
|
-
const chartData = chartEl.chartData;
|
|
15786
|
+
const chartEl = element, chartData = chartEl.chartData;
|
|
15715
15787
|
if (!chartData || chartData.series.length === 0) {
|
|
15716
15788
|
return buildFallbackViewModel(element.width, element.height, chartData?.title ?? 'Chart');
|
|
15717
15789
|
}
|
|
15718
|
-
const chartType = chartData.chartType ?? 'bar';
|
|
15719
|
-
const kind = resolveChartKind(chartType);
|
|
15790
|
+
const chartType = chartData.chartType ?? 'bar', kind = resolveChartKind(chartType);
|
|
15720
15791
|
if (kind === 'unsupported') {
|
|
15721
15792
|
return buildFallbackViewModel(element.width, element.height, chartData.title ?? chartType);
|
|
15722
15793
|
}
|
|
15723
|
-
const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0)
|
|
15724
|
-
const categoryLabels = chartData.categories.length > 0
|
|
15794
|
+
const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0), categoryLabels = chartData.categories.length > 0
|
|
15725
15795
|
? chartData.categories
|
|
15726
15796
|
: Array.from({ length: longestLen }, (_, i) => String(i + 1));
|
|
15727
15797
|
// Pie-of-pie / bar-of-pie splits one series across a primary + secondary plot.
|
|
@@ -15816,8 +15886,7 @@ function buildFlatViewModel(element, chartData, categoryLabels, kind) {
|
|
|
15816
15886
|
function buildFallbackViewModel(width, height, label) {
|
|
15817
15887
|
// Match the frame box exactly (bindings stretch with preserveAspectRatio
|
|
15818
15888
|
// "none"; a minimum here would scale the fallback non-uniformly).
|
|
15819
|
-
const svgWidth = Math.max(width, 1);
|
|
15820
|
-
const svgHeight = Math.max(height, 1);
|
|
15889
|
+
const svgWidth = Math.max(width, 1), svgHeight = Math.max(height, 1);
|
|
15821
15890
|
return {
|
|
15822
15891
|
svgWidth,
|
|
15823
15892
|
svgHeight,
|
|
@@ -15860,19 +15929,12 @@ function buildFallbackViewModel(width, height, label) {
|
|
|
15860
15929
|
};
|
|
15861
15930
|
}
|
|
15862
15931
|
function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
|
|
15863
|
-
const { cx, cy, outerR, innerR, size } = computePieLayout(element.width, element.height, chartData, isDoughnut)
|
|
15864
|
-
const svgWidth = Math.max(size, 100);
|
|
15865
|
-
const svgHeight = Math.max(size, 60);
|
|
15866
|
-
const pieSeries = chartData.series[0];
|
|
15867
|
-
const values = pieSeries?.values ?? [];
|
|
15932
|
+
const { cx, cy, outerR, innerR, size } = computePieLayout(element.width, element.height, chartData, isDoughnut), svgWidth = Math.max(size, 100), svgHeight = Math.max(size, 60), pieSeries = chartData.series[0], values = pieSeries?.values ?? [],
|
|
15868
15933
|
// c:firstSliceAng rotates the pie clockwise from 12 o'clock; c:explosion (per
|
|
15869
15934
|
// series or per c:dPt) pulls slices outward.
|
|
15870
|
-
|
|
15871
|
-
const explosions = pieSeries
|
|
15935
|
+
startAngle = -Math.PI / 2 + ((chartData.firstSliceAngle ?? 0) * Math.PI) / 180, explosions = pieSeries
|
|
15872
15936
|
? values.map((_v, i) => resolveDataPointExplosion(pieSeries, i))
|
|
15873
|
-
: undefined
|
|
15874
|
-
const slices = computePieSlices(values, cx, cy, outerR, innerR, { startAngle, explosions });
|
|
15875
|
-
const primitives = slices.map(({ d }, i) => ({
|
|
15937
|
+
: undefined, slices = computePieSlices(values, cx, cy, outerR, innerR, { startAngle, explosions }), primitives = slices.map(({ d }, i) => ({
|
|
15876
15938
|
kind: 'path',
|
|
15877
15939
|
d,
|
|
15878
15940
|
// Pie/doughnut vary colours per slice (c:varyColors defaults on), so each
|
|
@@ -15883,15 +15945,14 @@ function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
|
|
|
15883
15945
|
stroke: '#ffffff',
|
|
15884
15946
|
strokeWidth: 1.5,
|
|
15885
15947
|
part: { role: 'dataPoint', seriesIndex: 0, pointIndex: i },
|
|
15886
|
-
|
|
15887
|
-
|
|
15948
|
+
title: buildMarkTooltip(pieSeries?.name, categoryLabels[i], values[i] ?? 0, pieSeries?.numberFormat),
|
|
15949
|
+
})), dataLabels = [];
|
|
15888
15950
|
if (chartData.style?.hasDataLabels) {
|
|
15889
15951
|
// Offset (outEnd / bestFit) labels sit outside the rim with c:leaderLines.
|
|
15890
15952
|
// A pie's percentage base is the whole series, and `c:showPercent` is the
|
|
15891
15953
|
// flag that makes the difference between "40" and "40%" on the commonest
|
|
15892
15954
|
// labelled chart in a business deck.
|
|
15893
|
-
const percentBase = values.reduce((total, entry) => total + Math.abs(entry), 0)
|
|
15894
|
-
const labelResult = buildPieDataLabels({
|
|
15955
|
+
const percentBase = values.reduce((total, entry) => total + Math.abs(entry), 0), labelResult = buildPieDataLabels({
|
|
15895
15956
|
slices,
|
|
15896
15957
|
values,
|
|
15897
15958
|
cx,
|
|
@@ -15913,16 +15974,15 @@ function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
|
|
|
15913
15974
|
dataLabels.push(...labelResult.labels);
|
|
15914
15975
|
primitives.push(...labelResult.leaderLines);
|
|
15915
15976
|
}
|
|
15916
|
-
const legendPos = chartData.style?.legendPosition ?? 'b'
|
|
15977
|
+
const legendPos = chartData.style?.legendPosition ?? 'b',
|
|
15917
15978
|
// Legend swatches must match the slices: a per-point `c:dPt` fill overrides
|
|
15918
15979
|
// the palette on the slice, so it overrides it on the swatch too.
|
|
15919
|
-
|
|
15980
|
+
legend = categoryLabels.map((label, i) => ({
|
|
15920
15981
|
color: pieSeries
|
|
15921
15982
|
? resolveVaryColorFill(pieSeries, i, paletteColor(i, chartData.colorPalette))
|
|
15922
15983
|
: paletteColor(i, chartData.colorPalette),
|
|
15923
15984
|
label,
|
|
15924
|
-
}));
|
|
15925
|
-
const legendX = svgWidth / 2;
|
|
15985
|
+
})), legendX = svgWidth / 2;
|
|
15926
15986
|
let legendY = svgHeight - 8;
|
|
15927
15987
|
const legendAnchor = 'middle';
|
|
15928
15988
|
if (legendPos === 't') {
|
|
@@ -15947,10 +16007,7 @@ function buildPieViewModel(element, chartData, categoryLabels, isDoughnut) {
|
|
|
15947
16007
|
legendAnchor,
|
|
15948
16008
|
};
|
|
15949
16009
|
}
|
|
15950
|
-
const RADAR_RINGS = 4;
|
|
15951
|
-
const RADAR_RING_COLOR = '#cbd5e1';
|
|
15952
|
-
const RADAR_SPOKE_COLOR = '#94a3b8';
|
|
15953
|
-
const RADAR_LABEL_COLOR = '#64748b';
|
|
16010
|
+
const RADAR_RINGS = 4, RADAR_RING_COLOR = '#cbd5e1', RADAR_SPOKE_COLOR = '#94a3b8', RADAR_LABEL_COLOR = '#64748b';
|
|
15954
16011
|
/**
|
|
15955
16012
|
* Build the view-model for a radar / spider chart. Polar, so it has no
|
|
15956
16013
|
* cartesian gridlines/axes; ring + spoke geometry and the data polygons all
|
|
@@ -15958,14 +16015,7 @@ const RADAR_LABEL_COLOR = '#64748b';
|
|
|
15958
16015
|
* Mirrors React's `renderRadarChart` (chart-radar.tsx).
|
|
15959
16016
|
*/
|
|
15960
16017
|
function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
15961
|
-
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
15962
|
-
const cx = layout.plotLeft + layout.plotWidth / 2;
|
|
15963
|
-
const cy = layout.plotTop + layout.plotHeight / 2;
|
|
15964
|
-
const radius = Math.max(Math.min(layout.plotWidth, layout.plotHeight) / 2 - 4, 1);
|
|
15965
|
-
const catCount = Math.max(categoryLabels.length, 1);
|
|
15966
|
-
const maxVal = Math.max(1, ...chartData.series.flatMap((s) => s.values.map((v) => Math.abs(v))));
|
|
15967
|
-
const primitives = [];
|
|
15968
|
-
const perimeterLabels = [];
|
|
16018
|
+
const layout = computePlotLayout(element.width, element.height, chartData, false), cx = layout.plotLeft + layout.plotWidth / 2, cy = layout.plotTop + layout.plotHeight / 2, radius = Math.max(Math.min(layout.plotWidth, layout.plotHeight) / 2 - 4, 1), catCount = Math.max(categoryLabels.length, 1), maxVal = Math.max(1, ...chartData.series.flatMap((s) => s.values.map((v) => Math.abs(v)))), primitives = [], perimeterLabels = [];
|
|
15969
16019
|
// Concentric gridline rings (dashed except the outermost).
|
|
15970
16020
|
for (let r = 1; r <= RADAR_RINGS; r++) {
|
|
15971
16021
|
const rr = (radius * r) / RADAR_RINGS;
|
|
@@ -16005,8 +16055,7 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
|
16005
16055
|
// Per-series data polygons + vertex dots.
|
|
16006
16056
|
const dataLabels = [];
|
|
16007
16057
|
chartData.series.forEach((series, si) => {
|
|
16008
|
-
const c = seriesColor(series, si, chartData.colorPalette);
|
|
16009
|
-
const pts = computeRadarPoints(series.values, maxVal, radius, cx, cy, catCount);
|
|
16058
|
+
const c = seriesColor(series, si, chartData.colorPalette), pts = computeRadarPoints(series.values, maxVal, radius, cx, cy, catCount);
|
|
16010
16059
|
if (pts.length === 0) {
|
|
16011
16060
|
return;
|
|
16012
16061
|
}
|
|
@@ -16019,6 +16068,7 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
|
16019
16068
|
stroke: c,
|
|
16020
16069
|
strokeWidth: 1.5,
|
|
16021
16070
|
part: { role: 'series', seriesIndex: si },
|
|
16071
|
+
title: series.name.length > 0 ? series.name : undefined,
|
|
16022
16072
|
});
|
|
16023
16073
|
pts.forEach((p, vi) => {
|
|
16024
16074
|
primitives.push({
|
|
@@ -16028,6 +16078,7 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
|
16028
16078
|
r: 3,
|
|
16029
16079
|
fill: c,
|
|
16030
16080
|
part: { role: 'dataPoint', seriesIndex: si, pointIndex: vi },
|
|
16081
|
+
title: buildMarkTooltip(series.name, categoryLabels[vi], series.values[vi] ?? 0, series.numberFormat),
|
|
16031
16082
|
});
|
|
16032
16083
|
});
|
|
16033
16084
|
if (chartData.style?.hasDataLabels) {
|
|
@@ -16048,9 +16099,7 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
|
16048
16099
|
});
|
|
16049
16100
|
}
|
|
16050
16101
|
});
|
|
16051
|
-
const legendPos = chartData.style?.legendPosition ?? 'b';
|
|
16052
|
-
const { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop);
|
|
16053
|
-
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : undefined;
|
|
16102
|
+
const legendPos = chartData.style?.legendPosition ?? 'b', { legend, legendX, legendY, legendAnchor } = buildLegend(chartData.series, chartData.colorPalette, layout.svgWidth, legendPos, layout.svgHeight, layout.plotTop), title = chartData.style?.hasTitle && chartData.title ? chartData.title : undefined;
|
|
16054
16103
|
return {
|
|
16055
16104
|
svgWidth: layout.svgWidth,
|
|
16056
16105
|
svgHeight: layout.svgHeight,
|
|
@@ -17059,6 +17108,9 @@ const ANIMATION_KEYFRAMES_CSS = `
|
|
|
17059
17108
|
*
|
|
17060
17109
|
* @module render/animation-authoring
|
|
17061
17110
|
*/
|
|
17111
|
+
/* oxlint-disable eslint/one-var -- each exported helper below declares its own
|
|
17112
|
+
independent locals; merging unrelated declarations across the many
|
|
17113
|
+
functions here would hurt readability, not help it. */
|
|
17062
17114
|
// ==========================================================================
|
|
17063
17115
|
// Option catalogs (value-only — bindings supply labels/icons)
|
|
17064
17116
|
// ==========================================================================
|
|
@@ -17330,6 +17382,39 @@ function reorderAnimationUp(anims, elementId) {
|
|
|
17330
17382
|
function reorderAnimationDown(anims, elementId) {
|
|
17331
17383
|
return reorderByDelta(anims, elementId, +1);
|
|
17332
17384
|
}
|
|
17385
|
+
/**
|
|
17386
|
+
* Moves one animation entry to an arbitrary position and re-normalises
|
|
17387
|
+
* `order` on the result. This is the drag-and-drop / move-to-index
|
|
17388
|
+
* algorithm shared by every binding's animation timeline: sort by `order`,
|
|
17389
|
+
* splice the moved entry out of its source position, splice it into the
|
|
17390
|
+
* target position, then reassign `order` 0..n-1.
|
|
17391
|
+
*
|
|
17392
|
+
* `source` identifies the entry being moved, either as a resolved index into
|
|
17393
|
+
* the `order`-sorted sequence (row-index drag-and-drop, used by most
|
|
17394
|
+
* bindings) or as `{ elementId }` to resolve that position by id (the
|
|
17395
|
+
* Svelte ribbon tab drags by the element under the pointer rather than a
|
|
17396
|
+
* row index). `targetIndex` is always a resolved index into the sorted
|
|
17397
|
+
* sequence: a caller that only has a target elementId (also the Svelte
|
|
17398
|
+
* ribbon tab) resolves it with `animations.findIndex` first.
|
|
17399
|
+
*
|
|
17400
|
+
* Returns a sorted, re-indexed copy without moving anything when `source`
|
|
17401
|
+
* cannot be resolved, `targetIndex` is out of range, or the entry is
|
|
17402
|
+
* already at `targetIndex`.
|
|
17403
|
+
*/
|
|
17404
|
+
function reorderAnimationTo(animations, source, targetIndex) {
|
|
17405
|
+
const sorted = [...animations].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
17406
|
+
const sourceIndex = typeof source === 'number' ? source : sorted.findIndex((a) => a.elementId === source.elementId);
|
|
17407
|
+
if (sourceIndex < 0 ||
|
|
17408
|
+
sourceIndex >= sorted.length ||
|
|
17409
|
+
targetIndex < 0 ||
|
|
17410
|
+
targetIndex >= sorted.length ||
|
|
17411
|
+
sourceIndex === targetIndex) {
|
|
17412
|
+
return reindexOrder(sorted);
|
|
17413
|
+
}
|
|
17414
|
+
const [moved] = sorted.splice(sourceIndex, 1);
|
|
17415
|
+
sorted.splice(targetIndex, 0, moved);
|
|
17416
|
+
return reindexOrder(sorted);
|
|
17417
|
+
}
|
|
17333
17418
|
// ==========================================================================
|
|
17334
17419
|
// Immutable patch builders — coarse group preset apply/remove (Vue model)
|
|
17335
17420
|
// ==========================================================================
|
|
@@ -17361,6 +17446,23 @@ function applyAnimationPreset(animations, elementId, group, preset) {
|
|
|
17361
17446
|
function removeElementAnimation(animations, elementId) {
|
|
17362
17447
|
return animations.filter((a) => a.elementId !== elementId);
|
|
17363
17448
|
}
|
|
17449
|
+
/**
|
|
17450
|
+
* Lays out the proportional timeline-strip bars for a slide's animations:
|
|
17451
|
+
* each bar's `leftPercent`/`widthPercent` express its delay/duration as a
|
|
17452
|
+
* percentage of the longest end time (`delayMs + durationMs`) across every
|
|
17453
|
+
* entry, so the strip always spans the full authored sequence. Returned in
|
|
17454
|
+
* `order` order (matching the reorderable list every binding renders beside
|
|
17455
|
+
* the strip).
|
|
17456
|
+
*/
|
|
17457
|
+
function buildAnimationTimelineBars(animations) {
|
|
17458
|
+
const sorted = [...animations].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
17459
|
+
const total = Math.max(1, ...sorted.map((animation) => (animation.delayMs ?? 0) + (animation.durationMs ?? 500)));
|
|
17460
|
+
return sorted.map((animation) => ({
|
|
17461
|
+
elementId: animation.elementId,
|
|
17462
|
+
leftPercent: ((animation.delayMs ?? 0) / total) * 100,
|
|
17463
|
+
widthPercent: ((animation.durationMs ?? 500) / total) * 100,
|
|
17464
|
+
}));
|
|
17465
|
+
}
|
|
17364
17466
|
// ==========================================================================
|
|
17365
17467
|
// Internal ordering helpers
|
|
17366
17468
|
// ==========================================================================
|
|
@@ -24962,15 +25064,6 @@ function computeSplitCell(td, rowIndex, columnIndex) {
|
|
|
24962
25064
|
});
|
|
24963
25065
|
}
|
|
24964
25066
|
|
|
24965
|
-
/**
|
|
24966
|
-
* table-style-presets.ts - framework-agnostic table style preset catalogue.
|
|
24967
|
-
*
|
|
24968
|
-
* Pure data: the quick-style swatches (light / medium / dark families) shown in
|
|
24969
|
-
* every binding's table properties panel. Each preset carries the header fill /
|
|
24970
|
-
* foreground, banded-row background, and border colour applied when the user
|
|
24971
|
-
* picks it. Ported from the React viewer's `constants/table-styles.ts` so React,
|
|
24972
|
-
* Vue, and Angular consume one copy.
|
|
24973
|
-
*/
|
|
24974
25067
|
const TABLE_STYLE_PRESETS = [
|
|
24975
25068
|
// Light styles
|
|
24976
25069
|
{
|
|
@@ -25048,6 +25141,29 @@ const TABLE_STYLE_PRESETS = [
|
|
|
25048
25141
|
borderColor: '#548235',
|
|
25049
25142
|
},
|
|
25050
25143
|
];
|
|
25144
|
+
/**
|
|
25145
|
+
* Apply a table quick-style preset to every cell's style. Header cells (the
|
|
25146
|
+
* first row, when `firstRowHeader` is set) get the header fill / foreground
|
|
25147
|
+
* + bold; banded body rows get the band background; every cell gets the
|
|
25148
|
+
* preset border colour. Returns a new rows array; does not mutate `td`.
|
|
25149
|
+
*/
|
|
25150
|
+
function applyTableStylePreset(td, preset) {
|
|
25151
|
+
return td.rows.map((row, ri) => ({
|
|
25152
|
+
...row,
|
|
25153
|
+
cells: row.cells.map((cell) => {
|
|
25154
|
+
const isHeader = ri === 0 && Boolean(td.firstRowHeader);
|
|
25155
|
+
const isBand = Boolean(td.bandedRows) && (ri - (td.firstRowHeader ? 1 : 0)) % 2 === 0;
|
|
25156
|
+
const style = {
|
|
25157
|
+
...cell.style,
|
|
25158
|
+
backgroundColor: isHeader ? preset.headerBg : isBand ? preset.bandBg : undefined,
|
|
25159
|
+
color: isHeader ? preset.headerFg : cell.style?.color,
|
|
25160
|
+
bold: isHeader ? true : cell.style?.bold,
|
|
25161
|
+
borderColor: preset.borderColor,
|
|
25162
|
+
};
|
|
25163
|
+
return { ...cell, style };
|
|
25164
|
+
}),
|
|
25165
|
+
}));
|
|
25166
|
+
}
|
|
25051
25167
|
|
|
25052
25168
|
// ---------------------------------------------------------------------------
|
|
25053
25169
|
// Shared class tokens (match InspectorPane)
|
|
@@ -25072,22 +25188,14 @@ const GRADIENT_TYPE_OPTIONS = [
|
|
|
25072
25188
|
// Subset of common patterns shown in the cell-fill pattern picker.
|
|
25073
25189
|
const PATTERN_OPTIONS = OOXML_PATTERN_PRESETS.slice(0, 20);
|
|
25074
25190
|
|
|
25075
|
-
/**
|
|
25076
|
-
* table-resize.ts - framework-agnostic table drag-resize geometry.
|
|
25077
|
-
*
|
|
25078
|
-
* The pure math behind the column / row drag handles: cumulative column-boundary
|
|
25079
|
-
* positions, redistributing two adjacent column proportions by a drag delta
|
|
25080
|
-
* (clamped + renormalised to sum to 1), and clamping a dragged row height.
|
|
25081
|
-
* Extracted from the React viewer's `utils/table-render-resize.tsx` overlay so
|
|
25082
|
-
* every binding drives its own overlay component from one copy of the maths.
|
|
25083
|
-
* No framework imports.
|
|
25084
|
-
*/
|
|
25085
25191
|
/** Minimum proportion a single column may shrink to during a drag. */
|
|
25086
25192
|
const MIN_COLUMN_PROPORTION = 0.03;
|
|
25087
25193
|
/** Minimum height (px) a row may shrink to during a drag. */
|
|
25088
25194
|
const MIN_ROW_HEIGHT = 16;
|
|
25089
25195
|
/** Default row height (px) assumed when an actual measurement is unavailable. */
|
|
25090
25196
|
const DEFAULT_ROW_HEIGHT = 32;
|
|
25197
|
+
/** Minimum proportion a column may be set to via the properties-panel width control. */
|
|
25198
|
+
const MIN_COLUMN_WIDTH_FRACTION = 0.05;
|
|
25091
25199
|
/**
|
|
25092
25200
|
* Cumulative left-edge positions (as percentages, 0-100) of the internal column
|
|
25093
25201
|
* boundaries, i.e. one entry between each adjacent pair of columns. The leading
|
|
@@ -25134,6 +25242,58 @@ function computeResizedColumnWidths(initialWidths, index, deltaProportion) {
|
|
|
25134
25242
|
function computeResizedRowHeight(initialRowHeight, deltaY) {
|
|
25135
25243
|
return Math.round(Math.max(MIN_ROW_HEIGHT, initialRowHeight + deltaY));
|
|
25136
25244
|
}
|
|
25245
|
+
/**
|
|
25246
|
+
* Redistribute column widths when column `index` is set to `newFraction` (a
|
|
25247
|
+
* proportion of the table width, 0-1), scaling every OTHER column
|
|
25248
|
+
* proportionally so their relative ratios to each other are preserved and the
|
|
25249
|
+
* array still sums to 1. This is the table-properties-inspector "set this
|
|
25250
|
+
* column's width" slider/input, shared by every binding so a drag on one
|
|
25251
|
+
* behaves identically to a drag on any other.
|
|
25252
|
+
*
|
|
25253
|
+
* Each non-target column is floored at {@link MIN_COLUMN_WIDTH_FRACTION}
|
|
25254
|
+
* before the whole array is renormalised to sum to 1.
|
|
25255
|
+
*
|
|
25256
|
+
* Returns `widths` unchanged if `index` is out of range.
|
|
25257
|
+
*/
|
|
25258
|
+
function redistributeColumnWidth(widths, index, newFraction) {
|
|
25259
|
+
const oldFraction = widths[index];
|
|
25260
|
+
if (oldFraction === undefined) {
|
|
25261
|
+
return widths;
|
|
25262
|
+
}
|
|
25263
|
+
const diff = newFraction - oldFraction;
|
|
25264
|
+
const next = [...widths];
|
|
25265
|
+
next[index] = newFraction;
|
|
25266
|
+
const othersTotal = 1 - oldFraction;
|
|
25267
|
+
if (othersTotal > 0) {
|
|
25268
|
+
for (let j = 0; j < next.length; j++) {
|
|
25269
|
+
if (j !== index) {
|
|
25270
|
+
next[j] = Math.max(MIN_COLUMN_WIDTH_FRACTION, widths[j] - diff * (widths[j] / othersTotal));
|
|
25271
|
+
}
|
|
25272
|
+
}
|
|
25273
|
+
}
|
|
25274
|
+
const sum = next.reduce((a, b) => a + b, 0);
|
|
25275
|
+
return sum > 0 ? next.map((w) => w / sum) : next;
|
|
25276
|
+
}
|
|
25277
|
+
/** An equal-width column array for `count` columns, each `1 / count` (empty for `count <= 0`). */
|
|
25278
|
+
function evenColumnWidths(count) {
|
|
25279
|
+
if (count <= 0) {
|
|
25280
|
+
return [];
|
|
25281
|
+
}
|
|
25282
|
+
return Array.from({ length: count }, () => 1 / count);
|
|
25283
|
+
}
|
|
25284
|
+
/**
|
|
25285
|
+
* `rows` with a uniform height applied: every row's height is set to the
|
|
25286
|
+
* rounded average of the rows' current heights (falling back to
|
|
25287
|
+
* {@link DEFAULT_ROW_HEIGHT} for a row with no explicit height).
|
|
25288
|
+
*/
|
|
25289
|
+
function evenRowHeights(rows) {
|
|
25290
|
+
const count = rows.length;
|
|
25291
|
+
if (count === 0) {
|
|
25292
|
+
return rows;
|
|
25293
|
+
}
|
|
25294
|
+
const avg = Math.round(rows.reduce((s, r) => s + (r.height ?? DEFAULT_ROW_HEIGHT), 0) / count);
|
|
25295
|
+
return rows.map((r) => ({ ...r, height: avg }));
|
|
25296
|
+
}
|
|
25137
25297
|
|
|
25138
25298
|
/**
|
|
25139
25299
|
* Framework-agnostic helpers for inline (on-canvas) SmartArt node text editing.
|
|
@@ -29597,6 +29757,8 @@ const translationsEn = {
|
|
|
29597
29757
|
'pptx.ribbon.customColour': 'Custom colour...',
|
|
29598
29758
|
'pptx.ribbon.textHighlightColour': 'Text highlight colour',
|
|
29599
29759
|
'pptx.ribbon.highlightColourValue': 'Highlight colour {{color}}',
|
|
29760
|
+
'pptx.ribbon.fillColourValue': 'Fill colour {{color}}',
|
|
29761
|
+
'pptx.ribbon.outlineColourValue': 'Outline colour {{color}}',
|
|
29600
29762
|
'pptx.ribbon.bulletList': 'Bullet list',
|
|
29601
29763
|
'pptx.ribbon.justify': 'Justify',
|
|
29602
29764
|
'pptx.toolbar.findAndReplace': 'Find and replace',
|
|
@@ -50100,6 +50262,165 @@ function buildSmartArtA11y(data) {
|
|
|
50100
50262
|
};
|
|
50101
50263
|
}
|
|
50102
50264
|
|
|
50265
|
+
/**
|
|
50266
|
+
* Per-layout bounds table. Layouts not listed here fall back to
|
|
50267
|
+
* {@link DEFAULT_BOUNDS}.
|
|
50268
|
+
*/
|
|
50269
|
+
const LAYOUT_BOUNDS = {
|
|
50270
|
+
// A Venn diagram is typically drawn with 2-3 overlapping sets; beyond a
|
|
50271
|
+
// handful of circles it becomes unreadable.
|
|
50272
|
+
venn: { min: 2, max: 3 },
|
|
50273
|
+
// A 2x2 matrix has exactly four quadrants.
|
|
50274
|
+
matrix: { min: 4, max: 4 },
|
|
50275
|
+
// Pyramids and funnels need at least two tiers to convey a hierarchy.
|
|
50276
|
+
pyramid: { min: 2, max: 5 },
|
|
50277
|
+
funnel: { min: 2, max: 5 },
|
|
50278
|
+
// A target is a small set of concentric rings.
|
|
50279
|
+
target: { min: 2, max: 5 },
|
|
50280
|
+
// Gears mesh in small clusters.
|
|
50281
|
+
gear: { min: 2, max: 3 },
|
|
50282
|
+
// Cycles need at least three steps to read as a loop.
|
|
50283
|
+
cycle: { min: 3 },
|
|
50284
|
+
// Relationship / process / list / hierarchy / timeline are flexible but
|
|
50285
|
+
// still need at least one node to exist.
|
|
50286
|
+
};
|
|
50287
|
+
// Fallback bounds for any layout without an explicit entry. Kept as a
|
|
50288
|
+
// separate `export const` statement rather than combined with `LAYOUT_BOUNDS`
|
|
50289
|
+
// above: they have different export-ness, and combining them into one
|
|
50290
|
+
// declarator list (with a trailing `export { DEFAULT_BOUNDS };`) breaks the
|
|
50291
|
+
// generated .d.ts, which drops a name introduced only via a re-export.
|
|
50292
|
+
// eslint-disable-next-line one-var -- see comment above
|
|
50293
|
+
const DEFAULT_BOUNDS = { min: 1 };
|
|
50294
|
+
/**
|
|
50295
|
+
* Resolve the node-count bounds for a given layout category.
|
|
50296
|
+
* Returns {@link DEFAULT_BOUNDS} when the layout has no specific table entry.
|
|
50297
|
+
*/
|
|
50298
|
+
function getSmartArtNodeBounds(layout) {
|
|
50299
|
+
if (!layout) {
|
|
50300
|
+
return DEFAULT_BOUNDS;
|
|
50301
|
+
}
|
|
50302
|
+
return LAYOUT_BOUNDS[layout] ?? DEFAULT_BOUNDS;
|
|
50303
|
+
}
|
|
50304
|
+
/** Whether adding another top-level node stays within the layout's max. */
|
|
50305
|
+
function canAddTopLevelNode(layout, topLevelCount) {
|
|
50306
|
+
const { max } = getSmartArtNodeBounds(layout);
|
|
50307
|
+
return max === undefined || topLevelCount < max;
|
|
50308
|
+
}
|
|
50309
|
+
/** Whether removing a top-level node keeps the count at or above the min. */
|
|
50310
|
+
function canRemoveTopLevelNode(layout, topLevelCount) {
|
|
50311
|
+
const { min } = getSmartArtNodeBounds(layout);
|
|
50312
|
+
return topLevelCount > min;
|
|
50313
|
+
}
|
|
50314
|
+
/**
|
|
50315
|
+
* Build a short, human-readable explanation of the bounds for a layout, or
|
|
50316
|
+
* `undefined` when the layout imposes no meaningful limit (min <= 1, no max).
|
|
50317
|
+
* The returned string is intended as a tooltip / hint, not a hard error.
|
|
50318
|
+
*
|
|
50319
|
+
* This text is deliberately framework-neutral English (matching the
|
|
50320
|
+
* `pptx.smartArt.boundsHint*` i18n keys' English fallback text): a binding
|
|
50321
|
+
* that wants a translated string applies its own i18n lookup keyed off
|
|
50322
|
+
* {@link getSmartArtNodeBounds} on top of this, rather than this function
|
|
50323
|
+
* taking a translation dependency.
|
|
50324
|
+
*/
|
|
50325
|
+
function describeSmartArtBounds$1(layout) {
|
|
50326
|
+
const { min, max } = getSmartArtNodeBounds(layout);
|
|
50327
|
+
if (min <= 1 && max === undefined) {
|
|
50328
|
+
return undefined;
|
|
50329
|
+
}
|
|
50330
|
+
if (max === undefined) {
|
|
50331
|
+
return `Works best with at least ${min} items.`;
|
|
50332
|
+
}
|
|
50333
|
+
if (min === max) {
|
|
50334
|
+
return `This layout uses exactly ${max} items.`;
|
|
50335
|
+
}
|
|
50336
|
+
return `Works best with ${min} to ${max} items.`;
|
|
50337
|
+
}
|
|
50338
|
+
|
|
50339
|
+
/** Count of top-level nodes (no parentId) in the data model. */
|
|
50340
|
+
function countTopLevel(data) {
|
|
50341
|
+
return data.nodes.filter((n) => !n.parentId).length;
|
|
50342
|
+
}
|
|
50343
|
+
/**
|
|
50344
|
+
* Enter: insert a sibling node immediately after the current one.
|
|
50345
|
+
*
|
|
50346
|
+
* Returns the new data and the id of the inserted sibling so the caller can
|
|
50347
|
+
* move focus to it.
|
|
50348
|
+
*/
|
|
50349
|
+
function addSiblingAfter(data, nodeId) {
|
|
50350
|
+
const next = addSmartArtNode(data, '', nodeId);
|
|
50351
|
+
if (next === data) {
|
|
50352
|
+
return undefined;
|
|
50353
|
+
}
|
|
50354
|
+
// The inserted node is the one directly after `nodeId` in the new list.
|
|
50355
|
+
// oxlint-disable-next-line eslint/one-var -- depends on `next` past the conditional return above; can't merge.
|
|
50356
|
+
const idx = next.nodes.findIndex((n) => n.id === nodeId), inserted = idx >= 0 ? next.nodes[idx + 1] : undefined;
|
|
50357
|
+
return { data: next, focusNodeId: inserted?.id };
|
|
50358
|
+
}
|
|
50359
|
+
/**
|
|
50360
|
+
* Delete / Backspace on an empty node: remove it (when more than one node
|
|
50361
|
+
* remains). Returns the previous sibling / node id to focus when available.
|
|
50362
|
+
*/
|
|
50363
|
+
function removeEmptyNode(data, nodeId) {
|
|
50364
|
+
if (data.nodes.length <= 1) {
|
|
50365
|
+
return undefined;
|
|
50366
|
+
}
|
|
50367
|
+
const removedIndex = data.nodes.findIndex((n) => n.id === nodeId), next = removeSmartArtNode(data, nodeId);
|
|
50368
|
+
if (next === data) {
|
|
50369
|
+
return undefined;
|
|
50370
|
+
}
|
|
50371
|
+
// Prefer focusing the node that took the removed slot, else the previous one.
|
|
50372
|
+
// oxlint-disable-next-line eslint/one-var -- depends on `next` past the conditional return above; can't merge.
|
|
50373
|
+
const focusNode = next.nodes[Math.max(0, removedIndex - 1)];
|
|
50374
|
+
return { data: next, focusNodeId: focusNode?.id };
|
|
50375
|
+
}
|
|
50376
|
+
/** Tab: demote a node under its preceding sibling (connection-aware). */
|
|
50377
|
+
function demote(data, nodeId) {
|
|
50378
|
+
const next = demoteSmartArtNode(data, nodeId);
|
|
50379
|
+
return next === data ? undefined : next;
|
|
50380
|
+
}
|
|
50381
|
+
/** Shift+Tab: promote a node to its parent's level (connection-aware). */
|
|
50382
|
+
function promote(data, nodeId) {
|
|
50383
|
+
const next = promoteSmartArtNode(data, nodeId);
|
|
50384
|
+
return next === data ? undefined : next;
|
|
50385
|
+
}
|
|
50386
|
+
/** Move a node up (-1) or down (+1) within its sibling group. */
|
|
50387
|
+
function reorder(data, nodeId, direction) {
|
|
50388
|
+
const next = reorderSmartArtNode(data, nodeId, direction);
|
|
50389
|
+
return next === data ? undefined : next;
|
|
50390
|
+
}
|
|
50391
|
+
/** Index of a node among its siblings (nodes sharing its parentId). */
|
|
50392
|
+
function siblingIndex(data, nodeId) {
|
|
50393
|
+
const node = data.nodes.find((n) => n.id === nodeId);
|
|
50394
|
+
if (!node) {
|
|
50395
|
+
return -1;
|
|
50396
|
+
}
|
|
50397
|
+
// oxlint-disable-next-line eslint/one-var -- depends on `node` past the conditional return above; can't merge.
|
|
50398
|
+
const siblings = data.nodes.filter((n) => n.parentId === node.parentId);
|
|
50399
|
+
return siblings.findIndex((n) => n.id === nodeId);
|
|
50400
|
+
}
|
|
50401
|
+
/** Number of siblings (including the node itself) sharing its parentId. */
|
|
50402
|
+
function siblingCount(data, nodeId) {
|
|
50403
|
+
const node = data.nodes.find((n) => n.id === nodeId);
|
|
50404
|
+
if (!node) {
|
|
50405
|
+
return 0;
|
|
50406
|
+
}
|
|
50407
|
+
return data.nodes.filter((n) => n.parentId === node.parentId).length;
|
|
50408
|
+
}
|
|
50409
|
+
/** Connection types that represent the plain parent/child tree we edit inline. */
|
|
50410
|
+
const TREE_CONNECTION_TYPES = new Set(['parOf', 'presParOf', undefined]);
|
|
50411
|
+
/**
|
|
50412
|
+
* Connections beyond the editable parent/child tree (e.g. `sibTrans`,
|
|
50413
|
+
* `presOf`). Surfaced read-only so the user knows the diagram carries
|
|
50414
|
+
* relationships the simple text pane does not edit.
|
|
50415
|
+
*/
|
|
50416
|
+
function classifyExtraConnections(data) {
|
|
50417
|
+
return (data.connections ?? []).filter((c) => !TREE_CONNECTION_TYPES.has(c.type));
|
|
50418
|
+
}
|
|
50419
|
+
/** Count of connections beyond the editable parent/child tree. */
|
|
50420
|
+
function extraConnectionCount(data) {
|
|
50421
|
+
return classifyExtraConnections(data).length;
|
|
50422
|
+
}
|
|
50423
|
+
|
|
50103
50424
|
/**
|
|
50104
50425
|
* Vanilla three.js GLTF/GLB model scene controller (framework-agnostic).
|
|
50105
50426
|
*
|
|
@@ -51090,7 +51411,7 @@ const DEFAULT_FONT_SIZE$1 = 12;
|
|
|
51090
51411
|
* Returns the fill colour of the element's shapeStyle, or a white default.
|
|
51091
51412
|
* Only meaningful for elements that pass `hasShapeProperties`.
|
|
51092
51413
|
*/
|
|
51093
|
-
function fillColorOf(el) {
|
|
51414
|
+
function fillColorOf$1(el) {
|
|
51094
51415
|
if (hasShapeProperties(el)) {
|
|
51095
51416
|
return el.shapeStyle?.fillColor ?? DEFAULT_FILL;
|
|
51096
51417
|
}
|
|
@@ -51151,7 +51472,7 @@ function isUnderline(el) {
|
|
|
51151
51472
|
* Builds a Partial<PptxElement> patch that merges the given changes into the
|
|
51152
51473
|
* element's existing shapeStyle without dropping any other shapeStyle fields.
|
|
51153
51474
|
*/
|
|
51154
|
-
function shapeStylePatch(el, changes) {
|
|
51475
|
+
function shapeStylePatch$1(el, changes) {
|
|
51155
51476
|
const base = hasShapeProperties(el) ? (el.shapeStyle ?? {}) : {};
|
|
51156
51477
|
return {
|
|
51157
51478
|
shapeStyle: {
|
|
@@ -66163,16 +66484,24 @@ function overlayStored(base, stored) {
|
|
|
66163
66484
|
* download, short enough not to leak the object URL.
|
|
66164
66485
|
*/
|
|
66165
66486
|
/** Default fallback name used when a download filename is empty or unusable. */
|
|
66166
|
-
const FALLBACK_DOWNLOAD_NAME = 'presentation.pptx'
|
|
66487
|
+
const FALLBACK_DOWNLOAD_NAME = 'presentation.pptx',
|
|
66488
|
+
/** Fallback stem used by {@link resolveExportBaseName} when no usable name remains. */
|
|
66489
|
+
EXPORT_BASE_NAME_FALLBACK = 'presentation',
|
|
66490
|
+
/**
|
|
66491
|
+
* Extensions {@link resolveExportBaseName} strips by default: every format the
|
|
66492
|
+
* export surface (PNG/PDF/GIF/WebM) or the original source deck (pptx) can be
|
|
66493
|
+
* named after. Bare, lower-case, no leading dot.
|
|
66494
|
+
*/
|
|
66495
|
+
DEFAULT_EXPORT_STRIP_EXTENSIONS = ['pptx', 'pdf', 'png', 'gif', 'webm'],
|
|
66167
66496
|
/** Revoke delay (ms) after triggering a download click. */
|
|
66168
|
-
|
|
66497
|
+
REVOKE_DELAY_MS = 200,
|
|
66169
66498
|
/**
|
|
66170
66499
|
* Revoke delay (ms) after opening a payload in a new tab. Longer than the
|
|
66171
66500
|
* download delay: the new document may still be fetching the object URL (a PDF
|
|
66172
66501
|
* viewer, an image) well after the current task finishes, so we keep the URL
|
|
66173
66502
|
* alive for a minute before releasing it.
|
|
66174
66503
|
*/
|
|
66175
|
-
|
|
66504
|
+
OPEN_REVOKE_DELAY_MS = 60_000;
|
|
66176
66505
|
/**
|
|
66177
66506
|
* Strip control characters, filesystem-reserved characters, and path-traversal
|
|
66178
66507
|
* sequences from a user-supplied download filename. CR/LF in particular can
|
|
@@ -66213,6 +66542,28 @@ function sanitizeDownloadFilename(input) {
|
|
|
66213
66542
|
}
|
|
66214
66543
|
return cleaned;
|
|
66215
66544
|
}
|
|
66545
|
+
/**
|
|
66546
|
+
* Derive the base (extensionless) file name for an export download from a
|
|
66547
|
+
* user-supplied source file name: trims whitespace, strips one trailing
|
|
66548
|
+
* extension from `extensions` (case-insensitive), and falls back to
|
|
66549
|
+
* `presentation` when `sourceName` is `undefined` or the result is empty.
|
|
66550
|
+
*
|
|
66551
|
+
* Used by every binding's export controller to turn a loaded deck's source
|
|
66552
|
+
* name (e.g. `Deck.pptx`) into the stem for its own export downloads (e.g.
|
|
66553
|
+
* `Deck-slide-1.png`, `Deck.gif`) without doubling up the extension.
|
|
66554
|
+
*
|
|
66555
|
+
* @param sourceName - The raw source file name, or `undefined` when none is
|
|
66556
|
+
* known yet.
|
|
66557
|
+
* @param extensions - Extensions to strip, without the leading dot. Defaults
|
|
66558
|
+
* to the full export-surface set (`pptx`, `pdf`, `png`, `gif`, `webm`).
|
|
66559
|
+
*/
|
|
66560
|
+
function resolveExportBaseName(sourceName, extensions = DEFAULT_EXPORT_STRIP_EXTENSIONS) {
|
|
66561
|
+
if (sourceName === undefined) {
|
|
66562
|
+
return EXPORT_BASE_NAME_FALLBACK;
|
|
66563
|
+
}
|
|
66564
|
+
const pattern = new RegExp(`\\.(?:${extensions.join('|')})$`, 'iu'), trimmed = sourceName.trim().replace(pattern, '');
|
|
66565
|
+
return trimmed === '' ? EXPORT_BASE_NAME_FALLBACK : trimmed;
|
|
66566
|
+
}
|
|
66216
66567
|
/**
|
|
66217
66568
|
* Trigger a browser download for a Blob. The `filename` is used verbatim: pass a
|
|
66218
66569
|
* name through {@link sanitizeDownloadFilename} first if it may be hostile.
|
|
@@ -66221,8 +66572,7 @@ function sanitizeDownloadFilename(input) {
|
|
|
66221
66572
|
* @param filename - The suggested download name.
|
|
66222
66573
|
*/
|
|
66223
66574
|
function downloadBlob(blob, filename) {
|
|
66224
|
-
const url = URL.createObjectURL(blob);
|
|
66225
|
-
const anchor = document.createElement('a');
|
|
66575
|
+
const url = URL.createObjectURL(blob), anchor = document.createElement('a');
|
|
66226
66576
|
anchor.href = url;
|
|
66227
66577
|
anchor.download = filename;
|
|
66228
66578
|
document.body.appendChild(anchor);
|
|
@@ -66262,13 +66612,11 @@ function dataUrlToBlob(dataUrl) {
|
|
|
66262
66612
|
if (!match?.groups) {
|
|
66263
66613
|
return undefined;
|
|
66264
66614
|
}
|
|
66265
|
-
|
|
66266
|
-
const isBase64 = Boolean(match.groups.base64);
|
|
66267
|
-
const payload = match.groups.payload ?? '';
|
|
66615
|
+
// eslint-disable-next-line one-var -- separated from `match` above by a guard clause
|
|
66616
|
+
const mime = match.groups.mime || 'application/octet-stream', isBase64 = Boolean(match.groups.base64), payload = match.groups.payload ?? '';
|
|
66268
66617
|
try {
|
|
66269
66618
|
if (isBase64) {
|
|
66270
|
-
const binary = atob(payload);
|
|
66271
|
-
const bytes = new Uint8Array(binary.length);
|
|
66619
|
+
const binary = atob(payload), bytes = new Uint8Array(binary.length);
|
|
66272
66620
|
for (let i = 0; i < binary.length; i++) {
|
|
66273
66621
|
bytes[i] = binary.charCodeAt(i);
|
|
66274
66622
|
}
|
|
@@ -66291,14 +66639,13 @@ function dataUrlToBlob(dataUrl) {
|
|
|
66291
66639
|
* @param url - The payload URL (typically a recovered `data:` URL).
|
|
66292
66640
|
*/
|
|
66293
66641
|
function openUrlInNewTab(url) {
|
|
66294
|
-
const blob = url.startsWith('data:') ? dataUrlToBlob(url) : undefined
|
|
66295
|
-
const target = blob ? URL.createObjectURL(blob) : url;
|
|
66642
|
+
const blob = url.startsWith('data:') ? dataUrlToBlob(url) : undefined, target = blob ? URL.createObjectURL(blob) : url,
|
|
66296
66643
|
// NB: no `noopener` here. A `blob:` object URL is resolved from the opener's
|
|
66297
66644
|
// origin-partitioned blob store, and Chromium refuses to resolve it in the
|
|
66298
66645
|
// disconnected browsing context `noopener` creates (the new tab lands on an
|
|
66299
66646
|
// empty document). We instead sever the child's back-reference to us
|
|
66300
66647
|
// afterwards, which mitigates reverse-tabnabbing without breaking the blob.
|
|
66301
|
-
|
|
66648
|
+
opened = window.open(target, '_blank');
|
|
66302
66649
|
if (opened) {
|
|
66303
66650
|
try {
|
|
66304
66651
|
opened.opener = null;
|
|
@@ -70857,7 +71204,7 @@ function createLocalStorageBackend(namespace) {
|
|
|
70857
71204
|
/** Try IndexedDB first; fall back to localStorage on any failure. */
|
|
70858
71205
|
async function resolveBackend(dbName, namespace) {
|
|
70859
71206
|
try {
|
|
70860
|
-
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-
|
|
71207
|
+
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-BwJthigo.mjs');
|
|
70861
71208
|
const db = await openChatDb(dbName);
|
|
70862
71209
|
return createIdbBackend(db);
|
|
70863
71210
|
}
|
|
@@ -80675,7 +81022,11 @@ class ChartPrimitivesComponent {
|
|
|
80675
81022
|
[attr.data-chart-part]="partRole(prim)"
|
|
80676
81023
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80677
81024
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80678
|
-
|
|
81025
|
+
>
|
|
81026
|
+
@if (asRect(prim).title !== undefined) {
|
|
81027
|
+
<svg:title>{{ asRect(prim).title }}</svg:title>
|
|
81028
|
+
}
|
|
81029
|
+
</svg:rect>
|
|
80679
81030
|
}
|
|
80680
81031
|
@case ('path') {
|
|
80681
81032
|
<svg:path
|
|
@@ -80709,7 +81060,11 @@ class ChartPrimitivesComponent {
|
|
|
80709
81060
|
[attr.data-chart-part]="partRole(prim)"
|
|
80710
81061
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80711
81062
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80712
|
-
|
|
81063
|
+
>
|
|
81064
|
+
@if (asPolyline(prim).title !== undefined) {
|
|
81065
|
+
<svg:title>{{ asPolyline(prim).title }}</svg:title>
|
|
81066
|
+
}
|
|
81067
|
+
</svg:polyline>
|
|
80713
81068
|
}
|
|
80714
81069
|
@case ('circle') {
|
|
80715
81070
|
<svg:circle
|
|
@@ -80721,7 +81076,11 @@ class ChartPrimitivesComponent {
|
|
|
80721
81076
|
[attr.data-chart-part]="partRole(prim)"
|
|
80722
81077
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80723
81078
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80724
|
-
|
|
81079
|
+
>
|
|
81080
|
+
@if (asCircle(prim).title !== undefined) {
|
|
81081
|
+
<svg:title>{{ asCircle(prim).title }}</svg:title>
|
|
81082
|
+
}
|
|
81083
|
+
</svg:circle>
|
|
80725
81084
|
}
|
|
80726
81085
|
@case ('line') {
|
|
80727
81086
|
<svg:line
|
|
@@ -80731,7 +81090,11 @@ class ChartPrimitivesComponent {
|
|
|
80731
81090
|
[attr.y2]="asLine(prim).y2"
|
|
80732
81091
|
[attr.stroke]="asLine(prim).stroke"
|
|
80733
81092
|
[attr.stroke-width]="asLine(prim).strokeWidth"
|
|
80734
|
-
|
|
81093
|
+
>
|
|
81094
|
+
@if (asLine(prim).title !== undefined) {
|
|
81095
|
+
<svg:title>{{ asLine(prim).title }}</svg:title>
|
|
81096
|
+
}
|
|
81097
|
+
</svg:line>
|
|
80735
81098
|
}
|
|
80736
81099
|
@case ('polygon') {
|
|
80737
81100
|
<svg:polygon
|
|
@@ -80744,7 +81107,11 @@ class ChartPrimitivesComponent {
|
|
|
80744
81107
|
[attr.data-chart-part]="partRole(prim)"
|
|
80745
81108
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80746
81109
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80747
|
-
|
|
81110
|
+
>
|
|
81111
|
+
@if (asPolygon(prim).title !== undefined) {
|
|
81112
|
+
<svg:title>{{ asPolygon(prim).title }}</svg:title>
|
|
81113
|
+
}
|
|
81114
|
+
</svg:polygon>
|
|
80748
81115
|
}
|
|
80749
81116
|
@case ('text') {
|
|
80750
81117
|
<svg:text
|
|
@@ -80786,7 +81153,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80786
81153
|
[attr.data-chart-part]="partRole(prim)"
|
|
80787
81154
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80788
81155
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80789
|
-
|
|
81156
|
+
>
|
|
81157
|
+
@if (asRect(prim).title !== undefined) {
|
|
81158
|
+
<svg:title>{{ asRect(prim).title }}</svg:title>
|
|
81159
|
+
}
|
|
81160
|
+
</svg:rect>
|
|
80790
81161
|
}
|
|
80791
81162
|
@case ('path') {
|
|
80792
81163
|
<svg:path
|
|
@@ -80820,7 +81191,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80820
81191
|
[attr.data-chart-part]="partRole(prim)"
|
|
80821
81192
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80822
81193
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80823
|
-
|
|
81194
|
+
>
|
|
81195
|
+
@if (asPolyline(prim).title !== undefined) {
|
|
81196
|
+
<svg:title>{{ asPolyline(prim).title }}</svg:title>
|
|
81197
|
+
}
|
|
81198
|
+
</svg:polyline>
|
|
80824
81199
|
}
|
|
80825
81200
|
@case ('circle') {
|
|
80826
81201
|
<svg:circle
|
|
@@ -80832,7 +81207,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80832
81207
|
[attr.data-chart-part]="partRole(prim)"
|
|
80833
81208
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80834
81209
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80835
|
-
|
|
81210
|
+
>
|
|
81211
|
+
@if (asCircle(prim).title !== undefined) {
|
|
81212
|
+
<svg:title>{{ asCircle(prim).title }}</svg:title>
|
|
81213
|
+
}
|
|
81214
|
+
</svg:circle>
|
|
80836
81215
|
}
|
|
80837
81216
|
@case ('line') {
|
|
80838
81217
|
<svg:line
|
|
@@ -80842,7 +81221,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80842
81221
|
[attr.y2]="asLine(prim).y2"
|
|
80843
81222
|
[attr.stroke]="asLine(prim).stroke"
|
|
80844
81223
|
[attr.stroke-width]="asLine(prim).strokeWidth"
|
|
80845
|
-
|
|
81224
|
+
>
|
|
81225
|
+
@if (asLine(prim).title !== undefined) {
|
|
81226
|
+
<svg:title>{{ asLine(prim).title }}</svg:title>
|
|
81227
|
+
}
|
|
81228
|
+
</svg:line>
|
|
80846
81229
|
}
|
|
80847
81230
|
@case ('polygon') {
|
|
80848
81231
|
<svg:polygon
|
|
@@ -80855,7 +81238,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80855
81238
|
[attr.data-chart-part]="partRole(prim)"
|
|
80856
81239
|
[attr.data-chart-series]="partSeries(prim)"
|
|
80857
81240
|
[attr.data-chart-point]="partPoint(prim)"
|
|
80858
|
-
|
|
81241
|
+
>
|
|
81242
|
+
@if (asPolygon(prim).title !== undefined) {
|
|
81243
|
+
<svg:title>{{ asPolygon(prim).title }}</svg:title>
|
|
81244
|
+
}
|
|
81245
|
+
</svg:polygon>
|
|
80859
81246
|
}
|
|
80860
81247
|
@case ('text') {
|
|
80861
81248
|
<svg:text
|
|
@@ -80904,7 +81291,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
80904
81291
|
* Input: `element` - required `PptxElement` narrowed to `type === 'chart'`
|
|
80905
81292
|
*/
|
|
80906
81293
|
const LEGEND_SWATCH_SIZE = 10;
|
|
80907
|
-
const LEGEND_ITEM_WIDTH = 80;
|
|
80908
81294
|
class ChartRendererComponent {
|
|
80909
81295
|
constructor() {
|
|
80910
81296
|
this.element = input.required(/* @ts-ignore */
|
|
@@ -80914,15 +81300,11 @@ class ChartRendererComponent {
|
|
|
80914
81300
|
this.viewBox = computed(() => `0 0 ${this.vm().svgWidth} ${this.vm().svgHeight}`, /* @ts-ignore */
|
|
80915
81301
|
...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
|
|
80916
81302
|
this.swatchSize = LEGEND_SWATCH_SIZE;
|
|
81303
|
+
this.legendItems = computed(() => computeChartLegendLayout(this.vm()), /* @ts-ignore */
|
|
81304
|
+
...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
|
|
80917
81305
|
}
|
|
80918
|
-
legendTransform(
|
|
80919
|
-
|
|
80920
|
-
const isVertical = v.legendAnchor === 'start';
|
|
80921
|
-
const x = isVertical
|
|
80922
|
-
? v.legendX
|
|
80923
|
-
: v.legendX - (v.legend.length * LEGEND_ITEM_WIDTH) / 2 + index * LEGEND_ITEM_WIDTH;
|
|
80924
|
-
const y = isVertical ? v.legendY + index * 14 : v.legendY;
|
|
80925
|
-
return `translate(${x.toFixed(1)},${y.toFixed(1)})`;
|
|
81306
|
+
legendTransform(item) {
|
|
81307
|
+
return `translate(${item.x.toFixed(1)},${item.y.toFixed(1)})`;
|
|
80926
81308
|
}
|
|
80927
81309
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ChartRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
80928
81310
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ChartRendererComponent, isStandalone: true, selector: "pptx-chart-renderer", inputs: { element: { classPropertyName: "element", publicName: "element", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
@@ -81061,19 +81443,19 @@ class ChartRendererComponent {
|
|
|
81061
81443
|
}
|
|
81062
81444
|
|
|
81063
81445
|
<!-- Legend -->
|
|
81064
|
-
@if (
|
|
81065
|
-
@for (
|
|
81066
|
-
<g [attr.transform]="legendTransform(
|
|
81446
|
+
@if (legendItems().length > 0) {
|
|
81447
|
+
@for (item of legendItems(); track $index) {
|
|
81448
|
+
<g [attr.transform]="legendTransform(item)">
|
|
81067
81449
|
<rect
|
|
81068
81450
|
x="0"
|
|
81069
81451
|
y="-7"
|
|
81070
81452
|
[attr.width]="swatchSize"
|
|
81071
81453
|
[attr.height]="swatchSize"
|
|
81072
81454
|
rx="2"
|
|
81073
|
-
[attr.fill]="
|
|
81455
|
+
[attr.fill]="item.color"
|
|
81074
81456
|
/>
|
|
81075
81457
|
<text [attr.x]="swatchSize + 3" y="3" font-size="9" fill="#475569">
|
|
81076
|
-
{{
|
|
81458
|
+
{{ item.label }}
|
|
81077
81459
|
</text>
|
|
81078
81460
|
</g>
|
|
81079
81461
|
}
|
|
@@ -81224,19 +81606,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
81224
81606
|
}
|
|
81225
81607
|
|
|
81226
81608
|
<!-- Legend -->
|
|
81227
|
-
@if (
|
|
81228
|
-
@for (
|
|
81229
|
-
<g [attr.transform]="legendTransform(
|
|
81609
|
+
@if (legendItems().length > 0) {
|
|
81610
|
+
@for (item of legendItems(); track $index) {
|
|
81611
|
+
<g [attr.transform]="legendTransform(item)">
|
|
81230
81612
|
<rect
|
|
81231
81613
|
x="0"
|
|
81232
81614
|
y="-7"
|
|
81233
81615
|
[attr.width]="swatchSize"
|
|
81234
81616
|
[attr.height]="swatchSize"
|
|
81235
81617
|
rx="2"
|
|
81236
|
-
[attr.fill]="
|
|
81618
|
+
[attr.fill]="item.color"
|
|
81237
81619
|
/>
|
|
81238
81620
|
<text [attr.x]="swatchSize + 3" y="3" font-size="9" fill="#475569">
|
|
81239
|
-
{{
|
|
81621
|
+
{{ item.label }}
|
|
81240
81622
|
</text>
|
|
81241
81623
|
</g>
|
|
81242
81624
|
}
|
|
@@ -88739,36 +89121,40 @@ class MobileBottomBarComponent {
|
|
|
88739
89121
|
/** User tapped the Notes button. */
|
|
88740
89122
|
this.notes = output();
|
|
88741
89123
|
// ── Derived action list ───────────────────────────────────────────────────
|
|
89124
|
+
// Shared `buildBarActions` decides which slots are disabled (no slides
|
|
89125
|
+
// loaded); this binding only maps the resulting descriptor's `key` and
|
|
89126
|
+
// `disabled` onto its own labels, icons and outputs.
|
|
88742
89127
|
this.actions = computed(() => {
|
|
88743
|
-
const
|
|
88744
|
-
|
|
88745
|
-
|
|
89128
|
+
const active = this.activeSheet(), disabledByKey = new Map(buildBarActions({ slideCount: this.slideCount() }).map((descriptor) => [
|
|
89129
|
+
descriptor.key,
|
|
89130
|
+
descriptor.disabled,
|
|
89131
|
+
]));
|
|
88746
89132
|
return [
|
|
88747
89133
|
{
|
|
88748
89134
|
key: 'slides',
|
|
88749
89135
|
labelKey: 'pptx.sections.slides',
|
|
88750
|
-
disabled:
|
|
89136
|
+
disabled: disabledByKey.get('slides') ?? false,
|
|
88751
89137
|
active: active === 'slides',
|
|
88752
89138
|
emit: () => this.openSlides.emit(),
|
|
88753
89139
|
},
|
|
88754
89140
|
{
|
|
88755
89141
|
key: 'insert',
|
|
88756
89142
|
labelKey: 'pptx.editorToolbar.insert',
|
|
88757
|
-
disabled:
|
|
89143
|
+
disabled: disabledByKey.get('insert') ?? false,
|
|
88758
89144
|
active: false,
|
|
88759
89145
|
emit: () => this.insert.emit(),
|
|
88760
89146
|
},
|
|
88761
89147
|
{
|
|
88762
89148
|
key: 'inspector',
|
|
88763
89149
|
labelKey: 'pptx.arrange.format',
|
|
88764
|
-
disabled:
|
|
89150
|
+
disabled: disabledByKey.get('inspector') ?? false,
|
|
88765
89151
|
active: active === 'inspector',
|
|
88766
89152
|
emit: () => this.openFormat.emit(),
|
|
88767
89153
|
},
|
|
88768
89154
|
{
|
|
88769
89155
|
key: 'comments',
|
|
88770
89156
|
labelKey: 'pptx.toolbar.comments',
|
|
88771
|
-
disabled:
|
|
89157
|
+
disabled: disabledByKey.get('comments') ?? false,
|
|
88772
89158
|
active: active === 'comments',
|
|
88773
89159
|
badge: this.commentCount(),
|
|
88774
89160
|
emit: () => this.openComments.emit(),
|
|
@@ -88777,7 +89163,7 @@ class MobileBottomBarComponent {
|
|
|
88777
89163
|
key: 'notes',
|
|
88778
89164
|
labelKey: 'pptx.notes.title',
|
|
88779
89165
|
ariaLabelKey: 'pptx.statusBar.toggleNotes',
|
|
88780
|
-
disabled:
|
|
89166
|
+
disabled: disabledByKey.get('notes') ?? false,
|
|
88781
89167
|
active: active === 'notes',
|
|
88782
89168
|
emit: () => this.notes.emit(),
|
|
88783
89169
|
},
|
|
@@ -104025,9 +104411,138 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104025
104411
|
}]
|
|
104026
104412
|
}], propDecorators: { slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], formatPainterActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatPainterActive", required: false }] }], canActivateFormatPainter: [{ type: i0.Input, args: [{ isSignal: true, alias: "canActivateFormatPainter", required: false }] }], toggleFormatPainter: [{ type: i0.Output, args: ["toggleFormatPainter"] }] } });
|
|
104027
104413
|
|
|
104414
|
+
/**
|
|
104415
|
+
* ribbon-color-popover.component.ts: the hover-reveal swatch popover used for the
|
|
104416
|
+
* Font-colour and Text-highlight pickers in {@link RibbonFontControlsComponent}.
|
|
104417
|
+
* The trigger icon is projected via `<ng-content>`; the swatch grid + custom
|
|
104418
|
+
* colour input are shared. Split out to keep the font controls under the 300-LOC
|
|
104419
|
+
* cap; behaviour and markup are unchanged.
|
|
104420
|
+
*/
|
|
104421
|
+
class RibbonColorPopoverComponent {
|
|
104422
|
+
constructor() {
|
|
104423
|
+
this.current = input('#000000', /* @ts-ignore */
|
|
104424
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
104425
|
+
this.presets = input([], /* @ts-ignore */
|
|
104426
|
+
...(ngDevMode ? [{ debugName: "presets" }] : /* istanbul ignore next */ []));
|
|
104427
|
+
this.disabled = input(false, /* @ts-ignore */
|
|
104428
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
104429
|
+
this.titleKey = input('', /* @ts-ignore */
|
|
104430
|
+
...(ngDevMode ? [{ debugName: "titleKey" }] : /* istanbul ignore next */ []));
|
|
104431
|
+
this.swatchAriaKey = input('', /* @ts-ignore */
|
|
104432
|
+
...(ngDevMode ? [{ debugName: "swatchAriaKey" }] : /* istanbul ignore next */ []));
|
|
104433
|
+
this.pick = output();
|
|
104434
|
+
}
|
|
104435
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonColorPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
104436
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: RibbonColorPopoverComponent, isStandalone: true, selector: "pptx-ribbon-color-popover", inputs: { current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, swatchAriaKey: { classPropertyName: "swatchAriaKey", publicName: "swatchAriaKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick" }, ngImport: i0, template: `
|
|
104437
|
+
<div class="group relative">
|
|
104438
|
+
<button
|
|
104439
|
+
type="button"
|
|
104440
|
+
class="pptx-rb-pill"
|
|
104441
|
+
[disabled]="disabled()"
|
|
104442
|
+
[title]="titleKey() | translate"
|
|
104443
|
+
(mousedown)="$event.preventDefault()"
|
|
104444
|
+
>
|
|
104445
|
+
<ng-content />
|
|
104446
|
+
<span class="-mt-0.5 block h-1 w-4 rounded-sm" [style.background]="current()"></span>
|
|
104447
|
+
</button>
|
|
104448
|
+
<div class="absolute left-0 top-full z-50 hidden pt-1 group-hover:block">
|
|
104449
|
+
<div class="w-36 rounded-lg border border-border bg-card p-2 shadow-2xl">
|
|
104450
|
+
<div class="mb-2 grid grid-cols-5 gap-1.5">
|
|
104451
|
+
@for (c of presets(); track c) {
|
|
104452
|
+
<button
|
|
104453
|
+
type="button"
|
|
104454
|
+
data-pptx-compact
|
|
104455
|
+
class="h-5 w-5 rounded-full border transition-transform hover:scale-125"
|
|
104456
|
+
[ngClass]="
|
|
104457
|
+
current().toLowerCase() === c
|
|
104458
|
+
? 'border-primary ring-1 ring-primary'
|
|
104459
|
+
: 'border-border'
|
|
104460
|
+
"
|
|
104461
|
+
[style.background]="c"
|
|
104462
|
+
[attr.aria-label]="swatchAriaKey() | translate: { color: c }"
|
|
104463
|
+
(mousedown)="$event.preventDefault()"
|
|
104464
|
+
(click)="pick.emit(c)"
|
|
104465
|
+
></button>
|
|
104466
|
+
}
|
|
104467
|
+
</div>
|
|
104468
|
+
<label
|
|
104469
|
+
class="block w-full cursor-pointer py-1 text-center text-[10px] text-muted-foreground transition-colors hover:text-foreground"
|
|
104470
|
+
>
|
|
104471
|
+
{{ 'pptx.ribbon.customColour' | translate }}
|
|
104472
|
+
<input
|
|
104473
|
+
type="color"
|
|
104474
|
+
class="sr-only"
|
|
104475
|
+
[value]="current()"
|
|
104476
|
+
(change)="pick.emit($any($event.target).value)"
|
|
104477
|
+
/>
|
|
104478
|
+
</label>
|
|
104479
|
+
</div>
|
|
104480
|
+
</div>
|
|
104481
|
+
</div>
|
|
104482
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
104483
|
+
}
|
|
104484
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonColorPopoverComponent, decorators: [{
|
|
104485
|
+
type: Component,
|
|
104486
|
+
args: [{
|
|
104487
|
+
selector: 'pptx-ribbon-color-popover',
|
|
104488
|
+
standalone: true,
|
|
104489
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
104490
|
+
imports: [NgClass, TranslatePipe],
|
|
104491
|
+
template: `
|
|
104492
|
+
<div class="group relative">
|
|
104493
|
+
<button
|
|
104494
|
+
type="button"
|
|
104495
|
+
class="pptx-rb-pill"
|
|
104496
|
+
[disabled]="disabled()"
|
|
104497
|
+
[title]="titleKey() | translate"
|
|
104498
|
+
(mousedown)="$event.preventDefault()"
|
|
104499
|
+
>
|
|
104500
|
+
<ng-content />
|
|
104501
|
+
<span class="-mt-0.5 block h-1 w-4 rounded-sm" [style.background]="current()"></span>
|
|
104502
|
+
</button>
|
|
104503
|
+
<div class="absolute left-0 top-full z-50 hidden pt-1 group-hover:block">
|
|
104504
|
+
<div class="w-36 rounded-lg border border-border bg-card p-2 shadow-2xl">
|
|
104505
|
+
<div class="mb-2 grid grid-cols-5 gap-1.5">
|
|
104506
|
+
@for (c of presets(); track c) {
|
|
104507
|
+
<button
|
|
104508
|
+
type="button"
|
|
104509
|
+
data-pptx-compact
|
|
104510
|
+
class="h-5 w-5 rounded-full border transition-transform hover:scale-125"
|
|
104511
|
+
[ngClass]="
|
|
104512
|
+
current().toLowerCase() === c
|
|
104513
|
+
? 'border-primary ring-1 ring-primary'
|
|
104514
|
+
: 'border-border'
|
|
104515
|
+
"
|
|
104516
|
+
[style.background]="c"
|
|
104517
|
+
[attr.aria-label]="swatchAriaKey() | translate: { color: c }"
|
|
104518
|
+
(mousedown)="$event.preventDefault()"
|
|
104519
|
+
(click)="pick.emit(c)"
|
|
104520
|
+
></button>
|
|
104521
|
+
}
|
|
104522
|
+
</div>
|
|
104523
|
+
<label
|
|
104524
|
+
class="block w-full cursor-pointer py-1 text-center text-[10px] text-muted-foreground transition-colors hover:text-foreground"
|
|
104525
|
+
>
|
|
104526
|
+
{{ 'pptx.ribbon.customColour' | translate }}
|
|
104527
|
+
<input
|
|
104528
|
+
type="color"
|
|
104529
|
+
class="sr-only"
|
|
104530
|
+
[value]="current()"
|
|
104531
|
+
(change)="pick.emit($any($event.target).value)"
|
|
104532
|
+
/>
|
|
104533
|
+
</label>
|
|
104534
|
+
</div>
|
|
104535
|
+
</div>
|
|
104536
|
+
</div>
|
|
104537
|
+
`,
|
|
104538
|
+
}]
|
|
104539
|
+
}], propDecorators: { current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], presets: [{ type: i0.Input, args: [{ isSignal: true, alias: "presets", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], swatchAriaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "swatchAriaKey", required: false }] }], pick: [{ type: i0.Output, args: ["pick"] }] } });
|
|
104540
|
+
|
|
104028
104541
|
/**
|
|
104029
104542
|
* ribbon-drawing-group.component.ts: Drawing group for the Home tab ribbon.
|
|
104030
|
-
* Provides shape insertion, layer arrangement, and shape
|
|
104543
|
+
* Provides shape insertion, layer arrangement, and shape Fill/Outline colour
|
|
104544
|
+
* pickers (Shape Effects stays a disabled placeholder: it is genuinely
|
|
104545
|
+
* unimplemented in every binding).
|
|
104031
104546
|
*
|
|
104032
104547
|
* The Arrange menu also carries Group / Ungroup, which is where PowerPoint
|
|
104033
104548
|
* puts them (Home > Drawing > Arrange) and where the other bindings expose
|
|
@@ -104040,9 +104555,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104040
104555
|
* The picker lists the first 12 entries of the shared preset catalogue
|
|
104041
104556
|
* ({@link SHAPE_PRESET_DEFS}), so the geometry ids are valid OOXML
|
|
104042
104557
|
* `a:prstGeom` values shared with every binding.
|
|
104558
|
+
*
|
|
104559
|
+
* Fill/Outline commit through `pptx-viewer-shared`'s `shapeFillChange` /
|
|
104560
|
+
* `shapeOutlineChange` decision functions (the same ones React's and Vue's
|
|
104561
|
+
* DrawingGroup use), so the two keys written (`fillColor`+`fillMode` /
|
|
104562
|
+
* `strokeColor`) can't drift from the other bindings a third time. The
|
|
104563
|
+
* buttons used to be `disabled` placeholders that did nothing when clicked.
|
|
104043
104564
|
*/
|
|
104044
|
-
/**
|
|
104045
|
-
|
|
104565
|
+
/**
|
|
104566
|
+
* The quick "top shapes" row shared with React's toolbar (first 12 presets),
|
|
104567
|
+
* and the fallbacks the Fill/Outline swatch dots show when the selection has
|
|
104568
|
+
* none set (declared together: oxlint's `one-var` wants one `const` statement
|
|
104569
|
+
* per top-level scope).
|
|
104570
|
+
*/
|
|
104571
|
+
const TOP_SHAPES = SHAPE_PRESET_DEFS.slice(0, 12), DEFAULT_FILL_COLOR = '#ffffff', DEFAULT_OUTLINE_COLOR = '#000000';
|
|
104572
|
+
/** Fill/Outline only apply to an editable, selected shape-like element. */
|
|
104573
|
+
function canFormatShapeSelection(canEdit, element) {
|
|
104574
|
+
return canEdit && element !== null && hasShapeProperties(element);
|
|
104575
|
+
}
|
|
104576
|
+
/** The colour the Fill swatch dot shows for the current selection. */
|
|
104577
|
+
function fillColorOf(element) {
|
|
104578
|
+
if (element === null || !hasShapeProperties(element)) {
|
|
104579
|
+
return DEFAULT_FILL_COLOR;
|
|
104580
|
+
}
|
|
104581
|
+
return element.shapeStyle?.fillColor ?? DEFAULT_FILL_COLOR;
|
|
104582
|
+
}
|
|
104583
|
+
/** The colour the Outline swatch dot shows for the current selection. */
|
|
104584
|
+
function outlineColorOf(element) {
|
|
104585
|
+
if (element === null || !hasShapeProperties(element)) {
|
|
104586
|
+
return DEFAULT_OUTLINE_COLOR;
|
|
104587
|
+
}
|
|
104588
|
+
return element.shapeStyle?.strokeColor ?? DEFAULT_OUTLINE_COLOR;
|
|
104589
|
+
}
|
|
104590
|
+
/**
|
|
104591
|
+
* The element patch a Fill/Outline swatch pick commits, or `undefined` when
|
|
104592
|
+
* the selection has no shape style to patch. Merges into the EXISTING
|
|
104593
|
+
* `shapeStyle` (not a replace) so picking a fill colour never clears an
|
|
104594
|
+
* already-set outline, and vice versa.
|
|
104595
|
+
*/
|
|
104596
|
+
function shapeStylePatch(element, style) {
|
|
104597
|
+
if (element === null || !hasShapeProperties(element)) {
|
|
104598
|
+
return undefined;
|
|
104599
|
+
}
|
|
104600
|
+
return { shapeStyle: { ...element.shapeStyle, ...style } };
|
|
104601
|
+
}
|
|
104046
104602
|
class RibbonDrawingGroupComponent {
|
|
104047
104603
|
constructor() {
|
|
104048
104604
|
this.editor = inject(EditorStateService);
|
|
@@ -104051,11 +104607,36 @@ class RibbonDrawingGroupComponent {
|
|
|
104051
104607
|
/** Index of the active slide (insertion target). */
|
|
104052
104608
|
this.slideIndex = input(0, /* @ts-ignore */
|
|
104053
104609
|
...(ngDevMode ? [{ debugName: "slideIndex" }] : /* istanbul ignore next */ []));
|
|
104610
|
+
this.selectedElement = input(null, /* @ts-ignore */
|
|
104611
|
+
...(ngDevMode ? [{ debugName: "selectedElement" }] : /* istanbul ignore next */ []));
|
|
104054
104612
|
this.shapes = TOP_SHAPES;
|
|
104055
104613
|
this.shapesOpen = signal(false, /* @ts-ignore */
|
|
104056
104614
|
...(ngDevMode ? [{ debugName: "shapesOpen" }] : /* istanbul ignore next */ []));
|
|
104057
104615
|
this.arrangeOpen = signal(false, /* @ts-ignore */
|
|
104058
104616
|
...(ngDevMode ? [{ debugName: "arrangeOpen" }] : /* istanbul ignore next */ []));
|
|
104617
|
+
this.swatches = RIBBON_SHAPE_SWATCHES;
|
|
104618
|
+
this.canFormatShape = computed(() => canFormatShapeSelection(this.canEdit(), this.selectedElement()), /* @ts-ignore */
|
|
104619
|
+
...(ngDevMode ? [{ debugName: "canFormatShape" }] : /* istanbul ignore next */ []));
|
|
104620
|
+
this.fillColor = computed(() => fillColorOf(this.selectedElement()), /* @ts-ignore */
|
|
104621
|
+
...(ngDevMode ? [{ debugName: "fillColor" }] : /* istanbul ignore next */ []));
|
|
104622
|
+
this.outlineColor = computed(() => outlineColorOf(this.selectedElement()), /* @ts-ignore */
|
|
104623
|
+
...(ngDevMode ? [{ debugName: "outlineColor" }] : /* istanbul ignore next */ []));
|
|
104624
|
+
}
|
|
104625
|
+
/** Commit a picked Fill swatch through the shared decision function. */
|
|
104626
|
+
onFill(color) {
|
|
104627
|
+
this.patchShapeStyle(shapeFillChange(color));
|
|
104628
|
+
}
|
|
104629
|
+
/** Commit a picked Outline swatch through the shared decision function. */
|
|
104630
|
+
onOutline(color) {
|
|
104631
|
+
this.patchShapeStyle(shapeOutlineChange(color));
|
|
104632
|
+
}
|
|
104633
|
+
/** Merge a Fill/Outline patch into the selection's shape style, if it has one. */
|
|
104634
|
+
patchShapeStyle(style) {
|
|
104635
|
+
const el = this.selectedElement(), patch = shapeStylePatch(el, style);
|
|
104636
|
+
if (!this.canFormatShape() || el === null || !patch) {
|
|
104637
|
+
return;
|
|
104638
|
+
}
|
|
104639
|
+
this.editor.updateElement(this.slideIndex(), el.id, patch);
|
|
104059
104640
|
}
|
|
104060
104641
|
/** Insert the picked preset immediately (selects it and records history). */
|
|
104061
104642
|
onShapeSelect(shape) {
|
|
@@ -104088,7 +104669,7 @@ class RibbonDrawingGroupComponent {
|
|
|
104088
104669
|
this.editor.sendSelectedToBack(this.slideIndex());
|
|
104089
104670
|
}
|
|
104090
104671
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonDrawingGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
104091
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: RibbonDrawingGroupComponent, isStandalone: true, selector: "pptx-ribbon-drawing-group", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
|
|
104672
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: RibbonDrawingGroupComponent, isStandalone: true, selector: "pptx-ribbon-drawing-group", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, slideIndex: { classPropertyName: "slideIndex", publicName: "slideIndex", isSignal: true, isRequired: false, transformFunction: null }, selectedElement: { classPropertyName: "selectedElement", publicName: "selectedElement", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "contents" }, ngImport: i0, template: `
|
|
104092
104673
|
<!-- Shapes -->
|
|
104093
104674
|
<div class="flex flex-col items-center gap-0.5">
|
|
104094
104675
|
<div class="pptx-rb-grp">
|
|
@@ -104185,16 +104766,36 @@ class RibbonDrawingGroupComponent {
|
|
|
104185
104766
|
</span>
|
|
104186
104767
|
</div>
|
|
104187
104768
|
<span class="pptx-rb-sep"></span>
|
|
104188
|
-
<!-- Shape formatting
|
|
104769
|
+
<!-- Shape formatting -->
|
|
104189
104770
|
<div class="flex flex-col items-center gap-0.5">
|
|
104190
|
-
<div class="
|
|
104191
|
-
<
|
|
104771
|
+
<div class="flex items-center gap-1">
|
|
104772
|
+
<pptx-ribbon-color-popover
|
|
104773
|
+
[current]="fillColor()"
|
|
104774
|
+
[presets]="swatches"
|
|
104775
|
+
[disabled]="!canFormatShape()"
|
|
104776
|
+
titleKey="pptx.drawing.shapeFill"
|
|
104777
|
+
swatchAriaKey="pptx.ribbon.fillColourValue"
|
|
104778
|
+
(pick)="onFill($event)"
|
|
104779
|
+
>
|
|
104192
104780
|
{{ 'pptx.drawing.shapeFill' | translate }}
|
|
104193
|
-
</
|
|
104194
|
-
<
|
|
104781
|
+
</pptx-ribbon-color-popover>
|
|
104782
|
+
<pptx-ribbon-color-popover
|
|
104783
|
+
[current]="outlineColor()"
|
|
104784
|
+
[presets]="swatches"
|
|
104785
|
+
[disabled]="!canFormatShape()"
|
|
104786
|
+
titleKey="pptx.drawing.shapeOutline"
|
|
104787
|
+
swatchAriaKey="pptx.ribbon.outlineColourValue"
|
|
104788
|
+
(pick)="onOutline($event)"
|
|
104789
|
+
>
|
|
104195
104790
|
{{ 'pptx.drawing.shapeOutline' | translate }}
|
|
104196
|
-
</
|
|
104197
|
-
|
|
104791
|
+
</pptx-ribbon-color-popover>
|
|
104792
|
+
<!-- Shape Effects: genuinely unimplemented (React/Vue parity), left as a placeholder. -->
|
|
104793
|
+
<button
|
|
104794
|
+
type="button"
|
|
104795
|
+
class="pptx-rb-gb"
|
|
104796
|
+
disabled
|
|
104797
|
+
[title]="'pptx.drawing.shapeEffectsUnavailable' | translate"
|
|
104798
|
+
>
|
|
104198
104799
|
{{ 'pptx.drawing.shapeEffectsUnavailable' | translate }}
|
|
104199
104800
|
</button>
|
|
104200
104801
|
</div>
|
|
@@ -104202,7 +104803,7 @@ class RibbonDrawingGroupComponent {
|
|
|
104202
104803
|
{{ 'pptx.ribbon.groupShapeStyles' | translate }}
|
|
104203
104804
|
</span>
|
|
104204
104805
|
</div>
|
|
104205
|
-
`, isInline: true, dependencies: [{ kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
104806
|
+
`, isInline: true, dependencies: [{ kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: RibbonColorPopoverComponent, selector: "pptx-ribbon-color-popover", inputs: ["current", "presets", "disabled", "titleKey", "swatchAriaKey"], outputs: ["pick"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
104206
104807
|
}
|
|
104207
104808
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonDrawingGroupComponent, decorators: [{
|
|
104208
104809
|
type: Component,
|
|
@@ -104211,7 +104812,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104211
104812
|
standalone: true,
|
|
104212
104813
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
104213
104814
|
host: { class: 'contents' },
|
|
104214
|
-
imports: [TranslatePipe, LucideChevronDown],
|
|
104815
|
+
imports: [TranslatePipe, LucideChevronDown, RibbonColorPopoverComponent],
|
|
104215
104816
|
template: `
|
|
104216
104817
|
<!-- Shapes -->
|
|
104217
104818
|
<div class="flex flex-col items-center gap-0.5">
|
|
@@ -104309,16 +104910,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104309
104910
|
</span>
|
|
104310
104911
|
</div>
|
|
104311
104912
|
<span class="pptx-rb-sep"></span>
|
|
104312
|
-
<!-- Shape formatting
|
|
104913
|
+
<!-- Shape formatting -->
|
|
104313
104914
|
<div class="flex flex-col items-center gap-0.5">
|
|
104314
|
-
<div class="
|
|
104315
|
-
<
|
|
104915
|
+
<div class="flex items-center gap-1">
|
|
104916
|
+
<pptx-ribbon-color-popover
|
|
104917
|
+
[current]="fillColor()"
|
|
104918
|
+
[presets]="swatches"
|
|
104919
|
+
[disabled]="!canFormatShape()"
|
|
104920
|
+
titleKey="pptx.drawing.shapeFill"
|
|
104921
|
+
swatchAriaKey="pptx.ribbon.fillColourValue"
|
|
104922
|
+
(pick)="onFill($event)"
|
|
104923
|
+
>
|
|
104316
104924
|
{{ 'pptx.drawing.shapeFill' | translate }}
|
|
104317
|
-
</
|
|
104318
|
-
<
|
|
104925
|
+
</pptx-ribbon-color-popover>
|
|
104926
|
+
<pptx-ribbon-color-popover
|
|
104927
|
+
[current]="outlineColor()"
|
|
104928
|
+
[presets]="swatches"
|
|
104929
|
+
[disabled]="!canFormatShape()"
|
|
104930
|
+
titleKey="pptx.drawing.shapeOutline"
|
|
104931
|
+
swatchAriaKey="pptx.ribbon.outlineColourValue"
|
|
104932
|
+
(pick)="onOutline($event)"
|
|
104933
|
+
>
|
|
104319
104934
|
{{ 'pptx.drawing.shapeOutline' | translate }}
|
|
104320
|
-
</
|
|
104321
|
-
|
|
104935
|
+
</pptx-ribbon-color-popover>
|
|
104936
|
+
<!-- Shape Effects: genuinely unimplemented (React/Vue parity), left as a placeholder. -->
|
|
104937
|
+
<button
|
|
104938
|
+
type="button"
|
|
104939
|
+
class="pptx-rb-gb"
|
|
104940
|
+
disabled
|
|
104941
|
+
[title]="'pptx.drawing.shapeEffectsUnavailable' | translate"
|
|
104942
|
+
>
|
|
104322
104943
|
{{ 'pptx.drawing.shapeEffectsUnavailable' | translate }}
|
|
104323
104944
|
</button>
|
|
104324
104945
|
</div>
|
|
@@ -104328,10 +104949,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104328
104949
|
</div>
|
|
104329
104950
|
`,
|
|
104330
104951
|
}]
|
|
104331
|
-
}], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }] } });
|
|
104952
|
+
}], 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 }] }] } });
|
|
104332
104953
|
|
|
104333
104954
|
// Generated by scripts/inline-shared.mjs from package.json. Do not edit.
|
|
104334
|
-
const PPTX_ANGULAR_VIEWER_VERSION = "2.18.
|
|
104955
|
+
const PPTX_ANGULAR_VIEWER_VERSION = "2.18.7";
|
|
104335
104956
|
|
|
104336
104957
|
/**
|
|
104337
104958
|
* account-page.component.ts: File > Account content.
|
|
@@ -104682,133 +105303,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
104682
105303
|
args: [{ selector: 'pptx-ribbon-file-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'contents' }, imports: [BackstageNavIconComponent, AccountPageComponent, TranslatePipe], template: "<div\n\tclass=\"bs\"\n\trole=\"dialog\"\n\taria-modal=\"true\"\n\t[attr.aria-label]=\"'pptx.backstage.title' | translate\"\n>\n\t<aside>\n\t\t<button\n\t\t\tclass=\"back\"\n\t\t\ttype=\"button\"\n\t\t\t[attr.aria-label]=\"'pptx.backstage.back' | translate\"\n\t\t\t(click)=\"close.emit()\"\n\t\t>\n\t\t\t<pptx-backstage-nav-icon page=\"back\" />\n\t\t</button>\n\t\t<nav>\n\t\t\t@for (item of mainNav(); track item.id) {\n\t\t\t\t<button type=\"button\" [class.active]=\"page() === item.id\" (click)=\"selectPage(item.id)\">\n\t\t\t\t\t<span><pptx-backstage-nav-icon [page]=\"item.id\" /></span>{{ item.labelKey | translate }}\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<i></i>\n\t\t\t@for (item of footerNav; track item.id) {\n\t\t\t\t<button type=\"button\" [class.active]=\"page() === item.id\" (click)=\"selectPage(item.id)\">\n\t\t\t\t\t<span><pptx-backstage-nav-icon [page]=\"item.id\" /></span>{{ item.labelKey | translate }}\n\t\t\t\t</button>\n\t\t\t}\n\t\t</nav>\n\t</aside>\n\t<main>\n\t\t<h1>{{ (page() === 'home' ? 'pptx.backstage.greeting' : titleKey()) | translate }}</h1>\n\t\t@if (page() === 'home' || page() === 'new') {\n\t\t\t<h2>{{ 'pptx.backstage.newHeading' | translate }}</h2>\n\t\t\t<div class=\"templates\">\n\t\t\t\t@for (template of templates; track template.id) {\n\t\t\t\t\t<button type=\"button\" (click)=\"create(template.id)\">\n\t\t\t\t\t\t<b [style.background]=\"template.preview\"></b\n\t\t\t\t\t\t><strong>{{ template.nameKey | translate }}</strong\n\t\t\t\t\t\t><small>{{ template.descriptionKey | translate }}</small>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (page() === 'home' || page() === 'open') {\n\t\t\t<input\n\t\t\t\tclass=\"search\"\n\t\t\t\ttype=\"search\"\n\t\t\t\t[attr.placeholder]=\"'pptx.backstage.searchPlaceholder' | translate\"\n\t\t\t\t[value]=\"query()\"\n\t\t\t\t(input)=\"query.set($any($event.target).value)\"\n\t\t\t/>\n\t\t\t@if (page() === 'open') {\n\t\t\t\t<button class=\"primary\" type=\"button\" (click)=\"run(openFile)\">\n\t\t\t\t\t{{ 'pptx.backstage.browseDevice' | translate }}\n\t\t\t\t</button>\n\t\t\t}\n\t\t\t<h2>{{ 'pptx.backstage.recentHeading' | translate }}</h2>\n\t\t\t<div class=\"recent\">\n\t\t\t\t<header>\n\t\t\t\t\t<span>{{ 'pptx.backstage.columnName' | translate }}</span\n\t\t\t\t\t><span>{{ 'pptx.backstage.columnModified' | translate }}</span\n\t\t\t\t\t><span>{{ 'pptx.backstage.columnSize' | translate }}</span>\n\t\t\t\t</header>\n\t\t\t\t@for (file of visibleRecent(); track file.key) {\n\t\t\t\t\t<button type=\"button\" (click)=\"openRecent(file.key)\">\n\t\t\t\t\t\t<span class=\"name\"\n\t\t\t\t\t\t\t><b>P</b\n\t\t\t\t\t\t\t><span\n\t\t\t\t\t\t\t\t><strong>{{ file.name }}</strong\n\t\t\t\t\t\t\t\t><small>{{ file.location }}</small></span\n\t\t\t\t\t\t\t></span\n\t\t\t\t\t\t><span>{{ date(file.timestamp) }}</span\n\t\t\t\t\t\t><span>{{ size(file.size) }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t} @empty {\n\t\t\t\t\t<p>{{ 'pptx.backstage.noRecent' | translate }}</p>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (actions().length) {\n\t\t\t<div class=\"actions\">\n\t\t\t\t@for (action of actions(); track action.titleKey) {\n\t\t\t\t\t<button type=\"button\" (click)=\"run(action.event)\">\n\t\t\t\t\t\t<b>{{ action.icon }}</b\n\t\t\t\t\t\t><span\n\t\t\t\t\t\t\t><strong>{{ action.titleKey | translate }}</strong\n\t\t\t\t\t\t\t><small>{{ action.bodyKey | translate }}</small></span\n\t\t\t\t\t\t>\n\t\t\t\t\t</button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t\t@if (page() === 'account') {\n\t\t\t<pptx-account-page [accountAuth]=\"accountAuth()\" />\n\t\t}\n\t\t@if (page() === 'options') {\n\t\t\t<section class=\"card\">\n\t\t\t\t<b class=\"avatar\">\u2699</b>\n\t\t\t\t<h2>{{ 'pptx.backstage.optionsTitle' | translate }}</h2>\n\t\t\t\t<p>{{ 'pptx.backstage.optionsBody' | translate }}</p>\n\t\t\t\t<button class=\"primary\" type=\"button\" (click)=\"run(options)\">\n\t\t\t\t\t{{ 'pptx.backstage.openOptions' | translate }}\n\t\t\t\t</button>\n\t\t\t</section>\n\t\t}\n\t\t<footer>\n\t\t\t{{ fileName() || ('pptx.backstage.untitled' | translate) }} \u00B7\n\t\t\t{{ 'pptx.backstage.savedToBrowser' | translate }}\n\t\t</footer>\n\t</main>\n</div>\n", styles: [":host{font-family:Aptos,Segoe UI,sans-serif}.bs{position:fixed;inset:0;z-index:200;display:flex;background:var(--pptx-background, #fafafa);color:var(--pptx-foreground, #242424)}.bs aside{display:flex;width:148px;flex:none;flex-direction:column;border-right:1px solid var(--pptx-border, #d7d7d7);background:var(--pptx-secondary, #f5eee9)}.back{height:48px;border:0;border-bottom:1px solid var(--pptx-border, #ddd);background:none;color:inherit;font-size:22px}.back:hover,nav button:hover,.recent>button:hover{background:var(--pptx-accent, #eadfd8)}nav{display:flex;min-height:0;flex:1;flex-direction:column;padding:8px 0}nav i{flex:1}nav button{display:flex;min-height:40px;align-items:center;gap:12px;padding:0 16px;border:0;border-left:2px solid transparent;background:none;color:inherit;font-size:12px;text-align:left}nav button span{width:16px;font-size:16px;text-align:center}nav button.active{border-left-color:var(--pptx-primary, #c43e1c);background:var(--pptx-card, #fff);color:var(--pptx-primary, #c43e1c)}main{min-width:0;flex:1;overflow:auto;padding:20px clamp(32px,4vw,72px)}h1{margin:0;font-size:24px;font-weight:600}h2{margin:28px 0 18px;font-size:17px}.templates{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:24px}.templates button{border:0;background:none;color:inherit;text-align:left}.templates button>b{display:block;aspect-ratio:16/9;border:1px solid var(--pptx-border, #ccc);box-shadow:0 1px 2px #0002;transition:.15s}.templates button:hover>b{transform:translateY(-2px);border-color:var(--pptx-primary, #c43e1c);box-shadow:0 7px 18px #0002}.templates strong,.templates small{display:block;overflow:hidden;margin-top:8px;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.templates small{margin-top:2px;color:var(--pptx-muted-foreground, #777);font-size:10px}.search{display:block;width:min(540px,100%);height:40px;margin-top:32px;padding:0 14px;border:1px solid var(--pptx-input, #888);background:var(--pptx-card, #fff);color:var(--pptx-card-foreground, #242424)}.search:focus{border-color:var(--pptx-ring, #c43e1c);outline:none}.primary{margin-top:16px;padding:10px 20px;border:0;background:var(--pptx-primary, #c43e1c);color:var(--pptx-primary-foreground, #fff);font-weight:600}.recent{border-top:1px solid var(--pptx-border, #ddd)}.recent header,.recent>button{display:grid;grid-template-columns:1fr 120px 90px;align-items:center;padding:10px 12px}.recent header{color:var(--pptx-muted-foreground, #666);font-size:11px;font-weight:600}.recent>button{width:100%;border:0;border-top:1px solid var(--pptx-border, #e5e5e5);background:none;color:inherit;font-size:11px;text-align:left}.name{display:flex;min-width:0;align-items:center;gap:12px}.name>b{display:grid;width:32px;height:32px;place-items:center;background:var(--pptx-primary, #d24726);color:var(--pptx-primary-foreground, #fff)}.name strong,.name small{display:block;overflow:hidden;font-size:13px;font-weight:400;text-overflow:ellipsis;white-space:nowrap}.name small,.recent p{color:var(--pptx-muted-foreground, #666);font-size:11px}.recent p{text-align:center}.actions{display:grid;max-width:900px;grid-template-columns:1fr 1fr;gap:20px;margin-top:32px}.actions button{display:flex;min-height:112px;gap:16px;padding:20px;border:1px solid var(--pptx-border, #ddd);background:var(--pptx-card, #fff);color:var(--pptx-card-foreground, #242424);text-align:left}.actions button:hover{border-color:var(--pptx-primary, #c43e1c);box-shadow:0 5px 15px #0002}.actions button>b{display:grid;width:40px;height:40px;flex:none;place-items:center;background:var(--pptx-accent, #fbe9e3);color:var(--pptx-primary, #c43e1c)}.actions strong,.actions small{display:block;font-size:15px}.actions small{margin-top:7px;color:var(--pptx-muted-foreground, #666);font-size:12px;line-height:1.5}.card{max-width:760px;margin-top:32px;padding:28px;border:1px solid var(--pptx-border, #ddd);background:var(--pptx-card, #fff);color:var(--pptx-card-foreground, #242424)}.avatar{display:grid;width:56px;height:56px;place-items:center;border-radius:50%;background:var(--pptx-primary, #c43e1c);color:var(--pptx-primary-foreground, #fff);font-size:20px}.card p,footer{color:var(--pptx-muted-foreground, #666)}.card p{line-height:1.6}footer{margin-top:48px;font-size:11px}@media(max-width:640px),(pointer:coarse){.bs{flex-direction:column}.bs aside{width:100%;flex-direction:row;align-items:center;overflow-x:auto;border-right:0;border-bottom:1px solid var(--pptx-border, #d7d7d7)}.back{min-width:48px;flex-shrink:0;border-bottom:0;border-right:1px solid var(--pptx-border, #ddd)}nav{flex-direction:row;align-items:center;padding:0}nav i{display:none}nav button{flex-shrink:0;white-space:nowrap;border-left:0;border-bottom:2px solid transparent;padding:0 12px}nav button.active{border-left-color:transparent;border-bottom-color:var(--pptx-primary, #c43e1c)}main{padding:16px}.actions{grid-template-columns:1fr}.recent header,.recent>button{grid-template-columns:1fr 90px}.recent header span:last-child,.recent>button>span:last-child{display:none}}\n"] }]
|
|
104683
105304
|
}], ctorParameters: () => [], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], slideCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideCount", required: false }] }], exporting: [{ type: i0.Input, args: [{ isSignal: true, alias: "exporting", required: false }] }], hasMacros: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMacros", required: false }] }], hiddenActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenActions", required: false }] }], accountAuth: [{ type: i0.Input, args: [{ isSignal: true, alias: "accountAuth", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], createPresentation: [{ type: i0.Output, args: ["createPresentation"] }], openFile: [{ type: i0.Output, args: ["openFile"] }], openRecentFile: [{ type: i0.Output, args: ["openRecentFile"] }], save: [{ type: i0.Output, args: ["save"] }], savePpsx: [{ type: i0.Output, args: ["savePpsx"] }], savePptm: [{ type: i0.Output, args: ["savePptm"] }], packageForSharing: [{ type: i0.Output, args: ["packageForSharing"] }], exportPng: [{ type: i0.Output, args: ["exportPng"] }], exportPdf: [{ type: i0.Output, args: ["exportPdf"] }], exportGif: [{ type: i0.Output, args: ["exportGif"] }], exportVideo: [{ type: i0.Output, args: ["exportVideo"] }], exportJson: [{ type: i0.Output, args: ["exportJson"] }], copySlideAsImage: [{ type: i0.Output, args: ["copySlideAsImage"] }], print: [{ type: i0.Output, args: ["print"] }], info: [{ type: i0.Output, args: ["info"] }], signatures: [{ type: i0.Output, args: ["signatures"] }], replace: [{ type: i0.Output, args: ["replace"] }], openPassword: [{ type: i0.Output, args: ["openPassword"] }], openFontEmbedding: [{ type: i0.Output, args: ["openFontEmbedding"] }], openVersionHistory: [{ type: i0.Output, args: ["openVersionHistory"] }], share: [{ type: i0.Output, args: ["share"] }], options: [{ type: i0.Output, args: ["options"] }] } });
|
|
104684
105305
|
|
|
104685
|
-
/**
|
|
104686
|
-
* ribbon-color-popover.component.ts: the hover-reveal swatch popover used for the
|
|
104687
|
-
* Font-colour and Text-highlight pickers in {@link RibbonFontControlsComponent}.
|
|
104688
|
-
* The trigger icon is projected via `<ng-content>`; the swatch grid + custom
|
|
104689
|
-
* colour input are shared. Split out to keep the font controls under the 300-LOC
|
|
104690
|
-
* cap; behaviour and markup are unchanged.
|
|
104691
|
-
*/
|
|
104692
|
-
class RibbonColorPopoverComponent {
|
|
104693
|
-
constructor() {
|
|
104694
|
-
this.current = input('#000000', /* @ts-ignore */
|
|
104695
|
-
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
104696
|
-
this.presets = input([], /* @ts-ignore */
|
|
104697
|
-
...(ngDevMode ? [{ debugName: "presets" }] : /* istanbul ignore next */ []));
|
|
104698
|
-
this.disabled = input(false, /* @ts-ignore */
|
|
104699
|
-
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
104700
|
-
this.titleKey = input('', /* @ts-ignore */
|
|
104701
|
-
...(ngDevMode ? [{ debugName: "titleKey" }] : /* istanbul ignore next */ []));
|
|
104702
|
-
this.swatchAriaKey = input('', /* @ts-ignore */
|
|
104703
|
-
...(ngDevMode ? [{ debugName: "swatchAriaKey" }] : /* istanbul ignore next */ []));
|
|
104704
|
-
this.pick = output();
|
|
104705
|
-
}
|
|
104706
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonColorPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
104707
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: RibbonColorPopoverComponent, isStandalone: true, selector: "pptx-ribbon-color-popover", inputs: { current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, swatchAriaKey: { classPropertyName: "swatchAriaKey", publicName: "swatchAriaKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick" }, ngImport: i0, template: `
|
|
104708
|
-
<div class="group relative">
|
|
104709
|
-
<button
|
|
104710
|
-
type="button"
|
|
104711
|
-
class="pptx-rb-pill"
|
|
104712
|
-
[disabled]="disabled()"
|
|
104713
|
-
[title]="titleKey() | translate"
|
|
104714
|
-
(mousedown)="$event.preventDefault()"
|
|
104715
|
-
>
|
|
104716
|
-
<ng-content />
|
|
104717
|
-
<span class="-mt-0.5 block h-1 w-4 rounded-sm" [style.background]="current()"></span>
|
|
104718
|
-
</button>
|
|
104719
|
-
<div class="absolute left-0 top-full z-50 hidden pt-1 group-hover:block">
|
|
104720
|
-
<div class="w-36 rounded-lg border border-border bg-card p-2 shadow-2xl">
|
|
104721
|
-
<div class="mb-2 grid grid-cols-5 gap-1.5">
|
|
104722
|
-
@for (c of presets(); track c) {
|
|
104723
|
-
<button
|
|
104724
|
-
type="button"
|
|
104725
|
-
data-pptx-compact
|
|
104726
|
-
class="h-5 w-5 rounded-full border transition-transform hover:scale-125"
|
|
104727
|
-
[ngClass]="
|
|
104728
|
-
current().toLowerCase() === c
|
|
104729
|
-
? 'border-primary ring-1 ring-primary'
|
|
104730
|
-
: 'border-border'
|
|
104731
|
-
"
|
|
104732
|
-
[style.background]="c"
|
|
104733
|
-
[attr.aria-label]="swatchAriaKey() | translate: { color: c }"
|
|
104734
|
-
(mousedown)="$event.preventDefault()"
|
|
104735
|
-
(click)="pick.emit(c)"
|
|
104736
|
-
></button>
|
|
104737
|
-
}
|
|
104738
|
-
</div>
|
|
104739
|
-
<label
|
|
104740
|
-
class="block w-full cursor-pointer py-1 text-center text-[10px] text-muted-foreground transition-colors hover:text-foreground"
|
|
104741
|
-
>
|
|
104742
|
-
{{ 'pptx.ribbon.customColour' | translate }}
|
|
104743
|
-
<input
|
|
104744
|
-
type="color"
|
|
104745
|
-
class="sr-only"
|
|
104746
|
-
[value]="current()"
|
|
104747
|
-
(change)="pick.emit($any($event.target).value)"
|
|
104748
|
-
/>
|
|
104749
|
-
</label>
|
|
104750
|
-
</div>
|
|
104751
|
-
</div>
|
|
104752
|
-
</div>
|
|
104753
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
104754
|
-
}
|
|
104755
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonColorPopoverComponent, decorators: [{
|
|
104756
|
-
type: Component,
|
|
104757
|
-
args: [{
|
|
104758
|
-
selector: 'pptx-ribbon-color-popover',
|
|
104759
|
-
standalone: true,
|
|
104760
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
104761
|
-
imports: [NgClass, TranslatePipe],
|
|
104762
|
-
template: `
|
|
104763
|
-
<div class="group relative">
|
|
104764
|
-
<button
|
|
104765
|
-
type="button"
|
|
104766
|
-
class="pptx-rb-pill"
|
|
104767
|
-
[disabled]="disabled()"
|
|
104768
|
-
[title]="titleKey() | translate"
|
|
104769
|
-
(mousedown)="$event.preventDefault()"
|
|
104770
|
-
>
|
|
104771
|
-
<ng-content />
|
|
104772
|
-
<span class="-mt-0.5 block h-1 w-4 rounded-sm" [style.background]="current()"></span>
|
|
104773
|
-
</button>
|
|
104774
|
-
<div class="absolute left-0 top-full z-50 hidden pt-1 group-hover:block">
|
|
104775
|
-
<div class="w-36 rounded-lg border border-border bg-card p-2 shadow-2xl">
|
|
104776
|
-
<div class="mb-2 grid grid-cols-5 gap-1.5">
|
|
104777
|
-
@for (c of presets(); track c) {
|
|
104778
|
-
<button
|
|
104779
|
-
type="button"
|
|
104780
|
-
data-pptx-compact
|
|
104781
|
-
class="h-5 w-5 rounded-full border transition-transform hover:scale-125"
|
|
104782
|
-
[ngClass]="
|
|
104783
|
-
current().toLowerCase() === c
|
|
104784
|
-
? 'border-primary ring-1 ring-primary'
|
|
104785
|
-
: 'border-border'
|
|
104786
|
-
"
|
|
104787
|
-
[style.background]="c"
|
|
104788
|
-
[attr.aria-label]="swatchAriaKey() | translate: { color: c }"
|
|
104789
|
-
(mousedown)="$event.preventDefault()"
|
|
104790
|
-
(click)="pick.emit(c)"
|
|
104791
|
-
></button>
|
|
104792
|
-
}
|
|
104793
|
-
</div>
|
|
104794
|
-
<label
|
|
104795
|
-
class="block w-full cursor-pointer py-1 text-center text-[10px] text-muted-foreground transition-colors hover:text-foreground"
|
|
104796
|
-
>
|
|
104797
|
-
{{ 'pptx.ribbon.customColour' | translate }}
|
|
104798
|
-
<input
|
|
104799
|
-
type="color"
|
|
104800
|
-
class="sr-only"
|
|
104801
|
-
[value]="current()"
|
|
104802
|
-
(change)="pick.emit($any($event.target).value)"
|
|
104803
|
-
/>
|
|
104804
|
-
</label>
|
|
104805
|
-
</div>
|
|
104806
|
-
</div>
|
|
104807
|
-
</div>
|
|
104808
|
-
`,
|
|
104809
|
-
}]
|
|
104810
|
-
}], propDecorators: { current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], presets: [{ type: i0.Input, args: [{ isSignal: true, alias: "presets", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], swatchAriaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "swatchAriaKey", required: false }] }], pick: [{ type: i0.Output, args: ["pick"] }] } });
|
|
104811
|
-
|
|
104812
105306
|
/**
|
|
104813
105307
|
* ribbon-font-controls.component.ts: the ribbon's reusable Font control group
|
|
104814
105308
|
* (family/size dropdowns, grow/shrink, clear-formatting, bold/italic/underline/
|
|
@@ -106056,6 +106550,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
106056
106550
|
* markup are unchanged. Font/Paragraph controls are the shared
|
|
106057
106551
|
* {@link RibbonFontControlsComponent} / {@link RibbonParagraphControlsComponent}.
|
|
106058
106552
|
*/
|
|
106553
|
+
/**
|
|
106554
|
+
* Home > Reset: re-apply the active slide's own layout, restoring inherited
|
|
106555
|
+
* placeholder geometry and formatting (React/Vue parity via the shared
|
|
106556
|
+
* `resetSlideLayoutPath` decision function). A no-op when the slide records
|
|
106557
|
+
* no layout. Exported as a pure dispatch function (rather than inlined in the
|
|
106558
|
+
* component) so it is directly testable without constructing the component,
|
|
106559
|
+
* whose constructor runs an `effect()` that needs a full Angular
|
|
106560
|
+
* `ChangeDetectionScheduler` this package's TestBed-free unit tests don't
|
|
106561
|
+
* provide (see `action-settings-panel.component.test.ts`).
|
|
106562
|
+
*/
|
|
106563
|
+
function performResetSlide(editor, slideIndex) {
|
|
106564
|
+
const path = resetSlideLayoutPath(editor.slides()[slideIndex]);
|
|
106565
|
+
if (path) {
|
|
106566
|
+
void editor.applyLayout(slideIndex, path);
|
|
106567
|
+
}
|
|
106568
|
+
}
|
|
106059
106569
|
class RibbonHomeSectionComponent {
|
|
106060
106570
|
constructor() {
|
|
106061
106571
|
this.editor = inject(EditorStateService);
|
|
@@ -106091,6 +106601,7 @@ class RibbonHomeSectionComponent {
|
|
|
106091
106601
|
this.openTemplateGallery = output();
|
|
106092
106602
|
/** Emitted with the layout the user picked, after it has been applied. */
|
|
106093
106603
|
this.applyLayout = output();
|
|
106604
|
+
/** Emitted after Home > Reset has re-applied the slide's layout. */
|
|
106094
106605
|
this.resetSlide = output();
|
|
106095
106606
|
// The menus open on hover with no event to hang a lazy load off, so the
|
|
106096
106607
|
// fetch is kicked off once a deck is present. It is still deferred out of
|
|
@@ -106119,6 +106630,14 @@ class RibbonHomeSectionComponent {
|
|
|
106119
106630
|
void this.editor.applyLayout(this.slideIndex(), layoutPath);
|
|
106120
106631
|
this.applyLayout.emit(layoutPath);
|
|
106121
106632
|
}
|
|
106633
|
+
/**
|
|
106634
|
+
* The button used to `resetSlide.emit()` to nobody, so clicking it did
|
|
106635
|
+
* nothing; {@link performResetSlide} now actually performs the reset.
|
|
106636
|
+
*/
|
|
106637
|
+
onResetSlide() {
|
|
106638
|
+
performResetSlide(this.editor, this.slideIndex());
|
|
106639
|
+
this.resetSlide.emit();
|
|
106640
|
+
}
|
|
106122
106641
|
copy() {
|
|
106123
106642
|
this.editor.copySelected(this.slideIndex());
|
|
106124
106643
|
}
|
|
@@ -106273,8 +106792,9 @@ class RibbonHomeSectionComponent {
|
|
|
106273
106792
|
<button
|
|
106274
106793
|
type="button"
|
|
106275
106794
|
class="pptx-rb-gb whitespace-nowrap"
|
|
106795
|
+
[disabled]="!canEdit()"
|
|
106276
106796
|
[title]="'pptx.sections.resetSlideTitle' | translate"
|
|
106277
|
-
(click)="
|
|
106797
|
+
(click)="onResetSlide()"
|
|
106278
106798
|
>
|
|
106279
106799
|
<svg lucideRotateCcw class="h-4 w-4"></svg> {{ 'pptx.animations.reset' | translate }}
|
|
106280
106800
|
</button>
|
|
@@ -106498,8 +107018,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
106498
107018
|
<button
|
|
106499
107019
|
type="button"
|
|
106500
107020
|
class="pptx-rb-gb whitespace-nowrap"
|
|
107021
|
+
[disabled]="!canEdit()"
|
|
106501
107022
|
[title]="'pptx.sections.resetSlideTitle' | translate"
|
|
106502
|
-
(click)="
|
|
107023
|
+
(click)="onResetSlide()"
|
|
106503
107024
|
>
|
|
106504
107025
|
<svg lucideRotateCcw class="h-4 w-4"></svg> {{ 'pptx.animations.reset' | translate }}
|
|
106505
107026
|
</button>
|
|
@@ -107696,7 +108217,11 @@ class RibbonContentComponent {
|
|
|
107696
108217
|
(openTemplateGallery)="openTemplateGallery.emit()"
|
|
107697
108218
|
/>
|
|
107698
108219
|
<span class="pptx-rb-sep"></span>
|
|
107699
|
-
<pptx-ribbon-drawing-group
|
|
108220
|
+
<pptx-ribbon-drawing-group
|
|
108221
|
+
[canEdit]="canEdit()"
|
|
108222
|
+
[slideIndex]="slideIndex()"
|
|
108223
|
+
[selectedElement]="selectedElement()"
|
|
108224
|
+
/>
|
|
107700
108225
|
<span class="pptx-rb-sep"></span>
|
|
107701
108226
|
<!--
|
|
107702
108227
|
React parity (Toolbar.tsx: sArr = sHome || toolbarSection === 'arrange'):
|
|
@@ -107746,7 +108271,7 @@ class RibbonContentComponent {
|
|
|
107746
108271
|
/>
|
|
107747
108272
|
}
|
|
107748
108273
|
}
|
|
107749
|
-
`, isInline: true, dependencies: [{ kind: "component", type: RibbonFileSectionComponent, selector: "pptx-ribbon-file-section", inputs: ["fileName", "slideCount", "exporting", "hasMacros", "hiddenActions", "accountAuth"], outputs: ["close", "createPresentation", "openFile", "openRecentFile", "save", "savePpsx", "savePptm", "packageForSharing", "exportPng", "exportPdf", "exportGif", "exportVideo", "exportJson", "copySlideAsImage", "print", "info", "signatures", "replace", "openPassword", "openFontEmbedding", "openVersionHistory", "share", "options"] }, { kind: "component", type: RibbonHomeSectionComponent, selector: "pptx-ribbon-home-section", inputs: ["slideIndex", "selectedElement", "canEdit", "formatPainterActive", "canActivateFormatPainter"], outputs: ["toggleFormatPainter", "findReplace", "openTemplateGallery", "applyLayout", "resetSlide"] }, { kind: "component", type: RibbonInsertSectionComponent, selector: "pptx-ribbon-insert-section", inputs: ["slideIndex", "newChartType", "newShapeType"], outputs: ["openSmartArtDialog", "openEquationDialog", "openHyperlink", "chartTypeChange", "shapeTypeChange"] }, { kind: "component", type: RibbonFontControlsComponent, selector: "pptx-ribbon-font-controls", inputs: ["slideIndex", "selectedElement"] }, { kind: "component", type: RibbonParagraphControlsComponent, selector: "pptx-ribbon-paragraph-controls", inputs: ["slideIndex", "selectedElement"] }, { kind: "component", type: RibbonArrangeSectionComponent, selector: "pptx-ribbon-arrange-section", inputs: ["slideIndex", "selectedElement", "canEdit", "formatPainterActive", "canActivateFormatPainter"], outputs: ["toggleFormatPainter"] }, { kind: "component", type: RibbonDrawingGroupComponent, selector: "pptx-ribbon-drawing-group", inputs: ["canEdit", "slideIndex"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
108274
|
+
`, isInline: true, dependencies: [{ kind: "component", type: RibbonFileSectionComponent, selector: "pptx-ribbon-file-section", inputs: ["fileName", "slideCount", "exporting", "hasMacros", "hiddenActions", "accountAuth"], outputs: ["close", "createPresentation", "openFile", "openRecentFile", "save", "savePpsx", "savePptm", "packageForSharing", "exportPng", "exportPdf", "exportGif", "exportVideo", "exportJson", "copySlideAsImage", "print", "info", "signatures", "replace", "openPassword", "openFontEmbedding", "openVersionHistory", "share", "options"] }, { kind: "component", type: RibbonHomeSectionComponent, selector: "pptx-ribbon-home-section", inputs: ["slideIndex", "selectedElement", "canEdit", "formatPainterActive", "canActivateFormatPainter"], outputs: ["toggleFormatPainter", "findReplace", "openTemplateGallery", "applyLayout", "resetSlide"] }, { kind: "component", type: RibbonInsertSectionComponent, selector: "pptx-ribbon-insert-section", inputs: ["slideIndex", "newChartType", "newShapeType"], outputs: ["openSmartArtDialog", "openEquationDialog", "openHyperlink", "chartTypeChange", "shapeTypeChange"] }, { kind: "component", type: RibbonFontControlsComponent, selector: "pptx-ribbon-font-controls", inputs: ["slideIndex", "selectedElement"] }, { kind: "component", type: RibbonParagraphControlsComponent, selector: "pptx-ribbon-paragraph-controls", inputs: ["slideIndex", "selectedElement"] }, { kind: "component", type: RibbonArrangeSectionComponent, selector: "pptx-ribbon-arrange-section", inputs: ["slideIndex", "selectedElement", "canEdit", "formatPainterActive", "canActivateFormatPainter"], outputs: ["toggleFormatPainter"] }, { kind: "component", type: RibbonDrawingGroupComponent, selector: "pptx-ribbon-drawing-group", inputs: ["canEdit", "slideIndex", "selectedElement"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
107750
108275
|
}
|
|
107751
108276
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RibbonContentComponent, decorators: [{
|
|
107752
108277
|
type: Component,
|
|
@@ -107810,7 +108335,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
107810
108335
|
(openTemplateGallery)="openTemplateGallery.emit()"
|
|
107811
108336
|
/>
|
|
107812
108337
|
<span class="pptx-rb-sep"></span>
|
|
107813
|
-
<pptx-ribbon-drawing-group
|
|
108338
|
+
<pptx-ribbon-drawing-group
|
|
108339
|
+
[canEdit]="canEdit()"
|
|
108340
|
+
[slideIndex]="slideIndex()"
|
|
108341
|
+
[selectedElement]="selectedElement()"
|
|
108342
|
+
/>
|
|
107814
108343
|
<span class="pptx-rb-sep"></span>
|
|
107815
108344
|
<!--
|
|
107816
108345
|
React parity (Toolbar.tsx: sArr = sHome || toolbarSection === 'arrange'):
|
|
@@ -110411,31 +110940,10 @@ function previewAngularAnimation(animation) {
|
|
|
110411
110940
|
return true;
|
|
110412
110941
|
}
|
|
110413
110942
|
|
|
110414
|
-
|
|
110415
|
-
|
|
110416
|
-
|
|
110417
|
-
|
|
110418
|
-
sourceIndex >= sorted.length ||
|
|
110419
|
-
targetIndex >= sorted.length ||
|
|
110420
|
-
sourceIndex === targetIndex) {
|
|
110421
|
-
return sorted.map((animation, order) => ({ ...animation, order }));
|
|
110422
|
-
}
|
|
110423
|
-
const [moved] = sorted.splice(sourceIndex, 1);
|
|
110424
|
-
if (!moved) {
|
|
110425
|
-
return sorted;
|
|
110426
|
-
}
|
|
110427
|
-
sorted.splice(targetIndex, 0, moved);
|
|
110428
|
-
return sorted.map((animation, order) => ({ ...animation, order }));
|
|
110429
|
-
}
|
|
110430
|
-
function buildAnimationTimelineBars(animations) {
|
|
110431
|
-
const sorted = [...animations].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
110432
|
-
const total = Math.max(1, ...sorted.map((animation) => (animation.delayMs ?? 0) + (animation.durationMs ?? 500)));
|
|
110433
|
-
return sorted.map((animation) => ({
|
|
110434
|
-
elementId: animation.elementId,
|
|
110435
|
-
leftPercent: ((animation.delayMs ?? 0) / total) * 100,
|
|
110436
|
-
widthPercent: ((animation.durationMs ?? 500) / total) * 100,
|
|
110437
|
-
}));
|
|
110438
|
-
}
|
|
110943
|
+
/* oxlint-disable eslint/one-var -- the component's own field/method
|
|
110944
|
+
declarations below are independent, not adjacent initializations of
|
|
110945
|
+
related values; merging them would hurt readability far more than it
|
|
110946
|
+
helps. */
|
|
110439
110947
|
class AnimationTimelineComponent {
|
|
110440
110948
|
constructor() {
|
|
110441
110949
|
this.animations = input.required(/* @ts-ignore */
|
|
@@ -110493,7 +111001,7 @@ class AnimationTimelineComponent {
|
|
|
110493
111001
|
event.preventDefault();
|
|
110494
111002
|
const source = this.dragIndex();
|
|
110495
111003
|
if (source !== undefined) {
|
|
110496
|
-
this.animationsChange.emit(
|
|
111004
|
+
this.animationsChange.emit(reorderAnimationTo(this.animations(), source, index));
|
|
110497
111005
|
}
|
|
110498
111006
|
this.clearDrag();
|
|
110499
111007
|
}
|
|
@@ -117025,9 +117533,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
117025
117533
|
* drawingShapes so the renderer reflows with the override). Reading the
|
|
117026
117534
|
* current value off `node.style` drives the inspector controls.
|
|
117027
117535
|
*
|
|
117028
|
-
* 2. **Node-count bounds** - soft, per-layout min/max guards
|
|
117029
|
-
*
|
|
117030
|
-
*
|
|
117536
|
+
* 2. **Node-count bounds** - soft, per-layout min/max guards. The table,
|
|
117537
|
+
* `getSmartArtNodeBounds`, `canAddTopLevelNode` and `canRemoveTopLevelNode`
|
|
117538
|
+
* are re-exported from the single shared implementation in
|
|
117539
|
+
* `pptx-viewer-shared` (`packages/shared/src/render/smartart-node-limits.ts`),
|
|
117540
|
+
* which also backs the React and Vue bindings. `describeSmartArtBounds`
|
|
117541
|
+
* stays a thin Angular wrapper: it takes the shared function's neutral
|
|
117542
|
+
* English text and, when a `TranslateService` is supplied, swaps in the
|
|
117543
|
+
* translated string keyed off the same bounds, so the shared module never
|
|
117544
|
+
* takes an i18n dependency.
|
|
117031
117545
|
*
|
|
117032
117546
|
* @module angular-viewer/smart-art-node-style-helpers
|
|
117033
117547
|
*
|
|
@@ -117074,61 +117588,39 @@ function toggleNodeBold(data, node) {
|
|
|
117074
117588
|
function toggleNodeItalic(data, node) {
|
|
117075
117589
|
return setNodeStyle(data, node.id, { italic: !nodeItalic(node) });
|
|
117076
117590
|
}
|
|
117077
|
-
|
|
117078
|
-
|
|
117079
|
-
|
|
117080
|
-
|
|
117081
|
-
|
|
117082
|
-
|
|
117083
|
-
target: { min: 2, max: 5 },
|
|
117084
|
-
gear: { min: 2, max: 3 },
|
|
117085
|
-
cycle: { min: 3 },
|
|
117086
|
-
};
|
|
117087
|
-
/** Fallback bounds for any layout without an explicit entry. */
|
|
117088
|
-
const DEFAULT_BOUNDS = { min: 1 };
|
|
117089
|
-
/** Resolve the node-count bounds for a layout category. */
|
|
117090
|
-
function getSmartArtNodeBounds(layout) {
|
|
117091
|
-
if (!layout) {
|
|
117092
|
-
return DEFAULT_BOUNDS;
|
|
117093
|
-
}
|
|
117094
|
-
return LAYOUT_BOUNDS[layout] ?? DEFAULT_BOUNDS;
|
|
117095
|
-
}
|
|
117591
|
+
// ── Node-count bounds (re-exported from pptx-viewer-shared) ────────────────
|
|
117592
|
+
// See the module doc comment: the table, `getSmartArtNodeBounds`,
|
|
117593
|
+
// `canAddTopLevelNode` and `canRemoveTopLevelNode` are re-exported above,
|
|
117594
|
+
// unmodified, from the single shared implementation. Only
|
|
117595
|
+
// `describeSmartArtBounds` stays defined here, as a thin wrapper that applies
|
|
117596
|
+
// this binding's `TranslateService` on top of the shared neutral text.
|
|
117096
117597
|
/** Count of top-level (parentless) nodes in a diagram. */
|
|
117097
117598
|
function topLevelNodeCount(data) {
|
|
117098
117599
|
return (data.nodes ?? []).filter((node) => !node.parentId).length;
|
|
117099
117600
|
}
|
|
117100
|
-
/** Whether adding another top-level node stays within the layout's max. */
|
|
117101
|
-
function canAddTopLevelNode(layout, topLevelCount) {
|
|
117102
|
-
const { max } = getSmartArtNodeBounds(layout);
|
|
117103
|
-
return max === undefined || topLevelCount < max;
|
|
117104
|
-
}
|
|
117105
|
-
/** Whether removing a top-level node keeps the count at or above the min. */
|
|
117106
|
-
function canRemoveTopLevelNode(layout, topLevelCount) {
|
|
117107
|
-
const { min } = getSmartArtNodeBounds(layout);
|
|
117108
|
-
return topLevelCount > min;
|
|
117109
|
-
}
|
|
117110
117601
|
/**
|
|
117111
117602
|
* Short, human-readable explanation of a layout's bounds for a tooltip / hint,
|
|
117112
117603
|
* or `undefined` when the layout imposes no meaningful limit.
|
|
117604
|
+
*
|
|
117605
|
+
* Delegates the neutral English text to the shared implementation; when a
|
|
117606
|
+
* `TranslateService` is supplied, swaps in the translated string for the same
|
|
117607
|
+
* bounds instead.
|
|
117113
117608
|
*/
|
|
117114
117609
|
function describeSmartArtBounds(layout, translate) {
|
|
117115
117610
|
const { min, max } = getSmartArtNodeBounds(layout);
|
|
117611
|
+
if (!translate) {
|
|
117612
|
+
return describeSmartArtBounds$1(layout);
|
|
117613
|
+
}
|
|
117116
117614
|
if (min <= 1 && max === undefined) {
|
|
117117
117615
|
return undefined;
|
|
117118
117616
|
}
|
|
117119
117617
|
if (max === undefined) {
|
|
117120
|
-
return translate
|
|
117121
|
-
? translate.instant('pptx.smartArt.boundsHintMin', { min })
|
|
117122
|
-
: `Works best with at least ${min} items.`;
|
|
117618
|
+
return translate.instant('pptx.smartArt.boundsHintMin', { min });
|
|
117123
117619
|
}
|
|
117124
117620
|
if (min === max) {
|
|
117125
|
-
return translate
|
|
117126
|
-
? translate.instant('pptx.smartArt.boundsHintExact', { max })
|
|
117127
|
-
: `This layout uses exactly ${max} items.`;
|
|
117621
|
+
return translate.instant('pptx.smartArt.boundsHintExact', { max });
|
|
117128
117622
|
}
|
|
117129
|
-
return translate
|
|
117130
|
-
? translate.instant('pptx.smartArt.boundsHintRange', { min, max })
|
|
117131
|
-
: `Works best with ${min} to ${max} items.`;
|
|
117623
|
+
return translate.instant('pptx.smartArt.boundsHintRange', { min, max });
|
|
117132
117624
|
}
|
|
117133
117625
|
|
|
117134
117626
|
/**
|
|
@@ -117895,6 +118387,22 @@ function inputValue$1(event) {
|
|
|
117895
118387
|
return target instanceof HTMLInputElement ? target.value : null;
|
|
117896
118388
|
}
|
|
117897
118389
|
|
|
118390
|
+
/**
|
|
118391
|
+
* table-properties-helpers.ts: pure helpers for the table properties inspector.
|
|
118392
|
+
*
|
|
118393
|
+
* The small immutable transforms behind the Angular port of the React
|
|
118394
|
+
* `TablePropertiesPanel` / `TableCellAdvancedFill`: building a CSS gradient
|
|
118395
|
+
* string from structured stops so the renderer shows edited gradients live.
|
|
118396
|
+
* Table quick-style preset application (`applyTableStylePreset`) and
|
|
118397
|
+
* column-width redistribution / "distribute evenly" one-liners live in
|
|
118398
|
+
* `pptx-viewer-shared`'s `render/table-style-presets.ts` and
|
|
118399
|
+
* `render/table-resize.ts` (imported from `../internal/shared`) so every
|
|
118400
|
+
* binding shares one copy of that logic.
|
|
118401
|
+
*
|
|
118402
|
+
* No Angular imports, so they are unit-testable with plain vitest.
|
|
118403
|
+
*/
|
|
118404
|
+
/* oxlint-disable eslint/one-var -- independent, unrelated locals across these
|
|
118405
|
+
helpers; merging them into one statement would hurt readability. */
|
|
117898
118406
|
/** Default row height (px) used when a row has no explicit height. */
|
|
117899
118407
|
const DEFAULT_TABLE_ROW_HEIGHT = 32;
|
|
117900
118408
|
/** The structure / style toggle flags shown as checkboxes, with i18n dictionary keys. */
|
|
@@ -117906,69 +118414,6 @@ const TABLE_STRUCTURE_TOGGLES = [
|
|
|
117906
118414
|
{ key: 'lastCol', labelKey: 'pptx.table.lastColumn' },
|
|
117907
118415
|
{ key: 'lastRow', labelKey: 'pptx.table.lastRow' },
|
|
117908
118416
|
];
|
|
117909
|
-
/**
|
|
117910
|
-
* Apply a table quick-style preset to every cell's style, mirroring the React
|
|
117911
|
-
* `TablePropertiesPanel` preset `onClick`. Header cells get the header fill /
|
|
117912
|
-
* foreground + bold; banded body rows get the band background; every cell gets
|
|
117913
|
-
* the preset border colour. Returns a new rows array.
|
|
117914
|
-
*/
|
|
117915
|
-
function applyTableStylePreset(td, preset) {
|
|
117916
|
-
return td.rows.map((row, ri) => ({
|
|
117917
|
-
...row,
|
|
117918
|
-
cells: row.cells.map((cell) => {
|
|
117919
|
-
const isHeader = ri === 0 && Boolean(td.firstRowHeader);
|
|
117920
|
-
const isBand = Boolean(td.bandedRows) && (ri - (td.firstRowHeader ? 1 : 0)) % 2 === 0;
|
|
117921
|
-
const style = {
|
|
117922
|
-
...cell.style,
|
|
117923
|
-
backgroundColor: isHeader ? preset.headerBg : isBand ? preset.bandBg : undefined,
|
|
117924
|
-
color: isHeader ? preset.headerFg : cell.style?.color,
|
|
117925
|
-
bold: isHeader ? true : cell.style?.bold,
|
|
117926
|
-
borderColor: preset.borderColor,
|
|
117927
|
-
};
|
|
117928
|
-
return { ...cell, style };
|
|
117929
|
-
}),
|
|
117930
|
-
}));
|
|
117931
|
-
}
|
|
117932
|
-
/**
|
|
117933
|
-
* Redistribute column widths when a single column is set to `newFraction`,
|
|
117934
|
-
* scaling the other columns proportionally so the array still sums to 1.
|
|
117935
|
-
* Mirrors the React column-width slider `onChange`.
|
|
117936
|
-
*/
|
|
117937
|
-
function redistributeColumnWidth(widths, index, newFraction) {
|
|
117938
|
-
const oldFraction = widths[index];
|
|
117939
|
-
if (oldFraction === undefined) {
|
|
117940
|
-
return widths;
|
|
117941
|
-
}
|
|
117942
|
-
const diff = newFraction - oldFraction;
|
|
117943
|
-
const next = [...widths];
|
|
117944
|
-
next[index] = newFraction;
|
|
117945
|
-
const othersTotal = 1 - oldFraction;
|
|
117946
|
-
if (othersTotal > 0) {
|
|
117947
|
-
for (let j = 0; j < next.length; j++) {
|
|
117948
|
-
if (j !== index) {
|
|
117949
|
-
next[j] = Math.max(0.05, widths[j] - diff * (widths[j] / othersTotal));
|
|
117950
|
-
}
|
|
117951
|
-
}
|
|
117952
|
-
}
|
|
117953
|
-
const sum = next.reduce((a, b) => a + b, 0);
|
|
117954
|
-
return sum > 0 ? next.map((w) => w / sum) : next;
|
|
117955
|
-
}
|
|
117956
|
-
/** An equal-width column array for `count` columns. */
|
|
117957
|
-
function evenColumnWidths(count) {
|
|
117958
|
-
if (count <= 0) {
|
|
117959
|
-
return [];
|
|
117960
|
-
}
|
|
117961
|
-
return Array.from({ length: count }, () => 1 / count);
|
|
117962
|
-
}
|
|
117963
|
-
/** Rows with a uniform (average) height applied. */
|
|
117964
|
-
function evenRowHeights(td) {
|
|
117965
|
-
const count = td.rows.length;
|
|
117966
|
-
if (count === 0) {
|
|
117967
|
-
return td.rows;
|
|
117968
|
-
}
|
|
117969
|
-
const avg = Math.round(td.rows.reduce((s, r) => s + (r.height ?? DEFAULT_TABLE_ROW_HEIGHT), 0) / count);
|
|
117970
|
-
return td.rows.map((r) => ({ ...r, height: avg }));
|
|
117971
|
-
}
|
|
117972
118417
|
/**
|
|
117973
118418
|
* Build a CSS gradient string from structured cell-style gradient fields, so
|
|
117974
118419
|
* the renderer (which reads `gradientFillCss`) shows an edited gradient live.
|
|
@@ -119389,6 +119834,8 @@ function stringFromEvent$1(event) {
|
|
|
119389
119834
|
* controls. Every edit emits a fully-updated element through `elementChange`,
|
|
119390
119835
|
* committed by the inspector as one undoable history entry.
|
|
119391
119836
|
*/
|
|
119837
|
+
/* oxlint-disable eslint/one-var -- each handler declares its own independent
|
|
119838
|
+
locals; merging them into one statement would hurt readability. */
|
|
119392
119839
|
class TablePropertiesComponent {
|
|
119393
119840
|
constructor() {
|
|
119394
119841
|
/** The table element being edited. */
|
|
@@ -119435,7 +119882,7 @@ class TablePropertiesComponent {
|
|
|
119435
119882
|
onEvenRows() {
|
|
119436
119883
|
const data = this.td();
|
|
119437
119884
|
if (data) {
|
|
119438
|
-
this.emit({ rows: evenRowHeights(data) });
|
|
119885
|
+
this.emit({ rows: evenRowHeights(data.rows) });
|
|
119439
119886
|
}
|
|
119440
119887
|
}
|
|
119441
119888
|
onColWidth(index, event) {
|
|
@@ -120869,7 +121316,7 @@ class InspectorPanelComponent {
|
|
|
120869
121316
|
height: cur.height,
|
|
120870
121317
|
rotation: cur.rotation ?? 0,
|
|
120871
121318
|
opacity: cur.opacity ?? 1,
|
|
120872
|
-
fillColor: fillColorOf(cur),
|
|
121319
|
+
fillColor: fillColorOf$1(cur),
|
|
120873
121320
|
strokeColor: strokeColorOf(cur),
|
|
120874
121321
|
textColor: textColorOf(cur),
|
|
120875
121322
|
fontSize: fontSizeOf(cur),
|
|
@@ -121000,7 +121447,7 @@ class InspectorPanelComponent {
|
|
|
121000
121447
|
return;
|
|
121001
121448
|
}
|
|
121002
121449
|
const cur = this.el();
|
|
121003
|
-
this.editor.updateElement(this.slideIndex(), cur.id, shapeStylePatch(cur, { fillColor: color }));
|
|
121450
|
+
this.editor.updateElement(this.slideIndex(), cur.id, shapeStylePatch$1(cur, { fillColor: color }));
|
|
121004
121451
|
}
|
|
121005
121452
|
onStrokeColorChange(event) {
|
|
121006
121453
|
const color = stringFromEvent(event);
|
|
@@ -121008,7 +121455,7 @@ class InspectorPanelComponent {
|
|
|
121008
121455
|
return;
|
|
121009
121456
|
}
|
|
121010
121457
|
const cur = this.el();
|
|
121011
|
-
this.editor.updateElement(this.slideIndex(), cur.id, shapeStylePatch(cur, { strokeColor: color }));
|
|
121458
|
+
this.editor.updateElement(this.slideIndex(), cur.id, shapeStylePatch$1(cur, { strokeColor: color }));
|
|
121012
121459
|
}
|
|
121013
121460
|
// ── Text style ───────────────────────────────────────────────────────────
|
|
121014
121461
|
onTextColorChange(event) {
|
|
@@ -137493,5 +137940,5 @@ function cn(...values) {
|
|
|
137493
137940
|
* Generated bundle index. Do not edit.
|
|
137494
137941
|
*/
|
|
137495
137942
|
|
|
137496
|
-
export { CommentsPanelComponent as $, ALIGN_OPTIONS as A, AnimationPlaybackService as B, AutosaveRecoveryDialogComponent as C, AutosaveService as D, BroadcastDialogComponent as E, CHART_EDITOR_STYLES as F, CURSOR_PALETTE as G, CanvasFitService as H, ChartAxisOptionsComponent as I, ChartAxisStyleOptionsComponent as J, ChartComboTypeOptionsComponent as K, ChartDataEditorComponent as L, ChartDataLabelOptionsComponent as M, ChartDatapointMarkerOptionsComponent as N, ChartDatapointOptionsComponent as O, ChartDisplayOptionsComponent as P, ChartElementViewComponent as Q, ChartErrorBarOptionsComponent as R, ChartMarkerOptionsComponent as S, ChartPartSelectionService as T, ChartPrimitivesComponent as U, ChartRendererComponent as V, ChartTrendlineOptionsComponent as W, CollaborationCursorsComponent as X, CollaborationService as Y, ColorChangedImageComponent as Z, CommentMarkersOverlayComponent as _, ANIMATION_PRESET_CATEGORIES as a, InspectorPanelComponent as a$, CommentsService as a0, ComparePanelComponent as a1, ConnectorRendererComponent as a2, ConnectorTextOverlayComponent as a3, CustomShowsComponent as a4, DATA_TABLE_HEADER_H as a5, DATA_TABLE_KEY_W as a6, DATA_TABLE_PADDING as a7, DATA_TABLE_ROW_H as a8, DEFAULT_BOUNDS as a9, EditorToolbarComponent as aA, EffectsPanelComponent as aB, ElementRendererComponent as aC, EmbeddedFontsService as aD, EncryptedFileDialogComponent as aE, EquationEditorDialogComponent as aF, EquationRendererComponent as aG, EquationTemplateGalleryComponent as aH, ExportProgressModalComponent as aI, ExportService as aJ, FieldContextService as aK, FindBarComponent as aL, FindReplaceBarComponent as aM, FollowModeBarComponent as aN, FontEmbeddingListComponent as aO, FontEmbeddingPanelComponent as aP, GALLERY_THEME_PRESETS as aQ, GRIDLINE_COLOR as aR, GradientPickerComponent as aS, HANDOUT_OPTIONS as aT, HeaderFooterDialogComponent as aU, HyperlinkDialogComponent as aV, ImagePropertiesPanelComponent as aW, InkDrawingService as aX, InkRendererComponent as aY, InsertSmartArtDialogComponent as aZ, InspectorPaneHeaderComponent 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 as ae, DEFAULT_LAYOUT as af, DEFAULT_PALETTE$1 as ag, DEFAULT_PATTERN_FILL_PRESET as ah, DEFAULT_PRINT_SETTINGS as ai, DEFAULT_SLIDE_BACKGROUND as aj, DEFAULT_STROKE_COLOR as ak, DEFAULT_STYLE as al, DEFAULT_TABLE_ROW_HEIGHT as am, DEFAULT_TEXT_COLOR$1 as an, DEFAULT_VIEWER_PROFILE as ao, DIRECTIONAL_PRESETS as ap, DIRECTION_OPTIONS as aq, DocumentPropertiesCardComponent as ar, EMBEDDED_FONTS_STYLE_ID as as, EMPHASIS_PRESETS as at, ENTRANCE_PRESETS as au, TEMPLATES as av, EXIT_PRESETS as aw, EditorContextMenuComponent as ax, EditorHistory as ay, EditorStateService as az, AUDIENCE_HASH as b, RibbonComponent as b$, IsMobileService as b0, KeepAnnotationsDialogComponent as b1, LOCALE_CATALOG as b2, LONG_PRESS_DURATION_MS as b3, LONG_PRESS_MOVE_TOLERANCE_PX as b4, LoadContentService as b5, LocalPresencePublisher as b6, MAX_ZOOM_SCALE as b7, MIN_ZOOM_SCALE as b8, MOTION_PATH_COLUMNS as b9, PasswordStrengthMeterComponent as bA, PowerPointViewerComponent as bB, PresentToolbarAutoHide as bC, PresentationAnnotationOverlayComponent as bD, PresentationAnnotationsService as bE, PresentationOverlayComponent as bF, PresentationPropertiesPanelComponent as bG, PresentationSettingsCardComponent as bH, PresentationSubtitleBarComponent as bI, PresentationToolbarComponent as bJ, PresentationTransitionOverlayComponent as bK, PresenterViewComponent as bL, PresenterWindowService as bM, PrintDialogComponent as bN, PrintService as bO, PrintSettingsPanelComponent as bP, PropertiesDialogComponent as bQ, REPEAT_MODE_OPTIONS as bR, RESIZE_HANDLES as bS, RULER_FONT_SIZE as bT, RULER_THICKNESS as bU, ReadingViewOverlayComponent as bV, RemoteSelectionOverlayComponent as bW, RibbonAnimationGalleryComponent as bX, RibbonAnimationsSectionComponent as bY, RibbonArrangeSectionComponent as bZ, RibbonColorPopoverComponent as b_, MediaPreviewComponent as ba, MediaPropertiesPanelComponent as bb, MediaRendererComponent as bc, MediaTrimTimelineComponent as bd, MobileBottomBarComponent as be, MobileMenuSheetComponent as bf, MobilePresenterViewComponent as bg, MobileSheetComponent as bh, MobileSlidesSheetComponent as bi, MobileToolbarComponent as bj, ModalDialogComponent as bk, Model3DRendererComponent as bl, NotesHandoutCardComponent as bm, NotesPanelComponent as bn, NotesToolbarComponent as bo, OleRendererComponent as bp, OutlineViewOverlayComponent as bq, POWER_POINT_VIEWER_PROVIDERS as br, PPTX_OPEN_ACCEPT as bs, PRESENTATION_OPEN_EXTENSIONS as bt, PRESENTER_CHANNEL_NAME as bu, PRESENTER_MSG_ORIGIN as bv, PRESENTER_TIMER_SEGMENT_MS as bw, PX_PER_CM as bx, PX_PER_INCH as by, PasswordProtectionDialogComponent as bz, AUDIENCE_NONCE_KEY as c, TEXT_3D_TOP_BEVEL_KEYS as c$, RibbonDesignSectionComponent as c0, RibbonDrawSectionComponent as c1, RibbonDrawingGroupComponent as c2, RibbonEditingSectionComponent as c3, RibbonFileSectionComponent as c4, RibbonFontControlsComponent as c5, RibbonHomeSectionComponent as c6, RibbonHyperlinkButtonComponent as c7, RibbonInsertFieldsComponent as c8, RibbonInsertSectionComponent as c9, SettingsLanguageTabComponent as cA, ShareDialogComponent as cB, ShortcutPanelComponent as cC, ShowOptionsFieldsetComponent as cD, ShowSlidesFieldsetComponent as cE, SignatureStrippedDialogComponent as cF, SignaturesPanelComponent as cG, SignaturesService as cH, SlideBackgroundCardComponent as cI, SlideCanvasComponent as cJ, SlideDefaultInspectorComponent as cK, SlideDiffChangesComponent as cL, SlideDiffRowComponent as cM, SlideDiffThumbnailsComponent as cN, SlideSizeCardComponent as cO, SlideSorterOverlayComponent as cP, SlideThemeOverridePanelComponent as cQ, SlideTransitionCardComponent as cR, SlidesPanelComponent as cS, SmartArt3DRendererComponent as cT, SmartArt3DService as cU, SmartArtPreviewComponent as cV, SmartArtPropertiesComponent as cW, SmartArtRendererComponent as cX, StatusBarComponent as cY, TABLE_STRUCTURE_TOGGLES as cZ, TEXT_3D_BOTTOM_BEVEL_KEYS as c_, RibbonMotionPathGalleryComponent as ca, RibbonParagraphControlsComponent as cb, RibbonPrimaryRowComponent as cc, RibbonReviewSectionComponent as cd, RibbonShapeExtrasComponent as ce, RibbonSlideshowSectionComponent as cf, RibbonTransitionsSectionComponent as cg, RibbonViewSectionComponent as ch, RulerGuidesService as ci, SEQUENCE_OPTIONS as cj, SEVERITY_GROUPS as ck, SEVERITY_LABELS as cl, SHORTCUT_REFERENCE_ITEMS as cm, SLIDE_TRANSITION_KEYFRAMES as cn, DEFAULT_PALETTE as co, PALETTES$1 as cp, SMART_ART_COLOR_SCHEMES as cq, SMART_ART_STYLE_OPTIONS as cr, SUB_ITEM_LABEL as cs, SVG_WARP_PRESETS as ct, SWIPE_MAX_VERTICAL_PX as cu, SWIPE_THRESHOLD_PX as cv, SelectionPaneComponent as cw, SetUpSlideShowDialogComponent as cx, SettingsAppearanceTabComponent as cy, SettingsDialogComponent as cz, AVATAR_COLOR_SWATCHES as d, animationPresetLabelKey as d$, TEXT_DIRECTION_OPTIONS$1 as d0, THEME_CATALOG as d1, TIMING_CURVE_OPTIONS as d2, TRIGGER_OPTIONS as d3, TYPE_LABELS as d4, TableCellAdvancedFillComponent as d5, TableCellFormattingComponent as d6, TableDataEditorComponent as d7, TablePropertiesComponent as d8, TableRendererComponent as d9, ViewerFileIOService as dA, ViewerFindReplaceService as dB, ViewerFormatPainterService as dC, ViewerInspectorPanelService as dD, ViewerKeyboardService as dE, ViewerMobileSheetService as dF, ViewerPresentationModeService as dG, ViewerThemeGalleryService as dH, ViewerTouchGesturesService as dI, ViewerZoomService as dJ, WEBM_MIME_CANDIDATES as dK, WriteBackScheduler as dL, ZERO_LINE_COLOR as dM, ZoomNavigationService as dN, ZoomRendererComponent as dO, ZoomTargetService as dP, addCategory as dQ, addCommentToList as dR, addGradientStopPatch as dS, addItem as dT, addSeries as dU, addSubItem as dV, advanceStep as dW, affordanceElements as dX, aiToggleVisible as dY, alignPatch as dZ, animationFor as d_, TableResizeOverlayComponent as da, TableSelectionService as db, TagsCardComponent as dc, Text3DBevelSectionComponent as dd, Text3DPanelComponent as de, TextAdvancedPanelComponent as df, ThemeEditorFieldsComponent as dg, ThemeGalleryComponent as dh, ThemeSelectorCardComponent as di, TitleBarComponent as dj, TitleBarSearchComponent as dk, TransitionDirectionPickerComponent as dl, TransitionPreviewComponent as dm, VALIGN_OPTIONS as dn, VIEWER_THEME as dp, VersionHistoryPanelComponent as dq, ViewerCanvasEditingService as dr, ViewerCollabCursorService as ds, ViewerCollaborationSessionService as dt, ViewerCompareService as du, ViewerCustomShowsService as dv, ViewerDialogsService as dw, ViewerDocumentPropertiesService as dx, ViewerExportService as dy, ViewerExtraDialogsComponent as dz, AXIS_LABEL_COLOR as e, buildWaterfallViewModel as e$, annotationMapToInkInserts as e0, applyAcceptedDiff as e1, applyAnimationPreset as e2, applyFindReplacements as e3, applyFormatToElement as e4, applyMove as e5, applyResize as e6, applyTableStylePreset as e7, asMediaElement as e8, assignUserColor as e9, buildEquationElement as eA, buildEquationSegment as eB, buildFallbackViewModel as eC, buildFontFaceRule as eD, buildGradientFillCss as eE, buildGridlinesAndLabels as eF, buildHyperlinkPatch as eG, buildInkContainerStyle as eH, buildInkStrokes as eI, buildLegend as eJ, buildModel3DContainerStyle as eK, buildModel3DViewModel as eL, buildOleActionModel as eM, buildOleInfoRows as eN, buildPatternFillCss as eO, buildPrintHtmlDocument as eP, buildPropertiesPatch as eQ, buildRegionMapViewModel as eR, buildSaveSlides as eS, buildShareUrl as eT, buildSmartArtInsertElement as eU, buildSmartArtNodes as eV, buildStockViewModel as eW, buildSurfaceViewModel as eX, buildTableViewModel as eY, buildTreemapViewModel as eZ, buildTrimFragment as e_, attachShowVisibilityPause as ea, attachTouchGestures as eb, axisTickValues as ec, beginNodeEdit as ed, bevelSizePatch as ee, boolFromEvent as ef, bringForward as eg, bringToFront as eh, buildBarActions as ei, buildBroadcastConfig as ej, buildBroadcastViewerUrl as ek, buildCategoryLabels as el, buildCellParagraphs as em, buildChartViewModel as en, buildChatLogExport as eo, buildChatLogMarkdown as ep, buildChromeStyle as eq, buildClearHyperlinkPatch as er, buildClickGroups as es, buildColStyles as et, buildCollaborationConfig as eu, buildComboViewModel as ev, buildCssGradientFromShapeStyle as ew, buildDuotoneFilter as ex, buildDuotoneFilterId as ey, buildEmbeddedFontStyles as ez, AccessibilityPanelComponent as f, computeScatterXDomain as f$, buildZeroLine as f0, buildZoomContainerStyle as f1, buildZoomViewModel as f2, bulletIndentPx as f3, canAddTopLevelNode as f4, canGroupSelection as f5, canRemoveTopLevelNode as f6, canSetStrokeWidth as f7, canStartBroadcast as f8, canStartShare as f9, commitNodeText as fA, computeAlign as fB, computeAxisTitlePrimitives as fC, computeBarRects as fD, computeBubbleRadius as fE, computeCornerHandle as fF, computeDataTablePrimitives as fG, computeDistribute as fH, computeDrawingViewBox as fI, computeErrorBarPrimitives as fJ, computeFocusTargets as fK, computeHandleBoxes as fL, computeHandoutLayout as fM, computeIsMobile as fN, computeIsTablet as fO, computeLinePoints as fP, computeLinearRegression as fQ, computePageCount as fR, computePieLayout as fS, computePieSlicePath as fT, computePieSlices as fU, computePlotLayout as fV, computeRSquared as fW, computeRadarPoints as fX, computeResizeHandleBoxes as fY, computeRotateHandleBox as fZ, computeScatterDots as f_, canUngroupSelection as fa, canUseClipboard as fb, captionDisplayText as fc, cellRunStyle as fd, cellStyleToStyleMap as fe, cellTdStyle as ff, changeCountLabel as fg, changeIcon as fh, characterSpacingPatch as fi, chartPreserveAspectRatio as fj, checkFontAvailable as fk, clampCursorPosition as fl, clampGifDimensions as fm, clampIndex as fn, clampNotesFontSize as fo, clampScale as fp, clampStep as fq, clearAllLocalViewerData as fr, clearAudienceContent as fs, cn as ft, collectAccessibilityIssues as fu, collectElementText as fv, collectSlideText as fw, collectStoredChats as fx, collectUsedFontFamilies as fy, columnWidthStyle as fz, AccessibilityService as g, formatAxisValue as g$, computeSelectionBoxes as g0, computeSingleSelected as g1, computeSlideIndices as g2, computeSnap as g3, computeStackedBarRects as g4, computeStackedValueRange as g5, computeTrendlinePrimitives as g6, computeValueRange as g7, convertOmmlToMathMl as g8, copyFormatFromElement as g9, durationOf as gA, effectsStateOf as gB, enableGlowPatch as gC, enableInnerShadowPatch as gD, enableOuterShadowPatch as gE, enableReflectionPatch as gF, enableSoftEdgePatch as gG, encodeGif as gH, endShowMediaCleanup as gI, estimatePageCount as gJ, evenColumnWidths as gK, evenRowHeights as gL, exitPresentationFullscreen as gM, exportAiChatLogs as gN, extractPathPoints as gO, eyedropperAvailable as gP, fillColorOf as gQ, findInSlides as gR, findOwningSlideIndex as gS, findSlideIndexByElementId as gT, firstVisibleIndex as gU, fitPolynomial as gV, fitZoom as gW, focusTargetChips as gX, fontMimeForFormat as gY, fontSizeOf as gZ, forgetSessionDeck as g_, countAccessibilityIssues as ga, countAnnotationStrokes as gb, createAngularAiBridge as gc, createCustomShow as gd, createSwipeDismissDrag as ge, createWebrtcBundle as gf, createWebsocketBundle as gg, cssObjectToStyleMap as gh, currentColorScheme as gi, currentLayout as gj, currentStyle as gk, defaultCssVars as gl, defaultRadius as gm, defaultThemeColors as gn, deleteElementsByIds as go, deleteVersion as gp, demoteNode as gq, deriveModel3DBlobUrl as gr, derivePresenceList as gs, describeSmartArtBounds as gt, disableGlowPatch as gu, disableInnerShadowPatch as gv, disableOuterShadowPatch as gw, disableReflectionPatch as gx, disableSoftEdgePatch as gy, duplicateElementById as gz, AccountPageComponent as h, isInjectableUrl as h$, formatBytes as h0, formatCursorLabel as h1, formatElapsed as h2, formatFileSize as h3, formatPropertyDate as h4, formatTime as h5, fpsToFrameIntervalMs as h6, generateBroadcastRoomId as h7, generateCommentId as h8, generateCustomShowId as h9, getTextWarp as hA, getTouchDistance as hB, getWarpCategory as hC, getWarpPath as hD, gradientStateFromStyle as hE, gradientStateOf as hF, gradientStatePatch as hG, gridColumns as hH, groupIssuesBySeverity as hI, hasAnimation as hJ, hasCopyableFormat as hK, hasExistingLink as hL, hasExitedFullscreen as hM, hasGradientFill as hN, hasPressureVariation as hO, hasVisibleSlideAfter as hP, headerLabel as hQ, imageDimensions as hR, inkViewBox as hS, insertTableElementColumn as hT, insertTableElementRow as hU, interpolateWidth as hV, isAudienceTab as hW, isBold as hX, isBrowserOpenableMime as hY, isChildNode as hZ, isElementInteractive as h_, generatePressureCircles as ha, generateTicks as hb, getClrChangeParams as hc, getContainerStyle as hd, getDuotoneFilterDef as he, getImageSrc as hf, getLocalStorageUsageSummary as hg, getOleAriaLabel as hh, getOleBadgeLabel as hi, getOleDisplayName as hj, getOleDownloadFileName as hk, getOleTypeColor as hl, getOleTypeLabel as hm, getPasswordStrength as hn, getPatternSvg as ho, getPlaceholderStyle as hp, getVersions as hq, getResolvedShapeClipPath as hr, getResolvedShapeClipPathFor as hs, getSessionTabId as ht, getShapeFillStrokeStyle as hu, getSlideBackgroundStyle as hv, getSlideTransitionAnimations as hw, getSmartArtNodeBounds as hx, getSpeechRecognitionCtor as hy, getTextBlockStyle as hz, ActionSettingsPanelComponent as i, parseAudienceNonce as i$, isItalic as i0, isLegacyBinaryPresentation as i1, isPpactionUrl as i2, isPresenterMessage as i3, isSigned as i4, isSupportedPresentationFile as i5, isTextElement as i6, isTwoTableFocus as i7, isUnderline as i8, isUrlSafe as i9, narrowToCircle as iA, narrowToPolygon as iB, narrowToRect as iC, newChartElement as iD, newEquationElement as iE, newPresetShapeElement as iF, newShapeElement as iG, newSmartArtElement as iH, newTableElement as iI, newTextElement as iJ, nextVisibleIndex as iK, nodeBold as iL, nodeEditBox as iM, nodeFillColor as iN, nodeFontColor as iO, nodeIdFromKey as iP, nodeItalic as iQ, nodeStyle as iR, normalizeFontFormat as iS, normalizeSlidesPerPage as iT, normalizeValue as iU, numFromEvent as iV, ommlToMathml as iW, ooxmlDashToCssBorderStyle as iX, openNativeEyeDropper as iY, overallStatus as iZ, paletteColor as i_, isValidRoomId as ia, isViewportBackgroundPressTarget as ib, isZoomActivationKey as ic, issueTrackKey as id, issueTypeLabel as ie, keyToLabel as ig, lastVisibleIndex as ih, latexToMathml as ii, layoutConnectorPaints as ij, layoutNodeLabels as ik, linePointsToSvgString as il, lineSpacingPatch as im, loadAudienceContent as io, loadSessionDeck as ip, mediaFallbackFor as iq, mediaSurfaceFor as ir, mergeCaptionResults as is, mergeDown as it, mergeRight as iu, mergeSelection as iv, moveElementBy as iw, moveNodeDown as ix, moveNodeUp as iy, msToFrameDelayCs as iz, AdvancedChartEditorComponent as j, restoreSessionDeck as j$, parseNodeTextarea as j0, partitionSlides as j1, patchChartData as j2, patchChartStyle as j3, patchTableData as j4, patchTextStyle as j5, patternPresetOptions as j6, pendingElementStyles as j7, pickColorByClickFallback as j8, pickFile as j9, removeElementAnimation as jA, removeGradientStopPatch as jB, removeNode as jC, removeTableElementRow as jD, removeSeries as jE, renderToCanvas as jF, reorderAnimationDown as jG, reorderAnimationUp as jH, replaceInSlides as jI, replaceMatch as jJ, requestPresentationFullscreen as jK, resizeElement as jL, resolveCaptionTracks as jM, resolveChartKind as jN, resolveFontVariant as jO, resolveHyperlinkHref as jP, resolveInteractiveElementId as jQ, resolveMediaSrc as jR, resolveOleType as jS, resolveParagraphBullet as jT, resolvePresenterNotes as jU, resolveProfileInitial as jV, resolveRegionCode as jW, resolveSlideAutoAdvanceMs as jX, resolvePalette as jY, resolveThemeCatalogEntry as jZ, resolveTransitionDuration as j_, pickSupportedMimeType as ja, planGifFrames as jb, planVideoSegments 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, redistributeColumnWidth as jt, registerCrossSlideAudio as ju, rememberSessionDeck as jv, removeAnimation as jw, removeCategory as jx, removeTableElementColumn as jy, removeCommentFromList as jz, AiChangeOverlayComponent as k, shapeStylePatch as k$, revealedElementStyles as k0, routeOrthogonalConnector as k1, rowStyle as k2, rulerDragToGuidePosition as k3, rulerHighlight as k4, rulerStripTicks as k5, sampleColorFromSlide as k6, sanitizeColor as k7, sanitizeSlideIndex as k8, sanitizeUserName as k9, setDataPointFill as kA, setDataPointLabel as kB, setDataPointMarker as kC, setDelay as kD, setDirection as kE, setDuration as kF, setElementPosition as kG, setGridlineStyle as kH, setLayout as kI, setLegend as kJ, setNodeStyle as kK, setNodeText as kL, setRepeatCount as kM, setRepeatMode as kN, setSequence as kO, setSeriesChartType as kP, setSeriesColor as kQ, setSeriesErrorBars as kR, setSeriesMarker as kS, setSeriesName as kT, setSeriesTrendline as kU, setSeriesValue as kV, setStyle as kW, setTimingCurve as kX, setTitle as kY, setTrigger as kZ, setTriggerShapeId as k_, saveViewerProfile as ka, savedPresentationFileName as kb, scanAvailableFonts as kc, searchSlides as kd, seedBroadcastFields as ke, seedHyperlinkDraft as kf, seedPropertiesDraft as kg, seedShareFields as kh, segmentFrameCount as ki, selectValue$2 as kj, sendBackward as kk, sendToBack as kl, sequentialColorScale as km, serializeWriteBack as kn, seriesColor as ko, setAnimationEmphasis as kp, setAnimationEntrance as kq, setAnimationExit as kr, setAxis as ks, setAxisLogScale as kt, setAxisTitleStyle as ku, setCategoryLabel as kv, setCellText as kw, setColorScheme as kx, setDataLabels as ky, setDataPointExplosion as kz, AiChatPanelComponent as l, sheetAfterNavigate as l0, shouldBlockClickAdvance as l1, shouldUseSvgWarp as l2, showDirectionPicker as l3, showsTemplateAffordance as l4, signatureCountLabel as l5, signatureKey as l6, signatureTimestamp as l7, signerName as l8, statusLabel as l9, toggleCommentResolvedInList as lA, toggleNodeBold as lB, toggleNodeItalic as lC, toggleSheet as lD, topLevelNodeCount as lE, transformSelectedTextCase as lF, translationsEn as lG, updateElementById as lH, updateGlowPatch as lI, updateGradientStopPatch as lJ, updateInnerShadowPatch as lK, updateOuterShadowPatch as lL, updateReflectionPatch as lM, vAlignPatch as lN, validatePassword as lO, validatePrintSettings as lP, validateRoomId as lQ, valueToY as lR, vermilionDarkColors as lS, vermilionDarkTheme as lT, vermilionLightColors as lU, vermilionLightTheme as lV, vermilionRadius as lW, waypointsToPathD as lX, worstStatus as lY, zoomTargetSlideIndex as lZ, slideNumberOf as la, slidesWithReappliedLayout as lb, smartArtNodes as lc, paletteColour as ld, snapToGridStep as le, splitCursorCell as lf, splitMergedCell as lg, statusKind as lh, statusLabel$1 as li, storeAudienceContent as lj, stringFromEvent$5 as lk, strokeColorOf as ll, strokeToInkElement as lm, strokeWidthOf as ln, styleShadowFilter as lo, textAdvancedPatch as lp, textAdvancedStateFromStyle as lq, textAdvancedStateOf as lr, textColorOf as ls, textDirectionPatch as lt, textStyleOf as lu, textStylePatch as lv, themeStyle as lw, themeToCssVars as lx, thumbnailHeight as ly, thumbnailZoom as lz, AiChatService as m, AiComposerComponent as n, AiFocusBarComponent as o, AiFocusHighlightOverlayComponent as p, AiHistoryMenuComponent as q, AiHistoryService as r, AiMessageListComponent as s, toChatSummary as t, AiPanelStore as u, AiProposalCardComponent as v, AiSettingsSectionComponent as w, AiToolCallCardComponent as x, AnimationAuthorPanelComponent as y, AnimationPanelComponent as z };
|
|
137497
|
-
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-
|
|
137943
|
+
export { CommentsPanelComponent as $, ALIGN_OPTIONS as A, AnimationPlaybackService as B, AutosaveRecoveryDialogComponent as C, AutosaveService as D, BroadcastDialogComponent as E, CHART_EDITOR_STYLES as F, CURSOR_PALETTE as G, CanvasFitService as H, ChartAxisOptionsComponent as I, ChartAxisStyleOptionsComponent as J, ChartComboTypeOptionsComponent as K, ChartDataEditorComponent as L, ChartDataLabelOptionsComponent as M, ChartDatapointMarkerOptionsComponent as N, ChartDatapointOptionsComponent as O, ChartDisplayOptionsComponent as P, ChartElementViewComponent as Q, ChartErrorBarOptionsComponent as R, ChartMarkerOptionsComponent as S, ChartPartSelectionService as T, ChartPrimitivesComponent as U, ChartRendererComponent as V, ChartTrendlineOptionsComponent as W, CollaborationCursorsComponent as X, CollaborationService as Y, ColorChangedImageComponent as Z, CommentMarkersOverlayComponent as _, ANIMATION_PRESET_CATEGORIES as a, InspectorPanelComponent as a$, CommentsService as a0, ComparePanelComponent as a1, ConnectorRendererComponent as a2, ConnectorTextOverlayComponent as a3, CustomShowsComponent as a4, DATA_TABLE_HEADER_H as a5, DATA_TABLE_KEY_W as a6, DATA_TABLE_PADDING as a7, DATA_TABLE_ROW_H as a8, DEFAULT_BOUNDS as a9, EditorToolbarComponent as aA, EffectsPanelComponent as aB, ElementRendererComponent as aC, EmbeddedFontsService as aD, EncryptedFileDialogComponent as aE, EquationEditorDialogComponent as aF, EquationRendererComponent as aG, EquationTemplateGalleryComponent as aH, ExportProgressModalComponent as aI, ExportService as aJ, FieldContextService as aK, FindBarComponent as aL, FindReplaceBarComponent as aM, FollowModeBarComponent as aN, FontEmbeddingListComponent as aO, FontEmbeddingPanelComponent as aP, GALLERY_THEME_PRESETS as aQ, GRIDLINE_COLOR as aR, GradientPickerComponent as aS, HANDOUT_OPTIONS as aT, HeaderFooterDialogComponent as aU, HyperlinkDialogComponent as aV, ImagePropertiesPanelComponent as aW, InkDrawingService as aX, InkRendererComponent as aY, InsertSmartArtDialogComponent as aZ, InspectorPaneHeaderComponent 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_PALETTE$1 as ag, DEFAULT_PATTERN_FILL_PRESET as ah, DEFAULT_PRINT_SETTINGS as ai, DEFAULT_SLIDE_BACKGROUND as aj, DEFAULT_STROKE_COLOR as ak, DEFAULT_STYLE as al, DEFAULT_TABLE_ROW_HEIGHT as am, DEFAULT_TEXT_COLOR$1 as an, DEFAULT_VIEWER_PROFILE as ao, DIRECTIONAL_PRESETS as ap, DIRECTION_OPTIONS as aq, DocumentPropertiesCardComponent as ar, EMBEDDED_FONTS_STYLE_ID as as, EMPHASIS_PRESETS as at, ENTRANCE_PRESETS as au, TEMPLATES as av, EXIT_PRESETS as aw, EditorContextMenuComponent as ax, EditorHistory as ay, EditorStateService as az, AUDIENCE_HASH as b, RibbonComponent as b$, IsMobileService as b0, KeepAnnotationsDialogComponent as b1, LOCALE_CATALOG as b2, LONG_PRESS_DURATION_MS as b3, LONG_PRESS_MOVE_TOLERANCE_PX as b4, LoadContentService as b5, LocalPresencePublisher as b6, MAX_ZOOM_SCALE as b7, MIN_ZOOM_SCALE as b8, MOTION_PATH_COLUMNS as b9, PasswordStrengthMeterComponent as bA, PowerPointViewerComponent as bB, PresentToolbarAutoHide as bC, PresentationAnnotationOverlayComponent as bD, PresentationAnnotationsService as bE, PresentationOverlayComponent as bF, PresentationPropertiesPanelComponent as bG, PresentationSettingsCardComponent as bH, PresentationSubtitleBarComponent as bI, PresentationToolbarComponent as bJ, PresentationTransitionOverlayComponent as bK, PresenterViewComponent as bL, PresenterWindowService as bM, PrintDialogComponent as bN, PrintService as bO, PrintSettingsPanelComponent as bP, PropertiesDialogComponent as bQ, REPEAT_MODE_OPTIONS as bR, RESIZE_HANDLES as bS, RULER_FONT_SIZE as bT, RULER_THICKNESS as bU, ReadingViewOverlayComponent as bV, RemoteSelectionOverlayComponent as bW, RibbonAnimationGalleryComponent as bX, RibbonAnimationsSectionComponent as bY, RibbonArrangeSectionComponent as bZ, RibbonColorPopoverComponent as b_, MediaPreviewComponent as ba, MediaPropertiesPanelComponent as bb, MediaRendererComponent as bc, MediaTrimTimelineComponent as bd, MobileBottomBarComponent as be, MobileMenuSheetComponent as bf, MobilePresenterViewComponent as bg, MobileSheetComponent as bh, MobileSlidesSheetComponent as bi, MobileToolbarComponent as bj, ModalDialogComponent as bk, Model3DRendererComponent as bl, NotesHandoutCardComponent as bm, NotesPanelComponent as bn, NotesToolbarComponent as bo, OleRendererComponent as bp, OutlineViewOverlayComponent as bq, POWER_POINT_VIEWER_PROVIDERS as br, PPTX_OPEN_ACCEPT as bs, PRESENTATION_OPEN_EXTENSIONS as bt, PRESENTER_CHANNEL_NAME as bu, PRESENTER_MSG_ORIGIN as bv, PRESENTER_TIMER_SEGMENT_MS as bw, PX_PER_CM as bx, PX_PER_INCH as by, PasswordProtectionDialogComponent as bz, AUDIENCE_NONCE_KEY as c, TEXT_3D_TOP_BEVEL_KEYS as c$, RibbonDesignSectionComponent as c0, RibbonDrawSectionComponent as c1, RibbonDrawingGroupComponent as c2, RibbonEditingSectionComponent as c3, RibbonFileSectionComponent as c4, RibbonFontControlsComponent as c5, RibbonHomeSectionComponent as c6, RibbonHyperlinkButtonComponent as c7, RibbonInsertFieldsComponent as c8, RibbonInsertSectionComponent as c9, SettingsLanguageTabComponent as cA, ShareDialogComponent as cB, ShortcutPanelComponent as cC, ShowOptionsFieldsetComponent as cD, ShowSlidesFieldsetComponent as cE, SignatureStrippedDialogComponent as cF, SignaturesPanelComponent as cG, SignaturesService as cH, SlideBackgroundCardComponent as cI, SlideCanvasComponent as cJ, SlideDefaultInspectorComponent as cK, SlideDiffChangesComponent as cL, SlideDiffRowComponent as cM, SlideDiffThumbnailsComponent as cN, SlideSizeCardComponent as cO, SlideSorterOverlayComponent as cP, SlideThemeOverridePanelComponent as cQ, SlideTransitionCardComponent as cR, SlidesPanelComponent as cS, SmartArt3DRendererComponent as cT, SmartArt3DService as cU, SmartArtPreviewComponent as cV, SmartArtPropertiesComponent as cW, SmartArtRendererComponent as cX, StatusBarComponent as cY, TABLE_STRUCTURE_TOGGLES as cZ, TEXT_3D_BOTTOM_BEVEL_KEYS as c_, RibbonMotionPathGalleryComponent as ca, RibbonParagraphControlsComponent as cb, RibbonPrimaryRowComponent as cc, RibbonReviewSectionComponent as cd, RibbonShapeExtrasComponent as ce, RibbonSlideshowSectionComponent as cf, RibbonTransitionsSectionComponent as cg, RibbonViewSectionComponent as ch, RulerGuidesService as ci, SEQUENCE_OPTIONS as cj, SEVERITY_GROUPS as ck, SEVERITY_LABELS as cl, SHORTCUT_REFERENCE_ITEMS as cm, SLIDE_TRANSITION_KEYFRAMES as cn, DEFAULT_PALETTE as co, PALETTES$1 as cp, SMART_ART_COLOR_SCHEMES as cq, SMART_ART_STYLE_OPTIONS as cr, SUB_ITEM_LABEL as cs, SVG_WARP_PRESETS as ct, SWIPE_MAX_VERTICAL_PX as cu, SWIPE_THRESHOLD_PX as cv, SelectionPaneComponent as cw, SetUpSlideShowDialogComponent as cx, SettingsAppearanceTabComponent as cy, SettingsDialogComponent as cz, AVATAR_COLOR_SWATCHES as d, animationPresetLabelKey as d$, TEXT_DIRECTION_OPTIONS$1 as d0, THEME_CATALOG as d1, TIMING_CURVE_OPTIONS as d2, TRIGGER_OPTIONS as d3, TYPE_LABELS as d4, TableCellAdvancedFillComponent as d5, TableCellFormattingComponent as d6, TableDataEditorComponent as d7, TablePropertiesComponent as d8, TableRendererComponent as d9, ViewerFileIOService as dA, ViewerFindReplaceService as dB, ViewerFormatPainterService as dC, ViewerInspectorPanelService as dD, ViewerKeyboardService as dE, ViewerMobileSheetService as dF, ViewerPresentationModeService as dG, ViewerThemeGalleryService as dH, ViewerTouchGesturesService as dI, ViewerZoomService as dJ, WEBM_MIME_CANDIDATES as dK, WriteBackScheduler as dL, ZERO_LINE_COLOR as dM, ZoomNavigationService as dN, ZoomRendererComponent as dO, ZoomTargetService as dP, addCategory as dQ, addCommentToList as dR, addGradientStopPatch as dS, addItem as dT, addSeries as dU, addSubItem as dV, advanceStep as dW, affordanceElements as dX, aiToggleVisible as dY, alignPatch as dZ, animationFor as d_, TableResizeOverlayComponent as da, TableSelectionService as db, TagsCardComponent as dc, Text3DBevelSectionComponent as dd, Text3DPanelComponent as de, TextAdvancedPanelComponent as df, ThemeEditorFieldsComponent as dg, ThemeGalleryComponent as dh, ThemeSelectorCardComponent as di, TitleBarComponent as dj, TitleBarSearchComponent as dk, TransitionDirectionPickerComponent as dl, TransitionPreviewComponent as dm, VALIGN_OPTIONS as dn, VIEWER_THEME as dp, VersionHistoryPanelComponent as dq, ViewerCanvasEditingService as dr, ViewerCollabCursorService as ds, ViewerCollaborationSessionService as dt, ViewerCompareService as du, ViewerCustomShowsService as dv, ViewerDialogsService as dw, ViewerDocumentPropertiesService as dx, ViewerExportService as dy, ViewerExtraDialogsComponent as dz, AXIS_LABEL_COLOR as e, buildWaterfallViewModel as e$, annotationMapToInkInserts as e0, applyAcceptedDiff as e1, applyAnimationPreset as e2, applyFindReplacements as e3, applyFormatToElement as e4, applyMove as e5, applyResize as e6, asMediaElement as e7, assignUserColor as e8, attachShowVisibilityPause as e9, buildEquationSegment as eA, buildFallbackViewModel as eB, buildFontFaceRule as eC, buildGradientFillCss as eD, buildGridlinesAndLabels as eE, buildHyperlinkPatch as eF, buildInkContainerStyle as eG, buildInkStrokes as eH, buildLegend as eI, buildMarkTooltip as eJ, buildModel3DContainerStyle as eK, buildModel3DViewModel as eL, buildOleActionModel as eM, buildOleInfoRows as eN, buildPatternFillCss as eO, buildPrintHtmlDocument as eP, buildPropertiesPatch as eQ, buildRegionMapViewModel as eR, buildSaveSlides as eS, buildShareUrl as eT, buildSmartArtInsertElement as eU, buildSmartArtNodes as eV, buildStockViewModel as eW, buildSurfaceViewModel as eX, buildTableViewModel as eY, buildTreemapViewModel as eZ, buildTrimFragment as e_, attachTouchGestures as ea, axisTickValues as eb, beginNodeEdit as ec, bevelSizePatch as ed, boolFromEvent as ee, bringForward as ef, bringToFront as eg, buildBarActions as eh, buildBroadcastConfig as ei, buildBroadcastViewerUrl as ej, buildCategoryLabels as ek, buildCellParagraphs as el, buildChartViewModel as em, buildChatLogExport as en, buildChatLogMarkdown as eo, buildChromeStyle as ep, buildClearHyperlinkPatch as eq, buildClickGroups as er, buildColStyles as es, buildCollaborationConfig as et, buildComboViewModel as eu, buildCssGradientFromShapeStyle as ev, buildDuotoneFilter as ew, buildDuotoneFilterId as ex, buildEmbeddedFontStyles as ey, buildEquationElement as ez, AccessibilityPanelComponent as f, computeScatterXDomain as f$, buildZeroLine as f0, buildZoomContainerStyle as f1, buildZoomViewModel as f2, bulletIndentPx as f3, canAddTopLevelNode as f4, canGroupSelection as f5, canRemoveTopLevelNode as f6, canSetStrokeWidth as f7, canStartBroadcast as f8, canStartShare as f9, commitNodeText as fA, computeAlign as fB, computeAxisTitlePrimitives as fC, computeBarRects as fD, computeBubbleRadius as fE, computeCornerHandle as fF, computeDataTablePrimitives as fG, computeDistribute as fH, computeDrawingViewBox as fI, computeErrorBarPrimitives as fJ, computeFocusTargets as fK, computeHandleBoxes as fL, computeHandoutLayout as fM, computeIsMobile as fN, computeIsTablet as fO, computeLinePoints as fP, computeLinearRegression as fQ, computePageCount as fR, computePieLayout as fS, computePieSlicePath as fT, computePieSlices as fU, computePlotLayout as fV, computeRSquared as fW, computeRadarPoints as fX, computeResizeHandleBoxes as fY, computeRotateHandleBox as fZ, computeScatterDots as f_, canUngroupSelection as fa, canUseClipboard as fb, captionDisplayText as fc, cellRunStyle as fd, cellStyleToStyleMap as fe, cellTdStyle as ff, changeCountLabel as fg, changeIcon as fh, characterSpacingPatch as fi, chartPreserveAspectRatio as fj, checkFontAvailable as fk, clampCursorPosition as fl, clampGifDimensions as fm, clampIndex as fn, clampNotesFontSize as fo, clampScale as fp, clampStep as fq, clearAllLocalViewerData as fr, clearAudienceContent as fs, cn as ft, collectAccessibilityIssues as fu, collectElementText as fv, collectSlideText as fw, collectStoredChats as fx, collectUsedFontFamilies as fy, columnWidthStyle as fz, AccessibilityService as g, formatCursorLabel as g$, computeSelectionBoxes as g0, computeSingleSelected as g1, computeSlideIndices as g2, computeSnap as g3, computeStackedBarRects as g4, computeStackedValueRange as g5, computeTrendlinePrimitives as g6, computeValueRange as g7, convertOmmlToMathMl as g8, copyFormatFromElement as g9, durationOf as gA, effectsStateOf as gB, enableGlowPatch as gC, enableInnerShadowPatch as gD, enableOuterShadowPatch as gE, enableReflectionPatch as gF, enableSoftEdgePatch as gG, encodeGif as gH, endShowMediaCleanup as gI, estimatePageCount as gJ, exitPresentationFullscreen as gK, exportAiChatLogs as gL, extractPathPoints as gM, eyedropperAvailable as gN, fillColorOf$1 as gO, findInSlides as gP, findOwningSlideIndex as gQ, findSlideIndexByElementId as gR, firstVisibleIndex as gS, fitPolynomial as gT, fitZoom as gU, focusTargetChips as gV, fontMimeForFormat as gW, fontSizeOf as gX, forgetSessionDeck as gY, formatAxisValue as gZ, formatBytes as g_, countAccessibilityIssues as ga, countAnnotationStrokes as gb, createAngularAiBridge as gc, createCustomShow as gd, createSwipeDismissDrag as ge, createWebrtcBundle as gf, createWebsocketBundle as gg, cssObjectToStyleMap as gh, currentColorScheme as gi, currentLayout as gj, currentStyle as gk, defaultCssVars as gl, defaultRadius as gm, defaultThemeColors as gn, deleteElementsByIds as go, deleteVersion as gp, demoteNode as gq, deriveModel3DBlobUrl as gr, derivePresenceList as gs, describeSmartArtBounds as gt, disableGlowPatch as gu, disableInnerShadowPatch as gv, disableOuterShadowPatch as gw, disableReflectionPatch as gx, disableSoftEdgePatch as gy, duplicateElementById as gz, AccountPageComponent as h, isLegacyBinaryPresentation as h$, formatElapsed as h0, formatFileSize as h1, formatPropertyDate as h2, formatTime as h3, fpsToFrameIntervalMs as h4, generateBroadcastRoomId as h5, generateCommentId as h6, generateCustomShowId as h7, generatePressureCircles as h8, generateTicks as h9, getWarpCategory as hA, getWarpPath as hB, gradientStateFromStyle as hC, gradientStateOf as hD, gradientStatePatch as hE, gridColumns as hF, groupIssuesBySeverity as hG, hasAnimation as hH, hasCopyableFormat as hI, hasExistingLink as hJ, hasExitedFullscreen as hK, hasGradientFill as hL, hasPressureVariation as hM, hasVisibleSlideAfter as hN, headerLabel as hO, imageDimensions as hP, inkViewBox as hQ, insertTableElementColumn as hR, insertTableElementRow as hS, interpolateWidth as hT, isAudienceTab as hU, isBold as hV, isBrowserOpenableMime as hW, isChildNode as hX, isElementInteractive as hY, isInjectableUrl as hZ, isItalic as h_, getClrChangeParams as ha, getContainerStyle as hb, getDuotoneFilterDef as hc, getImageSrc as hd, getLocalStorageUsageSummary as he, getOleAriaLabel as hf, getOleBadgeLabel as hg, getOleDisplayName as hh, getOleDownloadFileName as hi, getOleTypeColor as hj, getOleTypeLabel as hk, getPasswordStrength as hl, getPatternSvg as hm, getPlaceholderStyle as hn, getVersions as ho, getResolvedShapeClipPath as hp, getResolvedShapeClipPathFor as hq, getSessionTabId as hr, getShapeFillStrokeStyle as hs, getSlideBackgroundStyle as ht, getSlideTransitionAnimations as hu, getSmartArtNodeBounds as hv, getSpeechRecognitionCtor as hw, getTextBlockStyle as hx, getTextWarp as hy, getTouchDistance as hz, ActionSettingsPanelComponent as i, partitionSlides as i$, isPpactionUrl as i0, isPresenterMessage as i1, isSigned as i2, isSupportedPresentationFile as i3, isTextElement as i4, isTwoTableFocus as i5, isUnderline as i6, isUrlSafe as i7, isValidRoomId as i8, isViewportBackgroundPressTarget as i9, narrowToRect as iA, newChartElement as iB, newEquationElement as iC, newPresetShapeElement as iD, newShapeElement as iE, newSmartArtElement as iF, newTableElement as iG, newTextElement as iH, nextVisibleIndex as iI, nodeBold as iJ, nodeEditBox as iK, nodeFillColor as iL, nodeFontColor as iM, nodeIdFromKey as iN, nodeItalic as iO, nodeStyle as iP, normalizeFontFormat as iQ, normalizeSlidesPerPage as iR, normalizeValue as iS, numFromEvent as iT, ommlToMathml as iU, ooxmlDashToCssBorderStyle as iV, openNativeEyeDropper as iW, overallStatus as iX, paletteColor as iY, parseAudienceNonce as iZ, parseNodeTextarea as i_, isZoomActivationKey as ia, issueTrackKey as ib, issueTypeLabel as ic, keyToLabel as id, lastVisibleIndex as ie, latexToMathml as ig, layoutConnectorPaints as ih, layoutNodeLabels as ii, linePointsToSvgString as ij, lineSpacingPatch as ik, loadAudienceContent as il, loadSessionDeck as im, mediaFallbackFor as io, mediaSurfaceFor as ip, mergeCaptionResults as iq, mergeDown as ir, mergeRight as is, mergeSelection as it, moveElementBy as iu, moveNodeDown as iv, moveNodeUp as iw, msToFrameDelayCs as ix, narrowToCircle as iy, narrowToPolygon as iz, AdvancedChartEditorComponent as j, rowStyle as j$, patchChartData as j0, patchChartStyle as j1, patchTableData as j2, patchTextStyle as j3, patternPresetOptions as j4, pendingElementStyles as j5, pickColorByClickFallback as j6, pickFile as j7, pickSupportedMimeType as j8, planGifFrames as j9, removeTableElementRow as jA, removeSeries as jB, renderToCanvas as jC, reorderAnimationDown as jD, reorderAnimationUp as jE, replaceInSlides as jF, replaceMatch as jG, requestPresentationFullscreen as jH, resizeElement as jI, resolveCaptionTracks as jJ, resolveChartKind as jK, resolveFontVariant as jL, resolveHyperlinkHref as jM, resolveInteractiveElementId as jN, resolveMediaSrc as jO, resolveOleType as jP, resolveParagraphBullet as jQ, resolvePresenterNotes as jR, resolveProfileInitial as jS, resolveRegionCode as jT, resolveSlideAutoAdvanceMs as jU, resolvePalette as jV, resolveThemeCatalogEntry as jW, resolveTransitionDuration as jX, restoreSessionDeck as jY, revealedElementStyles as jZ, routeOrthogonalConnector as j_, planVideoSegments as ja, pointsToSvgPathD as jb, presenceToCursors as jc, presentationBaseName as jd, presentationStageStyle as je, presenterTimerProgress as jf, presetByLayout as jg, presetsForCategory as jh, pressuresToWidths as ji, prevVisibleIndex as jj, projectDrawingShapes as jk, promoteNode as jl, provideViewerTheme as jm, radarAngle as jn, radarRingPoints as jo, readAsDataUrl as jp, recordWebm as jq, registerCrossSlideAudio as jr, rememberSessionDeck as js, removeAnimation as jt, removeCategory as ju, removeTableElementColumn as jv, removeCommentFromList as jw, removeElementAnimation as jx, removeGradientStopPatch as jy, removeNode as jz, AiChangeOverlayComponent as k, shouldUseSvgWarp as k$, rulerDragToGuidePosition as k0, rulerHighlight as k1, rulerStripTicks as k2, sampleColorFromSlide as k3, sanitizeColor as k4, sanitizeSlideIndex as k5, sanitizeUserName as k6, saveViewerProfile as k7, savedPresentationFileName as k8, scanAvailableFonts as k9, setDelay as kA, setDirection as kB, setDuration as kC, setElementPosition as kD, setGridlineStyle as kE, setLayout as kF, setLegend as kG, setNodeStyle as kH, setNodeText as kI, setRepeatCount as kJ, setRepeatMode as kK, setSequence as kL, setSeriesChartType as kM, setSeriesColor as kN, setSeriesErrorBars as kO, setSeriesMarker as kP, setSeriesName as kQ, setSeriesTrendline as kR, setSeriesValue as kS, setStyle as kT, setTimingCurve as kU, setTitle as kV, setTrigger as kW, setTriggerShapeId as kX, shapeStylePatch$1 as kY, sheetAfterNavigate as kZ, shouldBlockClickAdvance as k_, searchSlides as ka, seedBroadcastFields as kb, seedHyperlinkDraft as kc, seedPropertiesDraft as kd, seedShareFields as ke, segmentFrameCount as kf, selectValue$2 as kg, sendBackward as kh, sendToBack as ki, sequentialColorScale as kj, serializeWriteBack as kk, seriesColor as kl, setAnimationEmphasis as km, setAnimationEntrance as kn, setAnimationExit as ko, setAxis as kp, setAxisLogScale as kq, setAxisTitleStyle as kr, setCategoryLabel as ks, setCellText as kt, setColorScheme as ku, setDataLabels as kv, setDataPointExplosion as kw, setDataPointFill as kx, setDataPointLabel as ky, setDataPointMarker as kz, AiChatPanelComponent as l, showDirectionPicker as l0, showsTemplateAffordance as l1, signatureCountLabel as l2, signatureKey as l3, signatureTimestamp as l4, signerName as l5, statusLabel as l6, slideNumberOf as l7, slidesWithReappliedLayout as l8, smartArtNodes as l9, toggleSheet as lA, topLevelNodeCount as lB, transformSelectedTextCase as lC, translationsEn as lD, updateElementById as lE, updateGlowPatch as lF, updateGradientStopPatch as lG, updateInnerShadowPatch as lH, updateOuterShadowPatch as lI, updateReflectionPatch as lJ, vAlignPatch as lK, validatePassword as lL, validatePrintSettings as lM, validateRoomId as lN, valueToY as lO, vermilionDarkColors as lP, vermilionDarkTheme as lQ, vermilionLightColors as lR, vermilionLightTheme as lS, vermilionRadius as lT, waypointsToPathD as lU, worstStatus as lV, zoomTargetSlideIndex as lW, paletteColour as la, snapToGridStep as lb, splitCursorCell as lc, splitMergedCell as ld, statusKind as le, statusLabel$1 as lf, storeAudienceContent as lg, stringFromEvent$5 as lh, strokeColorOf as li, strokeToInkElement as lj, strokeWidthOf as lk, styleShadowFilter as ll, textAdvancedPatch as lm, textAdvancedStateFromStyle as ln, textAdvancedStateOf as lo, textColorOf as lp, textDirectionPatch as lq, textStyleOf as lr, textStylePatch as ls, themeStyle as lt, themeToCssVars as lu, thumbnailHeight as lv, thumbnailZoom as lw, toggleCommentResolvedInList as lx, toggleNodeBold as ly, toggleNodeItalic as lz, AiChatService as m, AiComposerComponent as n, AiFocusBarComponent as o, AiFocusHighlightOverlayComponent as p, AiHistoryMenuComponent as q, AiHistoryService as r, AiMessageListComponent as s, toChatSummary as t, AiPanelStore as u, AiProposalCardComponent as v, AiSettingsSectionComponent as w, AiToolCallCardComponent as x, AnimationAuthorPanelComponent as y, AnimationPanelComponent as z };
|
|
137944
|
+
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-BLdLZTxr.mjs.map
|