pptx-angular-viewer 1.1.34 → 1.1.36
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NgStyle, NgClass, NgTemplateOutlet } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
3
|
import { InjectionToken, input, output, computed, ChangeDetectionStrategy, Component, signal, Injectable, HostListener, effect, inject, DestroyRef, ElementRef, viewChild, afterNextRender, Injector } from '@angular/core';
|
|
4
|
-
import { guideEmuToPx, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, THEME_COLOR_SCHEME_KEYS, getLinkedTextBoxSegments, deobfuscateFont, detectFontFormat, getSubstituteFontFamily, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, formatCommentTimestamp, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, chartDataChangeType, PptxHandler, EncryptedFileError, parseSignatureXml, isInkElement, isZoomElement, THEME_PRESETS, applyThemeToData, getAnimationPresetInfo } from 'pptx-viewer-core';
|
|
4
|
+
import { guideEmuToPx, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hasShapeProperties, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, isImageLikeElement, hasTextProperties, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$2, THEME_COLOR_SCHEME_KEYS, getLinkedTextBoxSegments, deobfuscateFont, detectFontFormat, getSubstituteFontFamily, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, formatCommentTimestamp, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, chartDataChangeType, updateSmartArtNodeText, addSmartArtNodeAsChild, removeSmartArtNode, promoteSmartArtNode, demoteSmartArtNode, reorderSmartArtNode, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, PptxHandler, EncryptedFileError, parseSignatureXml, isInkElement, isZoomElement, THEME_PRESETS, applyThemeToData, getAnimationPresetInfo } from 'pptx-viewer-core';
|
|
5
5
|
import { jsPDF } from 'jspdf';
|
|
6
6
|
import html2canvasPro from 'html2canvas-pro';
|
|
7
7
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -24354,6 +24354,57 @@ const PRESETS = [
|
|
|
24354
24354
|
category: 'process',
|
|
24355
24355
|
defaultItems: ['Phase 1', 'Phase 2', 'Phase 3'],
|
|
24356
24356
|
},
|
|
24357
|
+
{
|
|
24358
|
+
layout: 'basicTimeline',
|
|
24359
|
+
label: 'Basic Timeline',
|
|
24360
|
+
category: 'process',
|
|
24361
|
+
defaultItems: ['2021', '2022', '2023'],
|
|
24362
|
+
},
|
|
24363
|
+
{
|
|
24364
|
+
layout: 'bendingProcess',
|
|
24365
|
+
label: 'Bending Process',
|
|
24366
|
+
category: 'process',
|
|
24367
|
+
defaultItems: ['Step 1', 'Step 2', 'Step 3', 'Step 4'],
|
|
24368
|
+
},
|
|
24369
|
+
// Relationship: target / matrix / pyramid / funnel / gear families.
|
|
24370
|
+
// These share the dialog's "relationship" sidebar tab (PowerPoint groups
|
|
24371
|
+
// matrix/pyramid under separate tabs, which this gallery folds into one).
|
|
24372
|
+
{
|
|
24373
|
+
layout: 'basicTarget',
|
|
24374
|
+
label: 'Basic Target',
|
|
24375
|
+
category: 'relationship',
|
|
24376
|
+
defaultItems: ['Outer', 'Middle', 'Inner'],
|
|
24377
|
+
},
|
|
24378
|
+
{
|
|
24379
|
+
layout: 'interlockingGears',
|
|
24380
|
+
label: 'Interlocking Gears',
|
|
24381
|
+
category: 'relationship',
|
|
24382
|
+
defaultItems: ['Gear 1', 'Gear 2', 'Gear 3'],
|
|
24383
|
+
},
|
|
24384
|
+
{
|
|
24385
|
+
layout: 'basicMatrix',
|
|
24386
|
+
label: 'Basic Matrix',
|
|
24387
|
+
category: 'relationship',
|
|
24388
|
+
defaultItems: ['Quadrant 1', 'Quadrant 2', 'Quadrant 3', 'Quadrant 4'],
|
|
24389
|
+
},
|
|
24390
|
+
{
|
|
24391
|
+
layout: 'basicPyramid',
|
|
24392
|
+
label: 'Basic Pyramid',
|
|
24393
|
+
category: 'relationship',
|
|
24394
|
+
defaultItems: ['Top', 'Middle', 'Base'],
|
|
24395
|
+
},
|
|
24396
|
+
{
|
|
24397
|
+
layout: 'invertedPyramid',
|
|
24398
|
+
label: 'Inverted Pyramid',
|
|
24399
|
+
category: 'relationship',
|
|
24400
|
+
defaultItems: ['Wide', 'Narrower', 'Tip'],
|
|
24401
|
+
},
|
|
24402
|
+
{
|
|
24403
|
+
layout: 'basicFunnel',
|
|
24404
|
+
label: 'Basic Funnel',
|
|
24405
|
+
category: 'relationship',
|
|
24406
|
+
defaultItems: ['Input 1', 'Input 2', 'Input 3', 'Result'],
|
|
24407
|
+
},
|
|
24357
24408
|
];
|
|
24358
24409
|
/** Sidebar tabs, in display order. `label` is a (best-effort) i18n key. */
|
|
24359
24410
|
const CATEGORIES = [
|
|
@@ -25373,6 +25424,149 @@ function toggleCommentResolvedInList(comments, id) {
|
|
|
25373
25424
|
return next;
|
|
25374
25425
|
}
|
|
25375
25426
|
|
|
25427
|
+
/**
|
|
25428
|
+
* Framework-agnostic touch-gesture state machine for the viewer canvas.
|
|
25429
|
+
*
|
|
25430
|
+
* Recognises three gestures from raw DOM `TouchEvent`s and emits callbacks:
|
|
25431
|
+
* - Pinch-to-zoom: two-finger spread/pinch, scaled by the distance ratio.
|
|
25432
|
+
* - Swipe: single-finger horizontal swipe (slide navigation).
|
|
25433
|
+
* - Long-press: single-finger hold (context-menu trigger).
|
|
25434
|
+
*
|
|
25435
|
+
* The recogniser holds all mutable pinch/swipe/long-press state internally and
|
|
25436
|
+
* exposes four event handlers plus a `cancel()`. The listener attach/detach
|
|
25437
|
+
* lifecycle (and any framework reactivity) stays in each binding; only this
|
|
25438
|
+
* pure state machine is shared.
|
|
25439
|
+
*
|
|
25440
|
+
* @module touch-gestures
|
|
25441
|
+
*/
|
|
25442
|
+
// ---------------------------------------------------------------------------
|
|
25443
|
+
// Constants
|
|
25444
|
+
// ---------------------------------------------------------------------------
|
|
25445
|
+
/** The minimum horizontal distance (px) to recognise a swipe. */
|
|
25446
|
+
const SWIPE_THRESHOLD_PX = 50;
|
|
25447
|
+
/** Maximum vertical deviation (px) for a swipe to still count. */
|
|
25448
|
+
const SWIPE_MAX_VERTICAL_PX = 100;
|
|
25449
|
+
/** The hold duration (ms) for a long-press. */
|
|
25450
|
+
const LONG_PRESS_DURATION_MS = 500;
|
|
25451
|
+
/** If the finger moves more than this (px) during a hold, cancel the long-press. */
|
|
25452
|
+
const LONG_PRESS_MOVE_TOLERANCE_PX = 10;
|
|
25453
|
+
// ---------------------------------------------------------------------------
|
|
25454
|
+
// Pure helpers
|
|
25455
|
+
// ---------------------------------------------------------------------------
|
|
25456
|
+
/** Compute the Euclidean distance between two touch points. */
|
|
25457
|
+
function getTouchDistance(t1, t2) {
|
|
25458
|
+
const dx = t1.clientX - t2.clientX;
|
|
25459
|
+
const dy = t1.clientY - t2.clientY;
|
|
25460
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
25461
|
+
}
|
|
25462
|
+
/**
|
|
25463
|
+
* Clamp a scale value into the allowed zoom range. The bounds are parameters
|
|
25464
|
+
* (not imported constants) to keep this module framework- and binding-agnostic.
|
|
25465
|
+
*/
|
|
25466
|
+
function clampScale$1(value, minScale, maxScale) {
|
|
25467
|
+
return Math.min(Math.max(value, minScale), maxScale);
|
|
25468
|
+
}
|
|
25469
|
+
// ---------------------------------------------------------------------------
|
|
25470
|
+
// Recogniser factory
|
|
25471
|
+
// ---------------------------------------------------------------------------
|
|
25472
|
+
/**
|
|
25473
|
+
* Create a stateful touch-gesture recogniser. The returned handlers mirror the
|
|
25474
|
+
* React viewer's native `touch*` listeners exactly, including `preventDefault()`
|
|
25475
|
+
* on the pinch path and the `setTimeout`-based long-press timer.
|
|
25476
|
+
*/
|
|
25477
|
+
function createTouchGestureRecognizer(config) {
|
|
25478
|
+
const { getScale, minScale, maxScale, callbacks } = config;
|
|
25479
|
+
// Pinch state.
|
|
25480
|
+
let initialPinchDistance = 0;
|
|
25481
|
+
let pinchBaseScale = 1;
|
|
25482
|
+
let isPinching = false;
|
|
25483
|
+
// Swipe state.
|
|
25484
|
+
let swipeStartX = 0;
|
|
25485
|
+
let swipeStartY = 0;
|
|
25486
|
+
// Long-press state.
|
|
25487
|
+
let longPressTimer = null;
|
|
25488
|
+
let longPressStartX = 0;
|
|
25489
|
+
let longPressStartY = 0;
|
|
25490
|
+
const cancelLongPress = () => {
|
|
25491
|
+
if (longPressTimer !== null) {
|
|
25492
|
+
clearTimeout(longPressTimer);
|
|
25493
|
+
longPressTimer = null;
|
|
25494
|
+
}
|
|
25495
|
+
};
|
|
25496
|
+
const onTouchStart = (e) => {
|
|
25497
|
+
if (e.touches.length === 2) {
|
|
25498
|
+
// Start pinch.
|
|
25499
|
+
isPinching = true;
|
|
25500
|
+
initialPinchDistance = getTouchDistance(e.touches[0], e.touches[1]);
|
|
25501
|
+
pinchBaseScale = getScale();
|
|
25502
|
+
cancelLongPress();
|
|
25503
|
+
e.preventDefault();
|
|
25504
|
+
}
|
|
25505
|
+
else if (e.touches.length === 1) {
|
|
25506
|
+
// Potential swipe or long-press.
|
|
25507
|
+
swipeStartX = e.touches[0].clientX;
|
|
25508
|
+
swipeStartY = e.touches[0].clientY;
|
|
25509
|
+
longPressStartX = e.touches[0].clientX;
|
|
25510
|
+
longPressStartY = e.touches[0].clientY;
|
|
25511
|
+
cancelLongPress();
|
|
25512
|
+
longPressTimer = setTimeout(() => {
|
|
25513
|
+
longPressTimer = null;
|
|
25514
|
+
callbacks.onLongPress?.(longPressStartX, longPressStartY);
|
|
25515
|
+
}, LONG_PRESS_DURATION_MS);
|
|
25516
|
+
}
|
|
25517
|
+
};
|
|
25518
|
+
const onTouchMove = (e) => {
|
|
25519
|
+
if (e.touches.length === 2 && isPinching) {
|
|
25520
|
+
e.preventDefault();
|
|
25521
|
+
const currentDistance = getTouchDistance(e.touches[0], e.touches[1]);
|
|
25522
|
+
if (initialPinchDistance > 0) {
|
|
25523
|
+
const ratio = currentDistance / initialPinchDistance;
|
|
25524
|
+
const newScale = clampScale$1(pinchBaseScale * ratio, minScale, maxScale);
|
|
25525
|
+
callbacks.onPinchZoom?.(newScale);
|
|
25526
|
+
}
|
|
25527
|
+
}
|
|
25528
|
+
else if (e.touches.length === 1) {
|
|
25529
|
+
// Cancel the long-press if the finger moved too far.
|
|
25530
|
+
const dx = e.touches[0].clientX - longPressStartX;
|
|
25531
|
+
const dy = e.touches[0].clientY - longPressStartY;
|
|
25532
|
+
if (Math.abs(dx) > LONG_PRESS_MOVE_TOLERANCE_PX ||
|
|
25533
|
+
Math.abs(dy) > LONG_PRESS_MOVE_TOLERANCE_PX) {
|
|
25534
|
+
cancelLongPress();
|
|
25535
|
+
}
|
|
25536
|
+
}
|
|
25537
|
+
};
|
|
25538
|
+
const onTouchEnd = (e) => {
|
|
25539
|
+
if (isPinching) {
|
|
25540
|
+
isPinching = false;
|
|
25541
|
+
initialPinchDistance = 0;
|
|
25542
|
+
return;
|
|
25543
|
+
}
|
|
25544
|
+
cancelLongPress();
|
|
25545
|
+
// Detect a swipe from the touch that just ended.
|
|
25546
|
+
if (e.changedTouches.length === 1 && e.touches.length === 0) {
|
|
25547
|
+
const endX = e.changedTouches[0].clientX;
|
|
25548
|
+
const endY = e.changedTouches[0].clientY;
|
|
25549
|
+
const deltaX = endX - swipeStartX;
|
|
25550
|
+
const deltaY = endY - swipeStartY;
|
|
25551
|
+
if (Math.abs(deltaX) >= SWIPE_THRESHOLD_PX && Math.abs(deltaY) < SWIPE_MAX_VERTICAL_PX) {
|
|
25552
|
+
callbacks.onSwipe?.(deltaX > 0 ? 1 : -1);
|
|
25553
|
+
}
|
|
25554
|
+
}
|
|
25555
|
+
};
|
|
25556
|
+
const onTouchCancel = () => {
|
|
25557
|
+
isPinching = false;
|
|
25558
|
+
initialPinchDistance = 0;
|
|
25559
|
+
cancelLongPress();
|
|
25560
|
+
};
|
|
25561
|
+
return {
|
|
25562
|
+
onTouchStart,
|
|
25563
|
+
onTouchMove,
|
|
25564
|
+
onTouchEnd,
|
|
25565
|
+
onTouchCancel,
|
|
25566
|
+
cancel: onTouchCancel,
|
|
25567
|
+
};
|
|
25568
|
+
}
|
|
25569
|
+
|
|
25376
25570
|
/**
|
|
25377
25571
|
* insert-chart.ts - framework-agnostic factory for a sensible DEFAULT new
|
|
25378
25572
|
* chart element, the single source of truth every binding (React, Vue,
|
|
@@ -35190,6 +35384,555 @@ function stringFromEvent$2(event) {
|
|
|
35190
35384
|
* preserves the historical Angular import surface.
|
|
35191
35385
|
*/
|
|
35192
35386
|
|
|
35387
|
+
/**
|
|
35388
|
+
* smart-art-properties-helpers.ts: pure logic for the Angular SmartArt inspector.
|
|
35389
|
+
*
|
|
35390
|
+
* The presentational `SmartArtPropertiesComponent` is intentionally thin: every
|
|
35391
|
+
* mutation it performs is delegated to one of the pure functions below, which in
|
|
35392
|
+
* turn wrap the framework-agnostic SmartArt editing operations re-exported from
|
|
35393
|
+
* `editor-insert.ts` (sourced from `pptx-viewer-core`). Keeping this logic out of
|
|
35394
|
+
* the component lets it be unit-tested in plain vitest (the Angular package's
|
|
35395
|
+
* vitest setup has no Angular compiler, so TestBed component tests are not
|
|
35396
|
+
* available - see PORTING.md).
|
|
35397
|
+
*
|
|
35398
|
+
* Mirrors the React inspector:
|
|
35399
|
+
* packages/react/src/viewer/components/inspector/SmartArtPropertiesPanel.tsx
|
|
35400
|
+
* packages/react/src/viewer/components/inspector/SmartArtLayoutSwitcher.tsx
|
|
35401
|
+
*
|
|
35402
|
+
* @module angular-viewer/smart-art-properties-helpers
|
|
35403
|
+
*/
|
|
35404
|
+
// ── Option constants (mirror the React panel) ───────────────────────────────
|
|
35405
|
+
/** Selectable colour schemes, in display order. */
|
|
35406
|
+
const SMART_ART_COLOR_SCHEMES = [
|
|
35407
|
+
'colorful1',
|
|
35408
|
+
'colorful2',
|
|
35409
|
+
'colorful3',
|
|
35410
|
+
'monochromatic1',
|
|
35411
|
+
'monochromatic2',
|
|
35412
|
+
];
|
|
35413
|
+
/** Selectable style intensities, in display order. */
|
|
35414
|
+
const SMART_ART_STYLE_OPTIONS = [
|
|
35415
|
+
'flat',
|
|
35416
|
+
'moderate',
|
|
35417
|
+
'intense',
|
|
35418
|
+
];
|
|
35419
|
+
/** Default colour scheme used when the data has none set. */
|
|
35420
|
+
const DEFAULT_COLOR_SCHEME = 'colorful1';
|
|
35421
|
+
/** Default style used when the data has none set. */
|
|
35422
|
+
const DEFAULT_STYLE = 'flat';
|
|
35423
|
+
/** Default resolved layout used when the data has none set. */
|
|
35424
|
+
const DEFAULT_LAYOUT = 'list';
|
|
35425
|
+
/** Default label for a newly added sub-item (matches the React panel). */
|
|
35426
|
+
const SUB_ITEM_LABEL = 'Sub-item';
|
|
35427
|
+
// ── Read helpers ─────────────────────────────────────────────────────────────
|
|
35428
|
+
/** The diagram's nodes, or an empty array when none are present. */
|
|
35429
|
+
function smartArtNodes(data) {
|
|
35430
|
+
return data.nodes ?? [];
|
|
35431
|
+
}
|
|
35432
|
+
/** Whether a node has a parent (is a child / sub-item). */
|
|
35433
|
+
function isChildNode(node) {
|
|
35434
|
+
return Boolean(node.parentId);
|
|
35435
|
+
}
|
|
35436
|
+
/** Current colour scheme, falling back to the default. */
|
|
35437
|
+
function currentColorScheme(data) {
|
|
35438
|
+
return data.colorScheme ?? DEFAULT_COLOR_SCHEME;
|
|
35439
|
+
}
|
|
35440
|
+
/** Current style intensity, falling back to the default. */
|
|
35441
|
+
function currentStyle(data) {
|
|
35442
|
+
return data.style ?? DEFAULT_STYLE;
|
|
35443
|
+
}
|
|
35444
|
+
/** Current resolved layout, falling back to the default. */
|
|
35445
|
+
function currentLayout(data) {
|
|
35446
|
+
return data.resolvedLayoutType ?? DEFAULT_LAYOUT;
|
|
35447
|
+
}
|
|
35448
|
+
// ── Mutation helpers (all return new immutable PptxSmartArtData) ──────────────
|
|
35449
|
+
/** Update a single node's text. */
|
|
35450
|
+
function setNodeText(data, nodeId, text) {
|
|
35451
|
+
return updateSmartArtNodeText(data, nodeId, text);
|
|
35452
|
+
}
|
|
35453
|
+
/** Append a new top-level item. */
|
|
35454
|
+
function addItem(data) {
|
|
35455
|
+
return addSmartArtNodeAsChild(data);
|
|
35456
|
+
}
|
|
35457
|
+
/** Add a sub-item beneath the given parent node. */
|
|
35458
|
+
function addSubItem(data, parentId) {
|
|
35459
|
+
return addSmartArtNodeAsChild(data, parentId, SUB_ITEM_LABEL);
|
|
35460
|
+
}
|
|
35461
|
+
/** Remove a node by id (no-op when it would empty the diagram). */
|
|
35462
|
+
function removeNode(data, nodeId) {
|
|
35463
|
+
if (smartArtNodes(data).length <= 1) {
|
|
35464
|
+
return data;
|
|
35465
|
+
}
|
|
35466
|
+
return removeSmartArtNode(data, nodeId);
|
|
35467
|
+
}
|
|
35468
|
+
/** Promote a node up one level in the hierarchy. */
|
|
35469
|
+
function promoteNode(data, nodeId) {
|
|
35470
|
+
return promoteSmartArtNode(data, nodeId);
|
|
35471
|
+
}
|
|
35472
|
+
/** Demote a node beneath its preceding sibling. */
|
|
35473
|
+
function demoteNode(data, nodeId) {
|
|
35474
|
+
return demoteSmartArtNode(data, nodeId);
|
|
35475
|
+
}
|
|
35476
|
+
/** Move a node up among its siblings. */
|
|
35477
|
+
function moveNodeUp(data, nodeId) {
|
|
35478
|
+
return reorderSmartArtNode(data, nodeId, -1);
|
|
35479
|
+
}
|
|
35480
|
+
/** Move a node down among its siblings. */
|
|
35481
|
+
function moveNodeDown(data, nodeId) {
|
|
35482
|
+
return reorderSmartArtNode(data, nodeId, 1);
|
|
35483
|
+
}
|
|
35484
|
+
/** Apply a new colour scheme; clears stale drawing shapes for reflow. */
|
|
35485
|
+
function setColorScheme(data, scheme) {
|
|
35486
|
+
return { ...data, colorScheme: scheme, drawingShapes: undefined };
|
|
35487
|
+
}
|
|
35488
|
+
/** Apply a new style intensity; clears stale drawing shapes for reflow. */
|
|
35489
|
+
function setStyle(data, style) {
|
|
35490
|
+
return { ...data, style, drawingShapes: undefined };
|
|
35491
|
+
}
|
|
35492
|
+
/**
|
|
35493
|
+
* Switch the diagram to a new layout, preserving node data and connections.
|
|
35494
|
+
* Returns the original data unchanged when the target equals the current layout.
|
|
35495
|
+
*/
|
|
35496
|
+
function setLayout(data, layout) {
|
|
35497
|
+
if (currentLayout(data) === layout) {
|
|
35498
|
+
return data;
|
|
35499
|
+
}
|
|
35500
|
+
return switchSmartArtLayout(data, layout);
|
|
35501
|
+
}
|
|
35502
|
+
|
|
35503
|
+
/**
|
|
35504
|
+
* smart-art-properties.component.ts: SmartArt editing inspector (Angular).
|
|
35505
|
+
*
|
|
35506
|
+
* Selector: `pptx-smart-art-properties`
|
|
35507
|
+
*
|
|
35508
|
+
* Presentational panel for editing a selected SmartArt element's data model:
|
|
35509
|
+
* per-node text editing, add item / add sub-item, remove, promote / demote,
|
|
35510
|
+
* reorder up / down, colour-scheme select, style toggle (flat / moderate /
|
|
35511
|
+
* intense), and a layout switcher. All mutations are delegated to the pure
|
|
35512
|
+
* helpers in `smart-art-properties-helpers.ts` (which wrap the core editing ops
|
|
35513
|
+
* re-exported via `editor-insert.ts`); no editing logic lives here. Every edit
|
|
35514
|
+
* is emitted as a complete new `PptxSmartArtData` via `smartArtDataChange`, so
|
|
35515
|
+
* the parent (`InspectorPanelComponent`) can commit it through
|
|
35516
|
+
* `EditorStateService.updateElement()` as a single undo/redo history entry.
|
|
35517
|
+
*
|
|
35518
|
+
* Ported from the React inspector:
|
|
35519
|
+
* packages/react/src/viewer/components/inspector/SmartArtPropertiesPanel.tsx
|
|
35520
|
+
* packages/react/src/viewer/components/inspector/SmartArtLayoutSwitcher.tsx
|
|
35521
|
+
*
|
|
35522
|
+
* Usage:
|
|
35523
|
+
* ```html
|
|
35524
|
+
* <pptx-smart-art-properties
|
|
35525
|
+
* [smartArtData]="data"
|
|
35526
|
+
* [canEdit]="canEdit"
|
|
35527
|
+
* (smartArtDataChange)="onSmartArtChange($event)"
|
|
35528
|
+
* />
|
|
35529
|
+
* ```
|
|
35530
|
+
*
|
|
35531
|
+
* @module angular-viewer/smart-art-properties
|
|
35532
|
+
*/
|
|
35533
|
+
class SmartArtPropertiesComponent {
|
|
35534
|
+
/** The SmartArt data model being edited. */
|
|
35535
|
+
smartArtData = input.required(/* @ts-ignore */
|
|
35536
|
+
...(ngDevMode ? [{ debugName: "smartArtData" }] : /* istanbul ignore next */ []));
|
|
35537
|
+
/** Whether editing is enabled (read-only when false). */
|
|
35538
|
+
canEdit = input(true, /* @ts-ignore */
|
|
35539
|
+
...(ngDevMode ? [{ debugName: "canEdit" }] : /* istanbul ignore next */ []));
|
|
35540
|
+
/** Emits a complete new data model after any edit. */
|
|
35541
|
+
smartArtDataChange = output();
|
|
35542
|
+
// ── Static option lists (template-bound) ─────────────────────────────────
|
|
35543
|
+
layoutTypes = SWITCHABLE_LAYOUT_TYPES;
|
|
35544
|
+
colorSchemes = SMART_ART_COLOR_SCHEMES;
|
|
35545
|
+
styleOptions = SMART_ART_STYLE_OPTIONS;
|
|
35546
|
+
// ── Derived state ─────────────────────────────────────────────────────────
|
|
35547
|
+
nodes = computed(() => smartArtNodes(this.smartArtData()), /* @ts-ignore */
|
|
35548
|
+
...(ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []));
|
|
35549
|
+
activeLayout = computed(() => currentLayout(this.smartArtData()), /* @ts-ignore */
|
|
35550
|
+
...(ngDevMode ? [{ debugName: "activeLayout" }] : /* istanbul ignore next */ []));
|
|
35551
|
+
activeColorScheme = computed(() => currentColorScheme(this.smartArtData()), /* @ts-ignore */
|
|
35552
|
+
...(ngDevMode ? [{ debugName: "activeColorScheme" }] : /* istanbul ignore next */ []));
|
|
35553
|
+
activeStyle = computed(() => currentStyle(this.smartArtData()), /* @ts-ignore */
|
|
35554
|
+
...(ngDevMode ? [{ debugName: "activeStyle" }] : /* istanbul ignore next */ []));
|
|
35555
|
+
isChild = isChildNode;
|
|
35556
|
+
// ── Event handlers (each emits a fresh data model) ───────────────────────
|
|
35557
|
+
onLayout(layout) {
|
|
35558
|
+
this.commit(setLayout(this.smartArtData(), layout));
|
|
35559
|
+
}
|
|
35560
|
+
onColorScheme(event) {
|
|
35561
|
+
const value = selectValue(event);
|
|
35562
|
+
if (value === null) {
|
|
35563
|
+
return;
|
|
35564
|
+
}
|
|
35565
|
+
this.commit(setColorScheme(this.smartArtData(), value));
|
|
35566
|
+
}
|
|
35567
|
+
onStyle(style) {
|
|
35568
|
+
this.commit(setStyle(this.smartArtData(), style));
|
|
35569
|
+
}
|
|
35570
|
+
onAddItem() {
|
|
35571
|
+
this.commit(addItem(this.smartArtData()));
|
|
35572
|
+
}
|
|
35573
|
+
onAddSubItem(parentId) {
|
|
35574
|
+
this.commit(addSubItem(this.smartArtData(), parentId));
|
|
35575
|
+
}
|
|
35576
|
+
onRemove(nodeId) {
|
|
35577
|
+
this.commit(removeNode(this.smartArtData(), nodeId));
|
|
35578
|
+
}
|
|
35579
|
+
onPromote(nodeId) {
|
|
35580
|
+
this.commit(promoteNode(this.smartArtData(), nodeId));
|
|
35581
|
+
}
|
|
35582
|
+
onDemote(nodeId) {
|
|
35583
|
+
this.commit(demoteNode(this.smartArtData(), nodeId));
|
|
35584
|
+
}
|
|
35585
|
+
onMoveUp(nodeId) {
|
|
35586
|
+
this.commit(moveNodeUp(this.smartArtData(), nodeId));
|
|
35587
|
+
}
|
|
35588
|
+
onMoveDown(nodeId) {
|
|
35589
|
+
this.commit(moveNodeDown(this.smartArtData(), nodeId));
|
|
35590
|
+
}
|
|
35591
|
+
onNodeText(event, nodeId) {
|
|
35592
|
+
const value = inputValue(event);
|
|
35593
|
+
if (value === null) {
|
|
35594
|
+
return;
|
|
35595
|
+
}
|
|
35596
|
+
this.commit(setNodeText(this.smartArtData(), nodeId, value));
|
|
35597
|
+
}
|
|
35598
|
+
onNodeKeydown(event, nodeId) {
|
|
35599
|
+
if (event.key !== 'Tab') {
|
|
35600
|
+
return;
|
|
35601
|
+
}
|
|
35602
|
+
event.preventDefault();
|
|
35603
|
+
const next = event.shiftKey
|
|
35604
|
+
? promoteNode(this.smartArtData(), nodeId)
|
|
35605
|
+
: demoteNode(this.smartArtData(), nodeId);
|
|
35606
|
+
this.commit(next);
|
|
35607
|
+
}
|
|
35608
|
+
/** Emit a new data model only when it differs from the current one. */
|
|
35609
|
+
commit(next) {
|
|
35610
|
+
if (!this.canEdit() || next === this.smartArtData()) {
|
|
35611
|
+
return;
|
|
35612
|
+
}
|
|
35613
|
+
this.smartArtDataChange.emit(next);
|
|
35614
|
+
}
|
|
35615
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SmartArtPropertiesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
35616
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: SmartArtPropertiesComponent, isStandalone: true, selector: "pptx-smart-art-properties", inputs: { smartArtData: { classPropertyName: "smartArtData", publicName: "smartArtData", isSignal: true, isRequired: true, transformFunction: null }, canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { smartArtDataChange: "smartArtDataChange" }, ngImport: i0, template: `
|
|
35617
|
+
<section class="pptx-sa-props" aria-label="SmartArt properties">
|
|
35618
|
+
<h3 class="pptx-sa-props__heading">SmartArt</h3>
|
|
35619
|
+
|
|
35620
|
+
<!-- ── Layout switcher ──────────────────────────────────────────── -->
|
|
35621
|
+
<div class="pptx-sa-props__field">
|
|
35622
|
+
<span class="pptx-sa-props__label">Layout</span>
|
|
35623
|
+
<div class="pptx-sa-props__layouts" role="group" aria-label="Switch layout">
|
|
35624
|
+
@for (layout of layoutTypes; track layout) {
|
|
35625
|
+
<button
|
|
35626
|
+
type="button"
|
|
35627
|
+
class="pptx-sa-props__layout"
|
|
35628
|
+
[class.is-active]="activeLayout() === layout"
|
|
35629
|
+
[disabled]="!canEdit()"
|
|
35630
|
+
[attr.aria-pressed]="activeLayout() === layout"
|
|
35631
|
+
(click)="onLayout(layout)"
|
|
35632
|
+
>
|
|
35633
|
+
{{ layout }}
|
|
35634
|
+
</button>
|
|
35635
|
+
}
|
|
35636
|
+
</div>
|
|
35637
|
+
</div>
|
|
35638
|
+
|
|
35639
|
+
<!-- ── Colour scheme ────────────────────────────────────────────── -->
|
|
35640
|
+
<label class="pptx-sa-props__field">
|
|
35641
|
+
<span class="pptx-sa-props__label">Color scheme</span>
|
|
35642
|
+
<select
|
|
35643
|
+
class="pptx-sa-props__select"
|
|
35644
|
+
[disabled]="!canEdit()"
|
|
35645
|
+
[value]="activeColorScheme()"
|
|
35646
|
+
(change)="onColorScheme($event)"
|
|
35647
|
+
>
|
|
35648
|
+
@for (scheme of colorSchemes; track scheme) {
|
|
35649
|
+
<option [value]="scheme">{{ scheme }}</option>
|
|
35650
|
+
}
|
|
35651
|
+
</select>
|
|
35652
|
+
</label>
|
|
35653
|
+
|
|
35654
|
+
<!-- ── Style intensity ──────────────────────────────────────────── -->
|
|
35655
|
+
<div class="pptx-sa-props__field">
|
|
35656
|
+
<span class="pptx-sa-props__label">Style</span>
|
|
35657
|
+
<div class="pptx-sa-props__styles" role="group" aria-label="Style intensity">
|
|
35658
|
+
@for (styleOpt of styleOptions; track styleOpt) {
|
|
35659
|
+
<button
|
|
35660
|
+
type="button"
|
|
35661
|
+
class="pptx-sa-props__style"
|
|
35662
|
+
[class.is-active]="activeStyle() === styleOpt"
|
|
35663
|
+
[disabled]="!canEdit()"
|
|
35664
|
+
[attr.aria-pressed]="activeStyle() === styleOpt"
|
|
35665
|
+
(click)="onStyle(styleOpt)"
|
|
35666
|
+
>
|
|
35667
|
+
{{ styleOpt }}
|
|
35668
|
+
</button>
|
|
35669
|
+
}
|
|
35670
|
+
</div>
|
|
35671
|
+
</div>
|
|
35672
|
+
|
|
35673
|
+
<!-- ── Text pane / node list ────────────────────────────────────── -->
|
|
35674
|
+
<div class="pptx-sa-props__pane-header">
|
|
35675
|
+
<span class="pptx-sa-props__label">Text pane ({{ nodes().length }})</span>
|
|
35676
|
+
<button
|
|
35677
|
+
type="button"
|
|
35678
|
+
class="pptx-sa-props__btn"
|
|
35679
|
+
[disabled]="!canEdit()"
|
|
35680
|
+
(click)="onAddItem()"
|
|
35681
|
+
>
|
|
35682
|
+
+ Item
|
|
35683
|
+
</button>
|
|
35684
|
+
</div>
|
|
35685
|
+
|
|
35686
|
+
<ul class="pptx-sa-props__nodes" role="list">
|
|
35687
|
+
@for (node of nodes(); track node.id; let i = $index) {
|
|
35688
|
+
<li
|
|
35689
|
+
class="pptx-sa-props__node"
|
|
35690
|
+
[class.pptx-sa-props__node--child]="isChild(node)"
|
|
35691
|
+
[attr.data-node-id]="node.id"
|
|
35692
|
+
>
|
|
35693
|
+
<span class="pptx-sa-props__bullet">{{ isChild(node) ? '•' : i + 1 }}</span>
|
|
35694
|
+
<input
|
|
35695
|
+
type="text"
|
|
35696
|
+
class="pptx-sa-props__node-input"
|
|
35697
|
+
[disabled]="!canEdit()"
|
|
35698
|
+
[value]="node.text"
|
|
35699
|
+
placeholder="Type here"
|
|
35700
|
+
(change)="onNodeText($event, node.id)"
|
|
35701
|
+
(keydown)="onNodeKeydown($event, node.id)"
|
|
35702
|
+
/>
|
|
35703
|
+
<div class="pptx-sa-props__node-actions">
|
|
35704
|
+
@if (!isChild(node)) {
|
|
35705
|
+
<button
|
|
35706
|
+
type="button"
|
|
35707
|
+
class="pptx-sa-props__icon"
|
|
35708
|
+
title="Add sub-item"
|
|
35709
|
+
[disabled]="!canEdit()"
|
|
35710
|
+
(click)="onAddSubItem(node.id)"
|
|
35711
|
+
>
|
|
35712
|
+
+Sub
|
|
35713
|
+
</button>
|
|
35714
|
+
}
|
|
35715
|
+
<button
|
|
35716
|
+
type="button"
|
|
35717
|
+
class="pptx-sa-props__icon"
|
|
35718
|
+
title="Promote"
|
|
35719
|
+
[disabled]="!canEdit() || isChild(node) === false"
|
|
35720
|
+
(click)="onPromote(node.id)"
|
|
35721
|
+
>
|
|
35722
|
+
←
|
|
35723
|
+
</button>
|
|
35724
|
+
<button
|
|
35725
|
+
type="button"
|
|
35726
|
+
class="pptx-sa-props__icon"
|
|
35727
|
+
title="Demote"
|
|
35728
|
+
[disabled]="!canEdit()"
|
|
35729
|
+
(click)="onDemote(node.id)"
|
|
35730
|
+
>
|
|
35731
|
+
→
|
|
35732
|
+
</button>
|
|
35733
|
+
<button
|
|
35734
|
+
type="button"
|
|
35735
|
+
class="pptx-sa-props__icon"
|
|
35736
|
+
title="Move up"
|
|
35737
|
+
[disabled]="!canEdit()"
|
|
35738
|
+
(click)="onMoveUp(node.id)"
|
|
35739
|
+
>
|
|
35740
|
+
↑
|
|
35741
|
+
</button>
|
|
35742
|
+
<button
|
|
35743
|
+
type="button"
|
|
35744
|
+
class="pptx-sa-props__icon"
|
|
35745
|
+
title="Move down"
|
|
35746
|
+
[disabled]="!canEdit()"
|
|
35747
|
+
(click)="onMoveDown(node.id)"
|
|
35748
|
+
>
|
|
35749
|
+
↓
|
|
35750
|
+
</button>
|
|
35751
|
+
<button
|
|
35752
|
+
type="button"
|
|
35753
|
+
class="pptx-sa-props__icon pptx-sa-props__icon--danger"
|
|
35754
|
+
title="Remove"
|
|
35755
|
+
[disabled]="!canEdit() || nodes().length <= 1"
|
|
35756
|
+
(click)="onRemove(node.id)"
|
|
35757
|
+
>
|
|
35758
|
+
×
|
|
35759
|
+
</button>
|
|
35760
|
+
</div>
|
|
35761
|
+
</li>
|
|
35762
|
+
}
|
|
35763
|
+
</ul>
|
|
35764
|
+
|
|
35765
|
+
<p class="pptx-sa-props__hint">Tab to demote, Shift+Tab to promote.</p>
|
|
35766
|
+
</section>
|
|
35767
|
+
`, isInline: true, styles: [".pptx-sa-props{display:flex;flex-direction:column;gap:.4rem;padding:.5rem 0}.pptx-sa-props__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-sa-props__field{display:flex;flex-direction:column;gap:.2rem}.pptx-sa-props__label{font-size:10px;color:var(--pptx-inspector-muted, #888)}.pptx-sa-props__layouts,.pptx-sa-props__styles{display:flex;flex-wrap:wrap;gap:.25rem}.pptx-sa-props__layout,.pptx-sa-props__style,.pptx-sa-props__btn,.pptx-sa-props__icon{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:10px;padding:2px 6px;white-space:nowrap}.pptx-sa-props__layout,.pptx-sa-props__style{flex:1 0 auto;text-transform:capitalize}.pptx-sa-props__layout.is-active,.pptx-sa-props__style.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-sa-props__select{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:11px}.pptx-sa-props__pane-header{display:flex;align-items:center;justify-content:space-between;gap:.35rem;margin-top:.2rem}.pptx-sa-props__nodes{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.2rem;max-height:14rem;overflow-y:auto}.pptx-sa-props__node{display:flex;align-items:center;gap:.25rem;padding:2px;border:1px solid var(--pptx-inspector-border, #333);border-radius:3px}.pptx-sa-props__node--child{margin-left:1rem;border-color:var(--pptx-inspector-border, #2a2a2a)}.pptx-sa-props__bullet{font-size:9px;color:var(--pptx-inspector-muted, #888);min-width:12px;text-align:center;flex-shrink:0}.pptx-sa-props__node-input{flex:1;min-width:0;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:11px}.pptx-sa-props__node-actions{display:flex;gap:1px;flex-shrink:0}.pptx-sa-props__icon{padding:1px 3px;font-size:10px;line-height:1.2}.pptx-sa-props__icon--danger{color:var(--pptx-inspector-danger, #f47c7c)}.pptx-sa-props__layout:disabled,.pptx-sa-props__style:disabled,.pptx-sa-props__btn:disabled,.pptx-sa-props__icon:disabled,.pptx-sa-props__select:disabled,.pptx-sa-props__node-input:disabled{opacity:.45;cursor:not-allowed}.pptx-sa-props__hint{font-size:9px;color:var(--pptx-inspector-muted, #888);margin:.1rem 0 0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
35768
|
+
}
|
|
35769
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SmartArtPropertiesComponent, decorators: [{
|
|
35770
|
+
type: Component,
|
|
35771
|
+
args: [{ selector: 'pptx-smart-art-properties', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
35772
|
+
<section class="pptx-sa-props" aria-label="SmartArt properties">
|
|
35773
|
+
<h3 class="pptx-sa-props__heading">SmartArt</h3>
|
|
35774
|
+
|
|
35775
|
+
<!-- ── Layout switcher ──────────────────────────────────────────── -->
|
|
35776
|
+
<div class="pptx-sa-props__field">
|
|
35777
|
+
<span class="pptx-sa-props__label">Layout</span>
|
|
35778
|
+
<div class="pptx-sa-props__layouts" role="group" aria-label="Switch layout">
|
|
35779
|
+
@for (layout of layoutTypes; track layout) {
|
|
35780
|
+
<button
|
|
35781
|
+
type="button"
|
|
35782
|
+
class="pptx-sa-props__layout"
|
|
35783
|
+
[class.is-active]="activeLayout() === layout"
|
|
35784
|
+
[disabled]="!canEdit()"
|
|
35785
|
+
[attr.aria-pressed]="activeLayout() === layout"
|
|
35786
|
+
(click)="onLayout(layout)"
|
|
35787
|
+
>
|
|
35788
|
+
{{ layout }}
|
|
35789
|
+
</button>
|
|
35790
|
+
}
|
|
35791
|
+
</div>
|
|
35792
|
+
</div>
|
|
35793
|
+
|
|
35794
|
+
<!-- ── Colour scheme ────────────────────────────────────────────── -->
|
|
35795
|
+
<label class="pptx-sa-props__field">
|
|
35796
|
+
<span class="pptx-sa-props__label">Color scheme</span>
|
|
35797
|
+
<select
|
|
35798
|
+
class="pptx-sa-props__select"
|
|
35799
|
+
[disabled]="!canEdit()"
|
|
35800
|
+
[value]="activeColorScheme()"
|
|
35801
|
+
(change)="onColorScheme($event)"
|
|
35802
|
+
>
|
|
35803
|
+
@for (scheme of colorSchemes; track scheme) {
|
|
35804
|
+
<option [value]="scheme">{{ scheme }}</option>
|
|
35805
|
+
}
|
|
35806
|
+
</select>
|
|
35807
|
+
</label>
|
|
35808
|
+
|
|
35809
|
+
<!-- ── Style intensity ──────────────────────────────────────────── -->
|
|
35810
|
+
<div class="pptx-sa-props__field">
|
|
35811
|
+
<span class="pptx-sa-props__label">Style</span>
|
|
35812
|
+
<div class="pptx-sa-props__styles" role="group" aria-label="Style intensity">
|
|
35813
|
+
@for (styleOpt of styleOptions; track styleOpt) {
|
|
35814
|
+
<button
|
|
35815
|
+
type="button"
|
|
35816
|
+
class="pptx-sa-props__style"
|
|
35817
|
+
[class.is-active]="activeStyle() === styleOpt"
|
|
35818
|
+
[disabled]="!canEdit()"
|
|
35819
|
+
[attr.aria-pressed]="activeStyle() === styleOpt"
|
|
35820
|
+
(click)="onStyle(styleOpt)"
|
|
35821
|
+
>
|
|
35822
|
+
{{ styleOpt }}
|
|
35823
|
+
</button>
|
|
35824
|
+
}
|
|
35825
|
+
</div>
|
|
35826
|
+
</div>
|
|
35827
|
+
|
|
35828
|
+
<!-- ── Text pane / node list ────────────────────────────────────── -->
|
|
35829
|
+
<div class="pptx-sa-props__pane-header">
|
|
35830
|
+
<span class="pptx-sa-props__label">Text pane ({{ nodes().length }})</span>
|
|
35831
|
+
<button
|
|
35832
|
+
type="button"
|
|
35833
|
+
class="pptx-sa-props__btn"
|
|
35834
|
+
[disabled]="!canEdit()"
|
|
35835
|
+
(click)="onAddItem()"
|
|
35836
|
+
>
|
|
35837
|
+
+ Item
|
|
35838
|
+
</button>
|
|
35839
|
+
</div>
|
|
35840
|
+
|
|
35841
|
+
<ul class="pptx-sa-props__nodes" role="list">
|
|
35842
|
+
@for (node of nodes(); track node.id; let i = $index) {
|
|
35843
|
+
<li
|
|
35844
|
+
class="pptx-sa-props__node"
|
|
35845
|
+
[class.pptx-sa-props__node--child]="isChild(node)"
|
|
35846
|
+
[attr.data-node-id]="node.id"
|
|
35847
|
+
>
|
|
35848
|
+
<span class="pptx-sa-props__bullet">{{ isChild(node) ? '•' : i + 1 }}</span>
|
|
35849
|
+
<input
|
|
35850
|
+
type="text"
|
|
35851
|
+
class="pptx-sa-props__node-input"
|
|
35852
|
+
[disabled]="!canEdit()"
|
|
35853
|
+
[value]="node.text"
|
|
35854
|
+
placeholder="Type here"
|
|
35855
|
+
(change)="onNodeText($event, node.id)"
|
|
35856
|
+
(keydown)="onNodeKeydown($event, node.id)"
|
|
35857
|
+
/>
|
|
35858
|
+
<div class="pptx-sa-props__node-actions">
|
|
35859
|
+
@if (!isChild(node)) {
|
|
35860
|
+
<button
|
|
35861
|
+
type="button"
|
|
35862
|
+
class="pptx-sa-props__icon"
|
|
35863
|
+
title="Add sub-item"
|
|
35864
|
+
[disabled]="!canEdit()"
|
|
35865
|
+
(click)="onAddSubItem(node.id)"
|
|
35866
|
+
>
|
|
35867
|
+
+Sub
|
|
35868
|
+
</button>
|
|
35869
|
+
}
|
|
35870
|
+
<button
|
|
35871
|
+
type="button"
|
|
35872
|
+
class="pptx-sa-props__icon"
|
|
35873
|
+
title="Promote"
|
|
35874
|
+
[disabled]="!canEdit() || isChild(node) === false"
|
|
35875
|
+
(click)="onPromote(node.id)"
|
|
35876
|
+
>
|
|
35877
|
+
←
|
|
35878
|
+
</button>
|
|
35879
|
+
<button
|
|
35880
|
+
type="button"
|
|
35881
|
+
class="pptx-sa-props__icon"
|
|
35882
|
+
title="Demote"
|
|
35883
|
+
[disabled]="!canEdit()"
|
|
35884
|
+
(click)="onDemote(node.id)"
|
|
35885
|
+
>
|
|
35886
|
+
→
|
|
35887
|
+
</button>
|
|
35888
|
+
<button
|
|
35889
|
+
type="button"
|
|
35890
|
+
class="pptx-sa-props__icon"
|
|
35891
|
+
title="Move up"
|
|
35892
|
+
[disabled]="!canEdit()"
|
|
35893
|
+
(click)="onMoveUp(node.id)"
|
|
35894
|
+
>
|
|
35895
|
+
↑
|
|
35896
|
+
</button>
|
|
35897
|
+
<button
|
|
35898
|
+
type="button"
|
|
35899
|
+
class="pptx-sa-props__icon"
|
|
35900
|
+
title="Move down"
|
|
35901
|
+
[disabled]="!canEdit()"
|
|
35902
|
+
(click)="onMoveDown(node.id)"
|
|
35903
|
+
>
|
|
35904
|
+
↓
|
|
35905
|
+
</button>
|
|
35906
|
+
<button
|
|
35907
|
+
type="button"
|
|
35908
|
+
class="pptx-sa-props__icon pptx-sa-props__icon--danger"
|
|
35909
|
+
title="Remove"
|
|
35910
|
+
[disabled]="!canEdit() || nodes().length <= 1"
|
|
35911
|
+
(click)="onRemove(node.id)"
|
|
35912
|
+
>
|
|
35913
|
+
×
|
|
35914
|
+
</button>
|
|
35915
|
+
</div>
|
|
35916
|
+
</li>
|
|
35917
|
+
}
|
|
35918
|
+
</ul>
|
|
35919
|
+
|
|
35920
|
+
<p class="pptx-sa-props__hint">Tab to demote, Shift+Tab to promote.</p>
|
|
35921
|
+
</section>
|
|
35922
|
+
`, styles: [".pptx-sa-props{display:flex;flex-direction:column;gap:.4rem;padding:.5rem 0}.pptx-sa-props__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0}.pptx-sa-props__field{display:flex;flex-direction:column;gap:.2rem}.pptx-sa-props__label{font-size:10px;color:var(--pptx-inspector-muted, #888)}.pptx-sa-props__layouts,.pptx-sa-props__styles{display:flex;flex-wrap:wrap;gap:.25rem}.pptx-sa-props__layout,.pptx-sa-props__style,.pptx-sa-props__btn,.pptx-sa-props__icon{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:10px;padding:2px 6px;white-space:nowrap}.pptx-sa-props__layout,.pptx-sa-props__style{flex:1 0 auto;text-transform:capitalize}.pptx-sa-props__layout.is-active,.pptx-sa-props__style.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-sa-props__select{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:11px}.pptx-sa-props__pane-header{display:flex;align-items:center;justify-content:space-between;gap:.35rem;margin-top:.2rem}.pptx-sa-props__nodes{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.2rem;max-height:14rem;overflow-y:auto}.pptx-sa-props__node{display:flex;align-items:center;gap:.25rem;padding:2px;border:1px solid var(--pptx-inspector-border, #333);border-radius:3px}.pptx-sa-props__node--child{margin-left:1rem;border-color:var(--pptx-inspector-border, #2a2a2a)}.pptx-sa-props__bullet{font-size:9px;color:var(--pptx-inspector-muted, #888);min-width:12px;text-align:center;flex-shrink:0}.pptx-sa-props__node-input{flex:1;min-width:0;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:11px}.pptx-sa-props__node-actions{display:flex;gap:1px;flex-shrink:0}.pptx-sa-props__icon{padding:1px 3px;font-size:10px;line-height:1.2}.pptx-sa-props__icon--danger{color:var(--pptx-inspector-danger, #f47c7c)}.pptx-sa-props__layout:disabled,.pptx-sa-props__style:disabled,.pptx-sa-props__btn:disabled,.pptx-sa-props__icon:disabled,.pptx-sa-props__select:disabled,.pptx-sa-props__node-input:disabled{opacity:.45;cursor:not-allowed}.pptx-sa-props__hint{font-size:9px;color:var(--pptx-inspector-muted, #888);margin:.1rem 0 0}\n"] }]
|
|
35923
|
+
}], propDecorators: { smartArtData: [{ type: i0.Input, args: [{ isSignal: true, alias: "smartArtData", required: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], smartArtDataChange: [{ type: i0.Output, args: ["smartArtDataChange"] }] } });
|
|
35924
|
+
// ── Module-private helpers ───────────────────────────────────────────────────
|
|
35925
|
+
/** Read the value of a `<select>` change event. */
|
|
35926
|
+
function selectValue(event) {
|
|
35927
|
+
const target = event.target;
|
|
35928
|
+
return target instanceof HTMLSelectElement ? target.value : null;
|
|
35929
|
+
}
|
|
35930
|
+
/** Read the value of an `<input>` change event. */
|
|
35931
|
+
function inputValue(event) {
|
|
35932
|
+
const target = event.target;
|
|
35933
|
+
return target instanceof HTMLInputElement ? target.value : null;
|
|
35934
|
+
}
|
|
35935
|
+
|
|
35193
35936
|
/**
|
|
35194
35937
|
* table-data-helpers.ts: Pure immutable helpers for table data editing.
|
|
35195
35938
|
*
|
|
@@ -36385,6 +37128,23 @@ class InspectorPanelComponent {
|
|
|
36385
37128
|
/** The selected element narrowed to a chart, or undefined. */
|
|
36386
37129
|
chartEl = computed(() => this.el().type === 'chart' ? this.el() : undefined, /* @ts-ignore */
|
|
36387
37130
|
...(ngDevMode ? [{ debugName: "chartEl" }] : /* istanbul ignore next */ []));
|
|
37131
|
+
/** The selected element narrowed to SmartArt, or undefined. */
|
|
37132
|
+
smartArtEl = computed(() => this.el().type === 'smartArt' ? this.el() : undefined, /* @ts-ignore */
|
|
37133
|
+
...(ngDevMode ? [{ debugName: "smartArtEl" }] : /* istanbul ignore next */ []));
|
|
37134
|
+
/** The selected SmartArt element's data model, or undefined. */
|
|
37135
|
+
smartArtData = computed(() => this.smartArtEl()?.smartArtData, /* @ts-ignore */
|
|
37136
|
+
...(ngDevMode ? [{ debugName: "smartArtData" }] : /* istanbul ignore next */ []));
|
|
37137
|
+
/**
|
|
37138
|
+
* Commit an updated SmartArt data model as one history entry. Patching only
|
|
37139
|
+
* `smartArtData` routes through the same `EditorStateService.updateElement`
|
|
37140
|
+
* path as every other inspector section, so undo/redo and persistence work
|
|
37141
|
+
* identically.
|
|
37142
|
+
*/
|
|
37143
|
+
onSmartArtDataChange(smartArtData) {
|
|
37144
|
+
this.editor.updateElement(this.slideIndex(), this.el().id, {
|
|
37145
|
+
smartArtData,
|
|
37146
|
+
});
|
|
37147
|
+
}
|
|
36388
37148
|
/** Commit a partial-element patch from an advanced sub-panel as one history entry. */
|
|
36389
37149
|
onPatch(patch) {
|
|
36390
37150
|
this.editor.updateElement(this.slideIndex(), this.el().id, patch);
|
|
@@ -36740,6 +37500,16 @@ class InspectorPanelComponent {
|
|
|
36740
37500
|
</details>
|
|
36741
37501
|
}
|
|
36742
37502
|
|
|
37503
|
+
<!-- ── SmartArt editing ───────────────────────────────────────────── -->
|
|
37504
|
+
@if (smartArtData(); as sa) {
|
|
37505
|
+
<section class="pptx-ng-inspector__section">
|
|
37506
|
+
<pptx-smart-art-properties
|
|
37507
|
+
[smartArtData]="sa"
|
|
37508
|
+
(smartArtDataChange)="onSmartArtDataChange($event)"
|
|
37509
|
+
/>
|
|
37510
|
+
</section>
|
|
37511
|
+
}
|
|
37512
|
+
|
|
36743
37513
|
<!-- ── Animation authoring ────────────────────────────────────────── -->
|
|
36744
37514
|
<details class="pptx-ng-inspector__details">
|
|
36745
37515
|
<summary class="pptx-ng-inspector__summary">Animation</summary>
|
|
@@ -36771,7 +37541,7 @@ class InspectorPanelComponent {
|
|
|
36771
37541
|
</div>
|
|
36772
37542
|
</section>
|
|
36773
37543
|
</aside>
|
|
36774
|
-
`, isInline: true, styles: [".pptx-ng-inspector{display:flex;flex-direction:column;gap:0;padding:.5rem;background:var(--pptx-inspector-bg, #1e1e1e);color:var(--pptx-inspector-fg, #e0e0e0);font-size:12px;min-width:220px;overflow-y:auto}.pptx-ng-inspector__section{padding:.5rem 0;border-bottom:1px solid var(--pptx-inspector-border, #333)}.pptx-ng-inspector__section:last-child{border-bottom:none}.pptx-ng-inspector__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0 0 .35rem}.pptx-ng-inspector__details{border-bottom:1px solid var(--pptx-inspector-border, #333)}.pptx-ng-inspector__summary{padding:.5rem 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);cursor:pointer;-webkit-user-select:none;user-select:none}.pptx-ng-inspector__row{display:flex;align-items:center;gap:.35rem;margin-bottom:.35rem}.pptx-ng-inspector__row:last-child{margin-bottom:0}.pptx-ng-inspector__row--toggles{gap:.25rem}.pptx-ng-inspector__label{font-size:10px;color:var(--pptx-inspector-muted, #888);min-width:32px;text-align:right;flex-shrink:0}.pptx-ng-inspector__input{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:12px}.pptx-ng-inspector__input--number{width:62px;text-align:right}.pptx-ng-inspector__color{width:32px;height:22px;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;padding:1px;cursor:pointer;background:transparent;flex-shrink:0}.pptx-ng-inspector__toggle{width:26px;height:22px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px}@media(pointer:coarse),(max-width:640px){.pptx-ng-inspector{width:100%;min-width:0;box-sizing:border-box;font-size:14px}.pptx-ng-inspector__row{flex-wrap:wrap;gap:.5rem}.pptx-ng-inspector__label{min-width:28px}.pptx-ng-inspector__input{flex:1 1 auto;min-height:40px;font-size:16px;padding:6px 8px}.pptx-ng-inspector__input--number{width:auto;min-width:72px}.pptx-ng-inspector__color{width:44px;height:40px}.pptx-ng-inspector__toggle{min-width:44px;width:auto;flex:1 1 auto;height:40px;font-size:15px}.pptx-ng-inspector__btn{min-height:40px;padding:8px 10px;font-size:13px}}.pptx-ng-inspector__toggle.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-ng-inspector__btn{flex:1;padding:3px 6px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:11px;white-space:nowrap}.pptx-ng-inspector__btn:hover{background:var(--pptx-inspector-hover, #3a3a3a)}.pptx-ng-inspector__btn--danger{color:var(--pptx-inspector-danger, #f47c7c);border-color:var(--pptx-inspector-danger-border, #6b2a2a)}.pptx-ng-inspector__btn--danger:hover{background:var(--pptx-inspector-danger-hover, #4a1a1a)}\n"], dependencies: [{ kind: "component", type: GradientPickerComponent, selector: "pptx-gradient-picker", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: EffectsPanelComponent, selector: "pptx-effects-panel", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: TextAdvancedPanelComponent, selector: "pptx-text-advanced-panel", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: TableDataEditorComponent, selector: "pptx-table-data-editor", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDataEditorComponent, selector: "pptx-chart-data-editor", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: AnimationAuthorPanelComponent, selector: "pptx-animation-author-panel", inputs: ["element", "slideIndex", "animations", "canEdit"], outputs: ["animationsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
37544
|
+
`, isInline: true, styles: [".pptx-ng-inspector{display:flex;flex-direction:column;gap:0;padding:.5rem;background:var(--pptx-inspector-bg, #1e1e1e);color:var(--pptx-inspector-fg, #e0e0e0);font-size:12px;min-width:220px;overflow-y:auto}.pptx-ng-inspector__section{padding:.5rem 0;border-bottom:1px solid var(--pptx-inspector-border, #333)}.pptx-ng-inspector__section:last-child{border-bottom:none}.pptx-ng-inspector__heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);margin:0 0 .35rem}.pptx-ng-inspector__details{border-bottom:1px solid var(--pptx-inspector-border, #333)}.pptx-ng-inspector__summary{padding:.5rem 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--pptx-inspector-muted, #888);cursor:pointer;-webkit-user-select:none;user-select:none}.pptx-ng-inspector__row{display:flex;align-items:center;gap:.35rem;margin-bottom:.35rem}.pptx-ng-inspector__row:last-child{margin-bottom:0}.pptx-ng-inspector__row--toggles{gap:.25rem}.pptx-ng-inspector__label{font-size:10px;color:var(--pptx-inspector-muted, #888);min-width:32px;text-align:right;flex-shrink:0}.pptx-ng-inspector__input{background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;padding:2px 4px;font-size:12px}.pptx-ng-inspector__input--number{width:62px;text-align:right}.pptx-ng-inspector__color{width:32px;height:22px;border:1px solid var(--pptx-inspector-border, #444);border-radius:3px;padding:1px;cursor:pointer;background:transparent;flex-shrink:0}.pptx-ng-inspector__toggle{width:26px;height:22px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px}@media(pointer:coarse),(max-width:640px){.pptx-ng-inspector{width:100%;min-width:0;box-sizing:border-box;font-size:14px}.pptx-ng-inspector__row{flex-wrap:wrap;gap:.5rem}.pptx-ng-inspector__label{min-width:28px}.pptx-ng-inspector__input{flex:1 1 auto;min-height:40px;font-size:16px;padding:6px 8px}.pptx-ng-inspector__input--number{width:auto;min-width:72px}.pptx-ng-inspector__color{width:44px;height:40px}.pptx-ng-inspector__toggle{min-width:44px;width:auto;flex:1 1 auto;height:40px;font-size:15px}.pptx-ng-inspector__btn{min-height:40px;padding:8px 10px;font-size:13px}}.pptx-ng-inspector__toggle.is-active{background:var(--pptx-inspector-active, #0078d4);border-color:var(--pptx-inspector-active, #0078d4);color:#fff}.pptx-ng-inspector__btn{flex:1;padding:3px 6px;background:var(--pptx-inspector-input-bg, #2d2d2d);border:1px solid var(--pptx-inspector-border, #444);color:inherit;border-radius:3px;cursor:pointer;font-size:11px;white-space:nowrap}.pptx-ng-inspector__btn:hover{background:var(--pptx-inspector-hover, #3a3a3a)}.pptx-ng-inspector__btn--danger{color:var(--pptx-inspector-danger, #f47c7c);border-color:var(--pptx-inspector-danger-border, #6b2a2a)}.pptx-ng-inspector__btn--danger:hover{background:var(--pptx-inspector-danger-hover, #4a1a1a)}\n"], dependencies: [{ kind: "component", type: GradientPickerComponent, selector: "pptx-gradient-picker", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: EffectsPanelComponent, selector: "pptx-effects-panel", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: TextAdvancedPanelComponent, selector: "pptx-text-advanced-panel", inputs: ["element"], outputs: ["patch"] }, { kind: "component", type: TableDataEditorComponent, selector: "pptx-table-data-editor", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: ChartDataEditorComponent, selector: "pptx-chart-data-editor", inputs: ["element", "canEdit"], outputs: ["elementChange"] }, { kind: "component", type: SmartArtPropertiesComponent, selector: "pptx-smart-art-properties", inputs: ["smartArtData", "canEdit"], outputs: ["smartArtDataChange"] }, { kind: "component", type: AnimationAuthorPanelComponent, selector: "pptx-animation-author-panel", inputs: ["element", "slideIndex", "animations", "canEdit"], outputs: ["animationsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
36775
37545
|
}
|
|
36776
37546
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: InspectorPanelComponent, decorators: [{
|
|
36777
37547
|
type: Component,
|
|
@@ -36781,6 +37551,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
36781
37551
|
TextAdvancedPanelComponent,
|
|
36782
37552
|
TableDataEditorComponent,
|
|
36783
37553
|
ChartDataEditorComponent,
|
|
37554
|
+
SmartArtPropertiesComponent,
|
|
36784
37555
|
AnimationAuthorPanelComponent,
|
|
36785
37556
|
], template: `
|
|
36786
37557
|
<!--
|
|
@@ -37034,6 +37805,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
37034
37805
|
</details>
|
|
37035
37806
|
}
|
|
37036
37807
|
|
|
37808
|
+
<!-- ── SmartArt editing ───────────────────────────────────────────── -->
|
|
37809
|
+
@if (smartArtData(); as sa) {
|
|
37810
|
+
<section class="pptx-ng-inspector__section">
|
|
37811
|
+
<pptx-smart-art-properties
|
|
37812
|
+
[smartArtData]="sa"
|
|
37813
|
+
(smartArtDataChange)="onSmartArtDataChange($event)"
|
|
37814
|
+
/>
|
|
37815
|
+
</section>
|
|
37816
|
+
}
|
|
37817
|
+
|
|
37037
37818
|
<!-- ── Animation authoring ────────────────────────────────────────── -->
|
|
37038
37819
|
<details class="pptx-ng-inspector__details">
|
|
37039
37820
|
<summary class="pptx-ng-inspector__summary">Animation</summary>
|
|
@@ -53739,6 +54520,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
53739
54520
|
}]
|
|
53740
54521
|
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], activeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeName", required: false }] }], applyTheme: [{ type: i0.Output, args: ["applyTheme"] }], close: [{ type: i0.Output, args: ["close"] }] } });
|
|
53741
54522
|
|
|
54523
|
+
/**
|
|
54524
|
+
* touch-gestures.ts: thin Angular adapter over the framework-agnostic
|
|
54525
|
+
* `createTouchGestureRecognizer` from `pptx-viewer-shared`.
|
|
54526
|
+
*
|
|
54527
|
+
* The gesture state machine itself (pinch-to-zoom, swipe, long-press) lives in
|
|
54528
|
+
* shared and is identical across React / Vue / Angular. This adapter only owns
|
|
54529
|
+
* the DOM listener attach/detach lifecycle, mirroring the React hook
|
|
54530
|
+
* `packages/react/src/viewer/hooks/useTouchGestures.ts`:
|
|
54531
|
+
* - `touchstart` / `touchmove` are registered with `{ passive: false }` so the
|
|
54532
|
+
* recogniser can call `preventDefault()` to suppress the browser's native
|
|
54533
|
+
* pinch-zoom on the canvas;
|
|
54534
|
+
* - `touchend` / `touchcancel` are passive (they never call preventDefault).
|
|
54535
|
+
*
|
|
54536
|
+
* `attachTouchGestures(element, config)` returns a teardown function that
|
|
54537
|
+
* removes every listener and cancels any pending long-press timer. Components
|
|
54538
|
+
* call it from `afterNextRender` (when the target node is live) and run the
|
|
54539
|
+
* teardown via `DestroyRef.onDestroy`.
|
|
54540
|
+
*
|
|
54541
|
+
* @module touch-gestures (angular)
|
|
54542
|
+
*/
|
|
54543
|
+
/**
|
|
54544
|
+
* Viewer zoom bounds. The Angular viewer clamps zoom to [0.2, 3] (see
|
|
54545
|
+
* `ZOOM_MIN` / `ZOOM_MAX` in `power-point-viewer.component.ts`); the pinch path
|
|
54546
|
+
* must clamp to the same range so a pinch can never exceed the buttons.
|
|
54547
|
+
*/
|
|
54548
|
+
const MIN_ZOOM_SCALE = 0.2;
|
|
54549
|
+
const MAX_ZOOM_SCALE = 3;
|
|
54550
|
+
/** Clamp a scale value to the Angular viewer's allowed zoom range. */
|
|
54551
|
+
function clampScale(value) {
|
|
54552
|
+
return clampScale$1(value, MIN_ZOOM_SCALE, MAX_ZOOM_SCALE);
|
|
54553
|
+
}
|
|
54554
|
+
/**
|
|
54555
|
+
* Attach the shared touch-gesture recogniser to `element` and return a teardown
|
|
54556
|
+
* function. Mirrors the React hook's listener lifecycle exactly:
|
|
54557
|
+
* `touchstart`/`touchmove` are non-passive (so pinch can `preventDefault`),
|
|
54558
|
+
* `touchend`/`touchcancel` are passive.
|
|
54559
|
+
*/
|
|
54560
|
+
function attachTouchGestures(element, config) {
|
|
54561
|
+
const recognizer = createTouchGestureRecognizer({
|
|
54562
|
+
getScale: config.getScale,
|
|
54563
|
+
minScale: MIN_ZOOM_SCALE,
|
|
54564
|
+
maxScale: MAX_ZOOM_SCALE,
|
|
54565
|
+
callbacks: config.callbacks,
|
|
54566
|
+
});
|
|
54567
|
+
const onStart = (e) => recognizer.onTouchStart(e);
|
|
54568
|
+
const onMove = (e) => recognizer.onTouchMove(e);
|
|
54569
|
+
const onEnd = (e) => recognizer.onTouchEnd(e);
|
|
54570
|
+
const onCancel = () => recognizer.onTouchCancel();
|
|
54571
|
+
element.addEventListener('touchstart', onStart, { passive: false });
|
|
54572
|
+
element.addEventListener('touchmove', onMove, { passive: false });
|
|
54573
|
+
element.addEventListener('touchend', onEnd, { passive: true });
|
|
54574
|
+
element.addEventListener('touchcancel', onCancel, { passive: true });
|
|
54575
|
+
return () => {
|
|
54576
|
+
element.removeEventListener('touchstart', onStart);
|
|
54577
|
+
element.removeEventListener('touchmove', onMove);
|
|
54578
|
+
element.removeEventListener('touchend', onEnd);
|
|
54579
|
+
element.removeEventListener('touchcancel', onCancel);
|
|
54580
|
+
recognizer.cancel();
|
|
54581
|
+
};
|
|
54582
|
+
}
|
|
54583
|
+
|
|
53742
54584
|
const ZOOM_STEP = 0.1;
|
|
53743
54585
|
const ZOOM_MIN = 0.2;
|
|
53744
54586
|
const ZOOM_MAX = 3;
|
|
@@ -54172,6 +55014,9 @@ class PowerPointViewerComponent {
|
|
|
54172
55014
|
this.activeSession.set(null);
|
|
54173
55015
|
}
|
|
54174
55016
|
});
|
|
55017
|
+
// Attach multi-touch gestures (pinch-zoom / swipe-nav / long-press menu)
|
|
55018
|
+
// to the canvas host once it is rendered. See setupTouchGestures().
|
|
55019
|
+
this.setupTouchGestures();
|
|
54175
55020
|
}
|
|
54176
55021
|
/**
|
|
54177
55022
|
* Serialise the current presentation to `.pptx` bytes (imperative handle).
|
|
@@ -54308,54 +55153,60 @@ class PowerPointViewerComponent {
|
|
|
54308
55153
|
this.collab.disconnect();
|
|
54309
55154
|
this.activeSession.set(null);
|
|
54310
55155
|
}
|
|
54311
|
-
/** Horizontal-swipe tracking start coordinates (touch begins on the canvas). */
|
|
54312
|
-
swipeStartX = null;
|
|
54313
|
-
swipeStartY = null;
|
|
54314
55156
|
/**
|
|
54315
|
-
*
|
|
55157
|
+
* Wire the framework-agnostic touch-gesture recogniser to the `<main>` canvas
|
|
55158
|
+
* host once it is in the DOM. Mirrors React's `useTouchGestures` wiring:
|
|
55159
|
+
* - pinch-to-zoom always updates the zoom signal (clamped to the viewer
|
|
55160
|
+
* range), with `preventDefault()` on the pinch path to suppress the
|
|
55161
|
+
* browser's native pinch-zoom;
|
|
55162
|
+
* - horizontal swipe navigates slides, but only when editing is off
|
|
55163
|
+
* (`!canEdit()`): in edit mode single-finger gestures belong to element
|
|
55164
|
+
* manipulation (move/resize/rotate), so we never hijack them. The large
|
|
55165
|
+
* ‹ › buttons remain available for explicit navigation in all modes;
|
|
55166
|
+
* - long-press in edit mode opens the editor context menu at the press
|
|
55167
|
+
* point for the current selection (mirrors React's onLongPress path).
|
|
54316
55168
|
*
|
|
54317
|
-
*
|
|
54318
|
-
*
|
|
54319
|
-
* pointer/touch gestures belong to element manipulation (move/resize/rotate),
|
|
54320
|
-
* so we never hijack them. The large ‹ › buttons remain available in all
|
|
54321
|
-
* modes for explicit navigation.
|
|
55169
|
+
* The recogniser's swipe/long-press callbacks check the live `canEdit()` /
|
|
55170
|
+
* selection state, so a single attach handles every mode without re-binding.
|
|
54322
55171
|
*/
|
|
54323
|
-
|
|
54324
|
-
|
|
54325
|
-
|
|
54326
|
-
|
|
54327
|
-
|
|
54328
|
-
|
|
54329
|
-
|
|
54330
|
-
|
|
54331
|
-
|
|
54332
|
-
|
|
54333
|
-
|
|
54334
|
-
|
|
54335
|
-
|
|
54336
|
-
|
|
54337
|
-
|
|
54338
|
-
|
|
54339
|
-
|
|
54340
|
-
|
|
54341
|
-
|
|
54342
|
-
|
|
54343
|
-
|
|
54344
|
-
|
|
54345
|
-
|
|
54346
|
-
|
|
54347
|
-
|
|
54348
|
-
|
|
54349
|
-
|
|
54350
|
-
|
|
54351
|
-
|
|
54352
|
-
|
|
54353
|
-
|
|
54354
|
-
|
|
54355
|
-
|
|
54356
|
-
|
|
54357
|
-
|
|
54358
|
-
|
|
55172
|
+
setupTouchGestures() {
|
|
55173
|
+
const destroyRef = inject(DestroyRef);
|
|
55174
|
+
afterNextRender(() => {
|
|
55175
|
+
const el = this.mainEl()?.nativeElement;
|
|
55176
|
+
if (!el) {
|
|
55177
|
+
return;
|
|
55178
|
+
}
|
|
55179
|
+
const teardown = attachTouchGestures(el, {
|
|
55180
|
+
getScale: () => this.zoom(),
|
|
55181
|
+
callbacks: {
|
|
55182
|
+
onPinchZoom: (newScale) => this.zoom.set(newScale),
|
|
55183
|
+
onSwipe: (direction) => {
|
|
55184
|
+
// Edit mode: leave single-finger gestures to element manipulation.
|
|
55185
|
+
if (this.canEdit()) {
|
|
55186
|
+
return;
|
|
55187
|
+
}
|
|
55188
|
+
// direction 1 = swipe right (previous), -1 = swipe left (next).
|
|
55189
|
+
if (direction === 1) {
|
|
55190
|
+
this.goPrev();
|
|
55191
|
+
}
|
|
55192
|
+
else {
|
|
55193
|
+
this.goNext();
|
|
55194
|
+
}
|
|
55195
|
+
},
|
|
55196
|
+
onLongPress: (x, y) => {
|
|
55197
|
+
if (!this.canEdit() || this.presenting()) {
|
|
55198
|
+
return;
|
|
55199
|
+
}
|
|
55200
|
+
const selected = this.selectedElement();
|
|
55201
|
+
if (!selected) {
|
|
55202
|
+
return;
|
|
55203
|
+
}
|
|
55204
|
+
this.contextMenuPos.set({ x, y });
|
|
55205
|
+
},
|
|
55206
|
+
},
|
|
55207
|
+
});
|
|
55208
|
+
destroyRef.onDestroy(teardown);
|
|
55209
|
+
});
|
|
54359
55210
|
}
|
|
54360
55211
|
zoomIn() {
|
|
54361
55212
|
this.zoom.set(Math.min(ZOOM_MAX, Number((this.zoom() + ZOOM_STEP).toFixed(2))));
|
|
@@ -55153,12 +56004,7 @@ class PowerPointViewerComponent {
|
|
|
55153
56004
|
</nav>
|
|
55154
56005
|
}
|
|
55155
56006
|
|
|
55156
|
-
<main
|
|
55157
|
-
class="pptx-ng-main"
|
|
55158
|
-
#mainEl
|
|
55159
|
-
(touchstart)="onMainTouchStart($event)"
|
|
55160
|
-
(touchend)="onMainTouchEnd($event)"
|
|
55161
|
-
>
|
|
56007
|
+
<main class="pptx-ng-main" #mainEl>
|
|
55162
56008
|
<pptx-slide-canvas
|
|
55163
56009
|
[slide]="activeSlide()"
|
|
55164
56010
|
[canvasSize]="loader.canvasSize()"
|
|
@@ -55723,12 +56569,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
55723
56569
|
</nav>
|
|
55724
56570
|
}
|
|
55725
56571
|
|
|
55726
|
-
<main
|
|
55727
|
-
class="pptx-ng-main"
|
|
55728
|
-
#mainEl
|
|
55729
|
-
(touchstart)="onMainTouchStart($event)"
|
|
55730
|
-
(touchend)="onMainTouchEnd($event)"
|
|
55731
|
-
>
|
|
56572
|
+
<main class="pptx-ng-main" #mainEl>
|
|
55732
56573
|
<pptx-slide-canvas
|
|
55733
56574
|
[slide]="activeSlide()"
|
|
55734
56575
|
[canvasSize]="loader.canvasSize()"
|