@libs-ui/components-inputs-quill2x 0.2.357-16 → 0.2.357-18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/blots-custom/mention2x.component.d.ts +2 -0
- package/esm2022/blots-custom/mention2x.component.mjs +42 -2
- package/esm2022/interfaces/quill2x.interface.mjs +1 -1
- package/esm2022/quill2x.component.mjs +19 -10
- package/fesm2022/libs-ui-components-inputs-quill2x.mjs +59 -10
- package/fesm2022/libs-ui-components-inputs-quill2x.mjs.map +1 -1
- package/interfaces/quill2x.interface.d.ts +12 -0
- package/package.json +20 -20
|
@@ -683,23 +683,63 @@ const getStylesConvertInline = (customStyles) => {
|
|
|
683
683
|
|
|
684
684
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
685
685
|
const Embed = Quill2x.import('blots/embed');
|
|
686
|
+
// Đặt trên WRAPPER riêng vì Embed constructor của quill 2 sẽ bọc children vào contentNode — style
|
|
687
|
+
// để trên node ngoài sẽ không còn ôm trực tiếp các piece sau khi bị bọc.
|
|
688
|
+
const CHIP_WRAPPER_STYLE = 'display:inline-flex;align-items:center;gap:4px;padding:0 6px;height:20px;max-width:100%;overflow:hidden;border-radius:4px;background:#f1f3f5;vertical-align:middle;white-space:nowrap;user-select:none';
|
|
689
|
+
// Dựng nội dung chip từ attr `chip` (JSON mảng pieces) — textContent thuần, KHÔNG innerHTML (an toàn XSS).
|
|
690
|
+
// JSON hỏng → fallback hiển thị text `value` như blot thường.
|
|
691
|
+
const buildChipContent = (node, chip, value) => {
|
|
692
|
+
let pieces;
|
|
693
|
+
try {
|
|
694
|
+
pieces = JSON.parse(chip);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
node.innerText += value;
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (!Array.isArray(pieces) || !pieces.length) {
|
|
701
|
+
node.innerText += value;
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const wrapper = document.createElement('span');
|
|
705
|
+
wrapper.setAttribute('style', CHIP_WRAPPER_STYLE);
|
|
706
|
+
pieces.forEach((piece) => {
|
|
707
|
+
const span = document.createElement('span');
|
|
708
|
+
if (piece.className) {
|
|
709
|
+
span.className = piece.className;
|
|
710
|
+
}
|
|
711
|
+
if (piece.text !== undefined) {
|
|
712
|
+
span.textContent = piece.text;
|
|
713
|
+
}
|
|
714
|
+
wrapper.appendChild(span);
|
|
715
|
+
});
|
|
716
|
+
node.appendChild(wrapper);
|
|
717
|
+
};
|
|
686
718
|
class QuillMention2xBlot extends Embed {
|
|
687
719
|
static blotName = 'mention';
|
|
688
720
|
static tagName = 'span';
|
|
689
721
|
static className = 'libs-ui-quill-mention';
|
|
690
722
|
static create(data) {
|
|
691
723
|
const node = super.create();
|
|
692
|
-
node.innerText += data.value;
|
|
693
724
|
node.setAttribute('id', data.id);
|
|
694
725
|
node.setAttribute('feId', data.feId);
|
|
695
726
|
node.setAttribute('value', data.value);
|
|
727
|
+
if (data.chip) {
|
|
728
|
+
// Chip giàu (icon + nhãn) — pieces do consumer cấp qua IMentionItem.mentionInsertData.
|
|
729
|
+
node.setAttribute('chip', data.chip);
|
|
730
|
+
buildChipContent(node, data.chip, data.value);
|
|
731
|
+
return node;
|
|
732
|
+
}
|
|
733
|
+
node.innerText += data.value;
|
|
696
734
|
return node;
|
|
697
735
|
}
|
|
698
736
|
static value(domNode) {
|
|
737
|
+
const chip = domNode.getAttribute('chip');
|
|
699
738
|
return {
|
|
700
739
|
id: domNode.getAttribute('id'),
|
|
701
740
|
feId: domNode.getAttribute('feId'),
|
|
702
741
|
value: domNode.getAttribute('value'),
|
|
742
|
+
...(chip ? { chip } : {}),
|
|
703
743
|
};
|
|
704
744
|
}
|
|
705
745
|
attach() {
|
|
@@ -1638,7 +1678,12 @@ class LibsUiComponentsInputsQuill2xComponent {
|
|
|
1638
1678
|
handler: (range) => this.noPreventEmbedDeletion(range),
|
|
1639
1679
|
},
|
|
1640
1680
|
enter: {
|
|
1641
|
-
key
|
|
1681
|
+
// Quill 2 match binding theo event.key (string) — key số 13 (keyCode kiểu Quill 1) không bao giờ
|
|
1682
|
+
// match ⇒ Enter khi mention đang mở vẫn chèn newline (insertMention từng phải bù thủ công).
|
|
1683
|
+
key: 'Enter',
|
|
1684
|
+
// shiftKey: null ⇒ match cả Enter lẫn Shift+Enter. Mention directive KHÔNG loại trừ shiftKey khi
|
|
1685
|
+
// chèn mention, nên thiếu dòng này thì Shift+Enter vẫn rơi vào default handler và chèn newline dư.
|
|
1686
|
+
shiftKey: null,
|
|
1642
1687
|
handler: () => !this.showMention(),
|
|
1643
1688
|
},
|
|
1644
1689
|
},
|
|
@@ -2192,20 +2237,24 @@ class LibsUiComponentsInputsQuill2xComponent {
|
|
|
2192
2237
|
instance?.outClose.subscribe(() => this.dynamicComponentService.delete(this.addEditLinkComponentRef()));
|
|
2193
2238
|
this.dynamicComponentService.addToBody(this.addEditLinkComponentRef());
|
|
2194
2239
|
}
|
|
2240
|
+
// Chèn blot mention thay cho chuỗi '<triggerChar><search>' người dùng vừa gõ.
|
|
2241
|
+
// Caret đang đứng NGAY SAU chuỗi đó (lengthKey ký tự, tính cả trigger char) → vị trí chèn = caret - lengthKey.
|
|
2242
|
+
// Công thức cũ trừ thêm 1 (heuristic '@') làm cửa sổ xóa lệch trái 1 ký tự: sót trigger char trong nội dung,
|
|
2243
|
+
// nuốt nhầm ký tự đứng trước; mention ở đầu document còn ra index -1 → insertEmbed throw IndexSizeError.
|
|
2195
2244
|
insertMention(event) {
|
|
2196
|
-
|
|
2245
|
+
// Click chuột chọn item trên list (append body) có thể làm editor mất selection → getSelection(true)
|
|
2246
|
+
// focus lại editor và trả về range đã lưu trước khi blur.
|
|
2247
|
+
const range = this.quill?.getSelection() ?? this.quill?.getSelection(true);
|
|
2197
2248
|
if (!range) {
|
|
2198
2249
|
return;
|
|
2199
2250
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
const startPosInsert =
|
|
2203
|
-
|
|
2251
|
+
// Kẹp CẢ vị trí lẫn độ dài: nếu lengthKey > range.index (caret chưa đủ ký tự phía trước, vd range khôi
|
|
2252
|
+
// phục về 0) mà chỉ kẹp vị trí thì cửa sổ xóa trượt phải và nuốt mất ký tự đứng sau caret.
|
|
2253
|
+
const startPosInsert = Math.max(range.index - event.lengthKey, 0);
|
|
2254
|
+
const lengthDelete = Math.min(event.lengthKey, range.index);
|
|
2255
|
+
this.quill?.deleteText(startPosInsert, lengthDelete, Quill2x.sources.SILENT);
|
|
2204
2256
|
this.quill?.insertEmbed(startPosInsert, 'mention', event.data, Quill2x.sources.SILENT);
|
|
2205
2257
|
this.insertText(' ', startPosInsert + 1);
|
|
2206
|
-
if (indexSubtract) {
|
|
2207
|
-
this.quill?.deleteText(startPosInsert + 3, 1, Quill2x.sources.SILENT);
|
|
2208
|
-
}
|
|
2209
2258
|
this.quill?.setSelection(startPosInsert + 2, 0, Quill2x.sources.SILENT);
|
|
2210
2259
|
}
|
|
2211
2260
|
updateValueByRootQuillHtml() {
|