@aquera/nile-elements 0.1.67-beta-1.5 → 0.1.67-beta-1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/index.html +24 -11
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +130 -58
- package/dist/nile-avatar/nile-avatar.test.cjs.js +1 -1
- package/dist/nile-avatar/nile-avatar.test.cjs.js.map +1 -1
- package/dist/nile-avatar/nile-avatar.test.esm.js +1 -1
- package/dist/nile-badge/index.cjs.js +1 -1
- package/dist/nile-badge/index.esm.js +1 -1
- package/dist/nile-badge/nile-badge.cjs.js +1 -1
- package/dist/nile-badge/nile-badge.cjs.js.map +1 -1
- package/dist/nile-badge/nile-badge.esm.js +1 -1
- package/dist/nile-badge/nile-badge.test.cjs.js +1 -1
- package/dist/nile-badge/nile-badge.test.cjs.js.map +1 -1
- package/dist/nile-badge/nile-badge.test.esm.js +1 -1
- package/dist/nile-button/index.cjs.js +1 -1
- package/dist/nile-button/index.esm.js +1 -1
- package/dist/nile-button/nile-button.cjs.js +1 -1
- package/dist/nile-button/nile-button.cjs.js.map +1 -1
- package/dist/nile-button/nile-button.esm.js +1 -1
- package/dist/nile-button/nile-button.test.cjs.js +1 -1
- package/dist/nile-button/nile-button.test.cjs.js.map +1 -1
- package/dist/nile-button/nile-button.test.esm.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.cjs.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.cjs.js.map +1 -1
- package/dist/nile-calendar/nile-calendar.test.esm.js +1 -1
- package/dist/nile-chip/nile-chip.test.cjs.js +1 -1
- package/dist/nile-chip/nile-chip.test.cjs.js.map +1 -1
- package/dist/nile-chip/nile-chip.test.esm.js +1 -1
- package/dist/nile-dialog/index.cjs.js +1 -1
- package/dist/nile-dialog/index.esm.js +1 -1
- package/dist/nile-dialog/nile-dialog.cjs.js +1 -1
- package/dist/nile-dialog/nile-dialog.cjs.js.map +1 -1
- package/dist/nile-dialog/nile-dialog.esm.js +2 -2
- package/dist/nile-dialog/nile-dialog.test.cjs.js +1 -1
- package/dist/nile-dialog/nile-dialog.test.cjs.js.map +1 -1
- package/dist/nile-dialog/nile-dialog.test.esm.js +1 -1
- package/dist/nile-drawer/index.cjs.js +1 -1
- package/dist/nile-drawer/index.esm.js +1 -1
- package/dist/nile-drawer/nile-drawer.cjs.js +1 -1
- package/dist/nile-drawer/nile-drawer.cjs.js.map +1 -1
- package/dist/nile-drawer/nile-drawer.esm.js +1 -1
- package/dist/nile-drawer/nile-drawer.test.cjs.js +1 -1
- package/dist/nile-drawer/nile-drawer.test.cjs.js.map +1 -1
- package/dist/nile-drawer/nile-drawer.test.esm.js +1 -1
- package/dist/nile-icon/icons/svg/format_clear.cjs.js +2 -0
- package/dist/nile-icon/icons/svg/format_clear.cjs.js.map +1 -0
- package/dist/nile-icon/icons/svg/format_clear.esm.js +1 -0
- package/dist/nile-icon/icons/svg/format_list_bulleted.cjs.js +2 -0
- package/dist/nile-icon/icons/svg/format_list_bulleted.cjs.js.map +1 -0
- package/dist/nile-icon/icons/svg/format_list_bulleted.esm.js +1 -0
- package/dist/nile-icon/icons/svg/format_list_numbered.cjs.js +2 -0
- package/dist/nile-icon/icons/svg/format_list_numbered.cjs.js.map +1 -0
- package/dist/nile-icon/icons/svg/format_list_numbered.esm.js +1 -0
- package/dist/nile-icon/icons/svg/index.cjs.js +1 -1
- package/dist/nile-icon/icons/svg/index.esm.js +1 -1
- package/dist/nile-icon/index.cjs.js +1 -1
- package/dist/nile-icon/index.cjs.js.map +1 -1
- package/dist/nile-icon/index.esm.js +2 -2
- package/dist/nile-icon/nile-icon.test.cjs.js +1 -1
- package/dist/nile-icon/nile-icon.test.cjs.js.map +1 -1
- package/dist/nile-icon/nile-icon.test.esm.js +1 -1
- package/dist/nile-icon-button/index.cjs.js +1 -1
- package/dist/nile-icon-button/index.esm.js +1 -1
- package/dist/nile-icon-button/nile-icon-button.cjs.js +1 -1
- package/dist/nile-icon-button/nile-icon-button.cjs.js.map +1 -1
- package/dist/nile-icon-button/nile-icon-button.esm.js +1 -1
- package/dist/nile-input/index.cjs.js +1 -1
- package/dist/nile-input/index.esm.js +1 -1
- package/dist/nile-input/nile-input.cjs.js +1 -1
- package/dist/nile-input/nile-input.cjs.js.map +1 -1
- package/dist/nile-input/nile-input.esm.js +1 -1
- package/dist/nile-input/nile-input.test.cjs.js +1 -1
- package/dist/nile-input/nile-input.test.cjs.js.map +1 -1
- package/dist/nile-input/nile-input.test.esm.js +1 -1
- package/dist/nile-menu-item/index.cjs.js +1 -1
- package/dist/nile-menu-item/index.esm.js +1 -1
- package/dist/nile-menu-item/nile-menu-item.cjs.js +1 -1
- package/dist/nile-menu-item/nile-menu-item.cjs.js.map +1 -1
- package/dist/nile-menu-item/nile-menu-item.esm.js +1 -1
- package/dist/nile-option/index.cjs.js +1 -1
- package/dist/nile-option/index.esm.js +1 -1
- package/dist/nile-option/nile-option.cjs.js +1 -1
- package/dist/nile-option/nile-option.cjs.js.map +1 -1
- package/dist/nile-option/nile-option.esm.js +1 -1
- package/dist/nile-rich-text-editor/index.cjs.js +1 -1
- package/dist/nile-rich-text-editor/index.esm.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +14 -13
- package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-color.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-color.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rte-color.esm.js +54 -1
- package/dist/nile-rich-text-editor/nile-rte-link.cjs.js +2 -0
- package/dist/nile-rich-text-editor/nile-rte-link.cjs.js.map +1 -0
- package/dist/nile-rich-text-editor/nile-rte-link.esm.js +19 -0
- package/dist/nile-rich-text-editor/nile-rte-select.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-select.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rte-select.esm.js +39 -39
- package/dist/nile-rich-text-editor/utils.cjs.js +1 -1
- package/dist/nile-rich-text-editor/utils.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/utils.esm.js +1 -1
- package/dist/nile-select/index.cjs.js +1 -1
- package/dist/nile-select/index.esm.js +1 -1
- package/dist/nile-select/nile-select.cjs.js +1 -1
- package/dist/nile-select/nile-select.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.esm.js +1 -1
- package/dist/nile-select/nile-select.test.cjs.js +1 -1
- package/dist/nile-select/nile-select.test.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.test.esm.js +1 -1
- package/dist/nile-tab/index.cjs.js +1 -1
- package/dist/nile-tab/index.esm.js +1 -1
- package/dist/nile-tab/nile-tab.cjs.js +1 -1
- package/dist/nile-tab/nile-tab.cjs.js.map +1 -1
- package/dist/nile-tab/nile-tab.esm.js +1 -1
- package/dist/nile-tab-group/index.cjs.js +1 -1
- package/dist/nile-tab-group/index.esm.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.cjs.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.cjs.js.map +1 -1
- package/dist/nile-tab-group/nile-tab-group.esm.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.test.cjs.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.test.cjs.js.map +1 -1
- package/dist/nile-tab-group/nile-tab-group.test.esm.js +1 -1
- package/dist/nile-tag/index.cjs.js +1 -1
- package/dist/nile-tag/index.esm.js +1 -1
- package/dist/nile-tag/nile-tag.cjs.js +1 -1
- package/dist/nile-tag/nile-tag.cjs.js.map +1 -1
- package/dist/nile-tag/nile-tag.esm.js +1 -1
- package/dist/nile-toast/index.cjs.js +1 -1
- package/dist/nile-toast/index.esm.js +1 -1
- package/dist/nile-toast/nile-toast.cjs.js +1 -1
- package/dist/nile-toast/nile-toast.cjs.js.map +1 -1
- package/dist/nile-toast/nile-toast.esm.js +1 -1
- package/dist/nile-tree/index.cjs.js +1 -1
- package/dist/nile-tree/index.esm.js +1 -1
- package/dist/nile-tree/nile-tree.cjs.js +1 -1
- package/dist/nile-tree/nile-tree.cjs.js.map +1 -1
- package/dist/nile-tree/nile-tree.esm.js +1 -1
- package/dist/nile-tree-item/index.cjs.js +1 -1
- package/dist/nile-tree-item/index.esm.js +1 -1
- package/dist/nile-tree-item/nile-tree-item.cjs.js +1 -1
- package/dist/nile-tree-item/nile-tree-item.cjs.js.map +1 -1
- package/dist/nile-tree-item/nile-tree-item.esm.js +1 -1
- package/dist/nile-virtual-select/index.cjs.js +1 -1
- package/dist/nile-virtual-select/index.esm.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.esm.js +1 -1
- package/dist/src/nile-icon/icons/svg/format_clear.d.ts +5 -0
- package/dist/src/nile-icon/icons/svg/format_clear.js +5 -0
- package/dist/src/nile-icon/icons/svg/format_clear.js.map +1 -0
- package/dist/src/nile-icon/icons/svg/format_list_bulleted.d.ts +5 -0
- package/dist/src/nile-icon/icons/svg/format_list_bulleted.js +5 -0
- package/dist/src/nile-icon/icons/svg/format_list_bulleted.js.map +1 -0
- package/dist/src/nile-icon/icons/svg/format_list_numbered.d.ts +5 -0
- package/dist/src/nile-icon/icons/svg/format_list_numbered.js +5 -0
- package/dist/src/nile-icon/icons/svg/format_list_numbered.js.map +1 -0
- package/dist/src/nile-icon/icons/svg/index.d.ts +3 -0
- package/dist/src/nile-icon/icons/svg/index.js +3 -0
- package/dist/src/nile-icon/icons/svg/index.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +14 -13
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +38 -90
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rte-color.d.ts +11 -3
- package/dist/src/nile-rich-text-editor/nile-rte-color.js +171 -6
- package/dist/src/nile-rich-text-editor/nile-rte-color.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rte-link.d.ts +19 -0
- package/dist/src/nile-rich-text-editor/nile-rte-link.js +172 -0
- package/dist/src/nile-rich-text-editor/nile-rte-link.js.map +1 -0
- package/dist/src/nile-rich-text-editor/nile-rte-select.js +62 -57
- package/dist/src/nile-rich-text-editor/nile-rte-select.js.map +1 -1
- package/dist/src/nile-rich-text-editor/rte-utils/content.d.ts +2 -0
- package/dist/src/nile-rich-text-editor/rte-utils/content.js +25 -0
- package/dist/src/nile-rich-text-editor/rte-utils/content.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/css.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/css.js +9 -0
- package/dist/src/nile-rich-text-editor/rte-utils/css.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/dom.d.ts +2 -0
- package/dist/src/nile-rich-text-editor/rte-utils/dom.js +48 -0
- package/dist/src/nile-rich-text-editor/rte-utils/dom.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/formatting.d.ts +2 -0
- package/dist/src/nile-rich-text-editor/rte-utils/formatting.js +69 -0
- package/dist/src/nile-rich-text-editor/rte-utils/formatting.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/keys.d.ts +2 -0
- package/dist/src/nile-rich-text-editor/rte-utils/keys.js +38 -0
- package/dist/src/nile-rich-text-editor/rte-utils/keys.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/lists.d.ts +2 -0
- package/dist/src/nile-rich-text-editor/rte-utils/lists.js +28 -0
- package/dist/src/nile-rich-text-editor/rte-utils/lists.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/selection.d.ts +17 -0
- package/dist/src/nile-rich-text-editor/rte-utils/selection.js +39 -0
- package/dist/src/nile-rich-text-editor/rte-utils/selection.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbar.d.ts +28 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbar.js +161 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbar.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.d.ts +13 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.js +119 -0
- package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.js.map +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/vars.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/rte-utils/vars.js +14 -0
- package/dist/src/nile-rich-text-editor/rte-utils/vars.js.map +1 -0
- package/dist/src/nile-rich-text-editor/sanatize.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/sanatize.js +40 -0
- package/dist/src/nile-rich-text-editor/sanatize.js.map +1 -0
- package/dist/src/nile-rich-text-editor/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/nile-icon/icons/svg/format_clear.ts +5 -0
- package/src/nile-icon/icons/svg/format_list_bulleted.ts +5 -0
- package/src/nile-icon/icons/svg/format_list_numbered.ts +5 -0
- package/src/nile-icon/icons/svg/index.ts +3 -0
- package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +14 -13
- package/src/nile-rich-text-editor/nile-rich-text-editor.ts +56 -90
- package/src/nile-rich-text-editor/nile-rte-color.ts +182 -6
- package/src/nile-rich-text-editor/nile-rte-link.ts +161 -0
- package/src/nile-rich-text-editor/nile-rte-select.ts +178 -173
- package/src/nile-rich-text-editor/utils.ts +342 -341
- package/vscode-html-custom-data.json +33 -3
@@ -0,0 +1,161 @@
|
|
1
|
+
import { LitElement, html } from 'lit';
|
2
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
3
|
+
|
4
|
+
@customElement('nile-rte-link')
|
5
|
+
export class NileRteLink extends LitElement {
|
6
|
+
protected createRenderRoot() { return this; }
|
7
|
+
|
8
|
+
@property({ attribute: false }) editorEl!: HTMLElement;
|
9
|
+
@property({ type: String }) placeholder = 'https://';
|
10
|
+
|
11
|
+
@state() private linkValue = '';
|
12
|
+
private selectionRange: Range | null = null;
|
13
|
+
|
14
|
+
connectedCallback() {
|
15
|
+
super.connectedCallback();
|
16
|
+
this.injectCss(`
|
17
|
+
nile-rte-link nile-button.rte-link-trigger::part(base) {
|
18
|
+
width: 32px;
|
19
|
+
height: 32px;
|
20
|
+
padding: 0;
|
21
|
+
border: none;
|
22
|
+
border-radius: 6px;
|
23
|
+
}
|
24
|
+
.link-popup {
|
25
|
+
display: flex;
|
26
|
+
gap: 6px;
|
27
|
+
padding: 6px 8px;
|
28
|
+
align-items: center;
|
29
|
+
background: white;
|
30
|
+
}
|
31
|
+
.link-input {
|
32
|
+
flex: 1;
|
33
|
+
padding: 6px 8px;
|
34
|
+
font-size: 14px;
|
35
|
+
border: 1px solid #ccc;
|
36
|
+
border-radius: 4px;
|
37
|
+
}
|
38
|
+
nile-popover.rte-link-popover::part(popover) {
|
39
|
+
min-width: 250px;
|
40
|
+
height: 44px;
|
41
|
+
padding: 0px;
|
42
|
+
gap: 0px;
|
43
|
+
}
|
44
|
+
`);
|
45
|
+
}
|
46
|
+
|
47
|
+
private injectCss(cssText: string) {
|
48
|
+
if (this.querySelector('style[data-rte-link-style]')) return;
|
49
|
+
const style = document.createElement('style');
|
50
|
+
style.setAttribute('data-rte-link-style', 'true');
|
51
|
+
style.textContent = cssText;
|
52
|
+
this.insertBefore(style, this.firstChild);
|
53
|
+
}
|
54
|
+
|
55
|
+
private onOpen() {
|
56
|
+
if (!this.editorEl) return;
|
57
|
+
const sel = document.getSelection();
|
58
|
+
if (!sel || sel.rangeCount === 0) return;
|
59
|
+
|
60
|
+
this.selectionRange = sel.getRangeAt(0).cloneRange();
|
61
|
+
|
62
|
+
// Pre-fill with existing href if selection inside <a>
|
63
|
+
let n: Node | null = sel.anchorNode;
|
64
|
+
let a: HTMLAnchorElement | null = null;
|
65
|
+
while (n && n !== this.editorEl) {
|
66
|
+
if (n instanceof HTMLAnchorElement) { a = n; break; }
|
67
|
+
n = n.parentNode;
|
68
|
+
}
|
69
|
+
this.linkValue = a?.href || '';
|
70
|
+
|
71
|
+
setTimeout(() => {
|
72
|
+
this.querySelector<HTMLInputElement>('.link-input')?.focus();
|
73
|
+
}, 0);
|
74
|
+
}
|
75
|
+
|
76
|
+
private onApply() {
|
77
|
+
if (!this.editorEl || !this.selectionRange) return;
|
78
|
+
const url = this.linkValue.trim();
|
79
|
+
if (!url) return;
|
80
|
+
|
81
|
+
const sel = document.getSelection();
|
82
|
+
if (!sel) return;
|
83
|
+
sel.removeAllRanges();
|
84
|
+
sel.addRange(this.selectionRange);
|
85
|
+
|
86
|
+
// Check for existing <a>
|
87
|
+
let n: Node | null = this.selectionRange.startContainer;
|
88
|
+
let a: HTMLAnchorElement | null = null;
|
89
|
+
while (n && n !== this.editorEl) {
|
90
|
+
if (n instanceof HTMLAnchorElement) { a = n; break; }
|
91
|
+
n = n.parentNode;
|
92
|
+
}
|
93
|
+
|
94
|
+
if (a) {
|
95
|
+
a.href = url;
|
96
|
+
} else {
|
97
|
+
const link = document.createElement('a');
|
98
|
+
link.href = url;
|
99
|
+
if (this.selectionRange.collapsed) {
|
100
|
+
link.textContent = url;
|
101
|
+
this.selectionRange.insertNode(link);
|
102
|
+
} else {
|
103
|
+
try {
|
104
|
+
this.selectionRange.surroundContents(link);
|
105
|
+
} catch {
|
106
|
+
const frag = this.selectionRange.extractContents();
|
107
|
+
link.appendChild(frag);
|
108
|
+
this.selectionRange.insertNode(link);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
this.emit(url);
|
114
|
+
|
115
|
+
// Close the popover by toggling `open`
|
116
|
+
const pop = this.querySelector('nile-popover');
|
117
|
+
if (pop) (pop as any).isShow = false;
|
118
|
+
}
|
119
|
+
|
120
|
+
private emit(href: string) {
|
121
|
+
this.dispatchEvent(new CustomEvent('link-changed', {
|
122
|
+
detail: { href },
|
123
|
+
bubbles: true,
|
124
|
+
composed: true
|
125
|
+
}));
|
126
|
+
}
|
127
|
+
|
128
|
+
render() {
|
129
|
+
return html`
|
130
|
+
<nile-popover placement="bottom-start" class="rte-link-popover" distance="10" .arrow=${false}>
|
131
|
+
<nile-button class="rte-link-trigger" slot="anchor" variant="tertiary" size="small" @click=${() => this.onOpen()}>
|
132
|
+
<nile-icon name="link_2" aria-label="Insert/Edit Link"></nile-icon>
|
133
|
+
</nile-button>
|
134
|
+
|
135
|
+
<div class="link-popup">
|
136
|
+
<input
|
137
|
+
class="link-input"
|
138
|
+
type="text"
|
139
|
+
placeholder="Type or paste link here"
|
140
|
+
.value=${this.linkValue}
|
141
|
+
@input=${(e: any) => this.linkValue = e.target.value}
|
142
|
+
@keydown=${(e: KeyboardEvent) => {
|
143
|
+
if (e.key === 'Enter') this.onApply();
|
144
|
+
if (e.key === 'Escape') {
|
145
|
+
const pop = this.querySelector('nile-popover');
|
146
|
+
if (pop) (pop as any).isShow = false;
|
147
|
+
}
|
148
|
+
}}
|
149
|
+
/>
|
150
|
+
<nile-button @click=${() => this.onApply()}>Apply</nile-button>
|
151
|
+
</div>
|
152
|
+
</nile-popover>
|
153
|
+
`;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
declare global {
|
158
|
+
interface HTMLElementTagNameMap {
|
159
|
+
'nile-rte-link': NileRteLink;
|
160
|
+
}
|
161
|
+
}
|
@@ -1,195 +1,180 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
// nile-rte-select.ts
|
2
|
+
import { LitElement, html } from 'lit';
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
4
|
+
|
5
|
+
type HeadingTag = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
6
|
+
type GenericOption = { value: string; label?: string; icon?: string };
|
7
|
+
type HeadingOption = { value: HeadingTag; label?: string; icon?: string };
|
8
|
+
type NormalizedOption = { value: string; label: string; icon?: string };
|
9
|
+
|
10
|
+
const HEADING_ALLOWLIST: ReadonlySet<HeadingTag> = new Set([
|
11
|
+
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
12
|
+
]);
|
13
|
+
|
14
|
+
function isHeadingTag(v: string): v is HeadingTag {
|
15
|
+
return HEADING_ALLOWLIST.has(v as HeadingTag);
|
16
|
+
}
|
17
|
+
|
18
|
+
@customElement('nile-rte-select')
|
19
|
+
export class NileRteSelect extends LitElement {
|
20
|
+
protected createRenderRoot() { return this; }
|
21
|
+
|
22
|
+
/** 'heading' | 'font' | 'align' */
|
23
|
+
@property({ type: String }) type = '';
|
24
|
+
|
25
|
+
/** JSON: [{ value, label?, icon? }, ...] (attribute-based; runtime-validated) */
|
26
|
+
@property({ type: String }) options = '[]';
|
27
|
+
|
28
|
+
/** Programmatic options (preferred for TS safety). */
|
29
|
+
@property({ attribute: false })
|
30
|
+
optionsObj?: Array<GenericOption | HeadingOption>;
|
31
|
+
|
32
|
+
/** Fallback label for trigger (e.g., "Align") */
|
33
|
+
@property({ type: String }) label = '';
|
34
|
+
|
35
|
+
@state() private selectedValue = '';
|
36
|
+
|
37
|
+
private mapAlignIcon(v: string) {
|
38
|
+
const map: Record<string,string> = {
|
39
|
+
left: 'format_align_left',
|
40
|
+
center: 'format_align_middle',
|
41
|
+
right: 'format_align_right',
|
42
|
+
justify: 'format_align_justify'
|
43
|
+
};
|
44
|
+
return map[v] || 'format_align_left';
|
16
45
|
}
|
17
46
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
47
|
+
private get parsedOptions(): NormalizedOption[] {
|
48
|
+
// Prefer programmatic options if present (gives TS compile-time checks)
|
49
|
+
const source: unknown = this.optionsObj ?? this.options;
|
50
|
+
|
51
|
+
const rawArray: any[] = (() => {
|
52
|
+
if (Array.isArray(source)) return source;
|
53
|
+
try { return JSON.parse(String(source)); } catch { return []; }
|
54
|
+
})();
|
55
|
+
|
56
|
+
// Normalize to consistent shape
|
57
|
+
let items: NormalizedOption[] = rawArray.map((o: any) => {
|
58
|
+
const value: string = o?.value ?? o;
|
59
|
+
const label: string = o?.label ?? o?.value ?? o;
|
60
|
+
const icon: string | undefined =
|
61
|
+
o?.icon ?? (this.type === 'align' ? this.mapAlignIcon(String(value)) : undefined);
|
62
|
+
return { value, label, icon };
|
63
|
+
});
|
64
|
+
|
65
|
+
// If type is heading, enforce allowlist (runtime validation)
|
66
|
+
if (this.type === 'heading') {
|
67
|
+
const before = items.length;
|
68
|
+
items = items.filter(i => isHeadingTag(i.value));
|
69
|
+
if (items.length !== before) {
|
70
|
+
}
|
71
|
+
// If current selection is invalid for heading, reset
|
72
|
+
if (this.selectedValue && !isHeadingTag(this.selectedValue)) {
|
73
|
+
this.selectedValue = '';
|
74
|
+
}
|
75
|
+
}
|
31
76
|
|
32
|
-
|
33
|
-
|
77
|
+
return items;
|
78
|
+
}
|
34
79
|
|
35
|
-
|
80
|
+
private ensureDefault() {
|
81
|
+
if (!this.selectedValue) {
|
82
|
+
const first = this.parsedOptions[0];
|
83
|
+
if (first) this.selectedValue = first.value;
|
84
|
+
}
|
85
|
+
}
|
36
86
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
right: 'format_align_right',
|
42
|
-
justify: 'format_align_justify'
|
43
|
-
};
|
44
|
-
return map[v] || 'align-left';
|
87
|
+
private onSelect(value: string) {
|
88
|
+
if (this.type === 'heading' && !isHeadingTag(value)) {
|
89
|
+
console.warn(`[nile-rte-select] Ignoring invalid heading value: ${value}`);
|
90
|
+
return;
|
45
91
|
}
|
92
|
+
this.selectedValue = value;
|
93
|
+
this.dispatchEvent(new CustomEvent('change', {
|
94
|
+
detail: value, bubbles: true, composed: true
|
95
|
+
}));
|
96
|
+
}
|
46
97
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
const rawArray: any[] = (() => {
|
52
|
-
if (Array.isArray(source)) return source;
|
53
|
-
try { return JSON.parse(String(source)); } catch { return []; }
|
54
|
-
})();
|
55
|
-
|
56
|
-
// Normalize to consistent shape
|
57
|
-
let items: NormalizedOption[] = rawArray.map((o: any) => {
|
58
|
-
const value: string = o?.value ?? o;
|
59
|
-
const label: string = o?.label ?? o?.value ?? o;
|
60
|
-
const icon: string | undefined =
|
61
|
-
o?.icon ?? (this.type === 'align' ? this.mapAlignIcon(String(value)) : undefined);
|
62
|
-
return { value, label, icon };
|
63
|
-
});
|
64
|
-
|
65
|
-
// If type is heading, enforce allowlist (runtime validation)
|
66
|
-
if (this.type === 'heading') {
|
67
|
-
const before = items.length;
|
68
|
-
items = items.filter(i => isHeadingTag(i.value));
|
69
|
-
if (items.length !== before) {
|
70
|
-
}
|
71
|
-
// If current selection is invalid for heading, reset
|
72
|
-
if (this.selectedValue && !isHeadingTag(this.selectedValue)) {
|
73
|
-
this.selectedValue = '';
|
74
|
-
}
|
75
|
-
}
|
98
|
+
connectedCallback(): void {
|
99
|
+
super.connectedCallback();
|
100
|
+
this.injectLocalStyles();
|
101
|
+
}
|
76
102
|
|
77
|
-
|
78
|
-
|
103
|
+
private injectLocalStyles() {
|
104
|
+
if (this.querySelector('style[data-rte-select-style]')) return;
|
79
105
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
106
|
+
const style = document.createElement('style');
|
107
|
+
style.setAttribute('data-rte-select-style', 'true');
|
108
|
+
style.textContent = `
|
109
|
+
nile-menu.rte-align-menu::part(menu__items-wrapper) {
|
110
|
+
display: flex;
|
84
111
|
}
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
if (this.type === 'heading' && !isHeadingTag(value)) {
|
89
|
-
console.warn(`[nile-rte-select] Ignoring invalid heading value: ${value}`);
|
90
|
-
return;
|
112
|
+
nile-menu.rte-align-menu,
|
113
|
+
nile-menu.rte-default-menu {
|
114
|
+
margin-top: 0px;
|
91
115
|
}
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
116
|
+
nile-button.rte-align-trigger::part(base),
|
117
|
+
nile-button.rte-default-trigger::part(base) {
|
118
|
+
min-width: 32px;
|
119
|
+
height: 32px;
|
120
|
+
padding: 0px 6px;
|
121
|
+
box-shadow: none;
|
122
|
+
}
|
123
|
+
nile-button.rte-align-trigger::part(base) {
|
124
|
+
|
97
125
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
126
|
+
border: none;
|
127
|
+
}
|
128
|
+
`;
|
129
|
+
this.insertBefore(style, this.firstChild);
|
130
|
+
}
|
102
131
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
style.setAttribute('data-rte-select-style', 'true');
|
108
|
-
style.textContent = `
|
109
|
-
nile-menu.rte-align-menu::part(menu__items-wrapper) {
|
110
|
-
display: flex;
|
111
|
-
}
|
112
|
-
nile-menu.rte-align-menu,
|
113
|
-
nile-menu.rte-default-menu {
|
114
|
-
margin-top: 0px;
|
115
|
-
}
|
116
|
-
nile-button.rte-align-trigger::part(base),
|
117
|
-
nile-button.rte-default-trigger::part(base) {
|
118
|
-
min-width: 32px;
|
119
|
-
height: 32px;
|
120
|
-
padding: 0px 6px;
|
121
|
-
box-shadow: none;
|
122
|
-
}
|
123
|
-
`;
|
124
|
-
this.insertBefore(style, this.firstChild);
|
125
|
-
}
|
132
|
+
render() {
|
133
|
+
const opts = this.parsedOptions;
|
134
|
+
this.ensureDefault();
|
135
|
+
const current = opts.find(o => o.value === this.selectedValue);
|
126
136
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
// ► Align: icon-only items + icon trigger
|
133
|
-
if (this.type === 'align') {
|
134
|
-
const trigger = current?.icon
|
135
|
-
? html`<nile-icon name="${current.icon}"></nile-icon>`
|
136
|
-
: (this.label || 'Align');
|
137
|
-
|
138
|
-
return html`
|
139
|
-
<nile-dropdown class="rte-align-dd">
|
140
|
-
<nile-button slot="trigger" variant="tertiary" class="rte-align-trigger">
|
141
|
-
${trigger}
|
142
|
-
</nile-button>
|
143
|
-
<nile-menu class="rte-align-menu">
|
144
|
-
${opts.map(o => html`
|
145
|
-
<nile-menu-item
|
146
|
-
class="rte-align-item"
|
147
|
-
?active=${o.value === this.selectedValue}
|
148
|
-
@click=${() => this.onSelect(o.value)}>
|
149
|
-
<nile-icon name="${o.icon}"></nile-icon>
|
150
|
-
</nile-menu-item>
|
151
|
-
`)}
|
152
|
-
</nile-menu>
|
153
|
-
</nile-dropdown>
|
154
|
-
`;
|
155
|
-
}
|
137
|
+
// ► Align: icon-only items + icon trigger
|
138
|
+
if (this.type === 'align') {
|
139
|
+
const trigger = current?.icon
|
140
|
+
? html`<nile-icon name="${current.icon}"></nile-icon>`
|
141
|
+
: (this.label || 'Align');
|
156
142
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
${o.label}
|
176
|
-
</nile-menu-item>
|
177
|
-
`)}
|
178
|
-
</nile-menu>
|
179
|
-
</nile-dropdown>
|
180
|
-
`;
|
181
|
-
}
|
143
|
+
return html`
|
144
|
+
<nile-dropdown class="rte-align-dd">
|
145
|
+
<nile-button slot="trigger" variant="tertiary" class="rte-align-trigger">
|
146
|
+
${trigger}
|
147
|
+
</nile-button>
|
148
|
+
<nile-menu class="rte-align-menu">
|
149
|
+
${opts.map(o => html`
|
150
|
+
<nile-menu-item
|
151
|
+
class="rte-align-item"
|
152
|
+
?active=${o.value === this.selectedValue}
|
153
|
+
@click=${() => this.onSelect(o.value)}>
|
154
|
+
<nile-icon name="${o.icon}"></nile-icon>
|
155
|
+
</nile-menu-item>
|
156
|
+
`)}
|
157
|
+
</nile-menu>
|
158
|
+
</nile-dropdown>
|
159
|
+
`;
|
160
|
+
}
|
182
161
|
|
183
|
-
|
184
|
-
|
162
|
+
// ► Font: show labels, preview fonts in items and trigger
|
163
|
+
if (this.type === 'font') {
|
164
|
+
const triggerText = current?.label || this.label || 'Font';
|
185
165
|
return html`
|
186
166
|
<nile-dropdown class="rte-default-dd">
|
187
|
-
<nile-button
|
167
|
+
<nile-button
|
168
|
+
slot="trigger"
|
169
|
+
variant="tertiary"
|
170
|
+
class="rte-default-trigger"
|
171
|
+
style="font-family: ${current?.value || 'inherit'}">
|
188
172
|
${triggerText} <nile-icon name="arrowdown"></nile-icon>
|
189
173
|
</nile-button>
|
190
174
|
<nile-menu class="rte-default-menu">
|
191
175
|
${opts.map(o => html`
|
192
176
|
<nile-menu-item
|
177
|
+
style="font-family: ${o.value}"
|
193
178
|
?active=${o.value === this.selectedValue}
|
194
179
|
@click=${() => this.onSelect(o.value)}>
|
195
180
|
${o.label}
|
@@ -199,10 +184,30 @@
|
|
199
184
|
</nile-dropdown>
|
200
185
|
`;
|
201
186
|
}
|
187
|
+
|
188
|
+
// ► Default (e.g., heading): text items; heading values are validated already
|
189
|
+
const triggerText = current?.label || this.label || 'Select';
|
190
|
+
return html`
|
191
|
+
<nile-dropdown class="rte-default-dd">
|
192
|
+
<nile-button slot="trigger" variant="tertiary" class="rte-default-trigger">
|
193
|
+
${triggerText} <nile-icon name="arrowdown"></nile-icon>
|
194
|
+
</nile-button>
|
195
|
+
<nile-menu class="rte-default-menu">
|
196
|
+
${opts.map(o => html`
|
197
|
+
<nile-menu-item
|
198
|
+
?active=${o.value === this.selectedValue}
|
199
|
+
@click=${() => this.onSelect(o.value)}>
|
200
|
+
${o.label}
|
201
|
+
</nile-menu-item>
|
202
|
+
`)}
|
203
|
+
</nile-menu>
|
204
|
+
</nile-dropdown>
|
205
|
+
`;
|
202
206
|
}
|
207
|
+
}
|
203
208
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
}
|
209
|
+
declare global {
|
210
|
+
interface HTMLElementTagNameMap {
|
211
|
+
'nile-rte-select': NileRteSelect;
|
208
212
|
}
|
213
|
+
}
|