@phuong-tran-redoc/document-engine-angular 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -206,6 +206,12 @@ the contract.** It lists every token the library reads
|
|
|
206
206
|
hardcodes a colour.
|
|
207
207
|
- `--de-editor-min-height` (default `12rem`) sets the editing surface's floor; alternatively give the
|
|
208
208
|
`document-engine-editor` element a real height and the surface fills it.
|
|
209
|
+
- **The editing surface does not scroll internally — it grows, and the page scrolls.** `0.1.5` briefly
|
|
210
|
+
made it a scroll container; that was reverted in `0.1.6` because the selection bubble menu is an
|
|
211
|
+
absolutely-positioned descendant of it, so the scroller became the panel's clip rect and a dropdown
|
|
212
|
+
opening upward lost its top options to the clip. If you need a fixed-height box that scrolls its
|
|
213
|
+
content, put `overflow: auto` on **your own** wrapper around `document-engine-editor`, never on
|
|
214
|
+
`.tiptap-editor`.
|
|
209
215
|
- Prose and editing-affordance styles are **opt-in** subpaths (`styles/editor-content`,
|
|
210
216
|
`styles/editor-interaction`), so the baseline can never impose a look on your documents.
|
|
211
217
|
|
|
@@ -239,13 +245,9 @@ nx lint @phuong-tran-redoc/document-engine-angular # lint
|
|
|
239
245
|
|
|
240
246
|
---
|
|
241
247
|
|
|
242
|
-
## 👤
|
|
248
|
+
## 👤 Authors
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
- 💼 [LinkedIn](https://www.linkedin.com/in/tdp1999/)
|
|
247
|
-
- 🐙 [GitHub](https://github.com/tdp1999)
|
|
248
|
-
- 📧 [Email](mailto:tdp99.business@gmail.com)
|
|
250
|
+
See [AUTHORS.md](https://github.com/phuong-tran-redoc/document-engine/blob/main/AUTHORS.md).
|
|
249
251
|
|
|
250
252
|
---
|
|
251
253
|
|
|
@@ -3,27 +3,27 @@ import { DOCUMENT, CommonModule } from '@angular/common';
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, ElementRef, Input, Directive, Injectable, ApplicationRef, createComponent, HostBinding, InjectionToken, Renderer2, ChangeDetectorRef, forwardRef, ChangeDetectionStrategy, Component, ViewEncapsulation, EventEmitter, Output, HostListener, ContentChildren, ViewChild, ViewChildren, NgZone, ViewContainerRef, EnvironmentInjector, NgModule } from '@angular/core';
|
|
5
5
|
import { Extension, Editor } from '@tiptap/core';
|
|
6
|
-
import Blockquote from '@tiptap/extension-blockquote';
|
|
7
|
-
import Bold from '@tiptap/extension-bold';
|
|
8
|
-
import Code from '@tiptap/extension-code';
|
|
9
|
-
import CodeBlock from '@tiptap/extension-code-block';
|
|
6
|
+
import { Blockquote } from '@tiptap/extension-blockquote';
|
|
7
|
+
import { Bold } from '@tiptap/extension-bold';
|
|
8
|
+
import { Code } from '@tiptap/extension-code';
|
|
9
|
+
import { CodeBlock } from '@tiptap/extension-code-block';
|
|
10
10
|
import { Document } from '@tiptap/extension-document';
|
|
11
11
|
import { HardBreak } from '@tiptap/extension-hard-break';
|
|
12
12
|
import { HorizontalRule } from '@tiptap/extension-horizontal-rule';
|
|
13
13
|
import { Image } from '@tiptap/extension-image';
|
|
14
|
-
import Italic from '@tiptap/extension-italic';
|
|
15
|
-
import Link from '@tiptap/extension-link';
|
|
14
|
+
import { Italic } from '@tiptap/extension-italic';
|
|
15
|
+
import { Link } from '@tiptap/extension-link';
|
|
16
16
|
import { ListKeymap, ListItem, BulletList } from '@tiptap/extension-list';
|
|
17
17
|
import { Paragraph } from '@tiptap/extension-paragraph';
|
|
18
|
-
import Strike from '@tiptap/extension-strike';
|
|
18
|
+
import { Strike } from '@tiptap/extension-strike';
|
|
19
19
|
import { Subscript } from '@tiptap/extension-subscript';
|
|
20
20
|
import { Superscript } from '@tiptap/extension-superscript';
|
|
21
21
|
import { Text } from '@tiptap/extension-text';
|
|
22
22
|
import { TextAlign } from '@tiptap/extension-text-align';
|
|
23
23
|
import { TextStyleKit } from '@tiptap/extension-text-style';
|
|
24
|
-
import Underline from '@tiptap/extension-underline';
|
|
24
|
+
import { Underline } from '@tiptap/extension-underline';
|
|
25
25
|
import { Dropcursor, Gapcursor, TrailingNode, UndoRedo, CharacterCount, Placeholder } from '@tiptap/extensions';
|
|
26
|
-
import { ResetOnEnter, EditableRegion, RestrictedEditing, CustomOrderedList, StyledTableKit, ImageRef, PageBreak, ResetFormat, Indent, ClearContent, TextCase, NotumHeading, DynamicField, Color, normalizeColor, getActiveMarkRange, getSelectedCells, getCombinedCellAttributeValue, TableDefaultAttributes, getCursorCellInfo, getClosestDomElement } from '@phuong-tran-redoc/document-engine-core';
|
|
26
|
+
import { ResetOnEnter, EditableRegion, RestrictedEditing, CustomOrderedList, StyledTableKit, ImageRef, PageBreak, ResetFormat, Indent, ClearContent, TextCase, NotumHeading, DynamicField, CkCompat, Color, normalizeColor, getActiveMarkRange, getSelectedCells, getCombinedCellAttributeValue, TableDefaultAttributes, getCursorCellInfo, getClosestDomElement } from '@phuong-tran-redoc/document-engine-core';
|
|
27
27
|
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
|
|
28
28
|
import * as i2 from '@angular/forms';
|
|
29
29
|
import { NG_VALUE_ACCESSOR, FormsModule, FormControl, ReactiveFormsModule, FormBuilder, Validators, NonNullableFormBuilder } from '@angular/forms';
|
|
@@ -113,6 +113,7 @@ const DocumentEngineKit = Extension.create({
|
|
|
113
113
|
add(options.textCase, TextCase);
|
|
114
114
|
add(options.heading, NotumHeading);
|
|
115
115
|
add(options.dynamicField, DynamicField);
|
|
116
|
+
add(options.ckCompat, CkCompat);
|
|
116
117
|
return extensions;
|
|
117
118
|
},
|
|
118
119
|
});
|
|
@@ -3733,6 +3734,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
3733
3734
|
args: [ToggleOptionDirective, { descendants: true }]
|
|
3734
3735
|
}] } });
|
|
3735
3736
|
|
|
3737
|
+
/**
|
|
3738
|
+
* Normalize what a user typed into the border-width field into a valid CSS length.
|
|
3739
|
+
*
|
|
3740
|
+
* The field is a free-text input, and `border-width: 3` is not a valid declaration —
|
|
3741
|
+
* the browser drops it silently, so typing a bare number used to do nothing at all
|
|
3742
|
+
* with no feedback. A bare number is the single most likely thing to type, and in a
|
|
3743
|
+
* length context it conventionally means pixels, so it is completed to `px`.
|
|
3744
|
+
*
|
|
3745
|
+
* Anything already carrying a unit is left alone (only the unit is lower-cased).
|
|
3746
|
+
*
|
|
3747
|
+
* The two failure modes are deliberately NOT the same value, because the command this
|
|
3748
|
+
* feeds reads `null` as "clear this field" and `undefined` as "leave it alone":
|
|
3749
|
+
*
|
|
3750
|
+
* - an **empty** field is a real instruction to clear the width, so it returns `null`;
|
|
3751
|
+
* - **unusable** input (`abc`, `-2px`, `50%`) returns `undefined`, so a typo leaves the
|
|
3752
|
+
* width the cell already had instead of wiping it. Collapsing the two would mean a
|
|
3753
|
+
* user with a working `1px` border who mistypes `3 pixels` loses it on Save with no
|
|
3754
|
+
* message — the field is free text, so mistyping it is the expected case, not a rare one.
|
|
3755
|
+
*
|
|
3756
|
+
* @returns a valid CSS border-width; `null` to clear it; `undefined` to leave it alone.
|
|
3757
|
+
*/
|
|
3758
|
+
function normalizeBorderWidth(input) {
|
|
3759
|
+
const value = (input ?? '').trim();
|
|
3760
|
+
if (!value)
|
|
3761
|
+
return null;
|
|
3762
|
+
// `thin | medium | thick` are valid border-width keywords in their own right.
|
|
3763
|
+
if (/^(thin|medium|thick)$/i.test(value))
|
|
3764
|
+
return value.toLowerCase();
|
|
3765
|
+
// A bare number — the case this helper exists for. Border widths cannot be
|
|
3766
|
+
// negative, so a signed value is rejected rather than quietly clamped.
|
|
3767
|
+
if (/^\d+(\.\d+)?$/.test(value))
|
|
3768
|
+
return `${parseFloat(value)}px`;
|
|
3769
|
+
// Already a length. Units per CSS Values 4; `%` is not valid for border-width.
|
|
3770
|
+
const withUnit = /^(\d+(?:\.\d+)?)\s*(px|em|rem|pt|pc|in|cm|mm|q|ch|ex|vh|vw|vmin|vmax)$/i.exec(value);
|
|
3771
|
+
if (withUnit)
|
|
3772
|
+
return `${parseFloat(withUnit[1])}${withUnit[2].toLowerCase()}`;
|
|
3773
|
+
// Not a border-width at all — leave whatever the cell already has untouched.
|
|
3774
|
+
return undefined;
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3736
3777
|
/**
|
|
3737
3778
|
* Cell style view for table bubble menu
|
|
3738
3779
|
* Allows styling individual table cells (borders, background, alignment)
|
|
@@ -3834,10 +3875,14 @@ class TableCellStyleViewComponent {
|
|
|
3834
3875
|
this.cdr.markForCheck();
|
|
3835
3876
|
}
|
|
3836
3877
|
onSave() {
|
|
3878
|
+
// See `normalizeBorderWidth`: a bare number is completed to `px` so the field does
|
|
3879
|
+
// not silently do nothing, and unusable input yields `undefined` so the command
|
|
3880
|
+
// leaves the width the cell already has rather than clearing it.
|
|
3881
|
+
const width = normalizeBorderWidth(this.borderWidth);
|
|
3837
3882
|
this.editor
|
|
3838
3883
|
?.chain()
|
|
3839
3884
|
.focus()
|
|
3840
|
-
.setCellBorder({ style: this.borderStyle, color: this.borderColor, width
|
|
3885
|
+
.setCellBorder({ style: this.borderStyle, color: this.borderColor, width })
|
|
3841
3886
|
.setCellBackgroundColor(this.backgroundColor)
|
|
3842
3887
|
.setCellTextAlign(this.textAlign)
|
|
3843
3888
|
.setCellVerticalAlign(this.vAlign)
|
|
@@ -3902,6 +3947,7 @@ class TableCellStyleViewComponent {
|
|
|
3902
3947
|
<button
|
|
3903
3948
|
#borderColorTrigger
|
|
3904
3949
|
type="button"
|
|
3950
|
+
data-testid="border-color-trigger"
|
|
3905
3951
|
class="table-cell-style-view__color-swatch"
|
|
3906
3952
|
[style.background-color]="borderColor || 'transparent'"
|
|
3907
3953
|
(click)="toggleColorPicker('border')"
|
|
@@ -3955,6 +4001,7 @@ class TableCellStyleViewComponent {
|
|
|
3955
4001
|
<button
|
|
3956
4002
|
#bgColorTrigger
|
|
3957
4003
|
type="button"
|
|
4004
|
+
data-testid="background-color-trigger"
|
|
3958
4005
|
class="table-cell-style-view__color-swatch"
|
|
3959
4006
|
[style.background-color]="backgroundColor || 'transparent'"
|
|
3960
4007
|
(click)="toggleColorPicker('bg')"
|
|
@@ -4095,6 +4142,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
4095
4142
|
<button
|
|
4096
4143
|
#borderColorTrigger
|
|
4097
4144
|
type="button"
|
|
4145
|
+
data-testid="border-color-trigger"
|
|
4098
4146
|
class="table-cell-style-view__color-swatch"
|
|
4099
4147
|
[style.background-color]="borderColor || 'transparent'"
|
|
4100
4148
|
(click)="toggleColorPicker('border')"
|
|
@@ -4148,6 +4196,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
4148
4196
|
<button
|
|
4149
4197
|
#bgColorTrigger
|
|
4150
4198
|
type="button"
|
|
4199
|
+
data-testid="background-color-trigger"
|
|
4151
4200
|
class="table-cell-style-view__color-swatch"
|
|
4152
4201
|
[style.background-color]="backgroundColor || 'transparent'"
|
|
4153
4202
|
(click)="toggleColorPicker('bg')"
|
|
@@ -4631,7 +4680,7 @@ class TableMainViewComponent {
|
|
|
4631
4680
|
</document-engine-select>
|
|
4632
4681
|
|
|
4633
4682
|
<!-- Cell actions -->
|
|
4634
|
-
<document-engine-select [(value)]="cellAction" (valueChange)="handleCellAction($event)" labelMode="static">
|
|
4683
|
+
<document-engine-select data-testid="cell-actions" [(value)]="cellAction" (valueChange)="handleCellAction($event)" labelMode="static">
|
|
4635
4684
|
<document-engine-icon documentEngineSelectLabel name="table_merge_cell"></document-engine-icon>
|
|
4636
4685
|
|
|
4637
4686
|
<button documentEngineSelectOption data-testid="merge-cells" value="merge" [disabled]="!canMerge">
|
|
@@ -4693,7 +4742,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
4693
4742
|
</document-engine-select>
|
|
4694
4743
|
|
|
4695
4744
|
<!-- Cell actions -->
|
|
4696
|
-
<document-engine-select [(value)]="cellAction" (valueChange)="handleCellAction($event)" labelMode="static">
|
|
4745
|
+
<document-engine-select data-testid="cell-actions" [(value)]="cellAction" (valueChange)="handleCellAction($event)" labelMode="static">
|
|
4697
4746
|
<document-engine-icon documentEngineSelectLabel name="table_merge_cell"></document-engine-icon>
|
|
4698
4747
|
|
|
4699
4748
|
<button documentEngineSelectOption data-testid="merge-cells" value="merge" [disabled]="!canMerge">
|
|
@@ -4829,10 +4878,14 @@ class TableStyleViewComponent {
|
|
|
4829
4878
|
const editor = this.editor;
|
|
4830
4879
|
if (!editor)
|
|
4831
4880
|
return;
|
|
4881
|
+
// Complete a bare number to `px` before it reaches the command; `border-width: 3`
|
|
4882
|
+
// is invalid CSS and would be dropped without any feedback. `undefined` here means
|
|
4883
|
+
// the input was not a width at all, and the command leaves the current one alone.
|
|
4884
|
+
const width = normalizeBorderWidth(this.borderWidth);
|
|
4832
4885
|
editor
|
|
4833
4886
|
.chain()
|
|
4834
4887
|
.focus()
|
|
4835
|
-
.setTableBorder({ style: this.borderStyle, color: this.borderColor, width
|
|
4888
|
+
.setTableBorder({ style: this.borderStyle, color: this.borderColor, width })
|
|
4836
4889
|
.setTableBackgroundColor(this.tableBg)
|
|
4837
4890
|
.run();
|
|
4838
4891
|
this.cancel();
|
|
@@ -4896,6 +4949,7 @@ class TableStyleViewComponent {
|
|
|
4896
4949
|
<button
|
|
4897
4950
|
#borderColorTrigger
|
|
4898
4951
|
type="button"
|
|
4952
|
+
data-testid="border-color-trigger"
|
|
4899
4953
|
class="table-style-view__color-swatch"
|
|
4900
4954
|
[style.background-color]="borderColor || 'transparent'"
|
|
4901
4955
|
(click)="toggleColorPicker('border')"
|
|
@@ -4949,6 +5003,7 @@ class TableStyleViewComponent {
|
|
|
4949
5003
|
<button
|
|
4950
5004
|
#bgColorTrigger
|
|
4951
5005
|
type="button"
|
|
5006
|
+
data-testid="background-color-trigger"
|
|
4952
5007
|
class="table-style-view__color-swatch"
|
|
4953
5008
|
[style.background-color]="tableBg || 'transparent'"
|
|
4954
5009
|
(click)="toggleColorPicker('bg')"
|
|
@@ -5053,6 +5108,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
5053
5108
|
<button
|
|
5054
5109
|
#borderColorTrigger
|
|
5055
5110
|
type="button"
|
|
5111
|
+
data-testid="border-color-trigger"
|
|
5056
5112
|
class="table-style-view__color-swatch"
|
|
5057
5113
|
[style.background-color]="borderColor || 'transparent'"
|
|
5058
5114
|
(click)="toggleColorPicker('border')"
|
|
@@ -5106,6 +5162,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
|
|
|
5106
5162
|
<button
|
|
5107
5163
|
#bgColorTrigger
|
|
5108
5164
|
type="button"
|
|
5165
|
+
data-testid="background-color-trigger"
|
|
5109
5166
|
class="table-style-view__color-swatch"
|
|
5110
5167
|
[style.background-color]="tableBg || 'transparent'"
|
|
5111
5168
|
(click)="toggleColorPicker('bg')"
|