@productcloudos/editor 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pc-editor.esm.js +588 -226
- package/dist/pc-editor.esm.js.map +1 -1
- package/dist/pc-editor.js +588 -225
- package/dist/pc-editor.js.map +1 -1
- package/dist/pc-editor.min.js +1 -1
- package/dist/pc-editor.min.js.map +1 -1
- package/dist/types/lib/core/PCEditor.d.ts +31 -5
- package/dist/types/lib/core/PCEditor.d.ts.map +1 -1
- package/dist/types/lib/import/PDFParser.d.ts.map +1 -1
- package/dist/types/lib/index.d.ts +1 -0
- package/dist/types/lib/index.d.ts.map +1 -1
- package/dist/types/lib/objects/EmbeddedObjectFactory.d.ts.map +1 -1
- package/dist/types/lib/objects/ImageObject.d.ts +11 -0
- package/dist/types/lib/objects/ImageObject.d.ts.map +1 -1
- package/dist/types/lib/objects/TextBoxObject.d.ts +5 -2
- package/dist/types/lib/objects/TextBoxObject.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableCell.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableObject.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableRow.d.ts.map +1 -1
- package/dist/types/lib/objects/table/types.d.ts +15 -15
- package/dist/types/lib/objects/table/types.d.ts.map +1 -1
- package/dist/types/lib/panes/DocumentInfoPane.d.ts +3 -1
- package/dist/types/lib/panes/DocumentInfoPane.d.ts.map +1 -1
- package/dist/types/lib/panes/DocumentSettingsPane.d.ts +2 -0
- package/dist/types/lib/panes/DocumentSettingsPane.d.ts.map +1 -1
- package/dist/types/lib/panes/FormattingPane.d.ts.map +1 -1
- package/dist/types/lib/panes/HyperlinkPane.d.ts +1 -0
- package/dist/types/lib/panes/HyperlinkPane.d.ts.map +1 -1
- package/dist/types/lib/panes/ImagePane.d.ts +1 -0
- package/dist/types/lib/panes/ImagePane.d.ts.map +1 -1
- package/dist/types/lib/panes/TablePane.d.ts +5 -0
- package/dist/types/lib/panes/TablePane.d.ts.map +1 -1
- package/dist/types/lib/panes/TextBoxPane.d.ts +1 -0
- package/dist/types/lib/panes/TextBoxPane.d.ts.map +1 -1
- package/dist/types/lib/rendering/CanvasManager.d.ts +1 -0
- package/dist/types/lib/rendering/CanvasManager.d.ts.map +1 -1
- package/dist/types/lib/rendering/FlowingTextRenderer.d.ts.map +1 -1
- package/dist/types/lib/rendering/PDFGenerator.d.ts.map +1 -1
- package/dist/types/lib/text/FlowingTextContent.d.ts.map +1 -1
- package/dist/types/lib/text/TextFormatting.d.ts +12 -1
- package/dist/types/lib/text/TextFormatting.d.ts.map +1 -1
- package/dist/types/lib/types/index.d.ts +2 -0
- package/dist/types/lib/types/index.d.ts.map +1 -1
- package/dist/types/lib/undo/transaction/MutationUndo.d.ts.map +1 -1
- package/dist/types/lib/utils/logger.d.ts +20 -0
- package/dist/types/lib/utils/logger.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/pc-editor.esm.js
CHANGED
|
@@ -818,12 +818,13 @@ class TextFormattingManager extends EventEmitter {
|
|
|
818
818
|
}
|
|
819
819
|
/**
|
|
820
820
|
* Get formatting at a specific character position.
|
|
821
|
-
* Returns the position-specific formatting
|
|
821
|
+
* Returns the position-specific formatting merged with defaults,
|
|
822
|
+
* ensuring all properties are consistently present.
|
|
822
823
|
*/
|
|
823
824
|
getFormattingAt(position) {
|
|
824
825
|
const override = this.formatting.get(position);
|
|
825
826
|
if (override) {
|
|
826
|
-
return { ...override };
|
|
827
|
+
return { ...this._defaultFormatting, ...override };
|
|
827
828
|
}
|
|
828
829
|
return { ...this._defaultFormatting };
|
|
829
830
|
}
|
|
@@ -910,6 +911,43 @@ class TextFormattingManager extends EventEmitter {
|
|
|
910
911
|
getAllFormatting() {
|
|
911
912
|
return new Map(this.formatting);
|
|
912
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Get formatting as compressed runs for serialization.
|
|
916
|
+
* Only outputs entries where formatting changes from the previous character.
|
|
917
|
+
* Skips leading default formatting to minimize output size.
|
|
918
|
+
* @param textLength Length of the text to serialize formatting for
|
|
919
|
+
*/
|
|
920
|
+
getCompressedRuns(textLength) {
|
|
921
|
+
const runs = [];
|
|
922
|
+
const defaultFormat = this._defaultFormatting;
|
|
923
|
+
let lastFormat = null;
|
|
924
|
+
for (let i = 0; i < textLength; i++) {
|
|
925
|
+
const currentFormat = this.getFormattingAt(i);
|
|
926
|
+
const formatChanged = lastFormat === null ||
|
|
927
|
+
currentFormat.fontFamily !== lastFormat.fontFamily ||
|
|
928
|
+
currentFormat.fontSize !== lastFormat.fontSize ||
|
|
929
|
+
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
930
|
+
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
931
|
+
currentFormat.color !== lastFormat.color ||
|
|
932
|
+
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
933
|
+
if (formatChanged) {
|
|
934
|
+
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
935
|
+
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
936
|
+
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
937
|
+
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
938
|
+
currentFormat.color === defaultFormat.color &&
|
|
939
|
+
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
940
|
+
if (!isDefault || runs.length > 0) {
|
|
941
|
+
runs.push({
|
|
942
|
+
index: i,
|
|
943
|
+
formatting: { ...currentFormat }
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
lastFormat = currentFormat;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return runs;
|
|
950
|
+
}
|
|
913
951
|
/**
|
|
914
952
|
* Restore formatting from a map (for deserialization).
|
|
915
953
|
*/
|
|
@@ -3813,6 +3851,35 @@ class ImageObject extends BaseEmbeddedObject {
|
|
|
3813
3851
|
get hasError() {
|
|
3814
3852
|
return this._error;
|
|
3815
3853
|
}
|
|
3854
|
+
/**
|
|
3855
|
+
* Get the loaded HTMLImageElement, if available.
|
|
3856
|
+
* Used by PDFGenerator to convert unsupported formats to PNG.
|
|
3857
|
+
*/
|
|
3858
|
+
get imageElement() {
|
|
3859
|
+
return this._loaded ? this._image : null;
|
|
3860
|
+
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Convert the image to a PNG data URL via canvas.
|
|
3863
|
+
* Used when the original format (e.g., SVG, WebP, GIF) is not supported by pdf-lib.
|
|
3864
|
+
* Returns null if the image is not loaded or conversion fails.
|
|
3865
|
+
*/
|
|
3866
|
+
toPngDataUrl() {
|
|
3867
|
+
if (!this._loaded || !this._image)
|
|
3868
|
+
return null;
|
|
3869
|
+
try {
|
|
3870
|
+
const canvas = document.createElement('canvas');
|
|
3871
|
+
canvas.width = this._image.naturalWidth || this._size.width;
|
|
3872
|
+
canvas.height = this._image.naturalHeight || this._size.height;
|
|
3873
|
+
const ctx = canvas.getContext('2d');
|
|
3874
|
+
if (!ctx)
|
|
3875
|
+
return null;
|
|
3876
|
+
ctx.drawImage(this._image, 0, 0, canvas.width, canvas.height);
|
|
3877
|
+
return canvas.toDataURL('image/png');
|
|
3878
|
+
}
|
|
3879
|
+
catch {
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3816
3883
|
loadImage() {
|
|
3817
3884
|
if (!this._src) {
|
|
3818
3885
|
this._error = true;
|
|
@@ -4708,12 +4775,10 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4708
4775
|
this.editing = false;
|
|
4709
4776
|
}
|
|
4710
4777
|
toData() {
|
|
4711
|
-
// Serialize formatting
|
|
4712
|
-
const
|
|
4713
|
-
const
|
|
4714
|
-
|
|
4715
|
-
formattingEntries.push([key, { ...value }]);
|
|
4716
|
-
});
|
|
4778
|
+
// Serialize formatting as compressed runs (only at change boundaries)
|
|
4779
|
+
const text = this._flowingContent.getText();
|
|
4780
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
4781
|
+
const formattingEntries = compressedRuns.map(run => [run.index, { ...run.formatting }]);
|
|
4717
4782
|
// Get substitution fields as array
|
|
4718
4783
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
4719
4784
|
return {
|
|
@@ -4772,12 +4837,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4772
4837
|
this._border = { ...boxData.border };
|
|
4773
4838
|
if (boxData.padding !== undefined)
|
|
4774
4839
|
this._padding = boxData.padding;
|
|
4775
|
-
// Restore formatting runs
|
|
4776
|
-
if (boxData.formattingRuns) {
|
|
4840
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
4841
|
+
if (boxData.formattingRuns && boxData.formattingRuns.length > 0) {
|
|
4777
4842
|
const formattingManager = this._flowingContent.getFormattingManager();
|
|
4778
4843
|
formattingManager.clear();
|
|
4779
|
-
|
|
4780
|
-
|
|
4844
|
+
const textLength = this._flowingContent.getText().length;
|
|
4845
|
+
for (let i = 0; i < boxData.formattingRuns.length; i++) {
|
|
4846
|
+
const [startIndex, style] = boxData.formattingRuns[i];
|
|
4847
|
+
const nextIndex = i + 1 < boxData.formattingRuns.length
|
|
4848
|
+
? boxData.formattingRuns[i + 1][0]
|
|
4849
|
+
: textLength;
|
|
4850
|
+
if (startIndex < nextIndex) {
|
|
4851
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
4852
|
+
}
|
|
4781
4853
|
}
|
|
4782
4854
|
}
|
|
4783
4855
|
// Restore substitution fields
|
|
@@ -4927,15 +4999,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4927
4999
|
}
|
|
4928
5000
|
/**
|
|
4929
5001
|
* Check if a point is within this text box region.
|
|
5002
|
+
* Uses the full object bounds (including padding/border) so that
|
|
5003
|
+
* double-click to enter edit mode works on the entire text box area,
|
|
5004
|
+
* not just the inner text area.
|
|
4930
5005
|
* @param point Point in canvas coordinates
|
|
4931
|
-
* @param
|
|
5006
|
+
* @param _pageIndex The page index (ignored for text boxes)
|
|
4932
5007
|
*/
|
|
4933
|
-
containsPointInRegion(point,
|
|
4934
|
-
|
|
4935
|
-
if (!bounds)
|
|
5008
|
+
containsPointInRegion(point, _pageIndex) {
|
|
5009
|
+
if (!this._renderedPosition)
|
|
4936
5010
|
return false;
|
|
4937
|
-
return point.x >=
|
|
4938
|
-
point.
|
|
5011
|
+
return point.x >= this._renderedPosition.x &&
|
|
5012
|
+
point.x <= this._renderedPosition.x + this._size.width &&
|
|
5013
|
+
point.y >= this._renderedPosition.y &&
|
|
5014
|
+
point.y <= this._renderedPosition.y + this._size.height;
|
|
4939
5015
|
}
|
|
4940
5016
|
/**
|
|
4941
5017
|
* Trigger a reflow of text in this text box.
|
|
@@ -5020,6 +5096,41 @@ function getVerticalPadding(padding) {
|
|
|
5020
5096
|
return padding.top + padding.bottom;
|
|
5021
5097
|
}
|
|
5022
5098
|
|
|
5099
|
+
/**
|
|
5100
|
+
* Logger - Centralized logging for PC Editor.
|
|
5101
|
+
*
|
|
5102
|
+
* When enabled, logs informational messages to the console.
|
|
5103
|
+
* When disabled, only errors are logged.
|
|
5104
|
+
* Controlled via EditorOptions.enableLogging or Logger.setEnabled().
|
|
5105
|
+
*/
|
|
5106
|
+
let _enabled = false;
|
|
5107
|
+
const Logger = {
|
|
5108
|
+
/** Enable or disable logging. When disabled, only errors are logged. */
|
|
5109
|
+
setEnabled(enabled) {
|
|
5110
|
+
_enabled = enabled;
|
|
5111
|
+
},
|
|
5112
|
+
/** Check if logging is enabled. */
|
|
5113
|
+
isEnabled() {
|
|
5114
|
+
return _enabled;
|
|
5115
|
+
},
|
|
5116
|
+
/** Log an informational message. Only outputs when logging is enabled. */
|
|
5117
|
+
log(...args) {
|
|
5118
|
+
if (_enabled) {
|
|
5119
|
+
console.log(...args);
|
|
5120
|
+
}
|
|
5121
|
+
},
|
|
5122
|
+
/** Log a warning. Only outputs when logging is enabled. */
|
|
5123
|
+
warn(...args) {
|
|
5124
|
+
if (_enabled) {
|
|
5125
|
+
console.warn(...args);
|
|
5126
|
+
}
|
|
5127
|
+
},
|
|
5128
|
+
/** Log an error. Always outputs regardless of logging state. */
|
|
5129
|
+
error(...args) {
|
|
5130
|
+
console.error(...args);
|
|
5131
|
+
}
|
|
5132
|
+
};
|
|
5133
|
+
|
|
5023
5134
|
/**
|
|
5024
5135
|
* TableCell - A cell within a table that contains editable text.
|
|
5025
5136
|
* Implements EditableTextRegion for unified text interaction.
|
|
@@ -5070,7 +5181,7 @@ class TableCell extends EventEmitter {
|
|
|
5070
5181
|
});
|
|
5071
5182
|
// Prevent embedded objects in table cells (only substitution fields allowed)
|
|
5072
5183
|
this._flowingContent.insertEmbeddedObject = () => {
|
|
5073
|
-
|
|
5184
|
+
Logger.warn('[pc-editor:TableCell] Embedded objects are not allowed in table cells. Use insertSubstitutionField instead.');
|
|
5074
5185
|
};
|
|
5075
5186
|
// Set initial content
|
|
5076
5187
|
if (config.content) {
|
|
@@ -5387,7 +5498,7 @@ class TableCell extends EventEmitter {
|
|
|
5387
5498
|
this._reflowDirty = false;
|
|
5388
5499
|
this._lastReflowWidth = width;
|
|
5389
5500
|
this._cachedContentHeight = null; // Clear cached height since lines changed
|
|
5390
|
-
|
|
5501
|
+
Logger.log('[pc-editor:TableCell.reflow] cellId:', this._id, 'text:', JSON.stringify(this._flowingContent.getText()), 'lines:', this._flowedLines.length);
|
|
5391
5502
|
}
|
|
5392
5503
|
/**
|
|
5393
5504
|
* Mark this cell as needing reflow.
|
|
@@ -5425,7 +5536,7 @@ class TableCell extends EventEmitter {
|
|
|
5425
5536
|
return this._editing && this._flowingContent.hasFocus();
|
|
5426
5537
|
}
|
|
5427
5538
|
handleKeyDown(e) {
|
|
5428
|
-
|
|
5539
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Key:', e.key, '_editing:', this._editing, 'flowingContent.hasFocus:', this._flowingContent.hasFocus());
|
|
5429
5540
|
if (!this._editing)
|
|
5430
5541
|
return false;
|
|
5431
5542
|
// Let parent table handle Tab navigation
|
|
@@ -5433,9 +5544,9 @@ class TableCell extends EventEmitter {
|
|
|
5433
5544
|
return false; // Not handled - parent will handle
|
|
5434
5545
|
}
|
|
5435
5546
|
// Delegate to FlowingTextContent
|
|
5436
|
-
|
|
5547
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Delegating to FlowingTextContent.handleKeyDown');
|
|
5437
5548
|
const handled = this._flowingContent.handleKeyDown(e);
|
|
5438
|
-
|
|
5549
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] FlowingTextContent handled:', handled);
|
|
5439
5550
|
return handled;
|
|
5440
5551
|
}
|
|
5441
5552
|
onCursorBlink(handler) {
|
|
@@ -5507,28 +5618,49 @@ class TableCell extends EventEmitter {
|
|
|
5507
5618
|
// Serialization
|
|
5508
5619
|
// ============================================
|
|
5509
5620
|
toData() {
|
|
5510
|
-
const
|
|
5511
|
-
const
|
|
5512
|
-
|
|
5513
|
-
formattingRuns.push([index, { ...style }]);
|
|
5514
|
-
});
|
|
5621
|
+
const text = this._flowingContent.getText();
|
|
5622
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
5623
|
+
const formattingRuns = compressedRuns.map(run => [run.index, run.formatting]);
|
|
5515
5624
|
// Get substitution fields for serialization
|
|
5516
5625
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5626
|
+
// Only include non-default values to minimize export size
|
|
5627
|
+
const defaults = DEFAULT_TABLE_STYLE;
|
|
5628
|
+
const defaultBorder = DEFAULT_CELL_BORDER_SIDE;
|
|
5629
|
+
const isDefaultBorderSide = (side) => side.width === defaultBorder.width && side.color === defaultBorder.color && side.style === defaultBorder.style;
|
|
5630
|
+
const isDefaultBorder = isDefaultBorderSide(this._border.top) &&
|
|
5631
|
+
isDefaultBorderSide(this._border.right) &&
|
|
5632
|
+
isDefaultBorderSide(this._border.bottom) &&
|
|
5633
|
+
isDefaultBorderSide(this._border.left);
|
|
5634
|
+
const isDefaultPadding = this._padding.top === defaults.cellPadding &&
|
|
5635
|
+
this._padding.right === defaults.cellPadding &&
|
|
5636
|
+
this._padding.bottom === defaults.cellPadding &&
|
|
5637
|
+
this._padding.left === defaults.cellPadding;
|
|
5638
|
+
const data = {};
|
|
5639
|
+
if (this._rowSpan !== 1)
|
|
5640
|
+
data.rowSpan = this._rowSpan;
|
|
5641
|
+
if (this._colSpan !== 1)
|
|
5642
|
+
data.colSpan = this._colSpan;
|
|
5643
|
+
if (this._backgroundColor !== defaults.backgroundColor)
|
|
5644
|
+
data.backgroundColor = this._backgroundColor;
|
|
5645
|
+
if (!isDefaultBorder)
|
|
5646
|
+
data.border = this._border;
|
|
5647
|
+
if (!isDefaultPadding)
|
|
5648
|
+
data.padding = this._padding;
|
|
5649
|
+
if (this._verticalAlign !== 'top')
|
|
5650
|
+
data.verticalAlign = this._verticalAlign;
|
|
5651
|
+
if (text)
|
|
5652
|
+
data.content = text;
|
|
5653
|
+
if (this._fontFamily !== defaults.fontFamily)
|
|
5654
|
+
data.fontFamily = this._fontFamily;
|
|
5655
|
+
if (this._fontSize !== defaults.fontSize)
|
|
5656
|
+
data.fontSize = this._fontSize;
|
|
5657
|
+
if (this._color !== defaults.color)
|
|
5658
|
+
data.color = this._color;
|
|
5659
|
+
if (formattingRuns.length > 0)
|
|
5660
|
+
data.formattingRuns = formattingRuns;
|
|
5661
|
+
if (fields.length > 0)
|
|
5662
|
+
data.substitutionFields = fields;
|
|
5663
|
+
return data;
|
|
5532
5664
|
}
|
|
5533
5665
|
static fromData(data) {
|
|
5534
5666
|
const cell = new TableCell({
|
|
@@ -5544,14 +5676,19 @@ class TableCell extends EventEmitter {
|
|
|
5544
5676
|
fontSize: data.fontSize,
|
|
5545
5677
|
color: data.color
|
|
5546
5678
|
});
|
|
5547
|
-
// Restore formatting runs
|
|
5548
|
-
if (data.formattingRuns) {
|
|
5679
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
5680
|
+
if (data.formattingRuns && data.formattingRuns.length > 0) {
|
|
5549
5681
|
const formattingManager = cell._flowingContent.getFormattingManager();
|
|
5550
|
-
const
|
|
5551
|
-
for (
|
|
5552
|
-
|
|
5682
|
+
const textLength = (data.content || '').length;
|
|
5683
|
+
for (let i = 0; i < data.formattingRuns.length; i++) {
|
|
5684
|
+
const [startIndex, style] = data.formattingRuns[i];
|
|
5685
|
+
const nextIndex = i + 1 < data.formattingRuns.length
|
|
5686
|
+
? data.formattingRuns[i + 1][0]
|
|
5687
|
+
: textLength;
|
|
5688
|
+
if (startIndex < nextIndex) {
|
|
5689
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
5690
|
+
}
|
|
5553
5691
|
}
|
|
5554
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
5555
5692
|
}
|
|
5556
5693
|
// Restore substitution fields
|
|
5557
5694
|
if (data.substitutionFields && Array.isArray(data.substitutionFields)) {
|
|
@@ -5785,13 +5922,17 @@ class TableRow extends EventEmitter {
|
|
|
5785
5922
|
// Serialization
|
|
5786
5923
|
// ============================================
|
|
5787
5924
|
toData() {
|
|
5788
|
-
|
|
5789
|
-
id: this._id,
|
|
5790
|
-
height: this._height,
|
|
5791
|
-
minHeight: this._minHeight,
|
|
5792
|
-
isHeader: this._isHeader,
|
|
5925
|
+
const data = {
|
|
5793
5926
|
cells: this._cells.map(cell => cell.toData())
|
|
5794
5927
|
};
|
|
5928
|
+
// Only include non-default values
|
|
5929
|
+
if (this._height !== null)
|
|
5930
|
+
data.height = this._height;
|
|
5931
|
+
if (this._minHeight !== DEFAULT_TABLE_STYLE.minRowHeight)
|
|
5932
|
+
data.minHeight = this._minHeight;
|
|
5933
|
+
if (this._isHeader)
|
|
5934
|
+
data.isHeader = this._isHeader;
|
|
5935
|
+
return data;
|
|
5795
5936
|
}
|
|
5796
5937
|
static fromData(data) {
|
|
5797
5938
|
const row = new TableRow({
|
|
@@ -6125,7 +6266,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6125
6266
|
set position(value) {
|
|
6126
6267
|
// Tables only support block positioning - ignore any attempt to set other modes
|
|
6127
6268
|
if (value !== 'block') {
|
|
6128
|
-
|
|
6269
|
+
Logger.warn(`[pc-editor:TableObject] Tables only support 'block' positioning. Ignoring attempt to set '${value}'.`);
|
|
6129
6270
|
}
|
|
6130
6271
|
// Always set to block
|
|
6131
6272
|
super.position = 'block';
|
|
@@ -6664,24 +6805,24 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6664
6805
|
createRowLoop(startRowIndex, endRowIndex, fieldPath) {
|
|
6665
6806
|
// Validate range
|
|
6666
6807
|
if (startRowIndex < 0 || endRowIndex >= this._rows.length) {
|
|
6667
|
-
|
|
6808
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Invalid row range');
|
|
6668
6809
|
return null;
|
|
6669
6810
|
}
|
|
6670
6811
|
if (startRowIndex > endRowIndex) {
|
|
6671
|
-
|
|
6812
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Start index must be <= end index');
|
|
6672
6813
|
return null;
|
|
6673
6814
|
}
|
|
6674
6815
|
// Check for overlap with existing loops
|
|
6675
6816
|
for (const existingLoop of this._rowLoops.values()) {
|
|
6676
6817
|
if (this.loopRangesOverlap(startRowIndex, endRowIndex, existingLoop.startRowIndex, existingLoop.endRowIndex)) {
|
|
6677
|
-
|
|
6818
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop range overlaps with existing loop');
|
|
6678
6819
|
return null;
|
|
6679
6820
|
}
|
|
6680
6821
|
}
|
|
6681
6822
|
// Check that loop rows are not header rows
|
|
6682
6823
|
for (let i = startRowIndex; i <= endRowIndex; i++) {
|
|
6683
6824
|
if (this._rows[i]?.isHeader) {
|
|
6684
|
-
|
|
6825
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop rows cannot be header rows');
|
|
6685
6826
|
return null;
|
|
6686
6827
|
}
|
|
6687
6828
|
}
|
|
@@ -7487,7 +7628,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
7487
7628
|
return this._editing;
|
|
7488
7629
|
}
|
|
7489
7630
|
handleKeyDown(e) {
|
|
7490
|
-
|
|
7631
|
+
Logger.log('[pc-editor:TableObject.handleKeyDown] Key:', e.key, '_editing:', this._editing, '_focusedCell:', this._focusedCell);
|
|
7491
7632
|
if (!this._editing)
|
|
7492
7633
|
return false;
|
|
7493
7634
|
// Handle Tab navigation
|
|
@@ -7596,6 +7737,9 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
7596
7737
|
result.mergedCell.markReflowDirty();
|
|
7597
7738
|
}
|
|
7598
7739
|
this.clearSelection();
|
|
7740
|
+
// Focus the anchor (top-left) cell of the merged range
|
|
7741
|
+
const normalized = TableCellMerger.normalizeRange(mergeRange);
|
|
7742
|
+
this.focusCell(normalized.start.row, normalized.start.col);
|
|
7599
7743
|
this.emit('cells-merged', { range: mergeRange });
|
|
7600
7744
|
this.emit('content-changed', {});
|
|
7601
7745
|
}
|
|
@@ -8275,14 +8419,20 @@ class EmbeddedObjectFactory {
|
|
|
8275
8419
|
border: data.data.border,
|
|
8276
8420
|
padding: data.data.padding
|
|
8277
8421
|
});
|
|
8278
|
-
// Restore formatting runs
|
|
8422
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
8279
8423
|
if (data.data.formattingRuns && Array.isArray(data.data.formattingRuns)) {
|
|
8280
|
-
const
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8424
|
+
const runs = data.data.formattingRuns;
|
|
8425
|
+
if (runs.length > 0) {
|
|
8426
|
+
const formattingManager = textBox.flowingContent.getFormattingManager();
|
|
8427
|
+
const textLength = textBox.flowingContent.getText().length;
|
|
8428
|
+
for (let i = 0; i < runs.length; i++) {
|
|
8429
|
+
const [startIndex, style] = runs[i];
|
|
8430
|
+
const nextIndex = i + 1 < runs.length ? runs[i + 1][0] : textLength;
|
|
8431
|
+
if (startIndex < nextIndex) {
|
|
8432
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8284
8435
|
}
|
|
8285
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
8286
8436
|
}
|
|
8287
8437
|
// Restore substitution fields if present
|
|
8288
8438
|
if (data.data.substitutionFields && Array.isArray(data.data.substitutionFields)) {
|
|
@@ -9259,9 +9409,9 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9259
9409
|
* @returns true if the event was handled, false otherwise
|
|
9260
9410
|
*/
|
|
9261
9411
|
handleKeyDown(e) {
|
|
9262
|
-
|
|
9412
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] Key:', e.key, '_hasFocus:', this._hasFocus);
|
|
9263
9413
|
if (!this._hasFocus) {
|
|
9264
|
-
|
|
9414
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] No focus, returning false');
|
|
9265
9415
|
return false;
|
|
9266
9416
|
}
|
|
9267
9417
|
switch (e.key) {
|
|
@@ -9776,46 +9926,19 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9776
9926
|
toData() {
|
|
9777
9927
|
// Serialize text content
|
|
9778
9928
|
const text = this.textState.getText();
|
|
9779
|
-
// Serialize text formatting as runs
|
|
9780
|
-
|
|
9781
|
-
const formattingRuns =
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
9791
|
-
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
9792
|
-
currentFormat.color !== lastFormat.color ||
|
|
9793
|
-
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
9794
|
-
if (formatChanged) {
|
|
9795
|
-
// Only output if different from default (to further reduce size)
|
|
9796
|
-
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
9797
|
-
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
9798
|
-
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
9799
|
-
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
9800
|
-
currentFormat.color === defaultFormat.color &&
|
|
9801
|
-
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
9802
|
-
// Always output first run if it's not default, or output when format changes
|
|
9803
|
-
if (!isDefault || formattingRuns.length > 0) {
|
|
9804
|
-
formattingRuns.push({
|
|
9805
|
-
index: i,
|
|
9806
|
-
formatting: {
|
|
9807
|
-
fontFamily: currentFormat.fontFamily,
|
|
9808
|
-
fontSize: currentFormat.fontSize,
|
|
9809
|
-
fontWeight: currentFormat.fontWeight,
|
|
9810
|
-
fontStyle: currentFormat.fontStyle,
|
|
9811
|
-
color: currentFormat.color,
|
|
9812
|
-
backgroundColor: currentFormat.backgroundColor
|
|
9813
|
-
}
|
|
9814
|
-
});
|
|
9815
|
-
}
|
|
9816
|
-
lastFormat = currentFormat;
|
|
9929
|
+
// Serialize text formatting as compressed runs (only at change boundaries)
|
|
9930
|
+
const compressedRuns = this.formatting.getCompressedRuns(text.length);
|
|
9931
|
+
const formattingRuns = compressedRuns.map(run => ({
|
|
9932
|
+
index: run.index,
|
|
9933
|
+
formatting: {
|
|
9934
|
+
fontFamily: run.formatting.fontFamily,
|
|
9935
|
+
fontSize: run.formatting.fontSize,
|
|
9936
|
+
fontWeight: run.formatting.fontWeight,
|
|
9937
|
+
fontStyle: run.formatting.fontStyle,
|
|
9938
|
+
color: run.formatting.color,
|
|
9939
|
+
backgroundColor: run.formatting.backgroundColor
|
|
9817
9940
|
}
|
|
9818
|
-
}
|
|
9941
|
+
}));
|
|
9819
9942
|
// Serialize paragraph formatting
|
|
9820
9943
|
const paragraphFormatting = this.paragraphFormatting.toJSON();
|
|
9821
9944
|
// Serialize substitution fields
|
|
@@ -9911,7 +10034,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9911
10034
|
content.getEmbeddedObjectManager().insert(object, ref.textIndex);
|
|
9912
10035
|
}
|
|
9913
10036
|
else {
|
|
9914
|
-
|
|
10037
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9915
10038
|
}
|
|
9916
10039
|
}
|
|
9917
10040
|
}
|
|
@@ -9965,7 +10088,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9965
10088
|
this.embeddedObjects.insert(object, ref.textIndex);
|
|
9966
10089
|
}
|
|
9967
10090
|
else {
|
|
9968
|
-
|
|
10091
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9969
10092
|
}
|
|
9970
10093
|
}
|
|
9971
10094
|
}
|
|
@@ -12504,7 +12627,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12504
12627
|
updateResizeHandleTargets(selectedObjects) {
|
|
12505
12628
|
// Clear existing resize handle targets
|
|
12506
12629
|
this._hitTestManager.clearCategory('resize-handles');
|
|
12507
|
-
|
|
12630
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets selectedObjects:', selectedObjects.length);
|
|
12508
12631
|
// Register resize handles for each selected object
|
|
12509
12632
|
for (const object of selectedObjects) {
|
|
12510
12633
|
if (!object.resizable)
|
|
@@ -12517,7 +12640,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12517
12640
|
// For regular objects, use renderedPosition
|
|
12518
12641
|
const pos = object.renderedPosition;
|
|
12519
12642
|
const pageIndex = object.renderedPageIndex;
|
|
12520
|
-
|
|
12643
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets object:', object.id, 'pageIndex:', pageIndex, 'pos:', pos);
|
|
12521
12644
|
if (pos && pageIndex >= 0) {
|
|
12522
12645
|
this.registerObjectResizeHandles(object, pageIndex, pos);
|
|
12523
12646
|
}
|
|
@@ -14173,7 +14296,8 @@ class CanvasManager extends EventEmitter {
|
|
|
14173
14296
|
const pageIndex = this.document.pages.findIndex(p => p.id === pageId);
|
|
14174
14297
|
// Get the slice for this page (for multi-page tables)
|
|
14175
14298
|
const slice = table.getRenderedSlice(pageIndex);
|
|
14176
|
-
const tablePosition = slice?.position ||
|
|
14299
|
+
const tablePosition = slice?.position ||
|
|
14300
|
+
(table.renderedPageIndex === pageIndex ? table.renderedPosition : null);
|
|
14177
14301
|
const sliceHeight = slice?.height || table.height;
|
|
14178
14302
|
// Check if point is within the table slice on this page
|
|
14179
14303
|
const isInsideTable = tablePosition &&
|
|
@@ -14206,6 +14330,7 @@ class CanvasManager extends EventEmitter {
|
|
|
14206
14330
|
end: cellAddr
|
|
14207
14331
|
});
|
|
14208
14332
|
this.render();
|
|
14333
|
+
this.emit('table-cell-selection-changed', { table });
|
|
14209
14334
|
e.preventDefault();
|
|
14210
14335
|
return;
|
|
14211
14336
|
}
|
|
@@ -14485,7 +14610,8 @@ class CanvasManager extends EventEmitter {
|
|
|
14485
14610
|
const currentPageIndex = this.document.pages.findIndex(p => p.id === pageId);
|
|
14486
14611
|
// Get the slice for the current page (for multi-page tables)
|
|
14487
14612
|
const slice = table.getRenderedSlice(currentPageIndex);
|
|
14488
|
-
const tablePosition = slice?.position ||
|
|
14613
|
+
const tablePosition = slice?.position ||
|
|
14614
|
+
(table.renderedPageIndex === currentPageIndex ? table.renderedPosition : null);
|
|
14489
14615
|
const sliceHeight = slice?.height || table.height;
|
|
14490
14616
|
if (tablePosition) {
|
|
14491
14617
|
// Check if point is within the table slice on this page
|
|
@@ -14515,6 +14641,7 @@ class CanvasManager extends EventEmitter {
|
|
|
14515
14641
|
end: cellAddr
|
|
14516
14642
|
});
|
|
14517
14643
|
this.render();
|
|
14644
|
+
this.emit('table-cell-selection-changed', { table });
|
|
14518
14645
|
}
|
|
14519
14646
|
}
|
|
14520
14647
|
}
|
|
@@ -15058,40 +15185,46 @@ class CanvasManager extends EventEmitter {
|
|
|
15058
15185
|
canvas.style.cursor = 'move';
|
|
15059
15186
|
return;
|
|
15060
15187
|
}
|
|
15061
|
-
// Show text cursor for
|
|
15062
|
-
if (object instanceof TextBoxObject) {
|
|
15063
|
-
canvas.style.cursor =
|
|
15064
|
-
return;
|
|
15188
|
+
// Show text cursor for objects in edit mode, arrow otherwise
|
|
15189
|
+
if (object instanceof TextBoxObject && this.editingTextBox === object) {
|
|
15190
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15065
15191
|
}
|
|
15192
|
+
else if (object instanceof TableObject && this._focusedControl === object) {
|
|
15193
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15194
|
+
}
|
|
15195
|
+
else {
|
|
15196
|
+
canvas.style.cursor = 'default';
|
|
15197
|
+
}
|
|
15198
|
+
return;
|
|
15066
15199
|
}
|
|
15067
15200
|
}
|
|
15068
15201
|
// Check for table cells (show text cursor)
|
|
15069
15202
|
const tableCellHit = hitTestManager.queryByType(pageIndex, point, 'table-cell');
|
|
15070
15203
|
if (tableCellHit && tableCellHit.data.type === 'table-cell') {
|
|
15071
|
-
canvas.style.cursor =
|
|
15204
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15072
15205
|
return;
|
|
15073
15206
|
}
|
|
15074
15207
|
// Check for text regions (body, header, footer - show text cursor)
|
|
15075
15208
|
const textRegionHit = hitTestManager.queryByType(pageIndex, point, 'text-region');
|
|
15076
15209
|
if (textRegionHit && textRegionHit.data.type === 'text-region') {
|
|
15077
|
-
canvas.style.cursor =
|
|
15210
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15078
15211
|
return;
|
|
15079
15212
|
}
|
|
15080
15213
|
// Also check if point is within any editable region (body, header, footer)
|
|
15081
15214
|
// This catches cases where text region hit targets may not cover empty space
|
|
15082
15215
|
const bodyRegion = this.regionManager.getBodyRegion();
|
|
15083
15216
|
if (bodyRegion && bodyRegion.containsPointInRegion(point, pageIndex)) {
|
|
15084
|
-
canvas.style.cursor =
|
|
15217
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15085
15218
|
return;
|
|
15086
15219
|
}
|
|
15087
15220
|
const headerRegion = this.regionManager.getHeaderRegion();
|
|
15088
15221
|
if (headerRegion && headerRegion.containsPointInRegion(point, pageIndex)) {
|
|
15089
|
-
canvas.style.cursor =
|
|
15222
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15090
15223
|
return;
|
|
15091
15224
|
}
|
|
15092
15225
|
const footerRegion = this.regionManager.getFooterRegion();
|
|
15093
15226
|
if (footerRegion && footerRegion.containsPointInRegion(point, pageIndex)) {
|
|
15094
|
-
canvas.style.cursor =
|
|
15227
|
+
canvas.style.cursor = CanvasManager.TEXT_CURSOR;
|
|
15095
15228
|
return;
|
|
15096
15229
|
}
|
|
15097
15230
|
canvas.style.cursor = 'default';
|
|
@@ -15109,7 +15242,8 @@ class CanvasManager extends EventEmitter {
|
|
|
15109
15242
|
const { table, dividerType, index } = target.data;
|
|
15110
15243
|
// Get the table position from slice info
|
|
15111
15244
|
const slice = table.getRenderedSlice(pageIndex);
|
|
15112
|
-
const tablePosition = slice?.position ||
|
|
15245
|
+
const tablePosition = slice?.position ||
|
|
15246
|
+
(table.renderedPageIndex === pageIndex ? table.renderedPosition : null);
|
|
15113
15247
|
if (tablePosition) {
|
|
15114
15248
|
// Calculate the divider position based on type and index
|
|
15115
15249
|
let position;
|
|
@@ -15163,7 +15297,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15163
15297
|
this.emit('element-removed', { elementId: objectId });
|
|
15164
15298
|
}
|
|
15165
15299
|
selectElement(elementId) {
|
|
15166
|
-
|
|
15300
|
+
Logger.log('[pc-editor:CanvasManager] Selecting element:', elementId);
|
|
15167
15301
|
this.selectedElements.add(elementId);
|
|
15168
15302
|
// Update embedded object's selected state
|
|
15169
15303
|
const flowingContents = [
|
|
@@ -15175,12 +15309,12 @@ class CanvasManager extends EventEmitter {
|
|
|
15175
15309
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15176
15310
|
for (const [, obj] of embeddedObjects.entries()) {
|
|
15177
15311
|
if (obj.id === elementId) {
|
|
15178
|
-
|
|
15312
|
+
Logger.log('[pc-editor:CanvasManager] Found embedded object to select:', obj.id);
|
|
15179
15313
|
obj.selected = true;
|
|
15180
15314
|
}
|
|
15181
15315
|
}
|
|
15182
15316
|
}
|
|
15183
|
-
|
|
15317
|
+
Logger.log('[pc-editor:CanvasManager] Selected elements after selection:', Array.from(this.selectedElements));
|
|
15184
15318
|
this.render();
|
|
15185
15319
|
this.updateResizeHandleHitTargets();
|
|
15186
15320
|
this.emit('selection-change', { selectedElements: Array.from(this.selectedElements) });
|
|
@@ -15230,10 +15364,10 @@ class CanvasManager extends EventEmitter {
|
|
|
15230
15364
|
this.flowingTextRenderer.updateResizeHandleTargets(selectedObjects);
|
|
15231
15365
|
}
|
|
15232
15366
|
clearSelection() {
|
|
15233
|
-
|
|
15367
|
+
Logger.log('[pc-editor:CanvasManager] clearSelection called, current selected elements:', Array.from(this.selectedElements));
|
|
15234
15368
|
// Clear selected state on all embedded objects
|
|
15235
15369
|
this.selectedElements.forEach(elementId => {
|
|
15236
|
-
|
|
15370
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection for element:', elementId);
|
|
15237
15371
|
// Check embedded objects in all flowing content sources (body, header, footer)
|
|
15238
15372
|
const flowingContents = [
|
|
15239
15373
|
this.document.bodyFlowingContent,
|
|
@@ -15244,7 +15378,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15244
15378
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15245
15379
|
for (const [, embeddedObj] of embeddedObjects.entries()) {
|
|
15246
15380
|
if (embeddedObj.id === elementId) {
|
|
15247
|
-
|
|
15381
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection on embedded object:', elementId);
|
|
15248
15382
|
embeddedObj.selected = false;
|
|
15249
15383
|
}
|
|
15250
15384
|
}
|
|
@@ -15252,7 +15386,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15252
15386
|
});
|
|
15253
15387
|
this.selectedElements.clear();
|
|
15254
15388
|
this.selectedSectionId = null;
|
|
15255
|
-
|
|
15389
|
+
Logger.log('[pc-editor:CanvasManager] About to render after clearing selection...');
|
|
15256
15390
|
this.render();
|
|
15257
15391
|
this.updateResizeHandleHitTargets();
|
|
15258
15392
|
this.emit('selection-change', { selectedElements: [] });
|
|
@@ -15501,7 +15635,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15501
15635
|
});
|
|
15502
15636
|
// Handle substitution field clicks
|
|
15503
15637
|
this.flowingTextRenderer.on('substitution-field-clicked', (data) => {
|
|
15504
|
-
|
|
15638
|
+
Logger.log('[pc-editor:CanvasManager] substitution-field-clicked Field:', data.field?.fieldName, 'Section:', data.section);
|
|
15505
15639
|
// Emit event for external handling (e.g., showing field properties panel)
|
|
15506
15640
|
this.emit('substitution-field-clicked', data);
|
|
15507
15641
|
});
|
|
@@ -15837,7 +15971,9 @@ class CanvasManager extends EventEmitter {
|
|
|
15837
15971
|
if (obj instanceof TableObject) {
|
|
15838
15972
|
// For multi-page tables, check if this page has a rendered slice
|
|
15839
15973
|
const slice = obj.getRenderedSlice(pageIndex);
|
|
15840
|
-
|
|
15974
|
+
// Only use renderedPosition if the table was actually rendered on this page
|
|
15975
|
+
const tablePosition = slice?.position ||
|
|
15976
|
+
(obj.renderedPageIndex === pageIndex ? obj.renderedPosition : null);
|
|
15841
15977
|
if (tablePosition) {
|
|
15842
15978
|
// Check if point is inside the table slice on this page
|
|
15843
15979
|
const sliceHeight = slice?.height || obj.height;
|
|
@@ -15976,14 +16112,15 @@ class CanvasManager extends EventEmitter {
|
|
|
15976
16112
|
this.editingTextBox = textBox;
|
|
15977
16113
|
this._editingTextBoxPageId = pageId || null;
|
|
15978
16114
|
if (textBox) {
|
|
15979
|
-
// Use the unified focus system to handle focus/blur and cursor blink
|
|
15980
|
-
// This blurs the previous control, hiding its cursor
|
|
15981
|
-
this.setFocus(textBox);
|
|
15982
16115
|
// Clear selection in main flowing content
|
|
15983
16116
|
this.document.bodyFlowingContent.clearSelection();
|
|
15984
|
-
// Select the text box
|
|
16117
|
+
// Select the text box visually (this calls setFocus(null) internally,
|
|
16118
|
+
// so we must set focus to the text box AFTER this call)
|
|
15985
16119
|
this.clearSelection();
|
|
15986
16120
|
this.selectInlineElement({ type: 'embedded-object', object: textBox, textIndex: textBox.textIndex });
|
|
16121
|
+
// Now set focus to the text box for editing — must be AFTER selectInlineElement
|
|
16122
|
+
// because selectBaseEmbeddedObject calls setFocus(null) which would undo it
|
|
16123
|
+
this.setFocus(textBox);
|
|
15987
16124
|
this.emit('textbox-editing-started', { textBox });
|
|
15988
16125
|
}
|
|
15989
16126
|
else {
|
|
@@ -16080,6 +16217,10 @@ class CanvasManager extends EventEmitter {
|
|
|
16080
16217
|
}
|
|
16081
16218
|
CanvasManager.CELL_SELECTION_THRESHOLD = 5; // Minimum pixels to drag before cell selection starts
|
|
16082
16219
|
CanvasManager.RELATIVE_DRAG_THRESHOLD = 3; // Minimum pixels to drag before moving starts
|
|
16220
|
+
// Custom text cursor as a black I-beam SVG data URI.
|
|
16221
|
+
// The native 'text' cursor can render as white on Windows browsers,
|
|
16222
|
+
// making it invisible over the white canvas background.
|
|
16223
|
+
CanvasManager.TEXT_CURSOR = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='20' viewBox='0 0 16 20'%3E%3Cpath d='M5 1h2v1h2V1h2v2h-2v6h2v2h-2v6h2v2h-2v-1H7v1H5v-2h2v-6H5V9h2V3H5z' fill='%23000'/%3E%3C/svg%3E\") 8 10, text";
|
|
16083
16224
|
|
|
16084
16225
|
/**
|
|
16085
16226
|
* DataBinder handles binding data to documents.
|
|
@@ -16722,7 +16863,15 @@ class PDFGenerator {
|
|
|
16722
16863
|
// Check if it's a data URL we can embed
|
|
16723
16864
|
if (src.startsWith('data:')) {
|
|
16724
16865
|
try {
|
|
16725
|
-
|
|
16866
|
+
let embeddedImage = await this.embedImageFromDataUrl(pdfDoc, src);
|
|
16867
|
+
// If the format isn't directly supported (e.g., SVG, WebP, GIF),
|
|
16868
|
+
// convert to PNG via canvas and try again
|
|
16869
|
+
if (!embeddedImage) {
|
|
16870
|
+
const pngDataUrl = image.toPngDataUrl();
|
|
16871
|
+
if (pngDataUrl) {
|
|
16872
|
+
embeddedImage = await this.embedImageFromDataUrl(pdfDoc, pngDataUrl);
|
|
16873
|
+
}
|
|
16874
|
+
}
|
|
16726
16875
|
if (embeddedImage) {
|
|
16727
16876
|
// Calculate draw position/size based on fit mode
|
|
16728
16877
|
const drawParams = this.calculateImageDrawParams(embeddedImage.width, embeddedImage.height, image.width, image.height, image.fit);
|
|
@@ -16745,7 +16894,7 @@ class PDFGenerator {
|
|
|
16745
16894
|
}
|
|
16746
16895
|
}
|
|
16747
16896
|
catch (e) {
|
|
16748
|
-
|
|
16897
|
+
Logger.warn('[pc-editor:PDFGenerator] Failed to embed image:', e);
|
|
16749
16898
|
}
|
|
16750
16899
|
}
|
|
16751
16900
|
// Fallback: draw placeholder rectangle for images we can't embed
|
|
@@ -18773,7 +18922,7 @@ class MutationUndo {
|
|
|
18773
18922
|
this.undoTableStructure(mutation);
|
|
18774
18923
|
break;
|
|
18775
18924
|
default:
|
|
18776
|
-
|
|
18925
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for undo:', mutation.type);
|
|
18777
18926
|
}
|
|
18778
18927
|
}
|
|
18779
18928
|
/**
|
|
@@ -18823,7 +18972,7 @@ class MutationUndo {
|
|
|
18823
18972
|
this.redoTableStructure(mutation);
|
|
18824
18973
|
break;
|
|
18825
18974
|
default:
|
|
18826
|
-
|
|
18975
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for redo:', mutation.type);
|
|
18827
18976
|
}
|
|
18828
18977
|
}
|
|
18829
18978
|
// --- Text Mutations ---
|
|
@@ -20155,13 +20304,13 @@ class PDFParser {
|
|
|
20155
20304
|
}
|
|
20156
20305
|
catch {
|
|
20157
20306
|
// Skip images that fail to extract
|
|
20158
|
-
|
|
20307
|
+
Logger.warn(`[pc-editor:PDFParser] Failed to extract image: ${imageName}`);
|
|
20159
20308
|
}
|
|
20160
20309
|
}
|
|
20161
20310
|
}
|
|
20162
20311
|
}
|
|
20163
20312
|
catch (error) {
|
|
20164
|
-
|
|
20313
|
+
Logger.warn('[pc-editor:PDFParser] Image extraction failed:', error);
|
|
20165
20314
|
}
|
|
20166
20315
|
return images;
|
|
20167
20316
|
}
|
|
@@ -21188,6 +21337,8 @@ class PCEditor extends EventEmitter {
|
|
|
21188
21337
|
}
|
|
21189
21338
|
this.container = container;
|
|
21190
21339
|
this.options = this.mergeOptions(options);
|
|
21340
|
+
// Initialize logging
|
|
21341
|
+
Logger.setEnabled(this.options.enableLogging ?? false);
|
|
21191
21342
|
this.document = new Document();
|
|
21192
21343
|
// Apply constructor options to document settings
|
|
21193
21344
|
this.document.updateSettings({
|
|
@@ -21212,7 +21363,8 @@ class PCEditor extends EventEmitter {
|
|
|
21212
21363
|
showControlCharacters: options?.showControlCharacters ?? false,
|
|
21213
21364
|
defaultFont: options?.defaultFont || 'Arial',
|
|
21214
21365
|
defaultFontSize: options?.defaultFontSize || 12,
|
|
21215
|
-
theme: options?.theme || 'light'
|
|
21366
|
+
theme: options?.theme || 'light',
|
|
21367
|
+
enableLogging: options?.enableLogging ?? false
|
|
21216
21368
|
};
|
|
21217
21369
|
}
|
|
21218
21370
|
initialize() {
|
|
@@ -21357,6 +21509,10 @@ class PCEditor extends EventEmitter {
|
|
|
21357
21509
|
this.canvasManager.on('tablecell-cursor-changed', (data) => {
|
|
21358
21510
|
this.emit('tablecell-cursor-changed', data);
|
|
21359
21511
|
});
|
|
21512
|
+
// Forward table cell selection changes (multi-cell drag/shift-click)
|
|
21513
|
+
this.canvasManager.on('table-cell-selection-changed', (data) => {
|
|
21514
|
+
this.emit('table-cell-selection-changed', data);
|
|
21515
|
+
});
|
|
21360
21516
|
this.canvasManager.on('repeating-section-clicked', (data) => {
|
|
21361
21517
|
// Repeating section clicked - update selection state
|
|
21362
21518
|
if (data.section && data.section.id) {
|
|
@@ -21619,6 +21775,7 @@ class PCEditor extends EventEmitter {
|
|
|
21619
21775
|
* This changes which section receives keyboard input and cursor positioning.
|
|
21620
21776
|
*/
|
|
21621
21777
|
setActiveSection(section) {
|
|
21778
|
+
Logger.log('[pc-editor] setActiveSection', section);
|
|
21622
21779
|
if (this._activeEditingSection !== section) {
|
|
21623
21780
|
this._activeEditingSection = section;
|
|
21624
21781
|
// Delegate to canvas manager which handles the section change and emits events
|
|
@@ -21709,6 +21866,7 @@ class PCEditor extends EventEmitter {
|
|
|
21709
21866
|
}
|
|
21710
21867
|
}
|
|
21711
21868
|
loadDocument(documentData) {
|
|
21869
|
+
Logger.log('[pc-editor] loadDocument');
|
|
21712
21870
|
if (!this._isReady) {
|
|
21713
21871
|
throw new Error('Editor is not ready');
|
|
21714
21872
|
}
|
|
@@ -21735,6 +21893,7 @@ class PCEditor extends EventEmitter {
|
|
|
21735
21893
|
return this.document.toData();
|
|
21736
21894
|
}
|
|
21737
21895
|
bindData(data) {
|
|
21896
|
+
Logger.log('[pc-editor] bindData');
|
|
21738
21897
|
if (!this._isReady) {
|
|
21739
21898
|
throw new Error('Editor is not ready');
|
|
21740
21899
|
}
|
|
@@ -21743,6 +21902,7 @@ class PCEditor extends EventEmitter {
|
|
|
21743
21902
|
this.emit('data-bound', { data });
|
|
21744
21903
|
}
|
|
21745
21904
|
async exportPDF(options) {
|
|
21905
|
+
Logger.log('[pc-editor] exportPDF');
|
|
21746
21906
|
if (!this._isReady) {
|
|
21747
21907
|
throw new Error('Editor is not ready');
|
|
21748
21908
|
}
|
|
@@ -21784,6 +21944,7 @@ class PCEditor extends EventEmitter {
|
|
|
21784
21944
|
* @returns JSON string representation of the document
|
|
21785
21945
|
*/
|
|
21786
21946
|
saveDocument() {
|
|
21947
|
+
Logger.log('[pc-editor] saveDocument');
|
|
21787
21948
|
if (!this._isReady) {
|
|
21788
21949
|
throw new Error('Editor is not ready');
|
|
21789
21950
|
}
|
|
@@ -21795,6 +21956,7 @@ class PCEditor extends EventEmitter {
|
|
|
21795
21956
|
* @param filename Optional filename (defaults to 'document.pceditor.json')
|
|
21796
21957
|
*/
|
|
21797
21958
|
saveDocumentToFile(filename = 'document.pceditor.json') {
|
|
21959
|
+
Logger.log('[pc-editor] saveDocumentToFile', filename);
|
|
21798
21960
|
const jsonString = this.saveDocument();
|
|
21799
21961
|
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
21800
21962
|
const url = URL.createObjectURL(blob);
|
|
@@ -21812,6 +21974,7 @@ class PCEditor extends EventEmitter {
|
|
|
21812
21974
|
* @param jsonString JSON string representation of the document
|
|
21813
21975
|
*/
|
|
21814
21976
|
loadDocumentFromJSON(jsonString) {
|
|
21977
|
+
Logger.log('[pc-editor] loadDocumentFromJSON');
|
|
21815
21978
|
if (!this._isReady) {
|
|
21816
21979
|
throw new Error('Editor is not ready');
|
|
21817
21980
|
}
|
|
@@ -21832,6 +21995,7 @@ class PCEditor extends EventEmitter {
|
|
|
21832
21995
|
* @returns Promise that resolves when loading is complete
|
|
21833
21996
|
*/
|
|
21834
21997
|
async loadDocumentFromFile(file) {
|
|
21998
|
+
Logger.log('[pc-editor] loadDocumentFromFile', file.name);
|
|
21835
21999
|
if (!this._isReady) {
|
|
21836
22000
|
throw new Error('Editor is not ready');
|
|
21837
22001
|
}
|
|
@@ -21920,22 +22084,25 @@ class PCEditor extends EventEmitter {
|
|
|
21920
22084
|
// Version compatibility check
|
|
21921
22085
|
const [major] = doc.version.split('.').map(Number);
|
|
21922
22086
|
if (major > 1) {
|
|
21923
|
-
|
|
22087
|
+
Logger.warn(`[pc-editor] Document version ${doc.version} may not be fully compatible with this editor`);
|
|
21924
22088
|
}
|
|
21925
22089
|
}
|
|
21926
22090
|
selectElement(elementId) {
|
|
22091
|
+
Logger.log('[pc-editor] selectElement', elementId);
|
|
21927
22092
|
if (!this._isReady) {
|
|
21928
22093
|
throw new Error('Editor is not ready');
|
|
21929
22094
|
}
|
|
21930
22095
|
this.canvasManager.selectElement(elementId);
|
|
21931
22096
|
}
|
|
21932
22097
|
clearSelection() {
|
|
22098
|
+
Logger.log('[pc-editor] clearSelection');
|
|
21933
22099
|
if (!this._isReady) {
|
|
21934
22100
|
throw new Error('Editor is not ready');
|
|
21935
22101
|
}
|
|
21936
22102
|
this.canvasManager.clearSelection();
|
|
21937
22103
|
}
|
|
21938
22104
|
removeEmbeddedObject(objectId) {
|
|
22105
|
+
Logger.log('[pc-editor] removeEmbeddedObject', objectId);
|
|
21939
22106
|
if (!this._isReady) {
|
|
21940
22107
|
throw new Error('Editor is not ready');
|
|
21941
22108
|
}
|
|
@@ -21945,6 +22112,7 @@ class PCEditor extends EventEmitter {
|
|
|
21945
22112
|
* Undo the last operation.
|
|
21946
22113
|
*/
|
|
21947
22114
|
undo() {
|
|
22115
|
+
Logger.log('[pc-editor] undo');
|
|
21948
22116
|
if (!this._isReady)
|
|
21949
22117
|
return;
|
|
21950
22118
|
const success = this.transactionManager.undo();
|
|
@@ -21957,6 +22125,7 @@ class PCEditor extends EventEmitter {
|
|
|
21957
22125
|
* Redo the last undone operation.
|
|
21958
22126
|
*/
|
|
21959
22127
|
redo() {
|
|
22128
|
+
Logger.log('[pc-editor] redo');
|
|
21960
22129
|
if (!this._isReady)
|
|
21961
22130
|
return;
|
|
21962
22131
|
const success = this.transactionManager.redo();
|
|
@@ -21990,16 +22159,19 @@ class PCEditor extends EventEmitter {
|
|
|
21990
22159
|
this.transactionManager.setMaxHistory(count);
|
|
21991
22160
|
}
|
|
21992
22161
|
zoomIn() {
|
|
22162
|
+
Logger.log('[pc-editor] zoomIn');
|
|
21993
22163
|
if (!this._isReady)
|
|
21994
22164
|
return;
|
|
21995
22165
|
this.canvasManager.zoomIn();
|
|
21996
22166
|
}
|
|
21997
22167
|
zoomOut() {
|
|
22168
|
+
Logger.log('[pc-editor] zoomOut');
|
|
21998
22169
|
if (!this._isReady)
|
|
21999
22170
|
return;
|
|
22000
22171
|
this.canvasManager.zoomOut();
|
|
22001
22172
|
}
|
|
22002
22173
|
setZoom(level) {
|
|
22174
|
+
Logger.log('[pc-editor] setZoom', level);
|
|
22003
22175
|
if (!this._isReady)
|
|
22004
22176
|
return;
|
|
22005
22177
|
this.canvasManager.setZoom(level);
|
|
@@ -22036,6 +22208,7 @@ class PCEditor extends EventEmitter {
|
|
|
22036
22208
|
return this.canvasManager.getContentOffset();
|
|
22037
22209
|
}
|
|
22038
22210
|
fitToWidth() {
|
|
22211
|
+
Logger.log('[pc-editor] fitToWidth');
|
|
22039
22212
|
if (!this._isReady)
|
|
22040
22213
|
return;
|
|
22041
22214
|
this.canvasManager.fitToWidth();
|
|
@@ -22044,23 +22217,27 @@ class PCEditor extends EventEmitter {
|
|
|
22044
22217
|
* Force a re-render of the canvas.
|
|
22045
22218
|
*/
|
|
22046
22219
|
render() {
|
|
22220
|
+
Logger.log('[pc-editor] render');
|
|
22047
22221
|
if (!this._isReady)
|
|
22048
22222
|
return;
|
|
22049
22223
|
this.canvasManager.render();
|
|
22050
22224
|
}
|
|
22051
22225
|
fitToPage() {
|
|
22226
|
+
Logger.log('[pc-editor] fitToPage');
|
|
22052
22227
|
if (!this._isReady)
|
|
22053
22228
|
return;
|
|
22054
22229
|
this.canvasManager.fitToPage();
|
|
22055
22230
|
}
|
|
22056
22231
|
// Layout control methods
|
|
22057
22232
|
setAutoFlow(enabled) {
|
|
22233
|
+
Logger.log('[pc-editor] setAutoFlow', enabled);
|
|
22058
22234
|
if (!this._isReady) {
|
|
22059
22235
|
throw new Error('Editor is not ready');
|
|
22060
22236
|
}
|
|
22061
22237
|
this.layoutEngine.setAutoFlow(enabled);
|
|
22062
22238
|
}
|
|
22063
22239
|
reflowDocument() {
|
|
22240
|
+
Logger.log('[pc-editor] reflowDocument');
|
|
22064
22241
|
if (!this._isReady) {
|
|
22065
22242
|
throw new Error('Editor is not ready');
|
|
22066
22243
|
}
|
|
@@ -22102,6 +22279,7 @@ class PCEditor extends EventEmitter {
|
|
|
22102
22279
|
};
|
|
22103
22280
|
}
|
|
22104
22281
|
addPage() {
|
|
22282
|
+
Logger.log('[pc-editor] addPage');
|
|
22105
22283
|
if (!this._isReady) {
|
|
22106
22284
|
throw new Error('Editor is not ready');
|
|
22107
22285
|
}
|
|
@@ -22113,6 +22291,7 @@ class PCEditor extends EventEmitter {
|
|
|
22113
22291
|
this.canvasManager.setDocument(this.document);
|
|
22114
22292
|
}
|
|
22115
22293
|
removePage(pageId) {
|
|
22294
|
+
Logger.log('[pc-editor] removePage', pageId);
|
|
22116
22295
|
if (!this._isReady) {
|
|
22117
22296
|
throw new Error('Editor is not ready');
|
|
22118
22297
|
}
|
|
@@ -22187,7 +22366,7 @@ class PCEditor extends EventEmitter {
|
|
|
22187
22366
|
}
|
|
22188
22367
|
// Use the unified focus system to get the currently focused control
|
|
22189
22368
|
const focusedControl = this.canvasManager.getFocusedControl();
|
|
22190
|
-
|
|
22369
|
+
Logger.log('[pc-editor:handleKeyDown] Key:', e.key, 'focusedControl:', focusedControl?.constructor?.name);
|
|
22191
22370
|
if (!focusedControl)
|
|
22192
22371
|
return;
|
|
22193
22372
|
// Vertical navigation needs layout context - handle specially
|
|
@@ -22211,9 +22390,9 @@ class PCEditor extends EventEmitter {
|
|
|
22211
22390
|
}
|
|
22212
22391
|
}
|
|
22213
22392
|
// Delegate to the focused control's handleKeyDown
|
|
22214
|
-
|
|
22393
|
+
Logger.log('[pc-editor:handleKeyDown] Calling focusedControl.handleKeyDown');
|
|
22215
22394
|
const handled = focusedControl.handleKeyDown(e);
|
|
22216
|
-
|
|
22395
|
+
Logger.log('[pc-editor:handleKeyDown] handled:', handled);
|
|
22217
22396
|
if (handled) {
|
|
22218
22397
|
this.canvasManager.render();
|
|
22219
22398
|
// Handle text box-specific post-processing
|
|
@@ -22577,6 +22756,7 @@ class PCEditor extends EventEmitter {
|
|
|
22577
22756
|
* This is useful when UI controls have stolen focus.
|
|
22578
22757
|
*/
|
|
22579
22758
|
applyFormattingWithFallback(start, end, formatting) {
|
|
22759
|
+
Logger.log('[pc-editor] applyFormattingWithFallback', start, end, formatting);
|
|
22580
22760
|
// Try current context first
|
|
22581
22761
|
let flowingContent = this.getEditingFlowingContent();
|
|
22582
22762
|
// Fall back to saved context
|
|
@@ -22777,6 +22957,7 @@ class PCEditor extends EventEmitter {
|
|
|
22777
22957
|
* Works for body text, text boxes, and table cells.
|
|
22778
22958
|
*/
|
|
22779
22959
|
setUnifiedAlignment(alignment) {
|
|
22960
|
+
Logger.log('[pc-editor] setUnifiedAlignment', alignment);
|
|
22780
22961
|
const flowingContent = this.getEditingFlowingContent();
|
|
22781
22962
|
if (!flowingContent) {
|
|
22782
22963
|
throw new Error('No text is being edited');
|
|
@@ -22793,6 +22974,7 @@ class PCEditor extends EventEmitter {
|
|
|
22793
22974
|
this.canvasManager.render();
|
|
22794
22975
|
}
|
|
22795
22976
|
insertText(text) {
|
|
22977
|
+
Logger.log('[pc-editor] insertText', text);
|
|
22796
22978
|
if (!this._isReady) {
|
|
22797
22979
|
throw new Error('Editor is not ready');
|
|
22798
22980
|
}
|
|
@@ -22809,6 +22991,7 @@ class PCEditor extends EventEmitter {
|
|
|
22809
22991
|
return flowingContent ? flowingContent.getText() : '';
|
|
22810
22992
|
}
|
|
22811
22993
|
setFlowingText(text) {
|
|
22994
|
+
Logger.log('[pc-editor] setFlowingText');
|
|
22812
22995
|
if (!this._isReady) {
|
|
22813
22996
|
throw new Error('Editor is not ready');
|
|
22814
22997
|
}
|
|
@@ -22822,6 +23005,7 @@ class PCEditor extends EventEmitter {
|
|
|
22822
23005
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22823
23006
|
*/
|
|
22824
23007
|
setCursorPosition(position) {
|
|
23008
|
+
Logger.log('[pc-editor] setCursorPosition', position);
|
|
22825
23009
|
if (!this._isReady) {
|
|
22826
23010
|
throw new Error('Editor is not ready');
|
|
22827
23011
|
}
|
|
@@ -22867,6 +23051,7 @@ class PCEditor extends EventEmitter {
|
|
|
22867
23051
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22868
23052
|
*/
|
|
22869
23053
|
insertEmbeddedObject(object, position = 'inline') {
|
|
23054
|
+
Logger.log('[pc-editor] insertEmbeddedObject', object.id, position);
|
|
22870
23055
|
if (!this._isReady) {
|
|
22871
23056
|
throw new Error('Editor is not ready');
|
|
22872
23057
|
}
|
|
@@ -22893,6 +23078,7 @@ class PCEditor extends EventEmitter {
|
|
|
22893
23078
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22894
23079
|
*/
|
|
22895
23080
|
insertSubstitutionField(fieldName, config) {
|
|
23081
|
+
Logger.log('[pc-editor] insertSubstitutionField', fieldName);
|
|
22896
23082
|
if (!this._isReady) {
|
|
22897
23083
|
throw new Error('Editor is not ready');
|
|
22898
23084
|
}
|
|
@@ -22918,6 +23104,7 @@ class PCEditor extends EventEmitter {
|
|
|
22918
23104
|
* @param displayFormat Optional format string (e.g., "Page %d" where %d is replaced by page number)
|
|
22919
23105
|
*/
|
|
22920
23106
|
insertPageNumberField(displayFormat) {
|
|
23107
|
+
Logger.log('[pc-editor] insertPageNumberField');
|
|
22921
23108
|
if (!this._isReady) {
|
|
22922
23109
|
throw new Error('Editor is not ready');
|
|
22923
23110
|
}
|
|
@@ -22943,6 +23130,7 @@ class PCEditor extends EventEmitter {
|
|
|
22943
23130
|
* @param displayFormat Optional format string (e.g., "of %d" where %d is replaced by page count)
|
|
22944
23131
|
*/
|
|
22945
23132
|
insertPageCountField(displayFormat) {
|
|
23133
|
+
Logger.log('[pc-editor] insertPageCountField');
|
|
22946
23134
|
if (!this._isReady) {
|
|
22947
23135
|
throw new Error('Editor is not ready');
|
|
22948
23136
|
}
|
|
@@ -22968,6 +23156,7 @@ class PCEditor extends EventEmitter {
|
|
|
22968
23156
|
* or table cells are not recommended as these don't span pages.
|
|
22969
23157
|
*/
|
|
22970
23158
|
insertPageBreak() {
|
|
23159
|
+
Logger.log('[pc-editor] insertPageBreak');
|
|
22971
23160
|
if (!this._isReady) {
|
|
22972
23161
|
throw new Error('Editor is not ready');
|
|
22973
23162
|
}
|
|
@@ -23116,7 +23305,7 @@ class PCEditor extends EventEmitter {
|
|
|
23116
23305
|
// Find the text box in all flowing contents
|
|
23117
23306
|
const textBox = this.findTextBoxById(textBoxId);
|
|
23118
23307
|
if (!textBox) {
|
|
23119
|
-
|
|
23308
|
+
Logger.warn(`[pc-editor:updateTextBox] Text box not found: ${textBoxId}`);
|
|
23120
23309
|
return false;
|
|
23121
23310
|
}
|
|
23122
23311
|
// Apply updates
|
|
@@ -23179,7 +23368,7 @@ class PCEditor extends EventEmitter {
|
|
|
23179
23368
|
// Find the image in all flowing contents
|
|
23180
23369
|
const image = this.findImageById(imageId);
|
|
23181
23370
|
if (!image) {
|
|
23182
|
-
|
|
23371
|
+
Logger.warn(`[pc-editor:updateImage] Image not found: ${imageId}`);
|
|
23183
23372
|
return false;
|
|
23184
23373
|
}
|
|
23185
23374
|
// Apply updates
|
|
@@ -23213,7 +23402,7 @@ class PCEditor extends EventEmitter {
|
|
|
23213
23402
|
return false;
|
|
23214
23403
|
const image = this.findImageById(imageId);
|
|
23215
23404
|
if (!image) {
|
|
23216
|
-
|
|
23405
|
+
Logger.warn(`[pc-editor:setImageSource] Image not found: ${imageId}`);
|
|
23217
23406
|
return false;
|
|
23218
23407
|
}
|
|
23219
23408
|
image.setSource(dataUrl, options);
|
|
@@ -23293,6 +23482,39 @@ class PCEditor extends EventEmitter {
|
|
|
23293
23482
|
table.removeColumn(colIndex);
|
|
23294
23483
|
this.canvasManager.render();
|
|
23295
23484
|
}
|
|
23485
|
+
/**
|
|
23486
|
+
* Merge selected cells in a table.
|
|
23487
|
+
* Uses the table's current cell selection range.
|
|
23488
|
+
* @param table The table containing the cells to merge
|
|
23489
|
+
* @returns true if cells were merged successfully
|
|
23490
|
+
*/
|
|
23491
|
+
tableMergeCells(table) {
|
|
23492
|
+
Logger.log('[pc-editor] tableMergeCells');
|
|
23493
|
+
if (!this._isReady)
|
|
23494
|
+
return false;
|
|
23495
|
+
const result = table.mergeCells();
|
|
23496
|
+
if (result.success) {
|
|
23497
|
+
this.canvasManager.render();
|
|
23498
|
+
}
|
|
23499
|
+
return result.success;
|
|
23500
|
+
}
|
|
23501
|
+
/**
|
|
23502
|
+
* Split a merged cell back into individual cells.
|
|
23503
|
+
* @param table The table containing the merged cell
|
|
23504
|
+
* @param row Row index of the merged cell
|
|
23505
|
+
* @param col Column index of the merged cell
|
|
23506
|
+
* @returns true if the cell was split successfully
|
|
23507
|
+
*/
|
|
23508
|
+
tableSplitCell(table, row, col) {
|
|
23509
|
+
Logger.log('[pc-editor] tableSplitCell', row, col);
|
|
23510
|
+
if (!this._isReady)
|
|
23511
|
+
return false;
|
|
23512
|
+
const result = table.splitCell(row, col);
|
|
23513
|
+
if (result.success) {
|
|
23514
|
+
this.canvasManager.render();
|
|
23515
|
+
}
|
|
23516
|
+
return result.success;
|
|
23517
|
+
}
|
|
23296
23518
|
/**
|
|
23297
23519
|
* Begin a compound operation. Groups multiple mutations into a single undo entry.
|
|
23298
23520
|
* Call endCompoundOperation after making changes.
|
|
@@ -23340,7 +23562,7 @@ class PCEditor extends EventEmitter {
|
|
|
23340
23562
|
* @deprecated Use insertEmbeddedObject instead
|
|
23341
23563
|
*/
|
|
23342
23564
|
insertInlineElement(_elementData, _position = 'inline') {
|
|
23343
|
-
|
|
23565
|
+
Logger.warn('[pc-editor] insertInlineElement is deprecated and no longer functional. Use insertEmbeddedObject instead.');
|
|
23344
23566
|
}
|
|
23345
23567
|
/**
|
|
23346
23568
|
* Apply merge data to substitute all substitution fields with their values.
|
|
@@ -24065,18 +24287,37 @@ class PCEditor extends EventEmitter {
|
|
|
24065
24287
|
return this.document.bodyFlowingContent.getParagraphBoundaries();
|
|
24066
24288
|
}
|
|
24067
24289
|
/**
|
|
24068
|
-
* Create a repeating section
|
|
24069
|
-
*
|
|
24070
|
-
*
|
|
24071
|
-
*
|
|
24290
|
+
* Create a repeating section.
|
|
24291
|
+
*
|
|
24292
|
+
* If a table is currently being edited (focused), creates a table row loop
|
|
24293
|
+
* based on the focused cell's row. In this case, startIndex and endIndex
|
|
24294
|
+
* are ignored — the focused cell's row determines the loop range.
|
|
24295
|
+
*
|
|
24296
|
+
* Otherwise, creates a body text repeating section at the given paragraph boundaries.
|
|
24297
|
+
*
|
|
24298
|
+
* @param startIndex Text index at paragraph start (ignored for table row loops)
|
|
24299
|
+
* @param endIndex Text index at closing paragraph start (ignored for table row loops)
|
|
24072
24300
|
* @param fieldPath The field path to the array to loop over (e.g., "items")
|
|
24073
|
-
* @returns The created section, or null if
|
|
24301
|
+
* @returns The created section/loop, or null if creation failed
|
|
24074
24302
|
*/
|
|
24075
24303
|
createRepeatingSection(startIndex, endIndex, fieldPath) {
|
|
24076
24304
|
if (!this._isReady) {
|
|
24077
24305
|
throw new Error('Editor is not ready');
|
|
24078
24306
|
}
|
|
24307
|
+
// If a table is focused, create a row loop instead of a text repeating section
|
|
24308
|
+
const focusedTable = this.getFocusedTable();
|
|
24309
|
+
if (focusedTable && focusedTable.focusedCell) {
|
|
24310
|
+
Logger.log('[pc-editor] createRepeatingSection → table row loop', fieldPath);
|
|
24311
|
+
const row = focusedTable.focusedCell.row;
|
|
24312
|
+
const loop = focusedTable.createRowLoop(row, row, fieldPath);
|
|
24313
|
+
if (loop) {
|
|
24314
|
+
this.canvasManager.render();
|
|
24315
|
+
this.emit('table-row-loop-added', { table: focusedTable, loop });
|
|
24316
|
+
}
|
|
24317
|
+
return null; // Row loops are not RepeatingSections, return null
|
|
24318
|
+
}
|
|
24079
24319
|
// Repeating sections only work in body (document-level)
|
|
24320
|
+
Logger.log('[pc-editor] createRepeatingSection', startIndex, endIndex, fieldPath);
|
|
24080
24321
|
const section = this.document.bodyFlowingContent.createRepeatingSection(startIndex, endIndex, fieldPath);
|
|
24081
24322
|
if (section) {
|
|
24082
24323
|
this.canvasManager.render();
|
|
@@ -24454,7 +24695,7 @@ class PCEditor extends EventEmitter {
|
|
|
24454
24695
|
createEmbeddedObjectFromData(data) {
|
|
24455
24696
|
const object = EmbeddedObjectFactory.tryCreate(data);
|
|
24456
24697
|
if (!object) {
|
|
24457
|
-
|
|
24698
|
+
Logger.warn('[pc-editor] Unknown object type:', data.objectType);
|
|
24458
24699
|
}
|
|
24459
24700
|
return object;
|
|
24460
24701
|
}
|
|
@@ -24483,6 +24724,13 @@ class PCEditor extends EventEmitter {
|
|
|
24483
24724
|
return false;
|
|
24484
24725
|
}
|
|
24485
24726
|
}
|
|
24727
|
+
/**
|
|
24728
|
+
* Enable or disable verbose logging.
|
|
24729
|
+
* When disabled (default), only errors are logged to the console.
|
|
24730
|
+
*/
|
|
24731
|
+
setLogging(enabled) {
|
|
24732
|
+
Logger.setEnabled(enabled);
|
|
24733
|
+
}
|
|
24486
24734
|
destroy() {
|
|
24487
24735
|
this.disableTextInput();
|
|
24488
24736
|
if (this.canvasManager) {
|
|
@@ -25625,33 +25873,38 @@ class DocumentInfoPane extends BasePane {
|
|
|
25625
25873
|
}
|
|
25626
25874
|
createContent() {
|
|
25627
25875
|
const container = document.createElement('div');
|
|
25628
|
-
container.className = 'pc-pane-
|
|
25876
|
+
container.className = 'pc-pane-label-value-grid';
|
|
25629
25877
|
// Page count
|
|
25630
|
-
|
|
25631
|
-
this.pageCountEl =
|
|
25632
|
-
container.appendChild(
|
|
25878
|
+
container.appendChild(this.createLabel('Pages:'));
|
|
25879
|
+
this.pageCountEl = this.createValue('0');
|
|
25880
|
+
container.appendChild(this.pageCountEl);
|
|
25881
|
+
container.appendChild(this.createSpacer());
|
|
25633
25882
|
// Page size
|
|
25634
|
-
|
|
25635
|
-
this.pageSizeEl =
|
|
25636
|
-
container.appendChild(
|
|
25883
|
+
container.appendChild(this.createLabel('Size:'));
|
|
25884
|
+
this.pageSizeEl = this.createValue('-');
|
|
25885
|
+
container.appendChild(this.pageSizeEl);
|
|
25886
|
+
container.appendChild(this.createSpacer());
|
|
25637
25887
|
// Page orientation
|
|
25638
|
-
|
|
25639
|
-
this.pageOrientationEl =
|
|
25640
|
-
container.appendChild(
|
|
25888
|
+
container.appendChild(this.createLabel('Orientation:'));
|
|
25889
|
+
this.pageOrientationEl = this.createValue('-');
|
|
25890
|
+
container.appendChild(this.pageOrientationEl);
|
|
25891
|
+
container.appendChild(this.createSpacer());
|
|
25641
25892
|
return container;
|
|
25642
25893
|
}
|
|
25643
|
-
|
|
25644
|
-
const
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25654
|
-
|
|
25894
|
+
createLabel(text) {
|
|
25895
|
+
const label = document.createElement('span');
|
|
25896
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
25897
|
+
label.textContent = text;
|
|
25898
|
+
return label;
|
|
25899
|
+
}
|
|
25900
|
+
createValue(text) {
|
|
25901
|
+
const value = document.createElement('span');
|
|
25902
|
+
value.className = 'pc-pane-info-value';
|
|
25903
|
+
value.textContent = text;
|
|
25904
|
+
return value;
|
|
25905
|
+
}
|
|
25906
|
+
createSpacer() {
|
|
25907
|
+
return document.createElement('div');
|
|
25655
25908
|
}
|
|
25656
25909
|
/**
|
|
25657
25910
|
* Update the displayed information from the editor.
|
|
@@ -25831,24 +26084,48 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25831
26084
|
const container = document.createElement('div');
|
|
25832
26085
|
// Margins section
|
|
25833
26086
|
const marginsSection = this.createSection('Margins (mm)');
|
|
26087
|
+
// Five-column grid: label, edit, label, edit, stretch
|
|
25834
26088
|
const marginsGrid = document.createElement('div');
|
|
25835
|
-
marginsGrid.className = 'pc-pane-margins-grid';
|
|
26089
|
+
marginsGrid.className = 'pc-pane-margins-grid-5col';
|
|
25836
26090
|
this.marginTopInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25837
26091
|
this.marginRightInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25838
26092
|
this.marginBottomInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25839
26093
|
this.marginLeftInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
26094
|
+
// Apply margins on blur
|
|
26095
|
+
const applyMargins = () => this.applyMargins();
|
|
26096
|
+
this.marginTopInput.addEventListener('blur', applyMargins);
|
|
26097
|
+
this.marginRightInput.addEventListener('blur', applyMargins);
|
|
26098
|
+
this.marginBottomInput.addEventListener('blur', applyMargins);
|
|
26099
|
+
this.marginLeftInput.addEventListener('blur', applyMargins);
|
|
26100
|
+
this.eventCleanup.push(() => {
|
|
26101
|
+
this.marginTopInput?.removeEventListener('blur', applyMargins);
|
|
26102
|
+
this.marginRightInput?.removeEventListener('blur', applyMargins);
|
|
26103
|
+
this.marginBottomInput?.removeEventListener('blur', applyMargins);
|
|
26104
|
+
this.marginLeftInput?.removeEventListener('blur', applyMargins);
|
|
26105
|
+
});
|
|
26106
|
+
// Row 1: Top / Right
|
|
26107
|
+
const topLabel = this.createMarginLabel('Top:');
|
|
26108
|
+
const rightLabel = this.createMarginLabel('Right:');
|
|
26109
|
+
marginsGrid.appendChild(topLabel);
|
|
26110
|
+
marginsGrid.appendChild(this.marginTopInput);
|
|
26111
|
+
marginsGrid.appendChild(rightLabel);
|
|
26112
|
+
marginsGrid.appendChild(this.marginRightInput);
|
|
26113
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
26114
|
+
// Row 2: Bottom / Left
|
|
26115
|
+
const bottomLabel = this.createMarginLabel('Bottom:');
|
|
26116
|
+
const leftLabel = this.createMarginLabel('Left:');
|
|
26117
|
+
marginsGrid.appendChild(bottomLabel);
|
|
26118
|
+
marginsGrid.appendChild(this.marginBottomInput);
|
|
26119
|
+
marginsGrid.appendChild(leftLabel);
|
|
26120
|
+
marginsGrid.appendChild(this.marginLeftInput);
|
|
26121
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
25844
26122
|
marginsSection.appendChild(marginsGrid);
|
|
25845
|
-
// Apply margins button
|
|
25846
|
-
const applyMarginsBtn = this.createButton('Apply Margins');
|
|
25847
|
-
this.addButtonListener(applyMarginsBtn, () => this.applyMargins());
|
|
25848
|
-
marginsSection.appendChild(applyMarginsBtn);
|
|
25849
26123
|
container.appendChild(marginsSection);
|
|
25850
|
-
// Page
|
|
25851
|
-
const
|
|
26124
|
+
// Page settings section using label-value grid: label, value, stretch
|
|
26125
|
+
const pageSection = this.createSection();
|
|
26126
|
+
const pageGrid = document.createElement('div');
|
|
26127
|
+
pageGrid.className = 'pc-pane-label-value-grid';
|
|
26128
|
+
// Page Size
|
|
25852
26129
|
this.pageSizeSelect = this.createSelect([
|
|
25853
26130
|
{ value: 'A4', label: 'A4' },
|
|
25854
26131
|
{ value: 'Letter', label: 'Letter' },
|
|
@@ -25856,19 +26133,32 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25856
26133
|
{ value: 'A3', label: 'A3' }
|
|
25857
26134
|
], 'A4');
|
|
25858
26135
|
this.addImmediateApplyListener(this.pageSizeSelect, () => this.applyPageSettings());
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
26136
|
+
pageGrid.appendChild(this.createMarginLabel('Page Size:'));
|
|
26137
|
+
pageGrid.appendChild(this.pageSizeSelect);
|
|
26138
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26139
|
+
// Orientation
|
|
25863
26140
|
this.orientationSelect = this.createSelect([
|
|
25864
26141
|
{ value: 'portrait', label: 'Portrait' },
|
|
25865
26142
|
{ value: 'landscape', label: 'Landscape' }
|
|
25866
26143
|
], 'portrait');
|
|
25867
26144
|
this.addImmediateApplyListener(this.orientationSelect, () => this.applyPageSettings());
|
|
25868
|
-
|
|
25869
|
-
|
|
26145
|
+
pageGrid.appendChild(this.createMarginLabel('Orientation:'));
|
|
26146
|
+
pageGrid.appendChild(this.orientationSelect);
|
|
26147
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26148
|
+
pageSection.appendChild(pageGrid);
|
|
26149
|
+
container.appendChild(pageSection);
|
|
25870
26150
|
return container;
|
|
25871
26151
|
}
|
|
26152
|
+
createMarginLabel(text) {
|
|
26153
|
+
const label = document.createElement('label');
|
|
26154
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
26155
|
+
label.textContent = text;
|
|
26156
|
+
return label;
|
|
26157
|
+
}
|
|
26158
|
+
createSpacer() {
|
|
26159
|
+
const spacer = document.createElement('div');
|
|
26160
|
+
return spacer;
|
|
26161
|
+
}
|
|
25872
26162
|
loadSettings() {
|
|
25873
26163
|
if (!this.editor)
|
|
25874
26164
|
return;
|
|
@@ -26267,9 +26557,15 @@ class FormattingPane extends BasePane {
|
|
|
26267
26557
|
this.bulletListBtn?.classList.toggle('pc-pane-button--active', listFormatting.listType === 'bullet');
|
|
26268
26558
|
this.numberedListBtn?.classList.toggle('pc-pane-button--active', listFormatting.listType === 'number');
|
|
26269
26559
|
}
|
|
26560
|
+
else {
|
|
26561
|
+
this.bulletListBtn?.classList.remove('pc-pane-button--active');
|
|
26562
|
+
this.numberedListBtn?.classList.remove('pc-pane-button--active');
|
|
26563
|
+
}
|
|
26270
26564
|
}
|
|
26271
26565
|
catch {
|
|
26272
26566
|
// No text editing active
|
|
26567
|
+
this.bulletListBtn?.classList.remove('pc-pane-button--active');
|
|
26568
|
+
this.numberedListBtn?.classList.remove('pc-pane-button--active');
|
|
26273
26569
|
}
|
|
26274
26570
|
}
|
|
26275
26571
|
getSelection() {
|
|
@@ -26408,6 +26704,7 @@ class HyperlinkPane extends BasePane {
|
|
|
26408
26704
|
this.titleInput = null;
|
|
26409
26705
|
this.rangeHint = null;
|
|
26410
26706
|
this.currentHyperlink = null;
|
|
26707
|
+
this._isUpdating = false;
|
|
26411
26708
|
this.onApply = options.onApply;
|
|
26412
26709
|
this.onRemove = options.onRemove;
|
|
26413
26710
|
}
|
|
@@ -26449,15 +26746,21 @@ class HyperlinkPane extends BasePane {
|
|
|
26449
26746
|
return container;
|
|
26450
26747
|
}
|
|
26451
26748
|
updateFromCursor() {
|
|
26452
|
-
if (!this.editor)
|
|
26749
|
+
if (!this.editor || this._isUpdating)
|
|
26453
26750
|
return;
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
this.
|
|
26751
|
+
this._isUpdating = true;
|
|
26752
|
+
try {
|
|
26753
|
+
const cursorPos = this.editor.getCursorPosition();
|
|
26754
|
+
const hyperlink = this.editor.getHyperlinkAt(cursorPos);
|
|
26755
|
+
if (hyperlink) {
|
|
26756
|
+
this.showHyperlink(hyperlink);
|
|
26757
|
+
}
|
|
26758
|
+
else {
|
|
26759
|
+
this.hideHyperlink();
|
|
26760
|
+
}
|
|
26458
26761
|
}
|
|
26459
|
-
|
|
26460
|
-
this.
|
|
26762
|
+
finally {
|
|
26763
|
+
this._isUpdating = false;
|
|
26461
26764
|
}
|
|
26462
26765
|
}
|
|
26463
26766
|
showHyperlink(hyperlink) {
|
|
@@ -27076,6 +27379,7 @@ class TextBoxPane extends BasePane {
|
|
|
27076
27379
|
this.borderColorInput = null;
|
|
27077
27380
|
this.borderStyleSelect = null;
|
|
27078
27381
|
this.paddingInput = null;
|
|
27382
|
+
this._isUpdating = false;
|
|
27079
27383
|
this.currentTextBox = null;
|
|
27080
27384
|
this.onApplyCallback = options.onApply;
|
|
27081
27385
|
}
|
|
@@ -27149,14 +27453,20 @@ class TextBoxPane extends BasePane {
|
|
|
27149
27453
|
return container;
|
|
27150
27454
|
}
|
|
27151
27455
|
updateFromSelection() {
|
|
27152
|
-
if (!this.editor)
|
|
27456
|
+
if (!this.editor || this._isUpdating)
|
|
27153
27457
|
return;
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
this.
|
|
27458
|
+
this._isUpdating = true;
|
|
27459
|
+
try {
|
|
27460
|
+
const textBox = this.editor.getSelectedTextBox?.();
|
|
27461
|
+
if (textBox && !textBox.editing) {
|
|
27462
|
+
this.showTextBox(textBox);
|
|
27463
|
+
}
|
|
27464
|
+
else {
|
|
27465
|
+
this.hideTextBox();
|
|
27466
|
+
}
|
|
27157
27467
|
}
|
|
27158
|
-
|
|
27159
|
-
this.
|
|
27468
|
+
finally {
|
|
27469
|
+
this._isUpdating = false;
|
|
27160
27470
|
}
|
|
27161
27471
|
}
|
|
27162
27472
|
/**
|
|
@@ -27310,6 +27620,7 @@ class ImagePane extends BasePane {
|
|
|
27310
27620
|
this.altTextInput = null;
|
|
27311
27621
|
this.fileInput = null;
|
|
27312
27622
|
this.currentImage = null;
|
|
27623
|
+
this._isUpdating = false;
|
|
27313
27624
|
this.maxImageWidth = options.maxImageWidth ?? 400;
|
|
27314
27625
|
this.maxImageHeight = options.maxImageHeight ?? 400;
|
|
27315
27626
|
this.onApplyCallback = options.onApply;
|
|
@@ -27391,14 +27702,20 @@ class ImagePane extends BasePane {
|
|
|
27391
27702
|
return container;
|
|
27392
27703
|
}
|
|
27393
27704
|
updateFromSelection() {
|
|
27394
|
-
if (!this.editor)
|
|
27705
|
+
if (!this.editor || this._isUpdating)
|
|
27395
27706
|
return;
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
this.
|
|
27707
|
+
this._isUpdating = true;
|
|
27708
|
+
try {
|
|
27709
|
+
const image = this.editor.getSelectedImage?.();
|
|
27710
|
+
if (image) {
|
|
27711
|
+
this.showImage(image);
|
|
27712
|
+
}
|
|
27713
|
+
else {
|
|
27714
|
+
this.hideImage();
|
|
27715
|
+
}
|
|
27399
27716
|
}
|
|
27400
|
-
|
|
27401
|
-
this.
|
|
27717
|
+
finally {
|
|
27718
|
+
this._isUpdating = false;
|
|
27402
27719
|
}
|
|
27403
27720
|
}
|
|
27404
27721
|
/**
|
|
@@ -27563,6 +27880,9 @@ class TablePane extends BasePane {
|
|
|
27563
27880
|
// Default controls
|
|
27564
27881
|
this.defaultPaddingInput = null;
|
|
27565
27882
|
this.defaultBorderColorInput = null;
|
|
27883
|
+
// Merge/split buttons
|
|
27884
|
+
this.mergeCellsBtn = null;
|
|
27885
|
+
this.splitCellBtn = null;
|
|
27566
27886
|
// Cell formatting controls
|
|
27567
27887
|
this.cellBgColorInput = null;
|
|
27568
27888
|
this.borderTopCheck = null;
|
|
@@ -27573,6 +27893,7 @@ class TablePane extends BasePane {
|
|
|
27573
27893
|
this.borderColorInput = null;
|
|
27574
27894
|
this.borderStyleSelect = null;
|
|
27575
27895
|
this.currentTable = null;
|
|
27896
|
+
this._isUpdating = false;
|
|
27576
27897
|
this.onApplyCallback = options.onApply;
|
|
27577
27898
|
}
|
|
27578
27899
|
attach(options) {
|
|
@@ -27581,12 +27902,12 @@ class TablePane extends BasePane {
|
|
|
27581
27902
|
// Listen for selection/focus changes
|
|
27582
27903
|
const updateHandler = () => this.updateFromFocusedTable();
|
|
27583
27904
|
this.editor.on('selection-change', updateHandler);
|
|
27584
|
-
this.editor.on('
|
|
27585
|
-
this.editor.on('table-cell-selection', updateHandler);
|
|
27905
|
+
this.editor.on('tablecell-cursor-changed', updateHandler);
|
|
27906
|
+
this.editor.on('table-cell-selection-changed', updateHandler);
|
|
27586
27907
|
this.eventCleanup.push(() => {
|
|
27587
27908
|
this.editor?.off('selection-change', updateHandler);
|
|
27588
|
-
this.editor?.off('
|
|
27589
|
-
this.editor?.off('table-cell-selection', updateHandler);
|
|
27909
|
+
this.editor?.off('tablecell-cursor-changed', updateHandler);
|
|
27910
|
+
this.editor?.off('table-cell-selection-changed', updateHandler);
|
|
27590
27911
|
});
|
|
27591
27912
|
// Initial update
|
|
27592
27913
|
this.updateFromFocusedTable();
|
|
@@ -27655,6 +27976,17 @@ class TablePane extends BasePane {
|
|
|
27655
27976
|
const cellSection = this.createSection('Cell Formatting');
|
|
27656
27977
|
this.cellSelectionDisplay = this.createHint('No cell selected');
|
|
27657
27978
|
cellSection.appendChild(this.cellSelectionDisplay);
|
|
27979
|
+
// Merge/Split buttons
|
|
27980
|
+
const mergeBtnGroup = this.createButtonGroup();
|
|
27981
|
+
this.mergeCellsBtn = this.createButton('Merge Cells');
|
|
27982
|
+
this.mergeCellsBtn.disabled = true;
|
|
27983
|
+
this.splitCellBtn = this.createButton('Split Cell');
|
|
27984
|
+
this.splitCellBtn.disabled = true;
|
|
27985
|
+
this.addButtonListener(this.mergeCellsBtn, () => this.doMergeCells());
|
|
27986
|
+
this.addButtonListener(this.splitCellBtn, () => this.doSplitCell());
|
|
27987
|
+
mergeBtnGroup.appendChild(this.mergeCellsBtn);
|
|
27988
|
+
mergeBtnGroup.appendChild(this.splitCellBtn);
|
|
27989
|
+
cellSection.appendChild(mergeBtnGroup);
|
|
27658
27990
|
// Background
|
|
27659
27991
|
this.cellBgColorInput = this.createColorInput('#ffffff');
|
|
27660
27992
|
cellSection.appendChild(this.createFormGroup('Background', this.cellBgColorInput));
|
|
@@ -27711,14 +28043,20 @@ class TablePane extends BasePane {
|
|
|
27711
28043
|
return container;
|
|
27712
28044
|
}
|
|
27713
28045
|
updateFromFocusedTable() {
|
|
27714
|
-
if (!this.editor)
|
|
28046
|
+
if (!this.editor || this._isUpdating)
|
|
27715
28047
|
return;
|
|
27716
|
-
|
|
27717
|
-
|
|
27718
|
-
this.
|
|
28048
|
+
this._isUpdating = true;
|
|
28049
|
+
try {
|
|
28050
|
+
const table = this.editor.getFocusedTable();
|
|
28051
|
+
if (table) {
|
|
28052
|
+
this.showTable(table);
|
|
28053
|
+
}
|
|
28054
|
+
else {
|
|
28055
|
+
this.hideTable();
|
|
28056
|
+
}
|
|
27719
28057
|
}
|
|
27720
|
-
|
|
27721
|
-
this.
|
|
28058
|
+
finally {
|
|
28059
|
+
this._isUpdating = false;
|
|
27722
28060
|
}
|
|
27723
28061
|
}
|
|
27724
28062
|
/**
|
|
@@ -27765,6 +28103,15 @@ class TablePane extends BasePane {
|
|
|
27765
28103
|
return;
|
|
27766
28104
|
const focusedCell = table.focusedCell;
|
|
27767
28105
|
const selectedRange = table.selectedRange;
|
|
28106
|
+
// Update merge/split button states
|
|
28107
|
+
if (this.mergeCellsBtn) {
|
|
28108
|
+
const canMerge = selectedRange ? table.canMergeRange(selectedRange).canMerge : false;
|
|
28109
|
+
this.mergeCellsBtn.disabled = !canMerge;
|
|
28110
|
+
}
|
|
28111
|
+
if (this.splitCellBtn) {
|
|
28112
|
+
const canSplit = focusedCell ? table.canSplitCell(focusedCell.row, focusedCell.col).canSplit : false;
|
|
28113
|
+
this.splitCellBtn.disabled = !canSplit;
|
|
28114
|
+
}
|
|
27768
28115
|
if (selectedRange) {
|
|
27769
28116
|
const count = (selectedRange.end.row - selectedRange.start.row + 1) *
|
|
27770
28117
|
(selectedRange.end.col - selectedRange.start.col + 1);
|
|
@@ -27922,6 +28269,21 @@ class TablePane extends BasePane {
|
|
|
27922
28269
|
hasTable() {
|
|
27923
28270
|
return this.currentTable !== null;
|
|
27924
28271
|
}
|
|
28272
|
+
doMergeCells() {
|
|
28273
|
+
if (!this.editor || !this.currentTable)
|
|
28274
|
+
return;
|
|
28275
|
+
this.editor.tableMergeCells(this.currentTable);
|
|
28276
|
+
this.updateFromFocusedTable();
|
|
28277
|
+
}
|
|
28278
|
+
doSplitCell() {
|
|
28279
|
+
if (!this.editor || !this.currentTable)
|
|
28280
|
+
return;
|
|
28281
|
+
const focused = this.currentTable.focusedCell;
|
|
28282
|
+
if (!focused)
|
|
28283
|
+
return;
|
|
28284
|
+
this.editor.tableSplitCell(this.currentTable, focused.row, focused.col);
|
|
28285
|
+
this.updateFromFocusedTable();
|
|
28286
|
+
}
|
|
27925
28287
|
/**
|
|
27926
28288
|
* Update the pane from current editor state.
|
|
27927
28289
|
*/
|
|
@@ -27930,5 +28292,5 @@ class TablePane extends BasePane {
|
|
|
27930
28292
|
}
|
|
27931
28293
|
}
|
|
27932
28294
|
|
|
27933
|
-
export { BaseControl, BaseEmbeddedObject, BasePane, BaseTextRegion, BodyTextRegion, ClipboardManager, ContentAnalyzer, DEFAULT_IMPORT_OPTIONS, Document, DocumentBuilder, DocumentInfoPane, DocumentSettingsPane, EmbeddedObjectFactory, EmbeddedObjectManager, EventEmitter, FlowingTextContent, FooterTextRegion, FormattingPane, HeaderTextRegion, HorizontalRuler, HtmlConverter, HyperlinkPane, ImageObject, ImagePane, MergeDataPane, PCEditor, PDFImportError, PDFImportErrorCode, PDFImporter, PDFParser, Page, RegionManager, RepeatingSectionManager, RepeatingSectionPane, RulerControl, SubstitutionFieldManager, SubstitutionFieldPane, TableCell, TableObject, TablePane, TableRow, TableRowLoopPane, TextBoxObject, TextBoxPane, TextFormattingManager, TextLayout, TextMeasurer, TextPositionCalculator, TextState, VerticalRuler, ViewSettingsPane };
|
|
28295
|
+
export { BaseControl, BaseEmbeddedObject, BasePane, BaseTextRegion, BodyTextRegion, ClipboardManager, ContentAnalyzer, DEFAULT_IMPORT_OPTIONS, Document, DocumentBuilder, DocumentInfoPane, DocumentSettingsPane, EmbeddedObjectFactory, EmbeddedObjectManager, EventEmitter, FlowingTextContent, FooterTextRegion, FormattingPane, HeaderTextRegion, HorizontalRuler, HtmlConverter, HyperlinkPane, ImageObject, ImagePane, Logger, MergeDataPane, PCEditor, PDFImportError, PDFImportErrorCode, PDFImporter, PDFParser, Page, RegionManager, RepeatingSectionManager, RepeatingSectionPane, RulerControl, SubstitutionFieldManager, SubstitutionFieldPane, TableCell, TableObject, TablePane, TableRow, TableRowLoopPane, TextBoxObject, TextBoxPane, TextFormattingManager, TextLayout, TextMeasurer, TextPositionCalculator, TextState, VerticalRuler, ViewSettingsPane };
|
|
27934
28296
|
//# sourceMappingURL=pc-editor.esm.js.map
|