@leanix/components 0.4.587 → 0.4.588
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/fesm2022/leanix-components.mjs +1922 -427
- package/fesm2022/leanix-components.mjs.map +1 -1
- package/index.d.ts +10 -0
- package/lib/forms-ui/components/rich-text-editor/components/rich-text-editor/rich-text-editor.component.d.ts +29 -0
- package/lib/forms-ui/components/rich-text-editor/components/rich-text-editor-toolbar/rich-text-editor-toolbar.component.d.ts +23 -0
- package/lib/forms-ui/components/rich-text-editor/directives/focus-editor.directive.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/directives/tiptap-editor.directive.d.ts +23 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/highlight-term/highlight-term-state.plugin.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/highlight-term/highlight-term.directive.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/highlight-term/highlight-term.extension.d.ts +9 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/highlight-term/highlight-term.plugin.d.ts +5 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/link/components/link-modal/link-modal.component.d.ts +18 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/link/link.extension.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/link/link.plugin.d.ts +10 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/link/url-validator.directive.d.ts +7 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/table/index.d.ts +2 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/table/table-bubble-menu/table-bubble-menu.component.d.ts +13 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/table/table-extensions.d.ts +4 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/table/utils.d.ts +28 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/tracking/tracking.directive.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/tracking/tracking.extension.d.ts +10 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/tracking/tracking.plugin.d.ts +4 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/truncate/index.d.ts +2 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/truncate/truncate-button.component.d.ts +22 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/truncate/truncate.directive.d.ts +9 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/truncate/truncate.extension.d.ts +6 -0
- package/lib/forms-ui/components/rich-text-editor/extensions/truncate/truncate.plugin.d.ts +10 -0
- package/lib/forms-ui/components/rich-text-editor/ngx-tiptap/AngularRenderer.d.ts +12 -0
- package/lib/forms-ui/components/rich-text-editor/ngx-tiptap/NodeViewRenderer.d.ts +18 -0
- package/lib/forms-ui/components/rich-text-editor/ngx-tiptap/editor.directive.d.ts +24 -0
- package/lib/forms-ui/components/rich-text-editor/ngx-tiptap/node-view.component.d.ts +14 -0
- package/lib/forms-ui/components/rich-text-editor/pipes/extension-enabled.pipe.d.ts +8 -0
- package/lib/forms-ui/components/rich-text-editor/pipes/remove-markdown.pipe.d.ts +14 -0
- package/lib/forms-ui/components/rich-text-editor/utils/extensions-builder.d.ts +13 -0
- package/lib/forms-ui/components/rich-text-editor/utils/features.d.ts +3 -0
- package/package.json +30 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as i0 from '@angular/core';
|
2
|
-
import { InjectionToken, Input, Component, signal, ChangeDetectionStrategy, HostBinding, inject, input, computed, EventEmitter, HostListener, Output, Injectable, ElementRef, ViewChild, Inject, model, Directive, Optional, Pipe, NgModule, DestroyRef, ChangeDetectorRef, effect, ContentChild, afterRenderEffect, ContentChildren, ViewChildren, forwardRef, TemplateRef, viewChild, booleanAttribute, SecurityContext, Self, Host } from '@angular/core';
|
2
|
+
import { InjectionToken, Input, Component, signal, ChangeDetectionStrategy, HostBinding, inject, input, computed, EventEmitter, HostListener, Output, Injectable, ElementRef, ViewChild, Inject, model, Directive, Optional, Pipe, NgModule, DestroyRef, ChangeDetectorRef, effect, ContentChild, afterRenderEffect, ContentChildren, ViewChildren, forwardRef, TemplateRef, viewChild, booleanAttribute, SecurityContext, Self, Host, Injector, output, ApplicationRef, createComponent } from '@angular/core';
|
3
3
|
import * as i1 from '@ngx-translate/core';
|
4
4
|
import { TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
5
5
|
import { NgTemplateOutlet, NgClass, AsyncPipe, UpperCasePipe, DecimalPipe, CommonModule, formatDate } from '@angular/common';
|
@@ -30,12 +30,42 @@ import * as i1$3 from '@ncstate/sat-popover';
|
|
30
30
|
import { SatPopoverModule, SatPopoverComponent } from '@ncstate/sat-popover';
|
31
31
|
import { ClipboardModule } from '@angular/cdk/clipboard';
|
32
32
|
import * as i1$5 from '@angular/forms';
|
33
|
-
import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, ReactiveFormsModule, UntypedFormControl, Validators, FormControl } from '@angular/forms';
|
33
|
+
import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, ReactiveFormsModule, UntypedFormControl, NgForm, Validators, FormControl } from '@angular/forms';
|
34
34
|
import * as i1$4 from 'ngx-infinite-scroll';
|
35
35
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
36
36
|
import * as i1$6 from '@angular/cdk/drag-drop';
|
37
37
|
import { moveItemInArray, CdkDropList, CdkDrag, DragDropModule } from '@angular/cdk/drag-drop';
|
38
38
|
import * as i1$7 from '@angular/platform-browser';
|
39
|
+
import { Extension, getMarkRange, findParentNode, Editor, NodeView } from '@tiptap/core';
|
40
|
+
import Bold from '@tiptap/extension-bold';
|
41
|
+
import BulletList from '@tiptap/extension-bullet-list';
|
42
|
+
import Code from '@tiptap/extension-code';
|
43
|
+
import CodeBlock from '@tiptap/extension-code-block';
|
44
|
+
import Document from '@tiptap/extension-document';
|
45
|
+
import Dropcursor from '@tiptap/extension-dropcursor';
|
46
|
+
import Gapcursor from '@tiptap/extension-gapcursor';
|
47
|
+
import HardBreak from '@tiptap/extension-hard-break';
|
48
|
+
import Heading from '@tiptap/extension-heading';
|
49
|
+
import History from '@tiptap/extension-history';
|
50
|
+
import HorizontalRule from '@tiptap/extension-horizontal-rule';
|
51
|
+
import Italic from '@tiptap/extension-italic';
|
52
|
+
import ListItem from '@tiptap/extension-list-item';
|
53
|
+
import OrderedList from '@tiptap/extension-ordered-list';
|
54
|
+
import Paragraph from '@tiptap/extension-paragraph';
|
55
|
+
import Strike from '@tiptap/extension-strike';
|
56
|
+
import Text from '@tiptap/extension-text';
|
57
|
+
import TextAlign from '@tiptap/extension-text-align';
|
58
|
+
import Underline from '@tiptap/extension-underline';
|
59
|
+
import { Markdown } from 'tiptap-markdown';
|
60
|
+
import { PluginKey, Plugin } from '@tiptap/pm/state';
|
61
|
+
import { DecorationSet, Decoration } from '@tiptap/pm/view';
|
62
|
+
import { Link } from '@tiptap/extension-link';
|
63
|
+
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
|
64
|
+
import Table, { Table as Table$1 } from '@tiptap/extension-table';
|
65
|
+
import { CellSelection, TableMap } from '@tiptap/pm/tables';
|
66
|
+
import TableCell from '@tiptap/extension-table-cell';
|
67
|
+
import TiptapTableHeader from '@tiptap/extension-table-header';
|
68
|
+
import TableRow from '@tiptap/extension-table-row';
|
39
69
|
import { trigger, transition, style, animate } from '@angular/animations';
|
40
70
|
import { coerceNumberProperty } from '@angular/cdk/coercion';
|
41
71
|
|
@@ -9025,497 +9055,1962 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImpor
|
|
9025
9055
|
}]
|
9026
9056
|
}] });
|
9027
9057
|
|
9028
|
-
|
9029
|
-
|
9030
|
-
|
9058
|
+
const highlightTermStatePluginKey = new PluginKey('highlight-term-state');
|
9059
|
+
function highlightTermState(_editor) {
|
9060
|
+
return new Plugin({
|
9061
|
+
key: highlightTermStatePluginKey,
|
9062
|
+
state: {
|
9063
|
+
init() {
|
9064
|
+
return { term: null };
|
9065
|
+
},
|
9066
|
+
apply: (tr, state) => {
|
9067
|
+
const meta = tr.getMeta(highlightTermStatePluginKey);
|
9068
|
+
return meta ? { ...state, ...meta } : state;
|
9069
|
+
}
|
9070
|
+
}
|
9071
|
+
});
|
9072
|
+
}
|
9073
|
+
|
9074
|
+
const highlightTermPluginKey = new PluginKey('highlight-term');
|
9075
|
+
function highlightTerm(_editor, options) {
|
9076
|
+
return new Plugin({
|
9077
|
+
key: highlightTermPluginKey,
|
9078
|
+
state: {
|
9079
|
+
init(_, state) {
|
9080
|
+
const term = highlightTermStatePluginKey.getState(state)?.term ?? null;
|
9081
|
+
return createDecorations(state.doc, term ?? null, options);
|
9082
|
+
},
|
9083
|
+
apply(tr, oldDecorationSet, state) {
|
9084
|
+
const meta = tr.getMeta(highlightTermStatePluginKey);
|
9085
|
+
const oldTerm = highlightTermStatePluginKey.getState(state)?.term ?? null;
|
9086
|
+
const termChanged = meta?.term && meta.term !== oldTerm;
|
9087
|
+
if (tr.docChanged || termChanged) {
|
9088
|
+
return createDecorations(tr.doc, meta?.term || oldTerm, options);
|
9089
|
+
}
|
9090
|
+
return oldDecorationSet.map(tr.mapping, tr.doc);
|
9091
|
+
}
|
9092
|
+
},
|
9093
|
+
props: {
|
9094
|
+
decorations(state) {
|
9095
|
+
return this.getState(state);
|
9096
|
+
}
|
9097
|
+
}
|
9098
|
+
});
|
9099
|
+
}
|
9100
|
+
function createDecorations(doc, term, options) {
|
9101
|
+
const decorations = [];
|
9102
|
+
if (!term)
|
9103
|
+
return DecorationSet.empty;
|
9104
|
+
const regex = prepareRegex(options, term);
|
9105
|
+
doc.descendants((node, pos) => {
|
9106
|
+
if (!node.isText || !node.text)
|
9107
|
+
return;
|
9108
|
+
const text = node.text;
|
9109
|
+
let match;
|
9110
|
+
while ((match = regex.exec(text)) !== null) {
|
9111
|
+
const start = pos + match.index;
|
9112
|
+
const end = start + match[0].length;
|
9113
|
+
// TODO: replace #fafda5 with $searchHighlightingColor
|
9114
|
+
decorations.push(Decoration.inline(start, end, { style: 'background-color: #fafda5' }));
|
9115
|
+
}
|
9116
|
+
});
|
9117
|
+
return DecorationSet.create(doc, decorations);
|
9118
|
+
}
|
9119
|
+
/**
|
9120
|
+
* This regex is copied from highlight-term.pipe.ts
|
9121
|
+
* TODO: get rid of duplication
|
9122
|
+
*/
|
9123
|
+
function prepareRegex(options, term) {
|
9124
|
+
const STANDARD_TOKENIZER_SEPERATORS = /[^a-zA-Z\d\s]/g;
|
9125
|
+
let pattern = options.exactMatch ? term.trim() : term.replace(STANDARD_TOKENIZER_SEPERATORS, ' ');
|
9126
|
+
// replace special chars for a backslash for RegExp
|
9127
|
+
pattern = pattern.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
9128
|
+
pattern = options.exactMatch
|
9129
|
+
? pattern
|
9130
|
+
: pattern
|
9131
|
+
.split(' ')
|
9132
|
+
.filter((t) => {
|
9133
|
+
return t.length > 0;
|
9134
|
+
})
|
9135
|
+
.join('|');
|
9136
|
+
return new RegExp(pattern, 'gi');
|
9137
|
+
}
|
9138
|
+
|
9139
|
+
/**
|
9140
|
+
* This extension replicates the search term highlight mechanism
|
9141
|
+
* implemented by highlight-term.pipe.ts for tiptap editor.
|
9142
|
+
*/
|
9143
|
+
const HighlightTerm = Extension.create({
|
9144
|
+
name: 'highlight-term',
|
9145
|
+
addOptions() {
|
9146
|
+
return {
|
9147
|
+
exactMatch: false
|
9148
|
+
};
|
9149
|
+
},
|
9150
|
+
addProseMirrorPlugins() {
|
9151
|
+
const { editor, options } = this;
|
9152
|
+
return [highlightTermState(editor), highlightTerm(editor, options)];
|
9153
|
+
}
|
9154
|
+
});
|
9155
|
+
|
9156
|
+
const linkPluginKey = new PluginKey('link');
|
9157
|
+
function linkPlugin() {
|
9158
|
+
return new Plugin({
|
9159
|
+
key: linkPluginKey,
|
9160
|
+
state: {
|
9161
|
+
init() {
|
9162
|
+
return { open: false, text: null, url: null };
|
9163
|
+
},
|
9164
|
+
apply: (tr, state) => {
|
9165
|
+
const meta = tr.getMeta(linkPluginKey);
|
9166
|
+
return meta ? { ...state, ...meta } : state;
|
9167
|
+
}
|
9168
|
+
},
|
9169
|
+
props: {
|
9170
|
+
handleClickOn: (view, pos, _node, _nodePos, event) => {
|
9171
|
+
const state = view.state;
|
9172
|
+
const $pos = state.doc.resolve(pos);
|
9173
|
+
const linkMark = $pos.marks().find((mark) => mark.type.name === 'link');
|
9174
|
+
if (!linkMark)
|
9175
|
+
return false;
|
9176
|
+
const markRange = getMarkRange($pos, linkMark.type);
|
9177
|
+
if (!markRange)
|
9178
|
+
return false;
|
9179
|
+
const { from, to } = markRange;
|
9180
|
+
const text = state.doc.textBetween(from, to, ' ');
|
9181
|
+
const href = linkMark.attrs['href'];
|
9182
|
+
if (!href || !view.editable)
|
9183
|
+
return false;
|
9184
|
+
event.preventDefault();
|
9185
|
+
const pluginState = linkPluginKey.getState(state);
|
9186
|
+
view.dispatch(state.tr.setMeta(linkPluginKey, {
|
9187
|
+
...pluginState,
|
9188
|
+
open: true,
|
9189
|
+
text,
|
9190
|
+
url: href
|
9191
|
+
}));
|
9192
|
+
return true;
|
9193
|
+
}
|
9194
|
+
}
|
9195
|
+
});
|
9196
|
+
}
|
9197
|
+
function dispatchLinkState(editor, props) {
|
9198
|
+
const pluginState = linkPluginKey.getState(editor.state);
|
9199
|
+
editor.view.dispatch(editor.view.state.tr.setMeta(linkPluginKey, {
|
9200
|
+
...pluginState,
|
9201
|
+
...props
|
9202
|
+
}));
|
9203
|
+
}
|
9204
|
+
|
9205
|
+
const CustomLink = Link.extend({
|
9206
|
+
addAttributes() {
|
9207
|
+
return {
|
9208
|
+
...this.parent?.(),
|
9209
|
+
href: {
|
9210
|
+
...this.parent?.()?.href,
|
9211
|
+
renderHTML: (attributes) => {
|
9212
|
+
return {
|
9213
|
+
href: attributes['href'],
|
9214
|
+
target: '_blank',
|
9215
|
+
rel: 'noopener noreferrer',
|
9216
|
+
'data-link': attributes['href']
|
9217
|
+
};
|
9218
|
+
}
|
9219
|
+
}
|
9220
|
+
};
|
9221
|
+
},
|
9222
|
+
addCommands() {
|
9223
|
+
return {
|
9224
|
+
...this.parent?.(),
|
9225
|
+
openLinkModal: () => ({ editor, state }) => {
|
9226
|
+
const selection = this.editor.state.selection;
|
9227
|
+
if (selection.empty) {
|
9228
|
+
const mark = selection.$head.marks().find(isLinkMark);
|
9229
|
+
if (!mark) {
|
9230
|
+
dispatchLinkState(editor, { url: null, text: null, open: true });
|
9231
|
+
return true;
|
9232
|
+
}
|
9233
|
+
const node = getNodeByMark(editor.state.doc, mark);
|
9234
|
+
if (!node) {
|
9235
|
+
dispatchLinkState(editor, { url: null, text: null, open: true });
|
9236
|
+
return true;
|
9237
|
+
}
|
9238
|
+
const url = mark.attrs['href'];
|
9239
|
+
const text = node.text ?? null;
|
9240
|
+
dispatchLinkState(editor, { url, text, open: true });
|
9241
|
+
return true;
|
9242
|
+
}
|
9243
|
+
const { from, to } = this.editor.state.selection;
|
9244
|
+
const text = this.editor.state.doc.textBetween(from, to, ' ');
|
9245
|
+
let url = '';
|
9246
|
+
state.doc.nodesBetween(from, to, (node, _pos) => {
|
9247
|
+
const linkMark = node.marks.find((mark) => mark.type.name === 'link');
|
9248
|
+
if (linkMark) {
|
9249
|
+
url = linkMark.attrs['href'];
|
9250
|
+
return false;
|
9251
|
+
}
|
9252
|
+
return true;
|
9253
|
+
});
|
9254
|
+
dispatchLinkState(editor, { url, text, open: true });
|
9255
|
+
return true;
|
9256
|
+
}
|
9257
|
+
};
|
9258
|
+
},
|
9259
|
+
addProseMirrorPlugins() {
|
9260
|
+
const editor = this.editor;
|
9261
|
+
const defaultPlugins = this.parent?.() ?? [];
|
9262
|
+
if (!editor.isEditable) {
|
9263
|
+
return [...defaultPlugins];
|
9264
|
+
}
|
9265
|
+
return [...defaultPlugins, linkPlugin()];
|
9266
|
+
}
|
9267
|
+
})
|
9268
|
+
.configure({ autolink: true, openOnClick: false })
|
9269
|
+
.extend({ inclusive: false });
|
9270
|
+
function getNodeByMark(doc, targetMark) {
|
9271
|
+
let result;
|
9272
|
+
doc.descendants((node) => {
|
9273
|
+
if (!node.isText)
|
9274
|
+
return true;
|
9275
|
+
if (targetMark.isInSet(node.marks)) {
|
9276
|
+
result = node;
|
9277
|
+
return false;
|
9278
|
+
}
|
9279
|
+
return true;
|
9280
|
+
});
|
9281
|
+
return result;
|
9282
|
+
}
|
9283
|
+
function isLinkMark(mark) {
|
9284
|
+
return mark.type.name === 'link';
|
9285
|
+
}
|
9286
|
+
|
9287
|
+
const isCellSelection = (selection) => selection instanceof CellSelection;
|
9288
|
+
const getTableMapFromSelection = (selection) => TableMap.get(selection.$anchorCell.node(-1));
|
9289
|
+
const checkIfRectIsFullySelected = (rect, selection) => {
|
9290
|
+
const map = getTableMapFromSelection(selection);
|
9291
|
+
const offset = selection.$anchorCell.start(-1);
|
9292
|
+
const rectCells = map.cellsInRect(rect);
|
9293
|
+
const selectedCells = map.cellsInRect(map.rectBetween(selection.$anchorCell.pos - offset, selection.$headCell.pos - offset));
|
9294
|
+
return rectCells.every((cell) => selectedCells.includes(cell));
|
9295
|
+
};
|
9296
|
+
const getTableNodeFromSelection = (selection) => findParentNode((node) => node.type.spec['tableRole'] === 'table')(selection);
|
9297
|
+
const isColumnSelected = (columnIndex, selection) => {
|
9298
|
+
return isCellSelection(selection)
|
9299
|
+
? checkIfRectIsFullySelected({
|
9300
|
+
left: columnIndex,
|
9301
|
+
right: columnIndex + 1,
|
9302
|
+
top: 0,
|
9303
|
+
bottom: getTableMapFromSelection(selection).height
|
9304
|
+
}, selection)
|
9305
|
+
: false;
|
9306
|
+
};
|
9307
|
+
const isRowSelected = (rowIndex, selection) => {
|
9308
|
+
return isCellSelection(selection)
|
9309
|
+
? checkIfRectIsFullySelected({
|
9310
|
+
left: 0,
|
9311
|
+
right: getTableMapFromSelection(selection).width,
|
9312
|
+
top: rowIndex,
|
9313
|
+
bottom: rowIndex + 1
|
9314
|
+
}, selection)
|
9315
|
+
: false;
|
9316
|
+
};
|
9317
|
+
const isTableSelected = (selection) => {
|
9318
|
+
return isCellSelection(selection)
|
9319
|
+
? checkIfRectIsFullySelected({
|
9320
|
+
left: 0,
|
9321
|
+
right: getTableMapFromSelection(selection).width,
|
9322
|
+
top: 0,
|
9323
|
+
bottom: getTableMapFromSelection(selection).height
|
9324
|
+
}, selection)
|
9325
|
+
: false;
|
9326
|
+
};
|
9327
|
+
const getCellsInColumn = (columnIndex, selection) => {
|
9328
|
+
const table = getTableNodeFromSelection(selection);
|
9329
|
+
if (!table)
|
9330
|
+
return null;
|
9331
|
+
const map = TableMap.get(table.node);
|
9332
|
+
const columns = Array.isArray(columnIndex) ? columnIndex : [columnIndex];
|
9333
|
+
return columns.flatMap((col) => {
|
9334
|
+
if (col < 0 || col >= map.width)
|
9335
|
+
return [];
|
9336
|
+
const cells = map.cellsInRect({
|
9337
|
+
left: col,
|
9338
|
+
right: col + 1,
|
9339
|
+
top: 0,
|
9340
|
+
bottom: map.height
|
9341
|
+
});
|
9342
|
+
return cells.map((cellPos) => {
|
9343
|
+
const node = table.node.nodeAt(cellPos);
|
9344
|
+
const absPos = cellPos + table.start;
|
9345
|
+
return { pos: absPos, start: absPos + 1, node };
|
9346
|
+
});
|
9347
|
+
});
|
9348
|
+
};
|
9349
|
+
const getCellsInRow = (rowIndex, selection) => {
|
9350
|
+
const table = getTableNodeFromSelection(selection);
|
9351
|
+
if (!table)
|
9352
|
+
return null;
|
9353
|
+
const map = TableMap.get(table.node);
|
9354
|
+
const rows = Array.isArray(rowIndex) ? rowIndex : [rowIndex];
|
9355
|
+
return rows.flatMap((row) => {
|
9356
|
+
if (row < 0 || row >= map.height)
|
9357
|
+
return [];
|
9358
|
+
const cells = map.cellsInRect({
|
9359
|
+
left: 0,
|
9360
|
+
right: map.width,
|
9361
|
+
top: row,
|
9362
|
+
bottom: row + 1
|
9363
|
+
});
|
9364
|
+
return cells.map((cellPos) => {
|
9365
|
+
const node = table.node.nodeAt(cellPos);
|
9366
|
+
const absPos = cellPos + table.start;
|
9367
|
+
return { pos: absPos, start: absPos + 1, node };
|
9368
|
+
});
|
9369
|
+
});
|
9370
|
+
};
|
9371
|
+
const createSelectionForIndex = (type, index, tr) => {
|
9372
|
+
const table = getTableNodeFromSelection(tr.selection);
|
9373
|
+
if (!table)
|
9374
|
+
return tr;
|
9375
|
+
const map = TableMap.get(table.node);
|
9376
|
+
const isRow = type === 'row';
|
9377
|
+
const maxIndex = isRow ? map.height : map.width;
|
9378
|
+
if (index < 0 || index >= maxIndex)
|
9379
|
+
return tr;
|
9380
|
+
const rect = {
|
9381
|
+
left: isRow ? 0 : index,
|
9382
|
+
top: isRow ? index : 0,
|
9383
|
+
right: isRow ? map.width : index + 1,
|
9384
|
+
bottom: isRow ? index + 1 : map.height
|
9385
|
+
};
|
9386
|
+
const edgeCellsStart = map.cellsInRect(rect);
|
9387
|
+
const edgeCellsEnd = rect.bottom - rect.top === 1
|
9388
|
+
? edgeCellsStart
|
9389
|
+
: map.cellsInRect({
|
9390
|
+
left: isRow ? 0 : rect.right - 1,
|
9391
|
+
top: isRow ? rect.bottom - 1 : 0,
|
9392
|
+
right: rect.right,
|
9393
|
+
bottom: rect.bottom
|
9394
|
+
});
|
9395
|
+
if (edgeCellsStart.length && edgeCellsEnd.length) {
|
9396
|
+
const startCell = edgeCellsStart[0];
|
9397
|
+
const endCell = edgeCellsEnd[edgeCellsEnd.length - 1];
|
9398
|
+
if (startCell != null && endCell != null && table?.start != null) {
|
9399
|
+
const head = table.start + startCell;
|
9400
|
+
const anchor = table.start + endCell;
|
9401
|
+
return tr.setSelection(new CellSelection(tr.doc.resolve(anchor), tr.doc.resolve(head)));
|
9402
|
+
}
|
9403
|
+
}
|
9404
|
+
return tr;
|
9405
|
+
};
|
9406
|
+
const selectColumn = (index, tr) => createSelectionForIndex('column', index, tr);
|
9407
|
+
const selectRow = (index, tr) => createSelectionForIndex('row', index, tr);
|
9408
|
+
const isSelectorActive = ({ editor, view, state, from, selectorType }) => {
|
9409
|
+
if (!editor.isActive(Table.name) || isTableSelected(state.selection)) {
|
9410
|
+
return false;
|
9411
|
+
}
|
9412
|
+
let resolvedNode = null;
|
9413
|
+
try {
|
9414
|
+
resolvedNode = view.nodeDOM(from) || view.domAtPos(from).node;
|
9415
|
+
}
|
9416
|
+
catch {
|
9417
|
+
return false;
|
9418
|
+
}
|
9419
|
+
if (!resolvedNode || !(resolvedNode instanceof HTMLElement))
|
9420
|
+
return false;
|
9421
|
+
// Traverse up the DOM tree to find the table cell (TD or TH)
|
9422
|
+
const cellElement = resolvedNode.closest?.('td, th');
|
9423
|
+
if (!cellElement)
|
9424
|
+
return false;
|
9425
|
+
// Check if a selected anchor exists within this cell
|
9426
|
+
return !!cellElement.querySelector(`button.${selectorType}.selected`);
|
9427
|
+
};
|
9428
|
+
const isColumnSelectorActive = (params) => {
|
9429
|
+
return isSelectorActive({ ...params, selectorType: 'selector-column' });
|
9430
|
+
};
|
9431
|
+
const isRowSelectorActive = (params) => {
|
9432
|
+
return isSelectorActive({ ...params, selectorType: 'selector-row' });
|
9433
|
+
};
|
9434
|
+
|
9435
|
+
class TableBubbleMenuComponent {
|
9436
|
+
constructor() {
|
9437
|
+
this.NAME = 'RichTextEditorTableActions';
|
9438
|
+
}
|
9439
|
+
ngAfterViewInit() {
|
9440
|
+
this.editor.registerPlugin(BubbleMenuPlugin({
|
9441
|
+
pluginKey: 'bubbleMenuRow',
|
9442
|
+
editor: this.editor,
|
9443
|
+
element: this.bubbleMenuRowRef.nativeElement,
|
9444
|
+
tippyOptions: {
|
9445
|
+
appendTo: () => document.body,
|
9446
|
+
placement: 'left',
|
9447
|
+
offset: () => {
|
9448
|
+
// A hack to fix wrong offset by tippy for first row's bubble menu
|
9449
|
+
if (document.querySelector('.selector-row.selected.first')) {
|
9450
|
+
return [43, 19];
|
9451
|
+
}
|
9452
|
+
return [37, 8];
|
9453
|
+
},
|
9454
|
+
popperOptions: {
|
9455
|
+
modifiers: [{ name: 'flip', enabled: false }]
|
9456
|
+
}
|
9457
|
+
},
|
9458
|
+
shouldShow: ({ editor, view, state, from }) => {
|
9459
|
+
return isRowSelectorActive({ editor, view, state, from: from || 0 });
|
9460
|
+
}
|
9461
|
+
}));
|
9462
|
+
this.editor.registerPlugin(BubbleMenuPlugin({
|
9463
|
+
pluginKey: 'bubbleMenuColumn',
|
9464
|
+
editor: this.editor,
|
9465
|
+
element: this.bubbleMenuColumnRef.nativeElement,
|
9466
|
+
tippyOptions: {
|
9467
|
+
appendTo: () => document.body,
|
9468
|
+
offset: [0, 8],
|
9469
|
+
popperOptions: {
|
9470
|
+
modifiers: [{ name: 'flip', enabled: false }]
|
9471
|
+
}
|
9472
|
+
},
|
9473
|
+
shouldShow: ({ editor, view, state, from }) => {
|
9474
|
+
return isColumnSelectorActive({ editor, view, state, from: from || 0 });
|
9475
|
+
}
|
9476
|
+
}));
|
9477
|
+
this.editor.registerPlugin(BubbleMenuPlugin({
|
9478
|
+
pluginKey: 'bubbleMenuTable',
|
9479
|
+
editor: this.editor,
|
9480
|
+
element: this.bubbleMenuTableRef.nativeElement,
|
9481
|
+
tippyOptions: {
|
9482
|
+
appendTo: () => document.body,
|
9483
|
+
offset: [0, 8],
|
9484
|
+
popperOptions: {
|
9485
|
+
modifiers: [{ name: 'flip', enabled: false }]
|
9486
|
+
}
|
9487
|
+
},
|
9488
|
+
shouldShow: ({ editor, state }) => {
|
9489
|
+
return editor.isEditable && isTableSelected(state.selection);
|
9490
|
+
}
|
9491
|
+
}));
|
9492
|
+
}
|
9493
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TableBubbleMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
9494
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: TableBubbleMenuComponent, isStandalone: true, selector: "lx-table-bubble-menu", inputs: { editor: "editor" }, viewQueries: [{ propertyName: "bubbleMenuRowRef", first: true, predicate: ["bubbleMenuRow"], descendants: true }, { propertyName: "bubbleMenuColumnRef", first: true, predicate: ["bubbleMenuColumn"], descendants: true }, { propertyName: "bubbleMenuTableRef", first: true, predicate: ["bubbleMenuTable"], descendants: true }], ngImport: i0, template: "<div #bubbleMenuRow class=\"bubble-menu-row\">\n <button (click)=\"editor.chain().focus().addRowBefore().run()\">{{ NAME + '.addRowBefore' | translate }}</button>\n <button (click)=\"editor.chain().focus().addRowAfter().run()\">{{ NAME + '.addRowAfter' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteRow().run()\">{{ NAME + '.deleteRow' | translate }}</button>\n</div>\n\n<div #bubbleMenuColumn class=\"bubble-menu-column\">\n <button (click)=\"editor.chain().focus().addColumnBefore().run()\">{{ NAME + '.addColumnBefore' | translate }}</button>\n <button (click)=\"editor.chain().focus().addColumnAfter().run()\">{{ NAME + '.addColumnAfter' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteColumn().run()\">{{ NAME + '.deleteColumn' | translate }}</button>\n</div>\n\n<div #bubbleMenuTable class=\"bubble-menu-table\">\n <button (click)=\"editor.chain().focus().addRowAfter().run()\">{{ NAME + '.addRow' | translate }}</button>\n <button (click)=\"editor.chain().focus().addColumnAfter().run()\">{{ NAME + '.addColumn' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteTable().run()\">{{ NAME + '.deleteTable' | translate }}</button>\n</div>\n", styles: [".bubble-menu-column,.bubble-menu-row,.bubble-menu-table{display:flex;flex-direction:column;background:#fff;border:1px solid #e1e5eb;border-radius:3px;z-index:10;box-shadow:0 8px 12px 2px #00000026;min-width:160px}.bubble-menu-column button,.bubble-menu-row button,.bubble-menu-table button{background:none;border:none;padding:8px;text-align:left;width:100%;font-size:var(--lxFontSize);color:#2a303d;cursor:pointer;border-radius:3px;transition:background-color .2s}.bubble-menu-column button:hover,.bubble-menu-row button:hover,.bubble-menu-table button:hover{background-color:#e1e5eb}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
9495
|
+
}
|
9496
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TableBubbleMenuComponent, decorators: [{
|
9497
|
+
type: Component,
|
9498
|
+
args: [{ selector: 'lx-table-bubble-menu', imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #bubbleMenuRow class=\"bubble-menu-row\">\n <button (click)=\"editor.chain().focus().addRowBefore().run()\">{{ NAME + '.addRowBefore' | translate }}</button>\n <button (click)=\"editor.chain().focus().addRowAfter().run()\">{{ NAME + '.addRowAfter' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteRow().run()\">{{ NAME + '.deleteRow' | translate }}</button>\n</div>\n\n<div #bubbleMenuColumn class=\"bubble-menu-column\">\n <button (click)=\"editor.chain().focus().addColumnBefore().run()\">{{ NAME + '.addColumnBefore' | translate }}</button>\n <button (click)=\"editor.chain().focus().addColumnAfter().run()\">{{ NAME + '.addColumnAfter' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteColumn().run()\">{{ NAME + '.deleteColumn' | translate }}</button>\n</div>\n\n<div #bubbleMenuTable class=\"bubble-menu-table\">\n <button (click)=\"editor.chain().focus().addRowAfter().run()\">{{ NAME + '.addRow' | translate }}</button>\n <button (click)=\"editor.chain().focus().addColumnAfter().run()\">{{ NAME + '.addColumn' | translate }}</button>\n <button (click)=\"editor.chain().focus().deleteTable().run()\">{{ NAME + '.deleteTable' | translate }}</button>\n</div>\n", styles: [".bubble-menu-column,.bubble-menu-row,.bubble-menu-table{display:flex;flex-direction:column;background:#fff;border:1px solid #e1e5eb;border-radius:3px;z-index:10;box-shadow:0 8px 12px 2px #00000026;min-width:160px}.bubble-menu-column button,.bubble-menu-row button,.bubble-menu-table button{background:none;border:none;padding:8px;text-align:left;width:100%;font-size:var(--lxFontSize);color:#2a303d;cursor:pointer;border-radius:3px;transition:background-color .2s}.bubble-menu-column button:hover,.bubble-menu-row button:hover,.bubble-menu-table button:hover{background-color:#e1e5eb}\n"] }]
|
9499
|
+
}], propDecorators: { editor: [{
|
9500
|
+
type: Input,
|
9501
|
+
args: [{ required: true }]
|
9502
|
+
}], bubbleMenuRowRef: [{
|
9503
|
+
type: ViewChild,
|
9504
|
+
args: ['bubbleMenuRow']
|
9505
|
+
}], bubbleMenuColumnRef: [{
|
9506
|
+
type: ViewChild,
|
9507
|
+
args: ['bubbleMenuColumn']
|
9508
|
+
}], bubbleMenuTableRef: [{
|
9509
|
+
type: ViewChild,
|
9510
|
+
args: ['bubbleMenuTable']
|
9511
|
+
}] } });
|
9512
|
+
|
9513
|
+
const CustomTable = Table$1.configure({
|
9514
|
+
resizable: true,
|
9515
|
+
HTMLAttributes: {
|
9516
|
+
class: 'table-base-class'
|
9517
|
+
}
|
9518
|
+
});
|
9519
|
+
const CustomTableRow = TableRow.extend({
|
9520
|
+
allowGapCursor: false,
|
9521
|
+
content: '(tableCell | tableHeader)*'
|
9522
|
+
});
|
9523
|
+
const CustomTableCell = TableCell.extend({
|
9524
|
+
addProseMirrorPlugins() {
|
9525
|
+
return [
|
9526
|
+
new Plugin({
|
9527
|
+
props: {
|
9528
|
+
decorations: (state) => {
|
9529
|
+
if (!this.editor.isEditable) {
|
9530
|
+
return DecorationSet.empty;
|
9531
|
+
}
|
9532
|
+
const { doc, selection } = state;
|
9533
|
+
const columnCells = getCellsInColumn(0, selection);
|
9534
|
+
const decoList = [];
|
9535
|
+
if (columnCells?.length) {
|
9536
|
+
columnCells.forEach(({ pos }, rowIndex) => {
|
9537
|
+
const isSelected = isRowSelected(rowIndex, selection);
|
9538
|
+
const button = document.createElement('button');
|
9539
|
+
const classList = ['selector-row'];
|
9540
|
+
if (isSelected)
|
9541
|
+
classList.push('selected');
|
9542
|
+
if (rowIndex === 0)
|
9543
|
+
classList.push('first');
|
9544
|
+
if (rowIndex === columnCells.length - 1)
|
9545
|
+
classList.push('last');
|
9546
|
+
button.ariaLabel = `select row ${rowIndex + 1}`;
|
9547
|
+
button.className = classList.join(' ');
|
9548
|
+
button.addEventListener('mousedown', (e) => {
|
9549
|
+
e.preventDefault();
|
9550
|
+
e.stopImmediatePropagation();
|
9551
|
+
const tr = selectRow(rowIndex, this.editor.state.tr);
|
9552
|
+
this.editor.view.dispatch(tr);
|
9553
|
+
});
|
9554
|
+
decoList.push(Decoration.widget(pos + 1, () => button));
|
9555
|
+
});
|
9556
|
+
}
|
9557
|
+
return DecorationSet.create(doc, decoList);
|
9558
|
+
}
|
9559
|
+
}
|
9560
|
+
})
|
9561
|
+
];
|
9562
|
+
}
|
9563
|
+
});
|
9564
|
+
const CustomTableHeader = TiptapTableHeader.extend({
|
9565
|
+
addAttributes() {
|
9566
|
+
return {
|
9567
|
+
colspan: {
|
9568
|
+
default: 1
|
9569
|
+
},
|
9570
|
+
rowspan: {
|
9571
|
+
default: 1
|
9572
|
+
},
|
9573
|
+
colwidth: {
|
9574
|
+
default: null,
|
9575
|
+
parseHTML: (element) => {
|
9576
|
+
const raw = element.getAttribute('colwidth');
|
9577
|
+
return raw ? raw.split(',').map(Number) : null;
|
9578
|
+
}
|
9579
|
+
},
|
9580
|
+
style: {
|
9581
|
+
default: null
|
9582
|
+
}
|
9583
|
+
};
|
9584
|
+
},
|
9585
|
+
addProseMirrorPlugins() {
|
9586
|
+
return [
|
9587
|
+
new Plugin({
|
9588
|
+
key: new PluginKey('custom-table-header'),
|
9589
|
+
props: {
|
9590
|
+
decorations: (state) => {
|
9591
|
+
if (!this.editor.isEditable) {
|
9592
|
+
return DecorationSet.empty;
|
9593
|
+
}
|
9594
|
+
const { doc, selection } = state;
|
9595
|
+
const headerCells = getCellsInRow(0, selection);
|
9596
|
+
const allDecorations = [];
|
9597
|
+
if (headerCells?.length) {
|
9598
|
+
headerCells.forEach(({ pos }, columnIndex) => {
|
9599
|
+
const isSelected = isColumnSelected(columnIndex, selection);
|
9600
|
+
const button = document.createElement('button');
|
9601
|
+
const classes = ['selector-column'];
|
9602
|
+
if (isSelected)
|
9603
|
+
classes.push('selected');
|
9604
|
+
if (columnIndex === 0)
|
9605
|
+
classes.push('first');
|
9606
|
+
if (columnIndex === headerCells.length - 1)
|
9607
|
+
classes.push('last');
|
9608
|
+
button.ariaLabel = `select column ${columnIndex + 1}`;
|
9609
|
+
button.className = classes.join(' ');
|
9610
|
+
button.addEventListener('mousedown', (e) => {
|
9611
|
+
e.preventDefault();
|
9612
|
+
e.stopImmediatePropagation();
|
9613
|
+
const tr = selectColumn(columnIndex, this.editor.state.tr);
|
9614
|
+
this.editor.view.dispatch(tr);
|
9615
|
+
});
|
9616
|
+
allDecorations.push(Decoration.widget(pos + 1, () => button));
|
9617
|
+
});
|
9618
|
+
}
|
9619
|
+
return DecorationSet.create(doc, allDecorations);
|
9620
|
+
}
|
9621
|
+
}
|
9622
|
+
})
|
9623
|
+
];
|
9624
|
+
}
|
9625
|
+
});
|
9626
|
+
|
9627
|
+
const trackingPluginKey = new PluginKey('tracking');
|
9628
|
+
function tracking() {
|
9629
|
+
return new Plugin({
|
9630
|
+
key: trackingPluginKey
|
9631
|
+
});
|
9632
|
+
}
|
9633
|
+
|
9634
|
+
const Tracking = Extension.create({
|
9635
|
+
name: 'tracking',
|
9636
|
+
addProseMirrorPlugins() {
|
9637
|
+
return [tracking()];
|
9638
|
+
},
|
9639
|
+
addCommands() {
|
9640
|
+
return {
|
9641
|
+
track: (trackingEvent) => ({ tr }) => {
|
9642
|
+
tr.setMeta(trackingPluginKey, trackingEvent);
|
9643
|
+
return true;
|
9644
|
+
}
|
9645
|
+
};
|
9646
|
+
}
|
9647
|
+
});
|
9648
|
+
|
9649
|
+
const truncatePluginKey = new PluginKey('truncate');
|
9650
|
+
function truncate(editor) {
|
9651
|
+
return new Plugin({
|
9652
|
+
key: truncatePluginKey,
|
9653
|
+
state: {
|
9654
|
+
init() {
|
9655
|
+
return { collapsed: true, activated: false, showToggleButton: true, maxLines: 1 };
|
9656
|
+
},
|
9657
|
+
apply: (tr, state) => {
|
9658
|
+
const meta = tr.getMeta(truncatePluginKey);
|
9659
|
+
return meta ? { ...state, ...meta } : state;
|
9660
|
+
}
|
9661
|
+
},
|
9662
|
+
view(view) {
|
9663
|
+
const resizeObserver = new ResizeObserver(([entry]) => {
|
9664
|
+
const state = truncatePluginKey.getState(view.state);
|
9665
|
+
if (state.activated || !entry || !entry.target) {
|
9666
|
+
return;
|
9667
|
+
}
|
9668
|
+
if (entry.target.clientHeight < entry.target.scrollHeight) {
|
9669
|
+
view.dispatch(view.state.tr.setMeta(truncatePluginKey, { ...state, activated: true }));
|
9670
|
+
}
|
9671
|
+
});
|
9672
|
+
resizeObserver.observe(view.dom);
|
9673
|
+
return {
|
9674
|
+
destroy() {
|
9675
|
+
resizeObserver.disconnect();
|
9676
|
+
}
|
9677
|
+
};
|
9678
|
+
},
|
9679
|
+
props: {
|
9680
|
+
attributes(state) {
|
9681
|
+
if (editor.storage['mode'] !== 'view') {
|
9682
|
+
return { style: '' };
|
9683
|
+
}
|
9684
|
+
const collapsed = truncatePluginKey.getState(state).collapsed;
|
9685
|
+
const clampStyles = `
|
9686
|
+
display: -webkit-box;
|
9687
|
+
-webkit-box-orient: vertical;
|
9688
|
+
-webkit-line-clamp: ${truncatePluginKey.getState(state).maxLines};
|
9689
|
+
overflow: hidden;
|
9690
|
+
`;
|
9691
|
+
return {
|
9692
|
+
style: collapsed ? clampStyles : ''
|
9693
|
+
};
|
9694
|
+
}
|
9695
|
+
}
|
9696
|
+
});
|
9697
|
+
}
|
9698
|
+
|
9699
|
+
class TruncateButtonComponent {
|
9700
|
+
constructor() {
|
9701
|
+
this.NAME = 'TruncateButtonComponent';
|
9702
|
+
this.cdr = inject(ChangeDetectorRef);
|
9703
|
+
this.update = (event) => {
|
9704
|
+
if (event.transaction.getMeta(truncatePluginKey)) {
|
9705
|
+
this.cdr.markForCheck();
|
9706
|
+
}
|
9707
|
+
};
|
9708
|
+
}
|
9709
|
+
get collapsed() {
|
9710
|
+
return truncatePluginKey.getState(this.editor.state).collapsed;
|
9711
|
+
}
|
9712
|
+
get key() {
|
9713
|
+
return this.collapsed ? '.showMore' : '.showLess';
|
9714
|
+
}
|
9715
|
+
get activated() {
|
9716
|
+
return truncatePluginKey.getState(this.editor.state).activated;
|
9717
|
+
}
|
9718
|
+
get showToggleButton() {
|
9719
|
+
return truncatePluginKey.getState(this.editor.state).showToggleButton;
|
9720
|
+
}
|
9721
|
+
ngOnInit() {
|
9722
|
+
this.editor.on('transaction', this.update);
|
9723
|
+
}
|
9724
|
+
ngOnDestroy() {
|
9725
|
+
this.editor.off('transaction', this.update);
|
9726
|
+
}
|
9727
|
+
onClick(event) {
|
9728
|
+
event.stopPropagation();
|
9729
|
+
const state = truncatePluginKey.getState(this.editor.view.state);
|
9730
|
+
this.editor.view.dispatch(this.editor.view.state.tr.setMeta(truncatePluginKey, {
|
9731
|
+
...state,
|
9732
|
+
collapsed: !state.collapsed
|
9733
|
+
}));
|
9734
|
+
}
|
9735
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TruncateButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
9736
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: TruncateButtonComponent, isStandalone: true, selector: "lx-truncate-button", inputs: { editor: "editor" }, ngImport: i0, template: `
|
9737
|
+
@if (showToggleButton && activated) {
|
9738
|
+
<button lx-button color="primary" mode="link" (click)="onClick($event)">
|
9739
|
+
{{ NAME + key | translate }}
|
9740
|
+
</button>
|
9741
|
+
}
|
9742
|
+
`, isInline: true, dependencies: [{ kind: "component", type: ButtonComponent, selector: "button[lx-button]", inputs: ["size", "color", "mode", "pressed", "selected", "square", "circle", "disabled", "icon", "endIcon", "showSpinner"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
9743
|
+
}
|
9744
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TruncateButtonComponent, decorators: [{
|
9745
|
+
type: Component,
|
9746
|
+
args: [{
|
9747
|
+
selector: 'lx-truncate-button',
|
9748
|
+
template: `
|
9749
|
+
@if (showToggleButton && activated) {
|
9750
|
+
<button lx-button color="primary" mode="link" (click)="onClick($event)">
|
9751
|
+
{{ NAME + key | translate }}
|
9752
|
+
</button>
|
9753
|
+
}
|
9754
|
+
`,
|
9755
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
9756
|
+
imports: [ButtonComponent, TranslatePipe]
|
9757
|
+
}]
|
9758
|
+
}], propDecorators: { editor: [{
|
9759
|
+
type: Input,
|
9760
|
+
args: [{ required: true }]
|
9761
|
+
}] } });
|
9762
|
+
|
9763
|
+
const Truncate = Extension.create({
|
9764
|
+
name: 'truncate',
|
9765
|
+
addOptions() {
|
9766
|
+
return {
|
9767
|
+
maxLines: 1,
|
9768
|
+
showToggleButton: true
|
9769
|
+
};
|
9770
|
+
},
|
9771
|
+
addProseMirrorPlugins() {
|
9772
|
+
const { editor } = this;
|
9773
|
+
return [truncate(editor)];
|
9774
|
+
}
|
9775
|
+
});
|
9776
|
+
|
9777
|
+
class ExtensionsBuilder {
|
9778
|
+
constructor() {
|
9779
|
+
this.injector = inject(Injector);
|
9780
|
+
this.pluginRegistry = {
|
9781
|
+
Bold: [Bold],
|
9782
|
+
Truncate: [Truncate],
|
9783
|
+
Heading: [Heading.configure({ levels: [1, 2, 3, 4] })],
|
9784
|
+
Italic: [Italic],
|
9785
|
+
Underline: [Underline],
|
9786
|
+
Strike: [Strike],
|
9787
|
+
List: [BulletList, OrderedList],
|
9788
|
+
TextAlign: [TextAlign.configure({ types: ['heading', 'paragraph'] })],
|
9789
|
+
Code: [Code, CodeBlock],
|
9790
|
+
HighlightTerm: [HighlightTerm],
|
9791
|
+
Table: [CustomTable, CustomTableCell, CustomTableHeader, CustomTableRow],
|
9792
|
+
Link: [CustomLink]
|
9793
|
+
};
|
9794
|
+
}
|
9795
|
+
build(features, output) {
|
9796
|
+
const extensions = [Document, Dropcursor, Gapcursor, History, HardBreak, ListItem, Paragraph, Text, Tracking];
|
9797
|
+
if (output === 'markdown') {
|
9798
|
+
extensions.push(...[
|
9799
|
+
Markdown.configure({
|
9800
|
+
transformPastedText: true, // Allow to paste markdown text in the editor
|
9801
|
+
breaks: true
|
9802
|
+
})
|
9803
|
+
]);
|
9804
|
+
}
|
9805
|
+
if (output === 'json') {
|
9806
|
+
extensions.push(...[HorizontalRule]);
|
9807
|
+
}
|
9808
|
+
for (const feature of features) {
|
9809
|
+
extensions.push(...(this.pluginRegistry[feature] ?? []));
|
9810
|
+
}
|
9811
|
+
return extensions;
|
9812
|
+
}
|
9813
|
+
buildCustomExtensions(extensionFactories) {
|
9814
|
+
const extensions = [];
|
9815
|
+
for (const extension of extensionFactories) {
|
9816
|
+
const node = extension(this.injector);
|
9817
|
+
if (node) {
|
9818
|
+
extensions.push(node);
|
9819
|
+
}
|
9820
|
+
}
|
9821
|
+
return extensions;
|
9822
|
+
}
|
9823
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ExtensionsBuilder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
9824
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ExtensionsBuilder }); }
|
9825
|
+
}
|
9826
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ExtensionsBuilder, decorators: [{
|
9827
|
+
type: Injectable
|
9828
|
+
}] });
|
9829
|
+
|
9830
|
+
const EDITOR_FEATURES = {
|
9831
|
+
markdown: ['Bold', 'Italic', 'List', 'Truncate', 'Link', 'Strike', 'Underline', 'HighlightTerm'],
|
9832
|
+
json: ['Heading', 'Bold', 'Italic', 'Link', 'Underline', 'Strike', 'List', 'TextAlign', 'Code', 'Table']
|
9833
|
+
};
|
9834
|
+
|
9835
|
+
class TipTapEditorDirective {
|
9836
|
+
constructor() {
|
9837
|
+
this.NAME = 'TipTapEditorDirective';
|
9838
|
+
this.outputFormat = input.required();
|
9839
|
+
this.ariaLabelledBy = input(null);
|
9840
|
+
this.additionalFeatures = input([]);
|
9841
|
+
this.customExtensions = input([]);
|
9842
|
+
this.mode = input.required();
|
9843
|
+
this.features = computed(() => EDITOR_FEATURES[this.outputFormat()]);
|
9844
|
+
this.attributes = computed(() => {
|
9845
|
+
const attributes = {
|
9846
|
+
role: 'textbox' // make conenteditable div behave as textarea, needed for a11y
|
9847
|
+
};
|
9848
|
+
if (this.ariaLabelledBy()) {
|
9849
|
+
attributes['aria-labelledby'] = this.ariaLabelledBy();
|
9850
|
+
}
|
9851
|
+
return attributes;
|
9852
|
+
});
|
9853
|
+
this.editor = computed(() => {
|
9854
|
+
const extensions = [
|
9855
|
+
...this.extensionsBuilder.build([...this.features(), ...this.additionalFeatures()], this.outputFormat()),
|
9856
|
+
...this.extensionsBuilder.buildCustomExtensions(this.customExtensions())
|
9857
|
+
];
|
9858
|
+
return new Editor({
|
9859
|
+
extensions,
|
9860
|
+
editorProps: {
|
9861
|
+
attributes: {
|
9862
|
+
...this.attributes()
|
9863
|
+
}
|
9864
|
+
}
|
9865
|
+
});
|
9866
|
+
});
|
9867
|
+
this.extensionsBuilder = inject(ExtensionsBuilder);
|
9868
|
+
afterRenderEffect(() => {
|
9869
|
+
this.editor().storage['mode'] = this.mode();
|
9870
|
+
});
|
9871
|
+
}
|
9872
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TipTapEditorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
9873
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.6", type: TipTapEditorDirective, isStandalone: true, selector: "lx-rich-text-editor[lxEditor]", inputs: { outputFormat: { classPropertyName: "outputFormat", publicName: "outputFormat", isSignal: true, isRequired: true, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, additionalFeatures: { classPropertyName: "additionalFeatures", publicName: "additionalFeatures", isSignal: true, isRequired: false, transformFunction: null }, customExtensions: { classPropertyName: "customExtensions", publicName: "customExtensions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
|
9874
|
+
}
|
9875
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TipTapEditorDirective, decorators: [{
|
9876
|
+
type: Directive,
|
9877
|
+
args: [{
|
9878
|
+
selector: 'lx-rich-text-editor[lxEditor]'
|
9879
|
+
}]
|
9880
|
+
}], ctorParameters: () => [] });
|
9881
|
+
|
9882
|
+
class TrackingDirective {
|
9883
|
+
constructor() {
|
9884
|
+
this.trackEvent = output();
|
9885
|
+
this.editor = inject(TipTapEditorDirective).editor;
|
9886
|
+
afterRenderEffect((onCleanup) => {
|
9887
|
+
this.editor().on('transaction', ({ transaction }) => {
|
9888
|
+
const meta = transaction.getMeta(trackingPluginKey);
|
9889
|
+
if (meta) {
|
9890
|
+
this.trackEvent.emit(meta);
|
9891
|
+
}
|
9892
|
+
onCleanup(() => {
|
9893
|
+
this.editor().off('transaction');
|
9894
|
+
});
|
9895
|
+
});
|
9896
|
+
});
|
9897
|
+
}
|
9898
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TrackingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
9899
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: TrackingDirective, isStandalone: true, selector: "lx-rich-text-editor[lxTracking]", outputs: { trackEvent: "trackEvent" }, ngImport: i0 }); }
|
9900
|
+
}
|
9901
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TrackingDirective, decorators: [{
|
9902
|
+
type: Directive,
|
9903
|
+
args: [{
|
9904
|
+
selector: 'lx-rich-text-editor[lxTracking]'
|
9905
|
+
}]
|
9906
|
+
}], ctorParameters: () => [] });
|
9907
|
+
|
9908
|
+
class EditorDirective {
|
9909
|
+
constructor(elRef, renderer, changeDetectorRef) {
|
9910
|
+
this.elRef = elRef;
|
9911
|
+
this.renderer = renderer;
|
9912
|
+
this.changeDetectorRef = changeDetectorRef;
|
9913
|
+
this.blur = new EventEmitter();
|
9914
|
+
this.onChange = () => {
|
9915
|
+
/** */
|
9916
|
+
};
|
9917
|
+
this.onTouched = () => {
|
9918
|
+
/** */
|
9919
|
+
};
|
9920
|
+
this.handleChange = ({ editor, transaction }) => {
|
9921
|
+
if (!transaction.docChanged) {
|
9922
|
+
return;
|
9923
|
+
}
|
9924
|
+
// Needed for ChangeDetectionStrategy.OnPush to get notified about changes
|
9925
|
+
this.changeDetectorRef.markForCheck();
|
9926
|
+
if (this.outputFormat === 'markdown') {
|
9927
|
+
this.onChange(editor.storage['markdown'].getMarkdown());
|
9928
|
+
return;
|
9929
|
+
}
|
9930
|
+
this.onChange(editor.getJSON());
|
9931
|
+
};
|
9932
|
+
}
|
9933
|
+
// Writes a new value to the element.
|
9934
|
+
// This methods is called when programmatic changes from model to view are requested.
|
9935
|
+
writeValue(value) {
|
9936
|
+
this.editor.chain().setContent(value, false).run();
|
9937
|
+
}
|
9938
|
+
// Registers a callback function that is called when the control's value changes in the UI.
|
9939
|
+
registerOnChange(fn) {
|
9940
|
+
this.onChange = fn;
|
9941
|
+
}
|
9942
|
+
// Registers a callback function that is called by the forms API on initialization to update the form model on blur.
|
9943
|
+
registerOnTouched(fn) {
|
9944
|
+
this.onTouched = fn;
|
9945
|
+
}
|
9946
|
+
// Called by the forms api to enable or disable the element
|
9947
|
+
setDisabledState(isDisabled) {
|
9948
|
+
this.editor.setEditable(!isDisabled);
|
9949
|
+
this.renderer.setProperty(this.elRef.nativeElement, 'disabled', isDisabled);
|
9950
|
+
}
|
9951
|
+
ngOnInit() {
|
9952
|
+
// take the inner contents and clear the block
|
9953
|
+
const { innerHTML } = this.elRef.nativeElement;
|
9954
|
+
this.elRef.nativeElement.innerHTML = '';
|
9955
|
+
// insert the editor in the dom
|
9956
|
+
this.elRef.nativeElement.append(...Array.from(this.editor.options.element.childNodes));
|
9957
|
+
// update the options for the editor
|
9958
|
+
this.editor.setOptions({ element: this.elRef.nativeElement });
|
9959
|
+
// update content to the editor
|
9960
|
+
if (innerHTML) {
|
9961
|
+
this.editor.chain().setContent(innerHTML, false).run();
|
9962
|
+
}
|
9963
|
+
// register blur handler to update `touched` property
|
9964
|
+
this.editor.on('blur', () => {
|
9965
|
+
this.blur.emit();
|
9966
|
+
this.onTouched();
|
9967
|
+
});
|
9968
|
+
// register update handler to listen to changes on update
|
9969
|
+
this.editor.on('update', this.handleChange);
|
9970
|
+
// Needed for ChangeDetectionStrategy.OnPush to get notified
|
9971
|
+
this.editor.on('selectionUpdate', () => this.changeDetectorRef.markForCheck());
|
9972
|
+
}
|
9973
|
+
ngAfterViewInit() {
|
9974
|
+
this.changeDetectorRef.detectChanges();
|
9975
|
+
}
|
9976
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EditorDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
9977
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: EditorDirective, isStandalone: true, selector: "tiptap-editor[editor]", inputs: { editor: "editor", outputFormat: "outputFormat" }, outputs: { blur: "blur" }, providers: [
|
9978
|
+
{
|
9979
|
+
provide: NG_VALUE_ACCESSOR,
|
9980
|
+
useExisting: forwardRef(() => EditorDirective),
|
9981
|
+
multi: true
|
9982
|
+
}
|
9983
|
+
], ngImport: i0 }); }
|
9984
|
+
}
|
9985
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: EditorDirective, decorators: [{
|
9986
|
+
type: Directive,
|
9987
|
+
args: [{
|
9988
|
+
// eslint-disable-next-line @angular-eslint/directive-selector
|
9989
|
+
selector: 'tiptap-editor[editor]',
|
9990
|
+
providers: [
|
9991
|
+
{
|
9992
|
+
provide: NG_VALUE_ACCESSOR,
|
9993
|
+
useExisting: forwardRef(() => EditorDirective),
|
9994
|
+
multi: true
|
9995
|
+
}
|
9996
|
+
]
|
9997
|
+
}]
|
9998
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }], propDecorators: { editor: [{
|
9999
|
+
type: Input,
|
10000
|
+
args: [{ required: true }]
|
10001
|
+
}], outputFormat: [{
|
10002
|
+
type: Input,
|
10003
|
+
args: [{ required: true }]
|
10004
|
+
}], blur: [{
|
10005
|
+
type: Output
|
10006
|
+
}] } });
|
10007
|
+
|
10008
|
+
class ExtensionEnabledPipe {
|
10009
|
+
transform(name, editor) {
|
10010
|
+
return !!editor.extensionManager.extensions.find((extension) => extension.name === name);
|
10011
|
+
}
|
10012
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ExtensionEnabledPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
10013
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.6", ngImport: i0, type: ExtensionEnabledPipe, isStandalone: true, name: "lxExtensionEnabled" }); }
|
10014
|
+
}
|
10015
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ExtensionEnabledPipe, decorators: [{
|
10016
|
+
type: Pipe,
|
10017
|
+
args: [{
|
10018
|
+
name: 'lxExtensionEnabled'
|
10019
|
+
}]
|
10020
|
+
}] });
|
10021
|
+
|
10022
|
+
class ModalFooterComponent {
|
10023
|
+
constructor() {
|
10024
|
+
this.border = false;
|
10025
|
+
}
|
10026
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10027
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: ModalFooterComponent, isStandalone: true, selector: "lx-modal-footer", inputs: { border: "border" }, ngImport: i0, template: "<div class=\"footerContainer\" [class.border]=\"border\">\n <ng-content />\n</div>\n", styles: [":host{display:block;text-align:right;--modal-footer-padding-left: 90px;--modal-footer-padding-right: 0;--modal-footer-display: block}:host-context(.fullscreen) .footerContainer{padding-left:var(--modal-footer-padding-left);padding-right:var(--modal-footer-padding-right);height:70px}:host-context(.dialog) .footerContainer,:host-context(.dialog-large) .footerContainer{padding:16px;height:64px;border-radius:0 0 6px 6px;display:var(--modal-footer-display)}.footerContainer.border{border-top:1px solid #cfd5df}\n"] }); }
|
10028
|
+
}
|
10029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalFooterComponent, decorators: [{
|
10030
|
+
type: Component,
|
10031
|
+
args: [{ selector: 'lx-modal-footer', template: "<div class=\"footerContainer\" [class.border]=\"border\">\n <ng-content />\n</div>\n", styles: [":host{display:block;text-align:right;--modal-footer-padding-left: 90px;--modal-footer-padding-right: 0;--modal-footer-display: block}:host-context(.fullscreen) .footerContainer{padding-left:var(--modal-footer-padding-left);padding-right:var(--modal-footer-padding-right);height:70px}:host-context(.dialog) .footerContainer,:host-context(.dialog-large) .footerContainer{padding:16px;height:64px;border-radius:0 0 6px 6px;display:var(--modal-footer-display)}.footerContainer.border{border-top:1px solid #cfd5df}\n"] }]
|
10032
|
+
}], propDecorators: { border: [{
|
10033
|
+
type: Input
|
10034
|
+
}] } });
|
10035
|
+
|
10036
|
+
class ModalHeaderComponent {
|
10037
|
+
constructor() {
|
10038
|
+
this.title = '';
|
10039
|
+
this.bottomBorder = true;
|
10040
|
+
}
|
10041
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10042
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: ModalHeaderComponent, isStandalone: true, selector: "lx-modal-header", inputs: { title: "title", bottomBorder: "bottomBorder" }, ngImport: i0, template: "<div class=\"headerContainer\" [class.bottomBorder]=\"bottomBorder\">\n <ng-content />\n @if (title.length > 0) {\n <h1 class=\"lx-heading-2\">{{ title }}</h1>\n }\n</div>\n", styles: [":host{display:block}:host.lxModalHeaderOneLine h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:var(--lx-modal-header-max-width)}:host-context(.fullscreen) .headerContainer{padding:24px 90px;border-bottom:none}:host-context(.fullscreen) .headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}:host-context(.dialog) .headerContainer,:host-context(.dialog-large) .headerContainer{padding:24px 16px;border-radius:6px 6px 0 0}:host-context(.dialog) .headerContainer h1,:host-context(.dialog-large) .headerContainer h1{word-break:break-word}h1{margin:0 auto;padding:0;color:#2a303d;text-align:center}.headerContainer{display:flex}.headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}\n"] }); }
|
10043
|
+
}
|
10044
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalHeaderComponent, decorators: [{
|
10045
|
+
type: Component,
|
10046
|
+
args: [{ selector: 'lx-modal-header', template: "<div class=\"headerContainer\" [class.bottomBorder]=\"bottomBorder\">\n <ng-content />\n @if (title.length > 0) {\n <h1 class=\"lx-heading-2\">{{ title }}</h1>\n }\n</div>\n", styles: [":host{display:block}:host.lxModalHeaderOneLine h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:var(--lx-modal-header-max-width)}:host-context(.fullscreen) .headerContainer{padding:24px 90px;border-bottom:none}:host-context(.fullscreen) .headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}:host-context(.dialog) .headerContainer,:host-context(.dialog-large) .headerContainer{padding:24px 16px;border-radius:6px 6px 0 0}:host-context(.dialog) .headerContainer h1,:host-context(.dialog-large) .headerContainer h1{word-break:break-word}h1{margin:0 auto;padding:0;color:#2a303d;text-align:center}.headerContainer{display:flex}.headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}\n"] }]
|
10047
|
+
}], propDecorators: { title: [{
|
10048
|
+
type: Input
|
10049
|
+
}], bottomBorder: [{
|
10050
|
+
type: Input
|
10051
|
+
}] } });
|
10052
|
+
|
10053
|
+
class ModalContentDirective {
|
10054
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
10055
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: ModalContentDirective, isStandalone: true, selector: "[lxModalContent]", ngImport: i0 }); }
|
10056
|
+
}
|
10057
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalContentDirective, decorators: [{
|
10058
|
+
type: Directive,
|
10059
|
+
args: [{
|
10060
|
+
selector: '[lxModalContent]'
|
10061
|
+
}]
|
10062
|
+
}] });
|
10063
|
+
|
10064
|
+
const MODAL_CLOSE = new InjectionToken('MODAL_CLOSE');
|
10065
|
+
/**
|
10066
|
+
* An enum to track how the modal was closed
|
10067
|
+
* Escape - Esc key press
|
10068
|
+
* CloseButton - top right close button (x)
|
10069
|
+
* OutsideClick - click outside the modal
|
10070
|
+
* Other - modal close with the trigger through closeModal$ subject
|
10071
|
+
*/
|
10072
|
+
var ModalCloseClickLocation;
|
10073
|
+
(function (ModalCloseClickLocation) {
|
10074
|
+
ModalCloseClickLocation["Escape"] = "escape";
|
10075
|
+
ModalCloseClickLocation["CloseButton"] = "closeButton";
|
10076
|
+
ModalCloseClickLocation["OutsideClick"] = "outsideClick";
|
10077
|
+
ModalCloseClickLocation["CancelButton"] = "cancelButton";
|
10078
|
+
ModalCloseClickLocation["Other"] = "other";
|
10079
|
+
})(ModalCloseClickLocation || (ModalCloseClickLocation = {}));
|
10080
|
+
|
10081
|
+
/**
|
10082
|
+
* This documentation provides details on the usage and configuration of the Modal.
|
10083
|
+
*
|
10084
|
+
* ## Usage
|
10085
|
+
*
|
10086
|
+
* 1. Import `LxModalModule` and `LxCoreUiModule` modules from `@leanix/components` in your module where you want to use the component.
|
10087
|
+
*
|
10088
|
+
* ```ts
|
10089
|
+
* import { LxModalModule, LxCoreUiModule } from '@leanix/components';
|
10090
|
+
* ```
|
10091
|
+
*
|
10092
|
+
* 2. Use the **lx-modal** component in your template with the parameters described below.
|
10093
|
+
*
|
10094
|
+
* - **`open`**: Whether the modal is open or closed.
|
10095
|
+
* - **`size`**: 'dialog' | 'dialog-large'.
|
10096
|
+
* - **`verticalScroll`**: Whether the modal is scrollable or not.
|
10097
|
+
* - **`showHeader`**: Whether the modal has a header or not.
|
10098
|
+
* - **`showFooter`**: Whether the modal has a footer or not.
|
10099
|
+
* - **`showCloseButton`**: Whether to show the close button.
|
10100
|
+
* - **`showBackButton`**: Whether to show the back button.
|
10101
|
+
*
|
10102
|
+
* 3. Use optional **lx-modal-header** component in your template with the parameters described below.
|
10103
|
+
*
|
10104
|
+
* - **`title`**: Title of the modal.
|
10105
|
+
* - **`subtitle`**: Subtitle of the modal.
|
10106
|
+
* - **`bottomBorder`**: Whether to show a bottom border.
|
10107
|
+
*
|
10108
|
+
* 4. Use optional **lx-modal-footer** component in your template with the parameters described below.
|
10109
|
+
*
|
10110
|
+
* - **`border`**: Whether to show the footer at the bottom of the modal.
|
10111
|
+
*
|
10112
|
+
* **GLOBAL PROVIDERS** required for this component:
|
10113
|
+
* - `provideAnimations()`
|
10114
|
+
*
|
10115
|
+
* **ATTENTION - SCROLLABLE DIALOG**:
|
10116
|
+
* The <lx-modal> component when used as "dialog" is not designed to work with a
|
10117
|
+
* scrollable body (via `overflow: auto | scroll`) in combination with dropdowns.
|
10118
|
+
* The overflow on the body will also clip the dropdowns, which is expected.
|
10119
|
+
*
|
10120
|
+
* Reasoning:
|
10121
|
+
* The contents within the dialog should be just a few elements which fit and
|
10122
|
+
* justify the usage of a dialog. If the content is larger than the dialog, and thus
|
10123
|
+
* requires scrolling, we should discuss whether to put it into a dialog at all
|
10124
|
+
* and rather think about putting the content on a separate route or
|
10125
|
+
* using the fullscreen version of the modal.
|
10126
|
+
*/
|
10127
|
+
class ModalComponent {
|
10128
|
+
get content() {
|
10129
|
+
return this.explicitContent || this.implicitContent;
|
10130
|
+
}
|
10131
|
+
/** @internal */
|
10132
|
+
onEscape() {
|
10133
|
+
if (this.open && this.showCloseButton) {
|
10134
|
+
this.closeModal(ModalCloseClickLocation.Escape);
|
10135
|
+
}
|
10136
|
+
}
|
10137
|
+
constructor(overlay, renderer, closeModal$, focusTrap) {
|
10138
|
+
this.overlay = overlay;
|
9031
10139
|
this.renderer = renderer;
|
9032
|
-
this.
|
9033
|
-
this.
|
9034
|
-
|
9035
|
-
this.
|
10140
|
+
this.closeModal$ = closeModal$;
|
10141
|
+
this.focusTrap = focusTrap;
|
10142
|
+
/** @internal */
|
10143
|
+
this.NAME = 'ModalComponent';
|
10144
|
+
/** Whether the modal is open or closed. */
|
10145
|
+
this.open = false;
|
10146
|
+
/** Whether to show the close button. */
|
10147
|
+
this.showCloseButton = true;
|
10148
|
+
/** Whether to show the back button. */
|
10149
|
+
this.showBackButton = false;
|
10150
|
+
/*
|
10151
|
+
* If true, then the content area scrolls vertically instead of expanding its height.
|
10152
|
+
* This can be a problem if the content has dropdowns or date inputs, but can be good if the content has a huge amount of text.
|
10153
|
+
*/
|
10154
|
+
this.verticalScroll = false;
|
10155
|
+
/** The size of the modal. */
|
10156
|
+
this.size = 'fullscreen';
|
10157
|
+
/**
|
10158
|
+
* Minimum width of the modal.
|
10159
|
+
*
|
10160
|
+
* _NB: Some modal implementations rely on this minWidth being 600px_
|
10161
|
+
*/
|
10162
|
+
this.minWidth = '600px';
|
10163
|
+
/** Whether the modal is a focus trap. */
|
10164
|
+
this.isFocusTrap = false;
|
10165
|
+
/** Event emitted when the modal is closed. */
|
10166
|
+
this.close = new EventEmitter();
|
10167
|
+
/** Event emitted when the back button is clicked. */
|
10168
|
+
this.back = new EventEmitter();
|
10169
|
+
/** @internal */
|
10170
|
+
this.closeLocation = ModalCloseClickLocation;
|
10171
|
+
/** @internal */
|
10172
|
+
this.destroyed$ = new Subject();
|
10173
|
+
}
|
10174
|
+
ngOnInit() {
|
10175
|
+
this.closeModal$
|
10176
|
+
?.pipe(takeUntil(this.destroyed$))
|
10177
|
+
.subscribe((closeLocation) => this.closeModal(closeLocation));
|
10178
|
+
if (this.size === 'fullscreen') {
|
10179
|
+
this.overlayRef = this.overlay.create({
|
10180
|
+
panelClass: this.size,
|
10181
|
+
width: '100%',
|
10182
|
+
height: '100vh'
|
10183
|
+
});
|
10184
|
+
}
|
10185
|
+
else if (this.size === 'dialog-large') {
|
10186
|
+
const positionStrategy = this.overlay.position().global().top('4vh').centerHorizontally();
|
10187
|
+
this.overlayRef = this.overlay.create({
|
10188
|
+
panelClass: this.size,
|
10189
|
+
positionStrategy,
|
10190
|
+
hasBackdrop: true,
|
10191
|
+
width: '90%',
|
10192
|
+
height: '90vh'
|
10193
|
+
});
|
10194
|
+
}
|
10195
|
+
else {
|
10196
|
+
// size 'dialog'
|
10197
|
+
const positionStrategy = this.overlay.position().global().top('8vh').centerHorizontally();
|
10198
|
+
this.overlayRef = this.overlay.create({
|
10199
|
+
panelClass: this.size,
|
10200
|
+
minWidth: this.minWidth,
|
10201
|
+
positionStrategy,
|
10202
|
+
hasBackdrop: true,
|
10203
|
+
scrollStrategy: this.overlay.scrollStrategies.block()
|
10204
|
+
});
|
10205
|
+
}
|
10206
|
+
if (this.size !== 'fullscreen') {
|
10207
|
+
this.overlayRef
|
10208
|
+
.backdropClick()
|
10209
|
+
.pipe(takeUntil(this.destroyed$))
|
10210
|
+
.subscribe(() => this.closeModal(ModalCloseClickLocation.OutsideClick));
|
10211
|
+
}
|
10212
|
+
}
|
10213
|
+
ngOnChanges() {
|
10214
|
+
if (this.open && this.overlayRef && !this.overlayRef.hasAttached()) {
|
10215
|
+
this.openModal();
|
10216
|
+
}
|
10217
|
+
if (!this.open && this.overlayRef && this.overlayRef.hasAttached()) {
|
10218
|
+
this.closeModal(ModalCloseClickLocation.Other);
|
10219
|
+
}
|
10220
|
+
if (this.open && this.overlayRef && this.overlayRef.hasAttached() && this.isFocusTrap) {
|
10221
|
+
this.trapFocusInModal(this.overlayRef.hostElement);
|
10222
|
+
}
|
10223
|
+
}
|
10224
|
+
ngAfterViewInit() {
|
10225
|
+
if (this.open) {
|
10226
|
+
timer(0)
|
10227
|
+
.pipe(takeUntil(this.destroyed$))
|
10228
|
+
.subscribe(() => this.openModal());
|
10229
|
+
}
|
10230
|
+
}
|
10231
|
+
/** @internal */
|
10232
|
+
openModal() {
|
10233
|
+
this.oldOverflow = document.documentElement.style.overflowY;
|
10234
|
+
if (this.size === 'fullscreen') {
|
10235
|
+
this.renderer.setStyle(document.documentElement, 'overflowY', 'hidden');
|
10236
|
+
}
|
10237
|
+
this.overlayRef.attach(this.cdkPortal);
|
10238
|
+
this.trapFocusInModal(this.overlayRef.hostElement);
|
10239
|
+
}
|
10240
|
+
/** @internal */
|
10241
|
+
emitBack() {
|
10242
|
+
this.back.emit();
|
10243
|
+
}
|
10244
|
+
ngOnDestroy() {
|
10245
|
+
this.destroyed$.next();
|
10246
|
+
if (this.size === 'fullscreen') {
|
10247
|
+
this.renderer.setStyle(document.documentElement, 'overflowY', this.oldOverflow);
|
10248
|
+
}
|
10249
|
+
if (this.overlayRef) {
|
10250
|
+
this.overlayRef.dispose();
|
10251
|
+
}
|
10252
|
+
}
|
10253
|
+
/** @internal */
|
10254
|
+
async closeModal(closeLocation) {
|
10255
|
+
if (!this.canModalBeClosed || (await this.canModalBeClosed(closeLocation))) {
|
10256
|
+
this.open = false;
|
10257
|
+
this.overlayRef.detach();
|
10258
|
+
if (this.size === 'fullscreen') {
|
10259
|
+
this.renderer.setStyle(document.documentElement, 'overflowY', this.oldOverflow);
|
10260
|
+
}
|
10261
|
+
this.close.emit(typeof closeLocation === 'boolean' ? ModalCloseClickLocation.Other : closeLocation);
|
10262
|
+
}
|
10263
|
+
}
|
10264
|
+
trapFocusInModal(hostElement) {
|
10265
|
+
this.focusTrap.create(hostElement);
|
10266
|
+
}
|
10267
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalComponent, deps: [{ token: i1$2.Overlay }, { token: i0.Renderer2 }, { token: MODAL_CLOSE, optional: true }, { token: i2.ConfigurableFocusTrapFactory }], target: i0.ɵɵFactoryTarget.Component }); }
|
10268
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: ModalComponent, isStandalone: true, selector: "lx-modal", inputs: { open: "open", showCloseButton: "showCloseButton", showBackButton: "showBackButton", verticalScroll: "verticalScroll", size: "size", minWidth: "minWidth", isFocusTrap: "isFocusTrap", canModalBeClosed: "canModalBeClosed" }, outputs: { close: "close", back: "back" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, queries: [{ propertyName: "header", first: true, predicate: ModalHeaderComponent, descendants: true }, { propertyName: "footer", first: true, predicate: ModalFooterComponent, descendants: true }, { propertyName: "explicitContent", first: true, predicate: ModalContentDirective, descendants: true, read: TemplateRef, static: true }], viewQueries: [{ propertyName: "cdkPortal", first: true, predicate: CdkPortal, descendants: true, static: true }, { propertyName: "implicitContent", first: true, predicate: ["implicitContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<ng-template cdkPortal>\n @if (open) {\n <div\n role=\"dialog\"\n class=\"lxmodal\"\n [class.lxmodal--fullscreen]=\"size === 'fullscreen'\"\n [class.lxmodal--dialog]=\"size === 'dialog'\"\n [class.lxmodal--dialog-large]=\"size === 'dialog-large'\"\n [class.lxmodal--withFooter]=\"!!footer\"\n [class.lxmodal--verticalScroll]=\"verticalScroll\"\n @modal\n >\n @if (size === 'fullscreen' && showBackButton) {\n <div (click)=\"emitBack()\" (keyup.enter)=\"emitBack()\" tabindex=\"0\" role=\"button\" class=\"fal fa-long-arrow-left\"></div>\n }\n @if (showCloseButton) {\n <!-- TODO lx-button doesn't comply with modal close button style yet -->\n <!-- eslint-disable-next-line @nx/workspace-no-icon-in-button-content -->\n <button\n (click)=\"closeModal(closeLocation.CloseButton)\"\n [attr.aria-label]=\"NAME + '.close' | translate\"\n class=\"fal fa-times closeButton\"\n ></button>\n }\n @if (header) {\n <ng-content select=\"lx-modal-header\" />\n }\n <div class=\"modalContentContainer\" [class.lxThinScrollbar]=\"verticalScroll\">\n <ng-container *ngTemplateOutlet=\"content\" />\n </div>\n @if (footer) {\n <ng-content select=\"lx-modal-footer\" />\n }\n </div>\n }\n</ng-template>\n<ng-template #implicitContent>\n <ng-content />\n</ng-template>\n", styles: ["@keyframes subtleScaleUpKeyFrames{0%{transform:scale(.95);opacity:0}}.lxmodal{background:#fff;width:100%}.lxmodal--withFooter.lxmodal--fullscreen.lxmodal--verticalScroll .modalContentContainer{overflow-y:auto;padding:16px}.lxmodal--withFooter.lxmodal--fullscreen:not(.lxmodal--verticalScroll) .modalContentContainer{bottom:70px;overflow:hidden}.lxmodal--verticalScroll .modalContentContainer{overflow-y:auto;padding:16px}.lxmodal--fullscreen{height:100%;display:flex;flex-direction:column}.lxmodal--fullscreen .closeButton{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;width:48px;height:48px;line-height:48px;right:36px;top:12px;z-index:1}.lxmodal--fullscreen .closeButton:before{cursor:pointer}.lxmodal--fullscreen .closeButton:hover,.lxmodal--fullscreen .closeButton:focus{color:#526179;background-color:#eaedf1}.lxmodal--fullscreen .closeButton:focus{outline:0}.lxmodal--fullscreen .fa-long-arrow-left{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;width:48px;height:48px;line-height:48px;left:36px;top:16px}.lxmodal--fullscreen .fa-long-arrow-left:before{cursor:pointer}.lxmodal--fullscreen .fa-long-arrow-left:hover,.lxmodal--fullscreen .fa-long-arrow-left:focus{color:#526179;background-color:#eaedf1}.lxmodal--fullscreen .fa-long-arrow-left:focus{outline:0}.lxmodal--dialog,.lxmodal--dialog-large{display:block;position:relative;border-radius:6px;box-shadow:0 8px 20px #0000003d}.lxmodal--dialog .modalContentContainer,.lxmodal--dialog-large .modalContentContainer{position:relative}.lxmodal--dialog .closeButton,.lxmodal--dialog-large .closeButton{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;height:32px;width:32px;z-index:999;right:10px;top:20px}.lxmodal--dialog .closeButton:before,.lxmodal--dialog-large .closeButton:before{cursor:pointer}.lxmodal--dialog .closeButton:hover,.lxmodal--dialog .closeButton:focus,.lxmodal--dialog-large .closeButton:hover,.lxmodal--dialog-large .closeButton:focus{color:#526179;background-color:#eaedf1}.lxmodal--dialog .closeButton:focus,.lxmodal--dialog-large .closeButton:focus{outline:0}.lxmodal--dialog .modalContentContainer{padding:16px 16px 0}.lxmodal--dialog.lxmodal--verticalScroll .modalContentContainer{padding:16px;max-height:calc(84vh - 136px)}.lxmodal--dialog-large .modalContentContainer{padding:16px;height:calc(100% - 136px)}.modalContentContainer{flex:1}\n"], dependencies: [{ kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i3.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: CommonModule }], animations: [
|
10269
|
+
trigger('modal', [
|
10270
|
+
transition(':enter', [style({ opacity: 0 }), animate('0.15s', style({ opacity: 1 }))]),
|
10271
|
+
transition(':leave', animate('0.15s', style({ opacity: 0 })))
|
10272
|
+
])
|
10273
|
+
] }); }
|
10274
|
+
}
|
10275
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalComponent, decorators: [{
|
10276
|
+
type: Component,
|
10277
|
+
args: [{ selector: 'lx-modal', animations: [
|
10278
|
+
trigger('modal', [
|
10279
|
+
transition(':enter', [style({ opacity: 0 }), animate('0.15s', style({ opacity: 1 }))]),
|
10280
|
+
transition(':leave', animate('0.15s', style({ opacity: 0 })))
|
10281
|
+
])
|
10282
|
+
], imports: [PortalModule, NgTemplateOutlet, TranslateModule, CommonModule], template: "<ng-template cdkPortal>\n @if (open) {\n <div\n role=\"dialog\"\n class=\"lxmodal\"\n [class.lxmodal--fullscreen]=\"size === 'fullscreen'\"\n [class.lxmodal--dialog]=\"size === 'dialog'\"\n [class.lxmodal--dialog-large]=\"size === 'dialog-large'\"\n [class.lxmodal--withFooter]=\"!!footer\"\n [class.lxmodal--verticalScroll]=\"verticalScroll\"\n @modal\n >\n @if (size === 'fullscreen' && showBackButton) {\n <div (click)=\"emitBack()\" (keyup.enter)=\"emitBack()\" tabindex=\"0\" role=\"button\" class=\"fal fa-long-arrow-left\"></div>\n }\n @if (showCloseButton) {\n <!-- TODO lx-button doesn't comply with modal close button style yet -->\n <!-- eslint-disable-next-line @nx/workspace-no-icon-in-button-content -->\n <button\n (click)=\"closeModal(closeLocation.CloseButton)\"\n [attr.aria-label]=\"NAME + '.close' | translate\"\n class=\"fal fa-times closeButton\"\n ></button>\n }\n @if (header) {\n <ng-content select=\"lx-modal-header\" />\n }\n <div class=\"modalContentContainer\" [class.lxThinScrollbar]=\"verticalScroll\">\n <ng-container *ngTemplateOutlet=\"content\" />\n </div>\n @if (footer) {\n <ng-content select=\"lx-modal-footer\" />\n }\n </div>\n }\n</ng-template>\n<ng-template #implicitContent>\n <ng-content />\n</ng-template>\n", styles: ["@keyframes subtleScaleUpKeyFrames{0%{transform:scale(.95);opacity:0}}.lxmodal{background:#fff;width:100%}.lxmodal--withFooter.lxmodal--fullscreen.lxmodal--verticalScroll .modalContentContainer{overflow-y:auto;padding:16px}.lxmodal--withFooter.lxmodal--fullscreen:not(.lxmodal--verticalScroll) .modalContentContainer{bottom:70px;overflow:hidden}.lxmodal--verticalScroll .modalContentContainer{overflow-y:auto;padding:16px}.lxmodal--fullscreen{height:100%;display:flex;flex-direction:column}.lxmodal--fullscreen .closeButton{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;width:48px;height:48px;line-height:48px;right:36px;top:12px;z-index:1}.lxmodal--fullscreen .closeButton:before{cursor:pointer}.lxmodal--fullscreen .closeButton:hover,.lxmodal--fullscreen .closeButton:focus{color:#526179;background-color:#eaedf1}.lxmodal--fullscreen .closeButton:focus{outline:0}.lxmodal--fullscreen .fa-long-arrow-left{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;width:48px;height:48px;line-height:48px;left:36px;top:16px}.lxmodal--fullscreen .fa-long-arrow-left:before{cursor:pointer}.lxmodal--fullscreen .fa-long-arrow-left:hover,.lxmodal--fullscreen .fa-long-arrow-left:focus{color:#526179;background-color:#eaedf1}.lxmodal--fullscreen .fa-long-arrow-left:focus{outline:0}.lxmodal--dialog,.lxmodal--dialog-large{display:block;position:relative;border-radius:6px;box-shadow:0 8px 20px #0000003d}.lxmodal--dialog .modalContentContainer,.lxmodal--dialog-large .modalContentContainer{position:relative}.lxmodal--dialog .closeButton,.lxmodal--dialog-large .closeButton{border:0;background:transparent;position:absolute;text-align:center;transition:color,background-color .18s;transition-delay:.1s;transition-timing-function:ease;border-radius:50%;color:#61779d;font-size:26px;height:32px;width:32px;z-index:999;right:10px;top:20px}.lxmodal--dialog .closeButton:before,.lxmodal--dialog-large .closeButton:before{cursor:pointer}.lxmodal--dialog .closeButton:hover,.lxmodal--dialog .closeButton:focus,.lxmodal--dialog-large .closeButton:hover,.lxmodal--dialog-large .closeButton:focus{color:#526179;background-color:#eaedf1}.lxmodal--dialog .closeButton:focus,.lxmodal--dialog-large .closeButton:focus{outline:0}.lxmodal--dialog .modalContentContainer{padding:16px 16px 0}.lxmodal--dialog.lxmodal--verticalScroll .modalContentContainer{padding:16px;max-height:calc(84vh - 136px)}.lxmodal--dialog-large .modalContentContainer{padding:16px;height:calc(100% - 136px)}.modalContentContainer{flex:1}\n"] }]
|
10283
|
+
}], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Renderer2 }, { type: i5.Observable, decorators: [{
|
10284
|
+
type: Optional
|
10285
|
+
}, {
|
10286
|
+
type: Inject,
|
10287
|
+
args: [MODAL_CLOSE]
|
10288
|
+
}] }, { type: i2.ConfigurableFocusTrapFactory }], propDecorators: { open: [{
|
10289
|
+
type: Input
|
10290
|
+
}], showCloseButton: [{
|
10291
|
+
type: Input
|
10292
|
+
}], showBackButton: [{
|
10293
|
+
type: Input
|
10294
|
+
}], verticalScroll: [{
|
10295
|
+
type: Input
|
10296
|
+
}], size: [{
|
10297
|
+
type: Input
|
10298
|
+
}], minWidth: [{
|
10299
|
+
type: Input
|
10300
|
+
}], isFocusTrap: [{
|
10301
|
+
type: Input
|
10302
|
+
}], canModalBeClosed: [{
|
10303
|
+
type: Input
|
10304
|
+
}], close: [{
|
10305
|
+
type: Output
|
10306
|
+
}], back: [{
|
10307
|
+
type: Output
|
10308
|
+
}], header: [{
|
10309
|
+
type: ContentChild,
|
10310
|
+
args: [ModalHeaderComponent]
|
10311
|
+
}], footer: [{
|
10312
|
+
type: ContentChild,
|
10313
|
+
args: [ModalFooterComponent]
|
10314
|
+
}], cdkPortal: [{
|
10315
|
+
type: ViewChild,
|
10316
|
+
args: [CdkPortal, { static: true }]
|
10317
|
+
}], implicitContent: [{
|
10318
|
+
type: ViewChild,
|
10319
|
+
args: ['implicitContent', { static: true }]
|
10320
|
+
}], explicitContent: [{
|
10321
|
+
type: ContentChild,
|
10322
|
+
args: [ModalContentDirective, { read: TemplateRef, static: true }]
|
10323
|
+
}], onEscape: [{
|
10324
|
+
type: HostListener,
|
10325
|
+
args: ['document:keydown.escape']
|
10326
|
+
}] } });
|
10327
|
+
|
10328
|
+
class UrlValidatorDirective {
|
10329
|
+
validate(control) {
|
10330
|
+
const urlPattern = /^(https?:\/\/)?([\w-]+\.)*[\w-]+\.[a-z]{2,}(\/[^\s]*)?$/i;
|
10331
|
+
return urlPattern.test(control.value || '') ? null : { invalidUrl: true };
|
9036
10332
|
}
|
9037
|
-
|
9038
|
-
|
9039
|
-
|
9040
|
-
|
9041
|
-
|
9042
|
-
|
9043
|
-
|
10333
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: UrlValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
10334
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: UrlValidatorDirective, isStandalone: true, selector: "[lxUrl][ngModel]", providers: [
|
10335
|
+
{
|
10336
|
+
provide: NG_VALIDATORS,
|
10337
|
+
useExisting: UrlValidatorDirective,
|
10338
|
+
multi: true
|
10339
|
+
}
|
10340
|
+
], ngImport: i0 }); }
|
10341
|
+
}
|
10342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: UrlValidatorDirective, decorators: [{
|
10343
|
+
type: Directive,
|
10344
|
+
args: [{
|
10345
|
+
selector: '[lxUrl][ngModel]',
|
10346
|
+
providers: [
|
10347
|
+
{
|
10348
|
+
provide: NG_VALIDATORS,
|
10349
|
+
useExisting: UrlValidatorDirective,
|
10350
|
+
multi: true
|
10351
|
+
}
|
10352
|
+
]
|
10353
|
+
}]
|
10354
|
+
}] });
|
10355
|
+
|
10356
|
+
class LinkModalComponent {
|
10357
|
+
constructor() {
|
10358
|
+
this.NAME = 'LinkModalComponent';
|
10359
|
+
this.ngForm = viewChild.required(NgForm);
|
10360
|
+
this.cd = inject(ChangeDetectorRef);
|
9044
10361
|
}
|
9045
|
-
|
9046
|
-
this.
|
9047
|
-
this.destroy$.complete();
|
10362
|
+
get open() {
|
10363
|
+
return linkPluginKey.getState(this.editor.state).open;
|
9048
10364
|
}
|
9049
|
-
|
9050
|
-
|
9051
|
-
const control = this.ngControl.control;
|
9052
|
-
if (maxLength !== null && control) {
|
9053
|
-
const validators = control.validator ? [control.validator, Validators.maxLength(maxLength)] : Validators.maxLength(maxLength);
|
9054
|
-
control.setValidators(validators);
|
9055
|
-
control.updateValueAndValidity();
|
9056
|
-
}
|
10365
|
+
get text() {
|
10366
|
+
return linkPluginKey.getState(this.editor.state).text;
|
9057
10367
|
}
|
9058
|
-
|
9059
|
-
|
9060
|
-
return this.lxMaxLengthCounter;
|
9061
|
-
}
|
9062
|
-
const control = this.ngControl.control;
|
9063
|
-
const validatorFn = control?.validator;
|
9064
|
-
const errors = validatorFn?.(new FormControl({ length: Infinity }));
|
9065
|
-
const requiredLength = errors?.['maxlength']['requiredLength'];
|
9066
|
-
this.lxMaxLengthCounter = requiredLength ? requiredLength.toString() : null;
|
9067
|
-
if (this.lxMaxLengthCounter === null) {
|
9068
|
-
console.warn('lxMaxLength directive is used without a value or a control with a maxLength validator');
|
9069
|
-
}
|
9070
|
-
return requiredLength;
|
10368
|
+
get url() {
|
10369
|
+
return linkPluginKey.getState(this.editor.state).url;
|
9071
10370
|
}
|
9072
|
-
|
9073
|
-
this.
|
9074
|
-
|
9075
|
-
|
9076
|
-
}
|
9077
|
-
else {
|
9078
|
-
this.createCounter();
|
9079
|
-
}
|
10371
|
+
removeLink() {
|
10372
|
+
const editorChain = this.editor.chain().focus().extendMarkRange('link');
|
10373
|
+
editorChain.focus().extendMarkRange('link').unsetLink().run();
|
10374
|
+
this.closeModal();
|
9080
10375
|
}
|
9081
|
-
|
9082
|
-
|
9083
|
-
|
9084
|
-
|
9085
|
-
|
9086
|
-
|
10376
|
+
saveLink() {
|
10377
|
+
if (!this.ngForm().valid) {
|
10378
|
+
this.ngForm().form.markAllAsTouched();
|
10379
|
+
return;
|
10380
|
+
}
|
10381
|
+
const formData = this.ngForm().value;
|
10382
|
+
const href = ensureHttpProtocol(formData.url);
|
10383
|
+
this.editor.chain().focus().extendMarkRange('link').run(); // Ensure that selection is expanded to the whole link mark
|
10384
|
+
const { from, to, empty } = this.editor.state.selection;
|
10385
|
+
const tr = this.editor.state.tr;
|
10386
|
+
const linkMark = this.editor.schema.marks['link'].create({ href });
|
10387
|
+
if (empty) {
|
10388
|
+
tr.insertText(formData.text, from);
|
9087
10389
|
}
|
9088
10390
|
else {
|
9089
|
-
|
10391
|
+
tr.insertText(formData.text, from, to);
|
9090
10392
|
}
|
9091
|
-
|
9092
|
-
|
9093
|
-
|
10393
|
+
tr.addMark(from, from + formData.text.length, linkMark);
|
10394
|
+
this.editor.view.dispatch(tr);
|
10395
|
+
this.closeModal();
|
9094
10396
|
}
|
9095
|
-
|
9096
|
-
|
9097
|
-
const currentLength = this.ngControl.value ? this.ngControl.value.length : 0;
|
9098
|
-
const maxLength = this.getMaxLength();
|
9099
|
-
this.renderer.setProperty(this.counterElement, 'textContent', `${currentLength} / ${maxLength}`);
|
9100
|
-
}
|
10397
|
+
closeModal() {
|
10398
|
+
dispatchLinkState(this.editor, { open: false, url: null, text: null });
|
9101
10399
|
}
|
9102
|
-
|
9103
|
-
|
9104
|
-
|
9105
|
-
|
9106
|
-
|
9107
|
-
|
9108
|
-
|
10400
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: LinkModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10401
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: LinkModalComponent, isStandalone: true, selector: "lx-link-modal", inputs: { editor: "editor" }, viewQueries: [{ propertyName: "ngForm", first: true, predicate: NgForm, descendants: true, isSignal: true }], ngImport: i0, template: "<lx-modal size=\"dialog\" [open]=\"true\" (close)=\"closeModal()\">\n <lx-modal-header [title]=\"NAME + '.link' | translate\" />\n\n <form #form=\"ngForm\" (submit)=\"saveLink()\">\n <div class=\"link-modal-body\">\n <div class=\"form-group required\">\n <label for=\"text\">{{ NAME + '.linkText' | translate }}</label>\n <input\n #titleInput=\"ngModel\"\n class=\"form-control\"\n name=\"text\"\n type=\"text\"\n [ngModel]=\"text\"\n [lxAutofocus]=\"true\"\n [lxAutofocusTimeout]=\"10\"\n [lxMarkInvalid]=\"form.submitted && titleInput.control.touched && titleInput.hasError('required')\"\n required\n />\n </div>\n\n <div class=\"form-group required\">\n <label for=\"url\">{{ NAME + '.url' | translate }}</label>\n <input\n #urlInput=\"ngModel\"\n class=\"form-control\"\n name=\"url\"\n type=\"text\"\n [ngModel]=\"url\"\n [lxMarkInvalid]=\"form.submitted && urlInput.control.touched && urlInput.hasError('invalidUrl')\"\n lxUrl\n />\n @if (form.submitted && urlInput.control.touched && urlInput.hasError('invalidUrl')) {\n <lx-error-message>\n {{ NAME + '.invalidUrl' | translate }}\n </lx-error-message>\n }\n </div>\n </div>\n\n <lx-modal-footer>\n <div class=\"link-modal-actions\">\n <div class=\"link-modal-left-buttons\">\n @if (url) {\n <button lx-button type=\"button\" size=\"large\" color=\"danger\" (click)=\"removeLink()\">\n {{ NAME + '.removeLink' | translate }}\n </button>\n }\n </div>\n\n <div class=\"link-modal-right-buttons\">\n <button lx-button type=\"button\" size=\"large\" (click)=\"closeModal()\">\n {{ NAME + '.cancel' | translate }}\n </button>\n <button lx-button type=\"submit\" [disabled]=\"!titleInput.control.value || !urlInput.control.value\" color=\"primary\" size=\"large\">\n {{ NAME + '.ok' | translate }}\n </button>\n </div>\n </div>\n </lx-modal-footer>\n </form>\n</lx-modal>\n", styles: [".link-modal-body{display:flex;flex-direction:column;padding:20px 64px}.link-modal-actions{display:flex;justify-content:space-between}.link-modal-right-buttons{display:flex;gap:10px}\n"], dependencies: [{ kind: "component", type: ModalComponent, selector: "lx-modal", inputs: ["open", "showCloseButton", "showBackButton", "verticalScroll", "size", "minWidth", "isFocusTrap", "canModalBeClosed"], outputs: ["close", "back"] }, { kind: "component", type: ModalHeaderComponent, selector: "lx-modal-header", inputs: ["title", "bottomBorder"] }, { kind: "component", type: ModalFooterComponent, selector: "lx-modal-footer", inputs: ["border"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$5.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$5.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "button[lx-button]", inputs: ["size", "color", "mode", "pressed", "selected", "square", "circle", "disabled", "icon", "endIcon", "showSpinner"] }, { kind: "directive", type: UrlValidatorDirective, selector: "[lxUrl][ngModel]" }, { kind: "component", type: ErrorMessageComponent, selector: "lx-error-message" }, { kind: "directive", type: MarkInvalidDirective, selector: "[lxMarkInvalid]", inputs: ["lxMarkInvalid"] }, { kind: "directive", type: AutofocusDirective, selector: "[lxAutofocus]", inputs: ["lxAutofocus", "lxAutofocusPreventScroll", "lxAutofocusTimeout"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
10402
|
+
}
|
10403
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: LinkModalComponent, decorators: [{
|
10404
|
+
type: Component,
|
10405
|
+
args: [{ selector: 'lx-link-modal', imports: [
|
10406
|
+
ModalComponent,
|
10407
|
+
ModalHeaderComponent,
|
10408
|
+
ModalFooterComponent,
|
10409
|
+
TranslateModule,
|
10410
|
+
FormsModule,
|
10411
|
+
ButtonComponent,
|
10412
|
+
UrlValidatorDirective,
|
10413
|
+
ErrorMessageComponent,
|
10414
|
+
MarkInvalidDirective,
|
10415
|
+
AutofocusDirective
|
10416
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<lx-modal size=\"dialog\" [open]=\"true\" (close)=\"closeModal()\">\n <lx-modal-header [title]=\"NAME + '.link' | translate\" />\n\n <form #form=\"ngForm\" (submit)=\"saveLink()\">\n <div class=\"link-modal-body\">\n <div class=\"form-group required\">\n <label for=\"text\">{{ NAME + '.linkText' | translate }}</label>\n <input\n #titleInput=\"ngModel\"\n class=\"form-control\"\n name=\"text\"\n type=\"text\"\n [ngModel]=\"text\"\n [lxAutofocus]=\"true\"\n [lxAutofocusTimeout]=\"10\"\n [lxMarkInvalid]=\"form.submitted && titleInput.control.touched && titleInput.hasError('required')\"\n required\n />\n </div>\n\n <div class=\"form-group required\">\n <label for=\"url\">{{ NAME + '.url' | translate }}</label>\n <input\n #urlInput=\"ngModel\"\n class=\"form-control\"\n name=\"url\"\n type=\"text\"\n [ngModel]=\"url\"\n [lxMarkInvalid]=\"form.submitted && urlInput.control.touched && urlInput.hasError('invalidUrl')\"\n lxUrl\n />\n @if (form.submitted && urlInput.control.touched && urlInput.hasError('invalidUrl')) {\n <lx-error-message>\n {{ NAME + '.invalidUrl' | translate }}\n </lx-error-message>\n }\n </div>\n </div>\n\n <lx-modal-footer>\n <div class=\"link-modal-actions\">\n <div class=\"link-modal-left-buttons\">\n @if (url) {\n <button lx-button type=\"button\" size=\"large\" color=\"danger\" (click)=\"removeLink()\">\n {{ NAME + '.removeLink' | translate }}\n </button>\n }\n </div>\n\n <div class=\"link-modal-right-buttons\">\n <button lx-button type=\"button\" size=\"large\" (click)=\"closeModal()\">\n {{ NAME + '.cancel' | translate }}\n </button>\n <button lx-button type=\"submit\" [disabled]=\"!titleInput.control.value || !urlInput.control.value\" color=\"primary\" size=\"large\">\n {{ NAME + '.ok' | translate }}\n </button>\n </div>\n </div>\n </lx-modal-footer>\n </form>\n</lx-modal>\n", styles: [".link-modal-body{display:flex;flex-direction:column;padding:20px 64px}.link-modal-actions{display:flex;justify-content:space-between}.link-modal-right-buttons{display:flex;gap:10px}\n"] }]
|
10417
|
+
}], propDecorators: { editor: [{
|
10418
|
+
type: Input,
|
10419
|
+
args: [{ required: true }]
|
10420
|
+
}] } });
|
10421
|
+
const ensureHttpProtocol = (url) => {
|
10422
|
+
if (!url)
|
10423
|
+
return '';
|
10424
|
+
return url.startsWith('http://') || url.startsWith('https://') ? url : `http://${url}`;
|
10425
|
+
};
|
10426
|
+
|
10427
|
+
class RichTextEditorToolbarComponent {
|
10428
|
+
constructor() {
|
10429
|
+
this.NAME = 'RichTextEditorToolbarComponent';
|
10430
|
+
this.headingLevels = [];
|
10431
|
+
this.cd = inject(ChangeDetectorRef);
|
10432
|
+
this.isLinkModalOpen$ = new BehaviorSubject(false);
|
10433
|
+
this.detectChanges = () => {
|
10434
|
+
this.cd.markForCheck();
|
10435
|
+
};
|
10436
|
+
this.update = (event) => {
|
10437
|
+
if (event.transaction.getMeta(linkPluginKey)) {
|
10438
|
+
this.isLinkModalOpen$.next(linkPluginKey.getState(event.editor.state).open);
|
10439
|
+
}
|
10440
|
+
};
|
9109
10441
|
}
|
9110
|
-
|
9111
|
-
|
9112
|
-
|
9113
|
-
|
9114
|
-
const input = event.target;
|
9115
|
-
const selectionStart = input.selectionStart || 0;
|
9116
|
-
const selectionEnd = input.selectionEnd || 0;
|
9117
|
-
const newValue = input.value.slice(0, selectionStart) + (event.data || '') + input.value.slice(selectionEnd);
|
9118
|
-
// Prevent input if the new value exceeds the max length and grows
|
9119
|
-
if (newValue.length > maxLength && newValue.length > input.value.length) {
|
9120
|
-
event.preventDefault();
|
9121
|
-
}
|
9122
|
-
});
|
9123
|
-
this.renderer.listen(this.el.nativeElement, 'input', () => {
|
9124
|
-
this.updateCounter();
|
9125
|
-
});
|
9126
|
-
}
|
10442
|
+
ngOnInit() {
|
10443
|
+
this.headingLevels = this.editor.extensionManager.extensions.find((ext) => ext.name === 'heading')?.options?.levels || [];
|
10444
|
+
this.editor.on('selectionUpdate', this.detectChanges);
|
10445
|
+
this.editor.on('transaction', this.update);
|
9127
10446
|
}
|
9128
|
-
|
9129
|
-
|
9130
|
-
|
9131
|
-
if (maxLength !== null && value && value.length > maxLength) {
|
9132
|
-
this.ngControl.control?.markAsDirty({ onlySelf: true });
|
9133
|
-
this.ngControl.control?.updateValueAndValidity({ onlySelf: true });
|
9134
|
-
}
|
10447
|
+
ngOnDestroy() {
|
10448
|
+
this.editor.off('selectionUpdate', this.detectChanges);
|
10449
|
+
this.editor.off('transaction', this.update);
|
9135
10450
|
}
|
9136
|
-
|
9137
|
-
|
10451
|
+
insertTable() {
|
10452
|
+
this.editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: false }).track('INSERT_TABLE').run();
|
10453
|
+
}
|
10454
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RichTextEditorToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10455
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: RichTextEditorToolbarComponent, isStandalone: true, selector: "lx-rich-text-editor-toolbar", inputs: { editor: "editor" }, ngImport: i0, template: "<div class=\"toolbar\">\n <div class=\"toolbarItemGroup\">\n @if ('heading' | lxExtensionEnabled: editor) {\n <lx-options-dropdown>\n <button\n class=\"toolbarItem toolbar-heading\"\n lxKeyboardActionSource\n lx-button\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"false\"\n [pressed]=\"editor.isActive('heading') || editor.isActive('paragraph')\"\n endIcon=\"fa-angle-down\"\n [disabled]=\"!editor.isActive('heading') && !editor.isActive('paragraph')\"\n >\n {{ editor.isActive('heading') ? `${NAME + '.heading' | translate} ${editor.getAttributes('heading')['level']}` : `${NAME + '.paragraph' | translate}` }}\n </button>\n\n @for (level of headingLevels; track headingLevels) {\n <lx-option\n (select)=\"editor.chain().setHeading({ level }).focus().run()\"\n [hasSelectedState]=\"editor.isActive('heading', { level })\"\n [selected]=\"editor.isActive('heading', { level })\"\n >\n @if (!editor.isActive('heading', { level })) {\n <i class=\"far fa-heading dropdown-icon\"></i>\n }\n <span>{{ NAME + '.headingOption' | translate }} {{ level }}</span>\n </lx-option>\n }\n\n <lx-option\n (select)=\"this.editor.chain().setParagraph().focus().run()\"\n [hasSelectedState]=\"editor.isActive('paragraph')\"\n [selected]=\"editor.isActive('paragraph')\"\n >\n @if (!editor.isActive('paragraph')) {\n <i class=\"far fa-paragraph dropdown-icon\"></i>\n }\n <span>{{ NAME + '.paragraphOption' | translate }}</span>\n </lx-option>\n </lx-options-dropdown>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('bold' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.bold' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [disabled]=\"editor.isActive('header')\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleBold().focus().run()\"\n [pressed]=\"editor.isActive('bold')\"\n icon=\"fa-bold\"\n ></button>\n }\n\n @if ('italic' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.italic' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleItalic().focus().run()\"\n [pressed]=\"editor.isActive('italic')\"\n icon=\"fa-italic\"\n ></button>\n }\n\n @if ('underline' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.underline' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleUnderline().focus().run()\"\n [pressed]=\"editor.isActive('underline')\"\n icon=\"fa-underline\"\n ></button>\n }\n\n @if ('strike' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.strikethrough' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleStrike().focus().run()\"\n [pressed]=\"editor.isActive('strike')\"\n icon=\"fa-strikethrough\"\n ></button>\n }\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('bulletList' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.list' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleBulletList().run()\"\n [pressed]=\"editor.isActive('listItem')\"\n icon=\"fa-list\"\n ></button>\n }\n\n @if ('orderedList' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.orderedList' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleOrderedList().run()\"\n [pressed]=\"editor.isActive('orderedList')\"\n icon=\"fa-list-ol\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('textAlign' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignLeft' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('left').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'left' })\"\n icon=\"fa-align-left\"\n ></button>\n\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignCenter' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('center').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'center' })\"\n icon=\"fa-align-center\"\n ></button>\n\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignRight' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('right').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'right' })\"\n icon=\"fa-align-right\"\n ></button>\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.justify' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('justify').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'justify' })\"\n icon=\"fa-align-justify\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('link' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.link' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().openLinkModal().run()\"\n [pressed]=\"editor.isActive('link')\"\n icon=\"fa-link\"\n ></button>\n <span class=\"separator\"></span>\n }\n </div>\n\n @let tableEnabled = 'table' | lxExtensionEnabled: editor;\n @let codeEnabled = 'codeBlock' | lxExtensionEnabled: editor;\n\n @if (tableEnabled || codeEnabled) {\n <div class=\"toolbarItemGroup\">\n @if (codeEnabled) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.code' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().setCodeBlock().run()\"\n [pressed]=\"editor.isActive('codeBlock')\"\n icon=\"fa-code\"\n ></button>\n }\n\n @if ('lxDiagram' | lxExtensionEnabled: editor) {\n <ng-content select=\".diagram-btn\" />\n }\n\n @if (tableEnabled) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.table' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"insertTable()\"\n [pressed]=\"editor.isActive('table')\"\n icon=\"fa-table\"\n [disabled]=\"editor.isActive('table')\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n }\n</div>\n\n@if (('link' | lxExtensionEnabled: editor) && (isLinkModalOpen$ | async)) {\n <lx-link-modal [editor]=\"editor\" />\n}\n", styles: [":host{width:100%;display:flex;border-bottom:#99a5bb 1px solid;padding:8px}.toolbar{display:flex;flex-wrap:wrap;width:100%}.toolbar .toolbarItemGroup{display:flex;align-items:center;gap:2px}.toolbar .toolbarItemGroup:not(.toolbarItemGroup:has(.toolbarItem)){display:none}.toolbar .toolbarItemGroup:last-child .separator{display:none}.toolbar .dropdown-icon{margin-left:-4px;margin-right:8px}.toolbar .toolbar-heading{width:100px}.toolbar .toolbarItem{text-wrap:nowrap;color:#2a303d;border:none;height:24px}.toolbar .toolbarItem:hover:not(disabled){border:none}.toolbar .toolbarItem.pressed{background:#f0f2f5!important}.toolbar .toolbarItem.square{border:none!important}.toolbar .separator{width:1px;height:16px;margin:0 4px 0 2px;border:#c2c9d6 .5px solid}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: OptionsDropdownComponent, selector: "lx-options-dropdown", inputs: ["align", "closeOnScroll", "disabled", "maxHeight", "closeOnSelect", "overlayPositioning", "dropdownClass", "isFlexEnabledTriggerContainer"], outputs: ["opened", "closed", "closedWithoutSelection"] }, { kind: "component", type: ButtonComponent, selector: "button[lx-button]", inputs: ["size", "color", "mode", "pressed", "selected", "square", "circle", "disabled", "icon", "endIcon", "showSpinner"] }, { kind: "directive", type: KeyboardActionSourceDirective, selector: "[lxKeyboardActionSource]", exportAs: ["keyboardActionSource"] }, { kind: "component", type: OptionComponent, selector: "lx-option", inputs: ["selected", "isHighlighted", "disabled", "value", "hasSelectedState", "selectIcon"], outputs: ["select", "highlight", "selectedClick", "keyDownAction"] }, { kind: "pipe", type: ExtensionEnabledPipe, name: "lxExtensionEnabled" }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: LinkModalComponent, selector: "lx-link-modal", inputs: ["editor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
9138
10456
|
}
|
9139
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
10457
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RichTextEditorToolbarComponent, decorators: [{
|
10458
|
+
type: Component,
|
10459
|
+
args: [{ selector: 'lx-rich-text-editor-toolbar', imports: [
|
10460
|
+
AsyncPipe,
|
10461
|
+
OptionsDropdownComponent,
|
10462
|
+
ButtonComponent,
|
10463
|
+
KeyboardActionSourceDirective,
|
10464
|
+
OptionComponent,
|
10465
|
+
ExtensionEnabledPipe,
|
10466
|
+
TranslatePipe,
|
10467
|
+
LinkModalComponent
|
10468
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"toolbar\">\n <div class=\"toolbarItemGroup\">\n @if ('heading' | lxExtensionEnabled: editor) {\n <lx-options-dropdown>\n <button\n class=\"toolbarItem toolbar-heading\"\n lxKeyboardActionSource\n lx-button\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"false\"\n [pressed]=\"editor.isActive('heading') || editor.isActive('paragraph')\"\n endIcon=\"fa-angle-down\"\n [disabled]=\"!editor.isActive('heading') && !editor.isActive('paragraph')\"\n >\n {{ editor.isActive('heading') ? `${NAME + '.heading' | translate} ${editor.getAttributes('heading')['level']}` : `${NAME + '.paragraph' | translate}` }}\n </button>\n\n @for (level of headingLevels; track headingLevels) {\n <lx-option\n (select)=\"editor.chain().setHeading({ level }).focus().run()\"\n [hasSelectedState]=\"editor.isActive('heading', { level })\"\n [selected]=\"editor.isActive('heading', { level })\"\n >\n @if (!editor.isActive('heading', { level })) {\n <i class=\"far fa-heading dropdown-icon\"></i>\n }\n <span>{{ NAME + '.headingOption' | translate }} {{ level }}</span>\n </lx-option>\n }\n\n <lx-option\n (select)=\"this.editor.chain().setParagraph().focus().run()\"\n [hasSelectedState]=\"editor.isActive('paragraph')\"\n [selected]=\"editor.isActive('paragraph')\"\n >\n @if (!editor.isActive('paragraph')) {\n <i class=\"far fa-paragraph dropdown-icon\"></i>\n }\n <span>{{ NAME + '.paragraphOption' | translate }}</span>\n </lx-option>\n </lx-options-dropdown>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('bold' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.bold' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [disabled]=\"editor.isActive('header')\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleBold().focus().run()\"\n [pressed]=\"editor.isActive('bold')\"\n icon=\"fa-bold\"\n ></button>\n }\n\n @if ('italic' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.italic' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleItalic().focus().run()\"\n [pressed]=\"editor.isActive('italic')\"\n icon=\"fa-italic\"\n ></button>\n }\n\n @if ('underline' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.underline' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleUnderline().focus().run()\"\n [pressed]=\"editor.isActive('underline')\"\n icon=\"fa-underline\"\n ></button>\n }\n\n @if ('strike' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.strikethrough' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleStrike().focus().run()\"\n [pressed]=\"editor.isActive('strike')\"\n icon=\"fa-strikethrough\"\n ></button>\n }\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('bulletList' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.list' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleBulletList().run()\"\n [pressed]=\"editor.isActive('listItem')\"\n icon=\"fa-list\"\n ></button>\n }\n\n @if ('orderedList' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.orderedList' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().toggleOrderedList().run()\"\n [pressed]=\"editor.isActive('orderedList')\"\n icon=\"fa-list-ol\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('textAlign' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignLeft' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('left').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'left' })\"\n icon=\"fa-align-left\"\n ></button>\n\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignCenter' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('center').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'center' })\"\n icon=\"fa-align-center\"\n ></button>\n\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.alignRight' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('right').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'right' })\"\n icon=\"fa-align-right\"\n ></button>\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.justify' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().focus().setTextAlign('justify').run()\"\n [pressed]=\"editor.isActive({ textAlign: 'justify' })\"\n icon=\"fa-align-justify\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n\n <div class=\"toolbarItemGroup\">\n @if ('link' | lxExtensionEnabled: editor) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.link' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().openLinkModal().run()\"\n [pressed]=\"editor.isActive('link')\"\n icon=\"fa-link\"\n ></button>\n <span class=\"separator\"></span>\n }\n </div>\n\n @let tableEnabled = 'table' | lxExtensionEnabled: editor;\n @let codeEnabled = 'codeBlock' | lxExtensionEnabled: editor;\n\n @if (tableEnabled || codeEnabled) {\n <div class=\"toolbarItemGroup\">\n @if (codeEnabled) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.code' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"editor.chain().setCodeBlock().run()\"\n [pressed]=\"editor.isActive('codeBlock')\"\n icon=\"fa-code\"\n ></button>\n }\n\n @if ('lxDiagram' | lxExtensionEnabled: editor) {\n <ng-content select=\".diagram-btn\" />\n }\n\n @if (tableEnabled) {\n <button\n class=\"toolbarItem\"\n lx-button\n [attr.aria-label]=\"NAME + '.table' | translate\"\n [mode]=\"'outline'\"\n [size]=\"'medium'\"\n [square]=\"true\"\n (click)=\"insertTable()\"\n [pressed]=\"editor.isActive('table')\"\n icon=\"fa-table\"\n [disabled]=\"editor.isActive('table')\"\n ></button>\n }\n\n <span class=\"separator\"></span>\n </div>\n }\n</div>\n\n@if (('link' | lxExtensionEnabled: editor) && (isLinkModalOpen$ | async)) {\n <lx-link-modal [editor]=\"editor\" />\n}\n", styles: [":host{width:100%;display:flex;border-bottom:#99a5bb 1px solid;padding:8px}.toolbar{display:flex;flex-wrap:wrap;width:100%}.toolbar .toolbarItemGroup{display:flex;align-items:center;gap:2px}.toolbar .toolbarItemGroup:not(.toolbarItemGroup:has(.toolbarItem)){display:none}.toolbar .toolbarItemGroup:last-child .separator{display:none}.toolbar .dropdown-icon{margin-left:-4px;margin-right:8px}.toolbar .toolbar-heading{width:100px}.toolbar .toolbarItem{text-wrap:nowrap;color:#2a303d;border:none;height:24px}.toolbar .toolbarItem:hover:not(disabled){border:none}.toolbar .toolbarItem.pressed{background:#f0f2f5!important}.toolbar .toolbarItem.square{border:none!important}.toolbar .separator{width:1px;height:16px;margin:0 4px 0 2px;border:#c2c9d6 .5px solid}\n"] }]
|
10469
|
+
}], propDecorators: { editor: [{
|
10470
|
+
type: Input,
|
10471
|
+
args: [{ required: true }]
|
10472
|
+
}] } });
|
10473
|
+
|
10474
|
+
class RichTextEditorComponent {
|
10475
|
+
constructor() {
|
10476
|
+
this.mode = input.required();
|
10477
|
+
this.outputFormat = input.required();
|
10478
|
+
this.additionalFeatures = input([]);
|
10479
|
+
this.ariaLabelledBy = input(null);
|
10480
|
+
this.maxHeight = input(null);
|
10481
|
+
this.customExtensions = input([]);
|
10482
|
+
this.blur = output();
|
10483
|
+
this.value = null;
|
10484
|
+
this.disabled = false;
|
10485
|
+
this.editor = inject(TipTapEditorDirective).editor;
|
10486
|
+
this.onChange = (_value) => { };
|
10487
|
+
this.onTouched = () => { };
|
10488
|
+
}
|
10489
|
+
ngOnDestroy() {
|
10490
|
+
this.editor().destroy();
|
10491
|
+
}
|
10492
|
+
writeValue(value) {
|
10493
|
+
this.value = value;
|
10494
|
+
}
|
10495
|
+
registerOnChange(fn) {
|
10496
|
+
this.onChange = fn;
|
10497
|
+
}
|
10498
|
+
registerOnTouched(fn) {
|
10499
|
+
this.onTouched = fn;
|
10500
|
+
}
|
10501
|
+
setDisabledState(isDisabled) {
|
10502
|
+
this.disabled = isDisabled;
|
10503
|
+
}
|
10504
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RichTextEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10505
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: RichTextEditorComponent, isStandalone: true, selector: "lx-rich-text-editor", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, outputFormat: { classPropertyName: "outputFormat", publicName: "outputFormat", isSignal: true, isRequired: true, transformFunction: null }, additionalFeatures: { classPropertyName: "additionalFeatures", publicName: "additionalFeatures", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, customExtensions: { classPropertyName: "customExtensions", publicName: "customExtensions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { blur: "blur" }, host: { properties: { "style.--editor-max-height": "maxHeight()" } }, providers: [
|
10506
|
+
ExtensionsBuilder,
|
10507
|
+
{
|
10508
|
+
provide: NG_VALUE_ACCESSOR,
|
10509
|
+
useExisting: forwardRef(() => RichTextEditorComponent),
|
10510
|
+
multi: true
|
10511
|
+
}
|
10512
|
+
], hostDirectives: [{ directive: TipTapEditorDirective, inputs: ["outputFormat", "outputFormat", "additionalFeatures", "additionalFeatures", "ariaLabelledBy", "ariaLabelledBy", "customExtensions", "customExtensions", "mode", "mode"] }, { directive: TrackingDirective, outputs: ["trackEvent", "trackEvent"] }], ngImport: i0, template: "<div [class.viewMode]=\"mode() === 'view'\" [class.editMode]=\"mode() === 'edit'\" [class.disabled]=\"disabled\">\n @if (mode() === 'edit' && !disabled) {\n <lx-rich-text-editor-toolbar (mousedown)=\"$event.preventDefault()\" [editor]=\"editor()\">\n <ng-content select=\".diagram-btn\" ngProjectAs=\".diagram-btn\" />\n </lx-rich-text-editor-toolbar>\n }\n <tiptap-editor\n [editor]=\"editor()\"\n [outputFormat]=\"outputFormat()\"\n [ngModel]=\"value\"\n (ngModelChange)=\"onChange($event)\"\n (blur)=\"blur.emit()\"\n [disabled]=\"mode() === 'view' || disabled\"\n />\n\n @if (('truncate' | lxExtensionEnabled: editor()) && mode() === 'view') {\n <lx-truncate-button [editor]=\"editor()\" />\n }\n</div>\n\n@if ('table' | lxExtensionEnabled: editor()) {\n <lx-table-bubble-menu [editor]=\"editor()\" />\n}\n\n<ng-content />\n", styles: [":host{display:flex;width:100%}.editorButton{margin:0 5px}.editMode{width:100%;border:1px solid #99a5bb;background-color:#fff;border-radius:2px}:host ::ng-deep .editMode .ProseMirror{padding:15px 18.9px;resize:vertical;max-height:var(--editor-max-height, auto)}.viewMode{width:100%}.disabled{background-color:#f0f2f5;color:#61779d}:host ::ng-deep .ProseMirror{outline-width:0;border-radius:2px;overflow:auto}:host ::ng-deep .ProseMirror-gapcursor:after{width:1px;height:14px;border-left:1px solid #000}:host ::ng-deep .ProseMirror p:first-child{margin-top:0}:host ::ng-deep .ProseMirror p:last-child{margin-bottom:0}:host ::ng-deep .ProseMirror h1:first-child,:host ::ng-deep .ProseMirror h2:first-child,:host ::ng-deep .ProseMirror h3:first-child,:host ::ng-deep .ProseMirror h4:first-child,:host ::ng-deep .ProseMirror h5:first-child,:host ::ng-deep .ProseMirror h6:first-child{margin-top:0}:host ::ng-deep .ProseMirror h1:last-child,:host ::ng-deep .ProseMirror h2:last-child,:host ::ng-deep .ProseMirror h3:last-child,:host ::ng-deep .ProseMirror h4:last-child,:host ::ng-deep .ProseMirror h5:last-child,:host ::ng-deep .ProseMirror h6:last-child{margin-bottom:0}:host ::ng-deep .ProseMirror h1{font-size:var(--lxFontHeader1Size)}:host ::ng-deep .ProseMirror h2{font-size:var(--lxFontHeader2Size)}:host ::ng-deep .ProseMirror h3{font-size:var(--lxFontHeader3Size)}:host ::ng-deep .ProseMirror h4{font-size:var(--lxFontHeader4Size)}:host ::ng-deep .ProseMirror .tableWrapper{margin:20px 0}:host ::ng-deep .ProseMirror .tableWrapper h1,:host ::ng-deep .ProseMirror .tableWrapper h2,:host ::ng-deep .ProseMirror .tableWrapper h3,:host ::ng-deep .ProseMirror .tableWrapper h4,:host ::ng-deep .ProseMirror .tableWrapper h5,:host ::ng-deep .ProseMirror .tableWrapper h6{margin-top:0}:host ::ng-deep .ProseMirror .tableWrapper table{border-collapse:collapse;margin:0;table-layout:fixed;width:100%}:host ::ng-deep .ProseMirror .tableWrapper table th,:host ::ng-deep .ProseMirror .tableWrapper table td{border:1px solid #c2c9d6;box-sizing:border-box;min-width:1em;padding:12px;position:relative;vertical-align:top}:host ::ng-deep .ProseMirror .tableWrapper table th>*,:host ::ng-deep .ProseMirror .tableWrapper table td>*{margin-bottom:0}:host ::ng-deep .ProseMirror .tableWrapper table th{background-color:#c2c9d6;font-weight:700;text-align:left}:host ::ng-deep .ProseMirror .tableWrapper table p{margin:0}:host ::ng-deep .ProseMirror .tableWrapper table .selectedCell:after{content:\"\";position:absolute;inset:0;pointer-events:none;z-index:2}:host ::ng-deep .ProseMirror .tableWrapper table .column-resize-handle{background-color:#b2bccc;bottom:-2px;right:-2px;position:absolute;top:0;width:4px;pointer-events:none}:host ::ng-deep .ProseMirror .selectedCell{background-color:#e1e5eb}:host ::ng-deep .ProseMirror .selector-column,:host ::ng-deep .ProseMirror .selector-row{all:unset;display:flex;align-items:center;justify-content:center;background-color:#f0f2f5;cursor:pointer;position:absolute;z-index:10;margin-left:-1px;margin-top:-.5px}:host ::ng-deep .ProseMirror .selector-column:hover,:host ::ng-deep .ProseMirror .selector-row:hover{background-color:#e1e5eb}:host ::ng-deep .ProseMirror .selector-column{width:calc(100% + 1px);border-left:1px solid #e1e5eb;height:11px;top:-11px;left:0}:host ::ng-deep .ProseMirror .selector-column.first{border-left:none}:host ::ng-deep .ProseMirror .selector-column.selected{background-color:#b2bccc;border-color:transparent}:host ::ng-deep .ProseMirror .selector-column.selected:before{content:\"\";border-bottom:2px dotted #fff;width:.6rem}:host ::ng-deep .ProseMirror .selector-column:hover:before{content:\"\";border-bottom:2px dotted #61779d;width:.6rem}:host ::ng-deep .ProseMirror .selector-column.selected:before{border-bottom:2px dotted #fff}:host ::ng-deep .ProseMirror .selector-row{height:calc(100% + 1px);border-top:1px solid #e1e5eb;width:11px;left:-11px;top:0}:host ::ng-deep .ProseMirror .selector-row.first{border-top:none}:host ::ng-deep .ProseMirror .selector-row.selected{background-color:#b2bccc;border-color:transparent}:host ::ng-deep .ProseMirror .selector-row.selected:before{content:\"\";border-left:2px dotted #fff;height:.575rem}:host ::ng-deep .ProseMirror .selector-row:hover:before{content:\"\";border-left:2px dotted #61779d;height:.575rem}:host ::ng-deep .ProseMirror .selector-row.selected:before{border-left:2px dotted #fff}\n"], dependencies: [{ kind: "directive", type: EditorDirective, selector: "tiptap-editor[editor]", inputs: ["editor", "outputFormat"], outputs: ["blur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: RichTextEditorToolbarComponent, selector: "lx-rich-text-editor-toolbar", inputs: ["editor"] }, { kind: "component", type: TableBubbleMenuComponent, selector: "lx-table-bubble-menu", inputs: ["editor"] }, { kind: "pipe", type: ExtensionEnabledPipe, name: "lxExtensionEnabled" }, { kind: "component", type: TruncateButtonComponent, selector: "lx-truncate-button", inputs: ["editor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
10513
|
+
}
|
10514
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RichTextEditorComponent, decorators: [{
|
10515
|
+
type: Component,
|
10516
|
+
args: [{ selector: 'lx-rich-text-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
10517
|
+
EditorDirective,
|
10518
|
+
FormsModule,
|
10519
|
+
RichTextEditorToolbarComponent,
|
10520
|
+
TableBubbleMenuComponent,
|
10521
|
+
ExtensionEnabledPipe,
|
10522
|
+
TruncateButtonComponent
|
10523
|
+
], hostDirectives: [
|
10524
|
+
{
|
10525
|
+
directive: TipTapEditorDirective,
|
10526
|
+
inputs: ['outputFormat', 'additionalFeatures', 'ariaLabelledBy', 'customExtensions', 'mode']
|
10527
|
+
},
|
10528
|
+
{
|
10529
|
+
directive: TrackingDirective,
|
10530
|
+
outputs: ['trackEvent']
|
10531
|
+
}
|
10532
|
+
], providers: [
|
10533
|
+
ExtensionsBuilder,
|
10534
|
+
{
|
10535
|
+
provide: NG_VALUE_ACCESSOR,
|
10536
|
+
useExisting: forwardRef(() => RichTextEditorComponent),
|
10537
|
+
multi: true
|
10538
|
+
}
|
10539
|
+
], host: {
|
10540
|
+
'[style.--editor-max-height]': 'maxHeight()'
|
10541
|
+
}, template: "<div [class.viewMode]=\"mode() === 'view'\" [class.editMode]=\"mode() === 'edit'\" [class.disabled]=\"disabled\">\n @if (mode() === 'edit' && !disabled) {\n <lx-rich-text-editor-toolbar (mousedown)=\"$event.preventDefault()\" [editor]=\"editor()\">\n <ng-content select=\".diagram-btn\" ngProjectAs=\".diagram-btn\" />\n </lx-rich-text-editor-toolbar>\n }\n <tiptap-editor\n [editor]=\"editor()\"\n [outputFormat]=\"outputFormat()\"\n [ngModel]=\"value\"\n (ngModelChange)=\"onChange($event)\"\n (blur)=\"blur.emit()\"\n [disabled]=\"mode() === 'view' || disabled\"\n />\n\n @if (('truncate' | lxExtensionEnabled: editor()) && mode() === 'view') {\n <lx-truncate-button [editor]=\"editor()\" />\n }\n</div>\n\n@if ('table' | lxExtensionEnabled: editor()) {\n <lx-table-bubble-menu [editor]=\"editor()\" />\n}\n\n<ng-content />\n", styles: [":host{display:flex;width:100%}.editorButton{margin:0 5px}.editMode{width:100%;border:1px solid #99a5bb;background-color:#fff;border-radius:2px}:host ::ng-deep .editMode .ProseMirror{padding:15px 18.9px;resize:vertical;max-height:var(--editor-max-height, auto)}.viewMode{width:100%}.disabled{background-color:#f0f2f5;color:#61779d}:host ::ng-deep .ProseMirror{outline-width:0;border-radius:2px;overflow:auto}:host ::ng-deep .ProseMirror-gapcursor:after{width:1px;height:14px;border-left:1px solid #000}:host ::ng-deep .ProseMirror p:first-child{margin-top:0}:host ::ng-deep .ProseMirror p:last-child{margin-bottom:0}:host ::ng-deep .ProseMirror h1:first-child,:host ::ng-deep .ProseMirror h2:first-child,:host ::ng-deep .ProseMirror h3:first-child,:host ::ng-deep .ProseMirror h4:first-child,:host ::ng-deep .ProseMirror h5:first-child,:host ::ng-deep .ProseMirror h6:first-child{margin-top:0}:host ::ng-deep .ProseMirror h1:last-child,:host ::ng-deep .ProseMirror h2:last-child,:host ::ng-deep .ProseMirror h3:last-child,:host ::ng-deep .ProseMirror h4:last-child,:host ::ng-deep .ProseMirror h5:last-child,:host ::ng-deep .ProseMirror h6:last-child{margin-bottom:0}:host ::ng-deep .ProseMirror h1{font-size:var(--lxFontHeader1Size)}:host ::ng-deep .ProseMirror h2{font-size:var(--lxFontHeader2Size)}:host ::ng-deep .ProseMirror h3{font-size:var(--lxFontHeader3Size)}:host ::ng-deep .ProseMirror h4{font-size:var(--lxFontHeader4Size)}:host ::ng-deep .ProseMirror .tableWrapper{margin:20px 0}:host ::ng-deep .ProseMirror .tableWrapper h1,:host ::ng-deep .ProseMirror .tableWrapper h2,:host ::ng-deep .ProseMirror .tableWrapper h3,:host ::ng-deep .ProseMirror .tableWrapper h4,:host ::ng-deep .ProseMirror .tableWrapper h5,:host ::ng-deep .ProseMirror .tableWrapper h6{margin-top:0}:host ::ng-deep .ProseMirror .tableWrapper table{border-collapse:collapse;margin:0;table-layout:fixed;width:100%}:host ::ng-deep .ProseMirror .tableWrapper table th,:host ::ng-deep .ProseMirror .tableWrapper table td{border:1px solid #c2c9d6;box-sizing:border-box;min-width:1em;padding:12px;position:relative;vertical-align:top}:host ::ng-deep .ProseMirror .tableWrapper table th>*,:host ::ng-deep .ProseMirror .tableWrapper table td>*{margin-bottom:0}:host ::ng-deep .ProseMirror .tableWrapper table th{background-color:#c2c9d6;font-weight:700;text-align:left}:host ::ng-deep .ProseMirror .tableWrapper table p{margin:0}:host ::ng-deep .ProseMirror .tableWrapper table .selectedCell:after{content:\"\";position:absolute;inset:0;pointer-events:none;z-index:2}:host ::ng-deep .ProseMirror .tableWrapper table .column-resize-handle{background-color:#b2bccc;bottom:-2px;right:-2px;position:absolute;top:0;width:4px;pointer-events:none}:host ::ng-deep .ProseMirror .selectedCell{background-color:#e1e5eb}:host ::ng-deep .ProseMirror .selector-column,:host ::ng-deep .ProseMirror .selector-row{all:unset;display:flex;align-items:center;justify-content:center;background-color:#f0f2f5;cursor:pointer;position:absolute;z-index:10;margin-left:-1px;margin-top:-.5px}:host ::ng-deep .ProseMirror .selector-column:hover,:host ::ng-deep .ProseMirror .selector-row:hover{background-color:#e1e5eb}:host ::ng-deep .ProseMirror .selector-column{width:calc(100% + 1px);border-left:1px solid #e1e5eb;height:11px;top:-11px;left:0}:host ::ng-deep .ProseMirror .selector-column.first{border-left:none}:host ::ng-deep .ProseMirror .selector-column.selected{background-color:#b2bccc;border-color:transparent}:host ::ng-deep .ProseMirror .selector-column.selected:before{content:\"\";border-bottom:2px dotted #fff;width:.6rem}:host ::ng-deep .ProseMirror .selector-column:hover:before{content:\"\";border-bottom:2px dotted #61779d;width:.6rem}:host ::ng-deep .ProseMirror .selector-column.selected:before{border-bottom:2px dotted #fff}:host ::ng-deep .ProseMirror .selector-row{height:calc(100% + 1px);border-top:1px solid #e1e5eb;width:11px;left:-11px;top:0}:host ::ng-deep .ProseMirror .selector-row.first{border-top:none}:host ::ng-deep .ProseMirror .selector-row.selected{background-color:#b2bccc;border-color:transparent}:host ::ng-deep .ProseMirror .selector-row.selected:before{content:\"\";border-left:2px dotted #fff;height:.575rem}:host ::ng-deep .ProseMirror .selector-row:hover:before{content:\"\";border-left:2px dotted #61779d;height:.575rem}:host ::ng-deep .ProseMirror .selector-row.selected:before{border-left:2px dotted #fff}\n"] }]
|
10542
|
+
}] });
|
10543
|
+
|
10544
|
+
class FocusEditorDirective {
|
10545
|
+
constructor() {
|
10546
|
+
this.editor = inject(TipTapEditorDirective).editor;
|
10547
|
+
}
|
10548
|
+
ngOnInit() {
|
10549
|
+
this.editor().on('create', () => {
|
10550
|
+
this.editor().commands.focus('start');
|
10551
|
+
});
|
10552
|
+
}
|
10553
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FocusEditorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
10554
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: FocusEditorDirective, isStandalone: true, selector: "lx-rich-text-editor[lxFocusEditor]", ngImport: i0 }); }
|
10555
|
+
}
|
10556
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: FocusEditorDirective, decorators: [{
|
9140
10557
|
type: Directive,
|
9141
10558
|
args: [{
|
9142
|
-
selector: '[
|
10559
|
+
selector: 'lx-rich-text-editor[lxFocusEditor]'
|
9143
10560
|
}]
|
9144
|
-
}]
|
9145
|
-
|
9146
|
-
|
10561
|
+
}] });
|
10562
|
+
|
10563
|
+
class HighlightTermDirective {
|
10564
|
+
constructor() {
|
10565
|
+
this.lxHighlightTerm = input();
|
10566
|
+
this.editor = inject(TipTapEditorDirective).editor;
|
10567
|
+
effect(() => {
|
10568
|
+
this.editor().view.dispatch(this.editor().view.state.tr.setMeta(highlightTermStatePluginKey, {
|
10569
|
+
term: this.lxHighlightTerm()
|
10570
|
+
}));
|
10571
|
+
});
|
10572
|
+
}
|
10573
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HighlightTermDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
10574
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.6", type: HighlightTermDirective, isStandalone: true, selector: "lx-rich-text-editor[lxHighlightTerm]", inputs: { lxHighlightTerm: { classPropertyName: "lxHighlightTerm", publicName: "lxHighlightTerm", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
10575
|
+
}
|
10576
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: HighlightTermDirective, decorators: [{
|
10577
|
+
type: Directive,
|
10578
|
+
args: [{
|
10579
|
+
selector: 'lx-rich-text-editor[lxHighlightTerm]'
|
10580
|
+
}]
|
10581
|
+
}], ctorParameters: () => [] });
|
10582
|
+
|
10583
|
+
class TruncateDirective {
|
10584
|
+
constructor() {
|
10585
|
+
this.lxTruncate = input();
|
10586
|
+
this.editor = inject(TipTapEditorDirective).editor;
|
10587
|
+
effect(() => {
|
10588
|
+
const options = this.lxTruncate();
|
10589
|
+
if (options) {
|
10590
|
+
this.editor().view.dispatch(this.editor().view.state.tr.setMeta(truncatePluginKey, options));
|
10591
|
+
}
|
10592
|
+
});
|
10593
|
+
}
|
10594
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TruncateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
10595
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.6", type: TruncateDirective, isStandalone: true, selector: "lx-rich-text-editor[lxTruncate]", inputs: { lxTruncate: { classPropertyName: "lxTruncate", publicName: "lxTruncate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
10596
|
+
}
|
10597
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: TruncateDirective, decorators: [{
|
10598
|
+
type: Directive,
|
10599
|
+
args: [{
|
10600
|
+
selector: 'lx-rich-text-editor[lxTruncate]'
|
10601
|
+
}]
|
10602
|
+
}], ctorParameters: () => [] });
|
10603
|
+
|
10604
|
+
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
10605
|
+
class AngularNodeViewComponent {
|
10606
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: AngularNodeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
10607
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: AngularNodeViewComponent, isStandalone: false, selector: "ng-component", inputs: { editor: "editor", node: "node", decorations: "decorations", selected: "selected", extension: "extension", getPos: "getPos", updateAttributes: "updateAttributes", deleteNode: "deleteNode" }, ngImport: i0, template: '', isInline: true }); }
|
10608
|
+
}
|
10609
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: AngularNodeViewComponent, decorators: [{
|
10610
|
+
type: Component,
|
10611
|
+
args: [{
|
10612
|
+
template: '',
|
10613
|
+
// eslint-disable-next-line @angular-eslint/prefer-standalone
|
10614
|
+
standalone: false
|
10615
|
+
}]
|
10616
|
+
}], propDecorators: { editor: [{
|
9147
10617
|
type: Input
|
9148
|
-
}],
|
10618
|
+
}], node: [{
|
10619
|
+
type: Input
|
10620
|
+
}], decorations: [{
|
10621
|
+
type: Input
|
10622
|
+
}], selected: [{
|
10623
|
+
type: Input
|
10624
|
+
}], extension: [{
|
10625
|
+
type: Input
|
10626
|
+
}], getPos: [{
|
10627
|
+
type: Input
|
10628
|
+
}], updateAttributes: [{
|
10629
|
+
type: Input
|
10630
|
+
}], deleteNode: [{
|
9149
10631
|
type: Input
|
9150
10632
|
}] } });
|
9151
|
-
|
9152
|
-
class
|
9153
|
-
constructor() {
|
9154
|
-
this.
|
9155
|
-
this.
|
10633
|
+
|
10634
|
+
class AngularRenderer {
|
10635
|
+
constructor(ViewComponent, injector, props) {
|
10636
|
+
this.applicationRef = injector.get(ApplicationRef);
|
10637
|
+
this.componentRef = createComponent(ViewComponent, {
|
10638
|
+
environmentInjector: this.applicationRef.injector,
|
10639
|
+
elementInjector: injector
|
10640
|
+
});
|
10641
|
+
// set input props to the component
|
10642
|
+
this.updateProps(props);
|
10643
|
+
this.applicationRef.attachView(this.componentRef.hostView);
|
10644
|
+
}
|
10645
|
+
get instance() {
|
10646
|
+
return this.componentRef.instance;
|
10647
|
+
}
|
10648
|
+
get elementRef() {
|
10649
|
+
return this.componentRef.injector.get(ElementRef);
|
10650
|
+
}
|
10651
|
+
get dom() {
|
10652
|
+
return this.elementRef.nativeElement;
|
10653
|
+
}
|
10654
|
+
updateProps(props) {
|
10655
|
+
Object.entries(props).forEach(([key, value]) => {
|
10656
|
+
this.componentRef.setInput(key, value);
|
10657
|
+
});
|
10658
|
+
}
|
10659
|
+
detectChanges() {
|
10660
|
+
this.componentRef.changeDetectorRef.detectChanges();
|
10661
|
+
}
|
10662
|
+
destroy() {
|
10663
|
+
this.componentRef.destroy();
|
10664
|
+
this.applicationRef.detachView(this.componentRef.hostView);
|
9156
10665
|
}
|
9157
10666
|
}
|
9158
10667
|
|
9159
|
-
|
9160
|
-
|
9161
|
-
|
9162
|
-
|
9163
|
-
|
9164
|
-
|
9165
|
-
|
9166
|
-
|
9167
|
-
|
9168
|
-
|
9169
|
-
|
9170
|
-
|
9171
|
-
|
9172
|
-
|
9173
|
-
|
9174
|
-
|
10668
|
+
class AngularNodeView extends NodeView {
|
10669
|
+
mount() {
|
10670
|
+
const injector = this.options.injector;
|
10671
|
+
const props = {
|
10672
|
+
editor: this.editor,
|
10673
|
+
node: this.node,
|
10674
|
+
decorations: this.decorations,
|
10675
|
+
selected: false,
|
10676
|
+
extension: this.extension,
|
10677
|
+
getPos: () => this.getPos(),
|
10678
|
+
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
10679
|
+
deleteNode: () => this.deleteNode()
|
10680
|
+
};
|
10681
|
+
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
|
10682
|
+
this.editor.on('selectionUpdate', this.handleSelectionUpdate);
|
10683
|
+
// create renderer
|
10684
|
+
this.renderer = new AngularRenderer(this.component, injector, props);
|
10685
|
+
// Register drag handler
|
10686
|
+
if (this.extension.config.draggable) {
|
10687
|
+
this.renderer.elementRef.nativeElement.ondragstart = (e) => {
|
10688
|
+
this.onDragStart(e);
|
9175
10689
|
};
|
9176
10690
|
}
|
9177
|
-
|
9178
|
-
|
10691
|
+
this.contentDOMElement = this.node.isLeaf ? null : document.createElement(this.node.isInline ? 'span' : 'div');
|
10692
|
+
if (this.contentDOMElement) {
|
10693
|
+
// For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
|
10694
|
+
// With this fix it seems to work fine
|
10695
|
+
// See: https://github.com/ueberdosis/tiptap/issues/1197
|
10696
|
+
this.contentDOMElement.style.whiteSpace = 'inherit';
|
10697
|
+
// Required for editable node views
|
10698
|
+
// The content won't be rendered if `editable` is set to `false`
|
10699
|
+
this.renderer.detectChanges();
|
9179
10700
|
}
|
10701
|
+
this.appendContendDom();
|
9180
10702
|
}
|
9181
|
-
|
9182
|
-
|
9183
|
-
|
9184
|
-
|
9185
|
-
|
9186
|
-
return array.map((item) => item.toLocaleLowerCase()).indexOf(valueToFind) > -1;
|
9187
|
-
}
|
9188
|
-
/**
|
9189
|
-
* Validates that a string is not inside an array of strings
|
9190
|
-
*/
|
9191
|
-
function ValidateStringNotInArray(array, valueMapFunction, validatorName = 'stringNotInArray') {
|
9192
|
-
return function (control) {
|
9193
|
-
if (!control.value) {
|
10703
|
+
get dom() {
|
10704
|
+
return this.renderer.dom;
|
10705
|
+
}
|
10706
|
+
get contentDOM() {
|
10707
|
+
if (this.node.isLeaf) {
|
9194
10708
|
return null;
|
9195
10709
|
}
|
9196
|
-
|
9197
|
-
|
9198
|
-
|
9199
|
-
|
10710
|
+
return this.contentDOMElement;
|
10711
|
+
}
|
10712
|
+
appendContendDom() {
|
10713
|
+
const contentElement = this.dom.querySelector('[data-node-view-content]');
|
10714
|
+
if (this.contentDOMElement && contentElement && !contentElement.contains(this.contentDOMElement)) {
|
10715
|
+
contentElement.appendChild(this.contentDOMElement);
|
9200
10716
|
}
|
9201
|
-
|
9202
|
-
|
9203
|
-
}
|
9204
|
-
|
9205
|
-
|
9206
|
-
if (!control.value) {
|
9207
|
-
return of(null);
|
10717
|
+
}
|
10718
|
+
handleSelectionUpdate() {
|
10719
|
+
const { from, to } = this.editor.state.selection;
|
10720
|
+
if (from <= this.getPos() && to >= this.getPos() + this.node.nodeSize) {
|
10721
|
+
this.selectNode();
|
9208
10722
|
}
|
9209
|
-
|
9210
|
-
|
10723
|
+
else {
|
10724
|
+
this.deselectNode();
|
10725
|
+
}
|
10726
|
+
}
|
10727
|
+
update(node, decorations) {
|
10728
|
+
const updateProps = () => {
|
10729
|
+
this.renderer.updateProps({ node, decorations });
|
10730
|
+
};
|
10731
|
+
if (this.options.update) {
|
10732
|
+
const oldNode = this.node;
|
10733
|
+
const oldDecorations = this.decorations;
|
10734
|
+
this.node = node;
|
10735
|
+
this.decorations = decorations;
|
10736
|
+
return this.options.update({
|
10737
|
+
oldNode,
|
10738
|
+
oldDecorations,
|
10739
|
+
newNode: node,
|
10740
|
+
newDecorations: decorations,
|
10741
|
+
updateProps: () => updateProps()
|
10742
|
+
});
|
10743
|
+
}
|
10744
|
+
if (node.type !== this.node.type) {
|
10745
|
+
return false;
|
10746
|
+
}
|
10747
|
+
if (node === this.node && this.decorations === decorations) {
|
10748
|
+
return true;
|
10749
|
+
}
|
10750
|
+
this.node = node;
|
10751
|
+
this.decorations = decorations;
|
10752
|
+
updateProps();
|
10753
|
+
return true;
|
10754
|
+
}
|
10755
|
+
selectNode() {
|
10756
|
+
this.renderer.updateProps({ selected: true });
|
10757
|
+
}
|
10758
|
+
deselectNode() {
|
10759
|
+
this.renderer.updateProps({ selected: false });
|
10760
|
+
}
|
10761
|
+
destroy() {
|
10762
|
+
this.renderer.destroy();
|
10763
|
+
this.editor.off('selectionUpdate', this.handleSelectionUpdate);
|
10764
|
+
this.contentDOMElement = null;
|
10765
|
+
}
|
9211
10766
|
}
|
10767
|
+
const AngularNodeViewRenderer = (ViewComponent, options) => {
|
10768
|
+
return (props) => {
|
10769
|
+
return new AngularNodeView(ViewComponent, props, options);
|
10770
|
+
};
|
10771
|
+
};
|
9212
10772
|
|
9213
|
-
const MODAL_CLOSE = new InjectionToken('MODAL_CLOSE');
|
9214
10773
|
/**
|
9215
|
-
*
|
9216
|
-
*
|
9217
|
-
*
|
9218
|
-
*
|
9219
|
-
* Other - modal close with the trigger through closeModal$ subject
|
10774
|
+
* Removes markdown syntax from text.
|
10775
|
+
* e.g '### This is a heading' will become 'This is a heading'
|
10776
|
+
*
|
10777
|
+
* It does not remove links. Links can be post processed by LxLinkifyPipe or LxUnLinkifyPipe
|
9220
10778
|
*/
|
9221
|
-
|
9222
|
-
(
|
9223
|
-
|
9224
|
-
ModalCloseClickLocation["CloseButton"] = "closeButton";
|
9225
|
-
ModalCloseClickLocation["OutsideClick"] = "outsideClick";
|
9226
|
-
ModalCloseClickLocation["CancelButton"] = "cancelButton";
|
9227
|
-
ModalCloseClickLocation["Other"] = "other";
|
9228
|
-
})(ModalCloseClickLocation || (ModalCloseClickLocation = {}));
|
9229
|
-
|
9230
|
-
class ModalFooterComponent {
|
9231
|
-
constructor() {
|
9232
|
-
this.border = false;
|
9233
|
-
}
|
9234
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
9235
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: ModalFooterComponent, isStandalone: true, selector: "lx-modal-footer", inputs: { border: "border" }, ngImport: i0, template: "<div class=\"footerContainer\" [class.border]=\"border\">\n <ng-content />\n</div>\n", styles: [":host{display:block;text-align:right;--modal-footer-padding-left: 90px;--modal-footer-padding-right: 0;--modal-footer-display: block}:host-context(.fullscreen) .footerContainer{padding-left:var(--modal-footer-padding-left);padding-right:var(--modal-footer-padding-right);height:70px}:host-context(.dialog) .footerContainer,:host-context(.dialog-large) .footerContainer{padding:16px;height:64px;border-radius:0 0 6px 6px;display:var(--modal-footer-display)}.footerContainer.border{border-top:1px solid #cfd5df}\n"] }); }
|
9236
|
-
}
|
9237
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalFooterComponent, decorators: [{
|
9238
|
-
type: Component,
|
9239
|
-
args: [{ selector: 'lx-modal-footer', template: "<div class=\"footerContainer\" [class.border]=\"border\">\n <ng-content />\n</div>\n", styles: [":host{display:block;text-align:right;--modal-footer-padding-left: 90px;--modal-footer-padding-right: 0;--modal-footer-display: block}:host-context(.fullscreen) .footerContainer{padding-left:var(--modal-footer-padding-left);padding-right:var(--modal-footer-padding-right);height:70px}:host-context(.dialog) .footerContainer,:host-context(.dialog-large) .footerContainer{padding:16px;height:64px;border-radius:0 0 6px 6px;display:var(--modal-footer-display)}.footerContainer.border{border-top:1px solid #cfd5df}\n"] }]
|
9240
|
-
}], propDecorators: { border: [{
|
9241
|
-
type: Input
|
9242
|
-
}] } });
|
9243
|
-
|
9244
|
-
class ModalHeaderComponent {
|
9245
|
-
constructor() {
|
9246
|
-
this.title = '';
|
9247
|
-
this.bottomBorder = true;
|
10779
|
+
class RemoveMarkdownPipe {
|
10780
|
+
transform(markdown) {
|
10781
|
+
return markdownToText(markdown);
|
9248
10782
|
}
|
9249
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
9250
|
-
static { this.ɵ
|
9251
|
-
}
|
9252
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalHeaderComponent, decorators: [{
|
9253
|
-
type: Component,
|
9254
|
-
args: [{ selector: 'lx-modal-header', template: "<div class=\"headerContainer\" [class.bottomBorder]=\"bottomBorder\">\n <ng-content />\n @if (title.length > 0) {\n <h1 class=\"lx-heading-2\">{{ title }}</h1>\n }\n</div>\n", styles: [":host{display:block}:host.lxModalHeaderOneLine h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:var(--lx-modal-header-max-width)}:host-context(.fullscreen) .headerContainer{padding:24px 90px;border-bottom:none}:host-context(.fullscreen) .headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}:host-context(.dialog) .headerContainer,:host-context(.dialog-large) .headerContainer{padding:24px 16px;border-radius:6px 6px 0 0}:host-context(.dialog) .headerContainer h1,:host-context(.dialog-large) .headerContainer h1{word-break:break-word}h1{margin:0 auto;padding:0;color:#2a303d;text-align:center}.headerContainer{display:flex}.headerContainer.bottomBorder{border-bottom:1px solid #cfd5df}\n"] }]
|
9255
|
-
}], propDecorators: { title: [{
|
9256
|
-
type: Input
|
9257
|
-
}], bottomBorder: [{
|
9258
|
-
type: Input
|
9259
|
-
}] } });
|
9260
|
-
|
9261
|
-
class ModalContentDirective {
|
9262
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: ModalContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
9263
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: ModalContentDirective, isStandalone: true, selector: "[lxModalContent]", ngImport: i0 }); }
|
10783
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RemoveMarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
10784
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.6", ngImport: i0, type: RemoveMarkdownPipe, isStandalone: true, name: "lxRemoveMarkdown" }); }
|
9264
10785
|
}
|
9265
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
9266
|
-
type:
|
10786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: RemoveMarkdownPipe, decorators: [{
|
10787
|
+
type: Pipe,
|
9267
10788
|
args: [{
|
9268
|
-
|
10789
|
+
name: 'lxRemoveMarkdown'
|
9269
10790
|
}]
|
9270
10791
|
}] });
|
10792
|
+
function markdownToText(md) {
|
10793
|
+
let output = md || '';
|
10794
|
+
try {
|
10795
|
+
output = output
|
10796
|
+
// Remove horizontal rules (stripListHeaders conflict with this rule, which is why it has been moved to the top)
|
10797
|
+
.replace(/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/gm, '')
|
10798
|
+
// stripListLeaders
|
10799
|
+
.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1')
|
10800
|
+
// Strikethrough
|
10801
|
+
.replace(/~~/g, '')
|
10802
|
+
// Fenced codeblocks with backticks
|
10803
|
+
.replace(/```(?:.*)\n([\s\S]*?)```/g, (_, code) => code.trim())
|
10804
|
+
// Remove HTML tags
|
10805
|
+
.replace(/<[^>]*>/g, '')
|
10806
|
+
// Remove images
|
10807
|
+
.replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
|
10808
|
+
// Remove blockquotes
|
10809
|
+
.replace(/^(\n)?\s{0,3}>\s?/gm, '$1')
|
10810
|
+
// Remove atx-style headers
|
10811
|
+
.replace(/^(\n)?\s{0,}#{1,6}\s*( (.+))? +#+$|^(\n)?\s{0,}#{1,6}\s*( (.+))?$/gm, '$1$3$4$6')
|
10812
|
+
// Remove * emphasis
|
10813
|
+
.replace(/([\*]+)(\S)(.*?\S)??\1/g, '$2$3')
|
10814
|
+
// Remove _ emphasis. Unlike *, _ emphasis gets rendered only if
|
10815
|
+
// 1. Either there is a whitespace character before opening _ and after closing _.
|
10816
|
+
// 2. Or _ is at the start/end of the string.
|
10817
|
+
.replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, '$1$3$4$5')
|
10818
|
+
// Remove single-line code blocks (already handled multiline above )
|
10819
|
+
.replace(/(`{3,})(.*?)\1/gm, '$2')
|
10820
|
+
// Remove inline code
|
10821
|
+
.replace(/`(.+?)`/g, '$1');
|
10822
|
+
}
|
10823
|
+
catch (e) {
|
10824
|
+
return md;
|
10825
|
+
}
|
10826
|
+
return output;
|
10827
|
+
}
|
9271
10828
|
|
9272
|
-
|
9273
|
-
|
9274
|
-
|
9275
|
-
* ## Usage
|
9276
|
-
*
|
9277
|
-
* 1. Import `LxModalModule` and `LxCoreUiModule` modules from `@leanix/components` in your module where you want to use the component.
|
9278
|
-
*
|
9279
|
-
* ```ts
|
9280
|
-
* import { LxModalModule, LxCoreUiModule } from '@leanix/components';
|
9281
|
-
* ```
|
9282
|
-
*
|
9283
|
-
* 2. Use the **lx-modal** component in your template with the parameters described below.
|
9284
|
-
*
|
9285
|
-
* - **`open`**: Whether the modal is open or closed.
|
9286
|
-
* - **`size`**: 'dialog' | 'dialog-large'.
|
9287
|
-
* - **`verticalScroll`**: Whether the modal is scrollable or not.
|
9288
|
-
* - **`showHeader`**: Whether the modal has a header or not.
|
9289
|
-
* - **`showFooter`**: Whether the modal has a footer or not.
|
9290
|
-
* - **`showCloseButton`**: Whether to show the close button.
|
9291
|
-
* - **`showBackButton`**: Whether to show the back button.
|
9292
|
-
*
|
9293
|
-
* 3. Use optional **lx-modal-header** component in your template with the parameters described below.
|
9294
|
-
*
|
9295
|
-
* - **`title`**: Title of the modal.
|
9296
|
-
* - **`subtitle`**: Subtitle of the modal.
|
9297
|
-
* - **`bottomBorder`**: Whether to show a bottom border.
|
9298
|
-
*
|
9299
|
-
* 4. Use optional **lx-modal-footer** component in your template with the parameters described below.
|
9300
|
-
*
|
9301
|
-
* - **`border`**: Whether to show the footer at the bottom of the modal.
|
9302
|
-
*
|
9303
|
-
* **GLOBAL PROVIDERS** required for this component:
|
9304
|
-
* - `provideAnimations()`
|
9305
|
-
*
|
9306
|
-
* **ATTENTION - SCROLLABLE DIALOG**:
|
9307
|
-
* The <lx-modal> component when used as "dialog" is not designed to work with a
|
9308
|
-
* scrollable body (via `overflow: auto | scroll`) in combination with dropdowns.
|
9309
|
-
* The overflow on the body will also clip the dropdowns, which is expected.
|
9310
|
-
*
|
9311
|
-
* Reasoning:
|
9312
|
-
* The contents within the dialog should be just a few elements which fit and
|
9313
|
-
* justify the usage of a dialog. If the content is larger than the dialog, and thus
|
9314
|
-
* requires scrolling, we should discuss whether to put it into a dialog at all
|
9315
|
-
* and rather think about putting the content on a separate route or
|
9316
|
-
* using the fullscreen version of the modal.
|
9317
|
-
*/
|
9318
|
-
class ModalComponent {
|
9319
|
-
get content() {
|
9320
|
-
return this.explicitContent || this.implicitContent;
|
9321
|
-
}
|
9322
|
-
/** @internal */
|
9323
|
-
onEscape() {
|
9324
|
-
if (this.open && this.showCloseButton) {
|
9325
|
-
this.closeModal(ModalCloseClickLocation.Escape);
|
9326
|
-
}
|
9327
|
-
}
|
9328
|
-
constructor(overlay, renderer, closeModal$, focusTrap) {
|
9329
|
-
this.overlay = overlay;
|
10829
|
+
class MaxLengthCounterDirective {
|
10830
|
+
constructor(el, renderer, ngControl) {
|
10831
|
+
this.el = el;
|
9330
10832
|
this.renderer = renderer;
|
9331
|
-
this.
|
9332
|
-
this.
|
9333
|
-
|
9334
|
-
this.
|
9335
|
-
/** Whether the modal is open or closed. */
|
9336
|
-
this.open = false;
|
9337
|
-
/** Whether to show the close button. */
|
9338
|
-
this.showCloseButton = true;
|
9339
|
-
/** Whether to show the back button. */
|
9340
|
-
this.showBackButton = false;
|
9341
|
-
/*
|
9342
|
-
* If true, then the content area scrolls vertically instead of expanding its height.
|
9343
|
-
* This can be a problem if the content has dropdowns or date inputs, but can be good if the content has a huge amount of text.
|
9344
|
-
*/
|
9345
|
-
this.verticalScroll = false;
|
9346
|
-
/** The size of the modal. */
|
9347
|
-
this.size = 'fullscreen';
|
9348
|
-
/**
|
9349
|
-
* Minimum width of the modal.
|
9350
|
-
*
|
9351
|
-
* _NB: Some modal implementations rely on this minWidth being 600px_
|
9352
|
-
*/
|
9353
|
-
this.minWidth = '600px';
|
9354
|
-
/** Whether the modal is a focus trap. */
|
9355
|
-
this.isFocusTrap = false;
|
9356
|
-
/** Event emitted when the modal is closed. */
|
9357
|
-
this.close = new EventEmitter();
|
9358
|
-
/** Event emitted when the back button is clicked. */
|
9359
|
-
this.back = new EventEmitter();
|
9360
|
-
/** @internal */
|
9361
|
-
this.closeLocation = ModalCloseClickLocation;
|
9362
|
-
/** @internal */
|
9363
|
-
this.destroyed$ = new Subject();
|
10833
|
+
this.ngControl = ngControl;
|
10834
|
+
this.lxMaxLengthCounter = null;
|
10835
|
+
this.counterElement = null;
|
10836
|
+
this.destroy$ = new Subject();
|
9364
10837
|
}
|
9365
10838
|
ngOnInit() {
|
9366
|
-
this.
|
9367
|
-
|
9368
|
-
|
9369
|
-
|
9370
|
-
|
9371
|
-
|
9372
|
-
|
9373
|
-
|
9374
|
-
|
9375
|
-
|
9376
|
-
|
9377
|
-
|
9378
|
-
|
9379
|
-
|
9380
|
-
|
9381
|
-
|
9382
|
-
|
9383
|
-
|
9384
|
-
});
|
10839
|
+
this.setupMaxLengthValidation();
|
10840
|
+
this.connectCounter();
|
10841
|
+
this.updateCounter();
|
10842
|
+
this.subscribeToValueChanges();
|
10843
|
+
this.setupInputRestriction();
|
10844
|
+
this.checkForInitialOversize();
|
10845
|
+
}
|
10846
|
+
ngOnDestroy() {
|
10847
|
+
this.destroy$.next();
|
10848
|
+
this.destroy$.complete();
|
10849
|
+
}
|
10850
|
+
setupMaxLengthValidation() {
|
10851
|
+
const maxLength = this.getMaxLength();
|
10852
|
+
const control = this.ngControl.control;
|
10853
|
+
if (maxLength !== null && control) {
|
10854
|
+
const validators = control.validator ? [control.validator, Validators.maxLength(maxLength)] : Validators.maxLength(maxLength);
|
10855
|
+
control.setValidators(validators);
|
10856
|
+
control.updateValueAndValidity();
|
9385
10857
|
}
|
9386
|
-
|
9387
|
-
|
9388
|
-
|
9389
|
-
|
9390
|
-
panelClass: this.size,
|
9391
|
-
minWidth: this.minWidth,
|
9392
|
-
positionStrategy,
|
9393
|
-
hasBackdrop: true,
|
9394
|
-
scrollStrategy: this.overlay.scrollStrategies.block()
|
9395
|
-
});
|
10858
|
+
}
|
10859
|
+
getMaxLength() {
|
10860
|
+
if (this.lxMaxLengthCounter) {
|
10861
|
+
return this.lxMaxLengthCounter;
|
9396
10862
|
}
|
9397
|
-
|
9398
|
-
|
9399
|
-
|
9400
|
-
|
9401
|
-
|
10863
|
+
const control = this.ngControl.control;
|
10864
|
+
const validatorFn = control?.validator;
|
10865
|
+
const errors = validatorFn?.(new FormControl({ length: Infinity }));
|
10866
|
+
const requiredLength = errors?.['maxlength']['requiredLength'];
|
10867
|
+
this.lxMaxLengthCounter = requiredLength ? requiredLength.toString() : null;
|
10868
|
+
if (this.lxMaxLengthCounter === null) {
|
10869
|
+
console.warn('lxMaxLength directive is used without a value or a control with a maxLength validator');
|
9402
10870
|
}
|
10871
|
+
return requiredLength;
|
9403
10872
|
}
|
9404
|
-
|
9405
|
-
|
9406
|
-
|
9407
|
-
|
9408
|
-
if (!this.open && this.overlayRef && this.overlayRef.hasAttached()) {
|
9409
|
-
this.closeModal(ModalCloseClickLocation.Other);
|
10873
|
+
connectCounter() {
|
10874
|
+
this.el.nativeElement.classList.add('lx-max-length-counter-input');
|
10875
|
+
if (this.lxMaxLengthCounterRef) {
|
10876
|
+
this.counterElement = this.lxMaxLengthCounterRef;
|
9410
10877
|
}
|
9411
|
-
|
9412
|
-
this.
|
10878
|
+
else {
|
10879
|
+
this.createCounter();
|
9413
10880
|
}
|
9414
10881
|
}
|
9415
|
-
|
9416
|
-
|
9417
|
-
|
9418
|
-
|
9419
|
-
|
10882
|
+
createCounter() {
|
10883
|
+
this.counterElement = this.renderer.createElement('span');
|
10884
|
+
this.renderer.addClass(this.counterElement, 'lx-max-length-counter');
|
10885
|
+
const nextSibling = this.el.nativeElement.nextSibling;
|
10886
|
+
if (nextSibling) {
|
10887
|
+
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.counterElement, nextSibling);
|
9420
10888
|
}
|
9421
|
-
|
9422
|
-
|
9423
|
-
openModal() {
|
9424
|
-
this.oldOverflow = document.documentElement.style.overflowY;
|
9425
|
-
if (this.size === 'fullscreen') {
|
9426
|
-
this.renderer.setStyle(document.documentElement, 'overflowY', 'hidden');
|
10889
|
+
else {
|
10890
|
+
this.renderer.appendChild(this.el.nativeElement.parentNode, this.counterElement);
|
9427
10891
|
}
|
9428
|
-
this.
|
9429
|
-
|
9430
|
-
|
9431
|
-
/** @internal */
|
9432
|
-
emitBack() {
|
9433
|
-
this.back.emit();
|
10892
|
+
this.destroy$.subscribe(() => {
|
10893
|
+
this.renderer.removeChild(this.el.nativeElement.parentNode, this.counterElement);
|
10894
|
+
});
|
9434
10895
|
}
|
9435
|
-
|
9436
|
-
this.
|
9437
|
-
|
9438
|
-
|
10896
|
+
updateCounter() {
|
10897
|
+
if (this.counterElement) {
|
10898
|
+
const currentLength = this.ngControl.value ? this.ngControl.value.length : 0;
|
10899
|
+
const maxLength = this.getMaxLength();
|
10900
|
+
this.renderer.setProperty(this.counterElement, 'textContent', `${currentLength} / ${maxLength}`);
|
9439
10901
|
}
|
9440
|
-
|
9441
|
-
|
10902
|
+
}
|
10903
|
+
subscribeToValueChanges() {
|
10904
|
+
const control = this.ngControl.control;
|
10905
|
+
if (control) {
|
10906
|
+
control.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
10907
|
+
this.updateCounter();
|
10908
|
+
});
|
9442
10909
|
}
|
9443
10910
|
}
|
9444
|
-
|
9445
|
-
|
9446
|
-
if (
|
9447
|
-
this.
|
9448
|
-
|
9449
|
-
|
9450
|
-
|
9451
|
-
|
9452
|
-
|
10911
|
+
setupInputRestriction() {
|
10912
|
+
const maxLength = this.getMaxLength();
|
10913
|
+
if (maxLength !== null) {
|
10914
|
+
this.renderer.listen(this.el.nativeElement, 'beforeinput', (event) => {
|
10915
|
+
const input = event.target;
|
10916
|
+
const selectionStart = input.selectionStart || 0;
|
10917
|
+
const selectionEnd = input.selectionEnd || 0;
|
10918
|
+
const newValue = input.value.slice(0, selectionStart) + (event.data || '') + input.value.slice(selectionEnd);
|
10919
|
+
// Prevent input if the new value exceeds the max length and grows
|
10920
|
+
if (newValue.length > maxLength && newValue.length > input.value.length) {
|
10921
|
+
event.preventDefault();
|
10922
|
+
}
|
10923
|
+
});
|
10924
|
+
this.renderer.listen(this.el.nativeElement, 'input', () => {
|
10925
|
+
this.updateCounter();
|
10926
|
+
});
|
9453
10927
|
}
|
9454
10928
|
}
|
9455
|
-
|
9456
|
-
this.
|
10929
|
+
checkForInitialOversize() {
|
10930
|
+
const maxLength = this.getMaxLength();
|
10931
|
+
const value = this.ngControl.value;
|
10932
|
+
if (maxLength !== null && value && value.length > maxLength) {
|
10933
|
+
this.ngControl.control?.markAsDirty({ onlySelf: true });
|
10934
|
+
this.ngControl.control?.updateValueAndValidity({ onlySelf: true });
|
10935
|
+
}
|
9457
10936
|
}
|
9458
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
9459
|
-
static { this.ɵ
|
9460
|
-
trigger('modal', [
|
9461
|
-
transition(':enter', [style({ opacity: 0 }), animate('0.15s', style({ opacity: 1 }))]),
|
9462
|
-
transition(':leave', animate('0.15s', style({ opacity: 0 })))
|
9463
|
-
])
|
9464
|
-
] }); }
|
10937
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MaxLengthCounterDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1$5.NgControl, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
10938
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: MaxLengthCounterDirective, isStandalone: true, selector: "[lxMaxLengthCounter]", inputs: { lxMaxLengthCounter: "lxMaxLengthCounter", lxMaxLengthCounterRef: "lxMaxLengthCounterRef" }, ngImport: i0 }); }
|
9465
10939
|
}
|
9466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type:
|
9467
|
-
type:
|
9468
|
-
args: [{
|
9469
|
-
|
9470
|
-
|
9471
|
-
|
9472
|
-
|
9473
|
-
|
9474
|
-
}], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Renderer2 }, { type: i5.Observable, decorators: [{
|
9475
|
-
type: Optional
|
9476
|
-
}, {
|
9477
|
-
type: Inject,
|
9478
|
-
args: [MODAL_CLOSE]
|
9479
|
-
}] }, { type: i2.ConfigurableFocusTrapFactory }], propDecorators: { open: [{
|
9480
|
-
type: Input
|
9481
|
-
}], showCloseButton: [{
|
9482
|
-
type: Input
|
9483
|
-
}], showBackButton: [{
|
9484
|
-
type: Input
|
9485
|
-
}], verticalScroll: [{
|
9486
|
-
type: Input
|
9487
|
-
}], size: [{
|
9488
|
-
type: Input
|
9489
|
-
}], minWidth: [{
|
9490
|
-
type: Input
|
9491
|
-
}], isFocusTrap: [{
|
10940
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MaxLengthCounterDirective, decorators: [{
|
10941
|
+
type: Directive,
|
10942
|
+
args: [{
|
10943
|
+
selector: '[lxMaxLengthCounter]'
|
10944
|
+
}]
|
10945
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1$5.NgControl, decorators: [{
|
10946
|
+
type: Self
|
10947
|
+
}] }], propDecorators: { lxMaxLengthCounter: [{
|
9492
10948
|
type: Input
|
9493
|
-
}],
|
10949
|
+
}], lxMaxLengthCounterRef: [{
|
9494
10950
|
type: Input
|
9495
|
-
}], close: [{
|
9496
|
-
type: Output
|
9497
|
-
}], back: [{
|
9498
|
-
type: Output
|
9499
|
-
}], header: [{
|
9500
|
-
type: ContentChild,
|
9501
|
-
args: [ModalHeaderComponent]
|
9502
|
-
}], footer: [{
|
9503
|
-
type: ContentChild,
|
9504
|
-
args: [ModalFooterComponent]
|
9505
|
-
}], cdkPortal: [{
|
9506
|
-
type: ViewChild,
|
9507
|
-
args: [CdkPortal, { static: true }]
|
9508
|
-
}], implicitContent: [{
|
9509
|
-
type: ViewChild,
|
9510
|
-
args: ['implicitContent', { static: true }]
|
9511
|
-
}], explicitContent: [{
|
9512
|
-
type: ContentChild,
|
9513
|
-
args: [ModalContentDirective, { read: TemplateRef, static: true }]
|
9514
|
-
}], onEscape: [{
|
9515
|
-
type: HostListener,
|
9516
|
-
args: ['document:keydown.escape']
|
9517
10951
|
}] } });
|
9518
10952
|
|
10953
|
+
class Sorting {
|
10954
|
+
constructor() {
|
10955
|
+
this.key = '';
|
10956
|
+
this.order = 'asc';
|
10957
|
+
}
|
10958
|
+
}
|
10959
|
+
|
10960
|
+
/**
|
10961
|
+
* Due to limitations of the native html datepicker this validator is needed:
|
10962
|
+
* In the datepicker the min and max values only apply if the date is altered e.g. via scrolling.
|
10963
|
+
* It is still possible to manually input a value lower or higher than min and max value.
|
10964
|
+
*/
|
10965
|
+
function ValidateDateInForeseeableFuture(control) {
|
10966
|
+
// matches yyyy-mm-dd
|
10967
|
+
const dateRegex = /^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$/;
|
10968
|
+
if (control.value && dateRegex.test(control.value)) {
|
10969
|
+
const today = formatDate(new Date(), 'yyyy-MM-dd', 'en');
|
10970
|
+
const maxDate = '2999-12-31';
|
10971
|
+
if (control.value < today || control.value > maxDate) {
|
10972
|
+
return {
|
10973
|
+
dateInForseeableFuture: {
|
10974
|
+
valid: false
|
10975
|
+
}
|
10976
|
+
};
|
10977
|
+
}
|
10978
|
+
else {
|
10979
|
+
return null;
|
10980
|
+
}
|
10981
|
+
}
|
10982
|
+
return null;
|
10983
|
+
}
|
10984
|
+
|
10985
|
+
function stringIsInArray(array, stringToFind, valueMapFunction) {
|
10986
|
+
const valueToFind = valueMapFunction ? valueMapFunction(stringToFind).toLocaleLowerCase() : stringToFind.toLowerCase();
|
10987
|
+
return array.map((item) => item.toLocaleLowerCase()).indexOf(valueToFind) > -1;
|
10988
|
+
}
|
10989
|
+
/**
|
10990
|
+
* Validates that a string is not inside an array of strings
|
10991
|
+
*/
|
10992
|
+
function ValidateStringNotInArray(array, valueMapFunction, validatorName = 'stringNotInArray') {
|
10993
|
+
return function (control) {
|
10994
|
+
if (!control.value) {
|
10995
|
+
return null;
|
10996
|
+
}
|
10997
|
+
if (stringIsInArray(array, control.value, valueMapFunction)) {
|
10998
|
+
return {
|
10999
|
+
[validatorName]: true
|
11000
|
+
};
|
11001
|
+
}
|
11002
|
+
return null;
|
11003
|
+
};
|
11004
|
+
}
|
11005
|
+
function ValidateStringNotInArrayAsync(array$, valueMapFunction, validatorName = 'stringNotInArrayAsync') {
|
11006
|
+
return function (control) {
|
11007
|
+
if (!control.value) {
|
11008
|
+
return of(null);
|
11009
|
+
}
|
11010
|
+
return array$.pipe(map$1((array) => ValidateStringNotInArray(array, valueMapFunction, validatorName)(control)));
|
11011
|
+
};
|
11012
|
+
}
|
11013
|
+
|
9519
11014
|
const MODAL_MODULE_EXPORTS = [ModalComponent, ModalHeaderComponent, ModalFooterComponent, ModalContentDirective];
|
9520
11015
|
class LxModalModule {
|
9521
11016
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: LxModalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
@@ -10200,5 +11695,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImpor
|
|
10200
11695
|
* Generated bundle index. Do not edit.
|
10201
11696
|
*/
|
10202
11697
|
|
10203
|
-
export { ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, AVATAR_COLORS, AVATAR_SIZE_MAPPING, AfterViewInitDirective, AutocloseDirective, AutocloseGroupService, AutofocusDirective, AvatarComponent, AvatarGroupComponent, BACKSPACE, BadgeComponent, BannerComponent, BaseSelectDirective, BasicDropdownComponent, BasicDropdownItemComponent, BrPipe, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CORE_MODULE_EXPORTS, CURRENCY_SYMBOL_MAP, CardComponent, CdkOptionsDropdownComponent, CdkOptionsSubDropdownComponent, CollapsibleComponent, ContenteditableDirective, ContrastColorPipe, CounterComponent, CurrencyInputComponent, CurrencySymbolComponent, CustomDatePipe, DATEPICKER_CONTROL_VALUE_ACCESSOR, DATE_FN_LOCALE, DATE_FORMATS, DEFAULT_IMAGE_ID, DateFormatter, DateInputComponent, DatePickerComponent, DatepickerConfig, DatepickerUiModule, DragAndDropListComponent, DragAndDropListItemComponent, END, ENTER, ESCAPE, EllipsisComponent, EmptyStateComponent, ErrorMessageComponent, FORMS_MODULE_EXPORTS, FORM_CONTROL_ERROR_DISPLAY_STRATEGY, FORM_CONTROL_ERROR_NAMESPACE, FilterSelectionPipe, FilterTermPipe, FormErrorComponent, FormErrorDirective, FormSubmitDirective, FormatNumberPipe, GLOBAL_TRANSLATION_OPTIONS, HOME, HighlightRangePipe, HighlightTermPipe, ICON_MAP, IMAGE_READER, IconComponent, IconScaleComponent, InputComponent, KeyboardActionSourceDirective, KeyboardSelectAction, KeyboardSelectDirective, LOCALE_FN, LX_BUTTON_USE_SAP_ICONS, LX_ELLIPSIS_DEBOUNCE_ON_RESIZE, LxCoreUiModule, LxDragAndDropListModule, LxFormsModule, LxIsUuidPipe, LxLinkifyPipe, LxModalModule, LxPopoverUiModule, LxTabUiModule, LxTimeAgo, LxTranslatePipe, LxUnlinkifyPipe, MODAL_CLOSE, MODAL_MODULE_EXPORTS, MarkInvalidDirective, MarkdownPipe, MaxLengthCounterDirective, ModalCloseClickLocation, ModalComponent, ModalContentDirective, ModalFooterComponent, ModalHeaderComponent, MultiSelectComponent, NbspPipe, OptionComponent, OptionGroupComponent, OptionGroupDropdownComponent, OptionsDropdownComponent, OptionsSubDropdownComponent, PickerComponent, PickerOptionComponent, PickerTriggerDirective, PillItemComponent, PillListComponent, PopoverClickDirective, PopoverComponent, PopoverContentDirective, PopoverHoverDirective, RELEVANCE_SORTING_KEY, ResizeObserverService, ResponsiveInputComponent, SPACE, SelectDropdownDirective, SelectableItemDirective, SelectedOptionDirective, SingleSelectComponent, SkeletonComponent, SortPipe, Sorting, SortingDropdownComponent, SortingDropdownTriggerComponent, SpinnerComponent, StepperComponent, SwitchComponent, TAB, TabComponent, TabGroupComponent, TableComponent, TableHeaderComponent, TinySpinnerComponent, TokenComponent, TokenizerComponent, TokenizerOverflowPopoverComponent, TooltipComponent, TooltipDirective, TranslationAfterPipe, TranslationBeforePipe, TranslationBetweenPipe, UnescapeCurlyBracesPipe, ValidateDateInForeseeableFuture, ValidateStringNotInArray, ValidateStringNotInArrayAsync, getContrastColor, getInitialsUrl, getKeyboardNavigationEvents, getTranslationParts, highlightText, isValidHexColor, isValidX, isValidY, provideFormControlErrorDisplayStrategy, provideFormControlErrorNamespace, shorthandHexHandle, stopKeyboardEventPropagation };
|
11698
|
+
export { ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, AVATAR_COLORS, AVATAR_SIZE_MAPPING, AfterViewInitDirective, AngularNodeViewComponent, AngularNodeViewRenderer, AutocloseDirective, AutocloseGroupService, AutofocusDirective, AvatarComponent, AvatarGroupComponent, BACKSPACE, BadgeComponent, BannerComponent, BaseSelectDirective, BasicDropdownComponent, BasicDropdownItemComponent, BrPipe, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CORE_MODULE_EXPORTS, CURRENCY_SYMBOL_MAP, CardComponent, CdkOptionsDropdownComponent, CdkOptionsSubDropdownComponent, CollapsibleComponent, ContenteditableDirective, ContrastColorPipe, CounterComponent, CurrencyInputComponent, CurrencySymbolComponent, CustomDatePipe, DATEPICKER_CONTROL_VALUE_ACCESSOR, DATE_FN_LOCALE, DATE_FORMATS, DEFAULT_IMAGE_ID, DateFormatter, DateInputComponent, DatePickerComponent, DatepickerConfig, DatepickerUiModule, DragAndDropListComponent, DragAndDropListItemComponent, END, ENTER, ESCAPE, EllipsisComponent, EmptyStateComponent, ErrorMessageComponent, FORMS_MODULE_EXPORTS, FORM_CONTROL_ERROR_DISPLAY_STRATEGY, FORM_CONTROL_ERROR_NAMESPACE, FilterSelectionPipe, FilterTermPipe, FocusEditorDirective, FormErrorComponent, FormErrorDirective, FormSubmitDirective, FormatNumberPipe, GLOBAL_TRANSLATION_OPTIONS, HOME, HighlightRangePipe, HighlightTermDirective, HighlightTermPipe, ICON_MAP, IMAGE_READER, IconComponent, IconScaleComponent, InputComponent, KeyboardActionSourceDirective, KeyboardSelectAction, KeyboardSelectDirective, LOCALE_FN, LX_BUTTON_USE_SAP_ICONS, LX_ELLIPSIS_DEBOUNCE_ON_RESIZE, LxCoreUiModule, LxDragAndDropListModule, LxFormsModule, LxIsUuidPipe, LxLinkifyPipe, LxModalModule, LxPopoverUiModule, LxTabUiModule, LxTimeAgo, LxTranslatePipe, LxUnlinkifyPipe, MODAL_CLOSE, MODAL_MODULE_EXPORTS, MarkInvalidDirective, MarkdownPipe, MaxLengthCounterDirective, ModalCloseClickLocation, ModalComponent, ModalContentDirective, ModalFooterComponent, ModalHeaderComponent, MultiSelectComponent, NbspPipe, OptionComponent, OptionGroupComponent, OptionGroupDropdownComponent, OptionsDropdownComponent, OptionsSubDropdownComponent, PickerComponent, PickerOptionComponent, PickerTriggerDirective, PillItemComponent, PillListComponent, PopoverClickDirective, PopoverComponent, PopoverContentDirective, PopoverHoverDirective, RELEVANCE_SORTING_KEY, RemoveMarkdownPipe, ResizeObserverService, ResponsiveInputComponent, RichTextEditorComponent, SPACE, SelectDropdownDirective, SelectableItemDirective, SelectedOptionDirective, SingleSelectComponent, SkeletonComponent, SortPipe, Sorting, SortingDropdownComponent, SortingDropdownTriggerComponent, SpinnerComponent, StepperComponent, SwitchComponent, TAB, TabComponent, TabGroupComponent, TableComponent, TableHeaderComponent, TinySpinnerComponent, TipTapEditorDirective, TokenComponent, TokenizerComponent, TokenizerOverflowPopoverComponent, TooltipComponent, TooltipDirective, TrackingDirective, TranslationAfterPipe, TranslationBeforePipe, TranslationBetweenPipe, TruncateDirective, UnescapeCurlyBracesPipe, ValidateDateInForeseeableFuture, ValidateStringNotInArray, ValidateStringNotInArrayAsync, getContrastColor, getInitialsUrl, getKeyboardNavigationEvents, getTranslationParts, highlightText, isValidHexColor, isValidX, isValidY, markdownToText, provideFormControlErrorDisplayStrategy, provideFormControlErrorNamespace, shorthandHexHandle, stopKeyboardEventPropagation };
|
10204
11699
|
//# sourceMappingURL=leanix-components.mjs.map
|