@aquera/nile-elements 0.1.64-beta-1.2 → 0.1.65-beta-1.0
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 +29 -109
- package/dist/index.js +608 -547
- 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 +45 -2
- package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-mentions.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-mentions.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rte-mentions.esm.js +1 -1
- 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 +31 -13
- 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/src/nile-rich-text-editor/nile-rich-text-editor.css.js +43 -0
- 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 +3 -0
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +81 -22
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rte-mentions.js +5 -0
- package/dist/src/nile-rich-text-editor/nile-rte-mentions.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rte-select.d.ts +15 -1
- package/dist/src/nile-rich-text-editor/nile-rte-select.js +85 -52
- package/dist/src/nile-rich-text-editor/nile-rte-select.js.map +1 -1
- package/dist/src/nile-rich-text-editor/utils.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/utils.js +17 -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-rich-text-editor/nile-rich-text-editor.css.ts +43 -0
- package/src/nile-rich-text-editor/nile-rich-text-editor.ts +112 -40
- package/src/nile-rich-text-editor/nile-rte-mentions.ts +5 -0
- package/src/nile-rich-text-editor/nile-rte-select.ts +97 -58
- package/src/nile-rich-text-editor/utils.ts +18 -0
- package/vscode-html-custom-data.json +3 -3
package/package.json
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
"description": "Webcomponent nile-elements following open-wc recommendations",
|
4
4
|
"license": "MIT",
|
5
5
|
"author": "nile-elements",
|
6
|
-
"version": "0.1.
|
6
|
+
"version": "0.1.65-beta-1.0",
|
7
7
|
"main": "dist/src/index.js",
|
8
8
|
"type": "module",
|
9
9
|
"module": "dist/src/index.js",
|
@@ -44,6 +44,49 @@ nile-rte-toolbar-item > nile-button::part(base) {
|
|
44
44
|
|
45
45
|
.editor { min-height:160px; padding:12px; border:1px solid #e5e7eb; border-radius:0 0 8px 8px; background:#fff; outline:none; }
|
46
46
|
nile-rte-preview { display:block; margin-top:10px; padding:10px; border:1px dashed #cbd5e1; border-radius:8px; background:#fafafa; }
|
47
|
+
|
48
|
+
.rte-color-trigger {
|
49
|
+
display: inline-flex;
|
50
|
+
align-items: center;
|
51
|
+
justify-content: center;
|
52
|
+
height: 28px;
|
53
|
+
padding: 0 8px;
|
54
|
+
border: 1px solid var(--nile-color-border, #d9d9d9);
|
55
|
+
border-radius: 6px;
|
56
|
+
background: #fff;
|
57
|
+
cursor: pointer;
|
58
|
+
}
|
59
|
+
|
60
|
+
.rte-color-trigger .glyph-stack {
|
61
|
+
display: grid; /* stack vertically */
|
62
|
+
grid-auto-rows: max-content;
|
63
|
+
align-items: center;
|
64
|
+
justify-items: center;
|
65
|
+
line-height: 1;
|
66
|
+
}
|
67
|
+
|
68
|
+
.rte-color-trigger .glyph {
|
69
|
+
font-size: 14px;
|
70
|
+
line-height: 1;
|
71
|
+
margin-bottom: 2px; /* little breathing space above underline */
|
72
|
+
}
|
73
|
+
|
74
|
+
.rte-color-trigger .underline {
|
75
|
+
width: 18px;
|
76
|
+
height: 3px;
|
77
|
+
border-radius: 2px;
|
78
|
+
background: currentColor; /* we override via JS with backgroundColor */
|
79
|
+
}
|
80
|
+
|
81
|
+
/* (unchanged) highlight square */
|
82
|
+
.rte-color-trigger .swatch-box {
|
83
|
+
width: 18px;
|
84
|
+
height: 16px;
|
85
|
+
border-radius: 4px;
|
86
|
+
border: 1px solid rgba(0,0,0,0.35);
|
87
|
+
background: currentColor; /* overridden via JS */
|
88
|
+
}
|
89
|
+
|
47
90
|
`;
|
48
91
|
|
49
92
|
export default [styles];
|
@@ -13,7 +13,7 @@ import './nile-rte-mentions';
|
|
13
13
|
import {
|
14
14
|
closestBlock, nearestElement, rgbToHex,
|
15
15
|
toggleInlineTag, setBlockTag, setAlignment,
|
16
|
-
setFontFamily, setForeColor, insertOrEditLink
|
16
|
+
setFontFamily, setForeColor, insertOrEditLink, setBackColor
|
17
17
|
} from './utils';
|
18
18
|
|
19
19
|
import {styles} from './nile-rich-text-editor.css';
|
@@ -83,6 +83,10 @@ export class NileRichTextEditor extends LitElement {
|
|
83
83
|
private headingSelect: HTMLSelectElement | null = null;
|
84
84
|
private fontSelect: HTMLSelectElement | null = null;
|
85
85
|
private colorInput: HTMLInputElement | null = null;
|
86
|
+
private bgColorInput: HTMLInputElement | null = null;
|
87
|
+
private colorSwatchEl: HTMLElement | null = null;
|
88
|
+
private bgSwatchEl: HTMLElement | null = null;
|
89
|
+
|
86
90
|
|
87
91
|
// Mentions controller (child)
|
88
92
|
private mentionsEl: HTMLElement | null = null;
|
@@ -100,27 +104,27 @@ export class NileRichTextEditor extends LitElement {
|
|
100
104
|
connectedCallback(): void {
|
101
105
|
super.connectedCallback();
|
102
106
|
|
103
|
-
|
107
|
+
|
104
108
|
this.injectCss(styles.cssText);
|
105
109
|
|
106
110
|
|
107
|
-
|
111
|
+
|
108
112
|
this.toolbarEl = this.querySelector('nile-rte-toolbar');
|
109
113
|
this.previewEl = this.querySelector('nile-rte-preview');
|
110
114
|
|
111
|
-
|
115
|
+
|
112
116
|
this.ensureEditor();
|
113
117
|
|
114
|
-
|
118
|
+
|
115
119
|
if (this.value && !this.editorEl.innerHTML.trim()) {
|
116
120
|
this.editorEl.innerHTML = this.value;
|
117
121
|
}
|
118
122
|
this.content = this.editorEl.innerHTML;
|
119
123
|
|
120
|
-
|
124
|
+
|
121
125
|
if (this.toolbarEl) this.wireAuthoredToolbar(this.toolbarEl);
|
122
126
|
|
123
|
-
|
127
|
+
|
124
128
|
this.mentionsEl = this.querySelector('nile-rte-mentions');
|
125
129
|
if (this.mentionsEl) {
|
126
130
|
|
@@ -142,7 +146,7 @@ export class NileRichTextEditor extends LitElement {
|
|
142
146
|
|
143
147
|
disconnectedCallback(): void {
|
144
148
|
document.removeEventListener('selectionchange', this.onSelectionChange, true);
|
145
|
-
|
149
|
+
|
146
150
|
if (this.mentionsEl && (this.mentionsEl as any).detach) {
|
147
151
|
(this.mentionsEl as any).detach();
|
148
152
|
}
|
@@ -167,7 +171,6 @@ export class NileRichTextEditor extends LitElement {
|
|
167
171
|
}
|
168
172
|
this.editorEl = editor;
|
169
173
|
}
|
170
|
-
// **NEW**: seed an empty paragraph so setBlockTag() has something to swap
|
171
174
|
if (!this.editorEl.innerHTML.trim()) {
|
172
175
|
this.editorEl.innerHTML = '<p><br></p>';
|
173
176
|
}
|
@@ -181,17 +184,17 @@ export class NileRichTextEditor extends LitElement {
|
|
181
184
|
this.updateContent();
|
182
185
|
});
|
183
186
|
this.editorEl.addEventListener('mouseup', () => this.saveSelection());
|
184
|
-
this.editorEl.addEventListener('keyup', () => this.saveSelection());
|
187
|
+
this.editorEl.addEventListener('keyup', () => this.saveSelection());
|
185
188
|
}
|
186
189
|
|
187
190
|
private wireAuthoredToolbar(tb: HTMLElement) {
|
188
|
-
|
191
|
+
|
189
192
|
this.buttonMap.clear();
|
190
193
|
this.headingSelect = null;
|
191
194
|
this.fontSelect = null;
|
192
195
|
this.colorInput = null;
|
193
196
|
|
194
|
-
|
197
|
+
|
195
198
|
Array.from(tb.children).forEach(child => {
|
196
199
|
const tag = child.tagName.toLowerCase();
|
197
200
|
|
@@ -208,7 +211,7 @@ export class NileRichTextEditor extends LitElement {
|
|
208
211
|
|
209
212
|
|
210
213
|
if (tag === 'nile-rte-toolbar-item') {
|
211
|
-
|
214
|
+
|
212
215
|
let btn = child.querySelector(':scope > nile-button') as HTMLElement | null;
|
213
216
|
|
214
217
|
const cmd = child.getAttribute('name') || '';
|
@@ -216,14 +219,14 @@ export class NileRichTextEditor extends LitElement {
|
|
216
219
|
const iconAttr = child.getAttribute('icon');
|
217
220
|
const authoredHasContent = child.innerHTML.trim().length > 0;
|
218
221
|
|
219
|
-
|
222
|
+
|
220
223
|
if (!btn) {
|
221
224
|
btn = document.createElement('nile-button');
|
222
225
|
(btn as any).variant = 'tertiary';
|
223
226
|
(btn as any).size = 'small'; // optional
|
224
227
|
}
|
225
228
|
|
226
|
-
|
229
|
+
|
227
230
|
if (iconAttr) {
|
228
231
|
btn.innerHTML = `<nile-icon name="${iconAttr}" aria-label="${label}"></nile-icon>`;
|
229
232
|
child.innerHTML = '';
|
@@ -284,32 +287,89 @@ export class NileRichTextEditor extends LitElement {
|
|
284
287
|
}
|
285
288
|
|
286
289
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
290
|
+
if (tag === 'nile-rte-color') {
|
291
|
+
const label = child.getAttribute('label') ?? 'Text color';
|
292
|
+
const value = child.getAttribute('value') ?? '#000000';
|
293
|
+
const mode = child.getAttribute('mode') ?? 'text'; // 'text' | 'background'
|
294
|
+
|
295
|
+
// Create/attach the hidden color input
|
296
|
+
let input = child.querySelector(':scope > input[type="color"]') as HTMLInputElement | null;
|
297
|
+
if (!input) {
|
298
|
+
input = document.createElement('input');
|
299
|
+
input.type = 'color';
|
300
|
+
input.style.position = 'absolute';
|
301
|
+
input.style.opacity = '0';
|
302
|
+
input.style.pointerEvents = 'none'; // we'll click it programmatically
|
303
|
+
child.appendChild(input);
|
304
|
+
}
|
305
|
+
input.title = label;
|
306
|
+
input.value = value;
|
307
|
+
|
308
|
+
// Build a custom trigger that shows the current color
|
309
|
+
let trigger = child.querySelector(':scope > button.rte-color-trigger') as HTMLButtonElement | null;
|
310
|
+
if (!trigger) {
|
311
|
+
trigger = document.createElement('button');
|
312
|
+
trigger.type = 'button';
|
313
|
+
trigger.className = 'rte-color-trigger';
|
314
|
+
trigger.setAttribute('aria-label', label);
|
315
|
+
|
316
|
+
if (mode === 'background') {
|
317
|
+
trigger.innerHTML = `
|
318
|
+
<span class="swatch-box" aria-hidden="true"></span>
|
319
|
+
`;
|
320
|
+
} else {
|
321
|
+
trigger.innerHTML = `
|
322
|
+
<span class="glyph-stack" aria-hidden="true">
|
323
|
+
<span class="glyph">A</span>
|
324
|
+
<span class="underline"></span>
|
325
|
+
</span>
|
326
|
+
`;
|
327
|
+
}
|
328
|
+
child.appendChild(trigger);
|
329
|
+
}
|
330
|
+
|
331
|
+
// Cache swatch elements to update later
|
332
|
+
const underline = trigger.querySelector('.underline') as HTMLElement | null;
|
333
|
+
const square = trigger.querySelector('.swatch-box') as HTMLElement | null;
|
334
|
+
|
335
|
+
if (mode === 'background') {
|
336
|
+
this.bgColorInput = input;
|
337
|
+
this.bgSwatchEl = square;
|
338
|
+
if (this.bgSwatchEl) this.bgSwatchEl.style.backgroundColor = input.value;
|
339
|
+
} else {
|
340
|
+
this.colorInput = input;
|
341
|
+
this.colorSwatchEl = underline;
|
342
|
+
if (this.colorSwatchEl) this.colorSwatchEl.style.backgroundColor = input.value;
|
343
|
+
}
|
344
|
+
|
345
|
+
// Open native picker on trigger click
|
346
|
+
trigger.addEventListener('click', (e) => {
|
347
|
+
e.preventDefault();
|
348
|
+
// Keep selection before opening the picker
|
349
|
+
this.focusAndRestore();
|
350
|
+
input!.click();
|
351
|
+
});
|
352
|
+
|
353
|
+
// When the user picks a color, apply it + update swatch
|
354
|
+
input.addEventListener('input', () => {
|
355
|
+
this.focusAndRestore();
|
356
|
+
if (mode === 'background') {
|
357
|
+
setBackColor(this.editorEl, input!.value);
|
358
|
+
if (this.bgSwatchEl) this.bgSwatchEl.style.backgroundColor = input!.value;
|
359
|
+
} else {
|
360
|
+
setForeColor(this.editorEl, input!.value);
|
361
|
+
if (this.colorSwatchEl) this.colorSwatchEl.style.backgroundColor = input!.value;
|
362
|
+
}
|
363
|
+
this.updateContent();
|
364
|
+
this.updateToolbarState();
|
365
|
+
});
|
366
|
+
|
367
|
+
// Prevent losing selection when interacting
|
368
|
+
trigger.addEventListener('mousedown', e => e.preventDefault());
|
369
|
+
input.addEventListener('mousedown', e => e.preventDefault());
|
370
|
+
}
|
309
371
|
});
|
310
372
|
}
|
311
|
-
|
312
|
-
// ---------- Selection helpers (for toolbar state only) ----------
|
313
373
|
private onSelectionChange = () => {
|
314
374
|
if (!this.editorEl) return;
|
315
375
|
const sel = document.getSelection();
|
@@ -533,13 +593,25 @@ private ensureAtLeastOneParagraph() {
|
|
533
593
|
}
|
534
594
|
}
|
535
595
|
|
536
|
-
// Reflect color
|
537
596
|
if (this.colorInput) {
|
538
597
|
const hex = rgbToHex(comp.color);
|
539
598
|
if (hex && this.colorInput.value.toLowerCase() !== hex.toLowerCase()) {
|
540
599
|
this.colorInput.value = hex;
|
541
600
|
}
|
601
|
+
if (this.colorSwatchEl) this.colorSwatchEl.style.backgroundColor = this.colorInput.value;
|
542
602
|
}
|
603
|
+
|
604
|
+
|
605
|
+
if (this.bgColorInput) {
|
606
|
+
const bg = getComputedStyle(startElm).backgroundColor;
|
607
|
+
if (bg && !/transparent|rgba\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/i.test(bg)) {
|
608
|
+
const bgHex = rgbToHex(bg);
|
609
|
+
if (bgHex && this.bgColorInput.value.toLowerCase() !== bgHex.toLowerCase()) {
|
610
|
+
this.bgColorInput.value = bgHex;
|
611
|
+
}
|
612
|
+
}
|
613
|
+
if (this.bgSwatchEl) this.bgSwatchEl.style.backgroundColor = this.bgColorInput.value;
|
614
|
+
}
|
543
615
|
}
|
544
616
|
|
545
617
|
private syncPreview() {
|
@@ -186,6 +186,10 @@ private triggerBtn: HTMLElement | null = null;
|
|
186
186
|
.mention-dropdown li { padding: 6px 8px; cursor: pointer; border-radius: 4px; }
|
187
187
|
.mention-dropdown li:hover { background: #f1f5f9; }
|
188
188
|
.mention { background: #eef2ff; padding: 0 3px; border-radius: 3px; }
|
189
|
+
nile-menu.mentions-menu::part(menu__items-wrapper){
|
190
|
+
max-height: 260px;
|
191
|
+
}
|
192
|
+
|
189
193
|
`;
|
190
194
|
this.insertBefore(style, this.firstChild);
|
191
195
|
}
|
@@ -214,6 +218,7 @@ private ensureMentionDropdown() {
|
|
214
218
|
dd.appendChild(btn);
|
215
219
|
this.triggerBtn = btn;
|
216
220
|
const menu = document.createElement('nile-menu');
|
221
|
+
menu.classList.add('mentions-menu');
|
217
222
|
dd.appendChild(menu);
|
218
223
|
|
219
224
|
this.hostEl.appendChild(dd);
|
@@ -2,6 +2,18 @@
|
|
2
2
|
import { LitElement, html } from 'lit';
|
3
3
|
import { customElement, property, state } from 'lit/decorators.js';
|
4
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
|
+
}
|
5
17
|
|
6
18
|
@customElement('nile-rte-select')
|
7
19
|
export class NileRteSelect extends LitElement {
|
@@ -9,8 +21,14 @@ export class NileRteSelect extends LitElement {
|
|
9
21
|
|
10
22
|
/** 'heading' | 'font' | 'align' */
|
11
23
|
@property({ type: String }) type = '';
|
12
|
-
|
24
|
+
|
25
|
+
/** JSON: [{ value, label?, icon? }, ...] (attribute-based; runtime-validated) */
|
13
26
|
@property({ type: String }) options = '[]';
|
27
|
+
|
28
|
+
/** Programmatic options (preferred for TS safety). */
|
29
|
+
@property({ attribute: false })
|
30
|
+
optionsObj?: Array<GenericOption | HeadingOption>;
|
31
|
+
|
14
32
|
/** Fallback label for trigger (e.g., "Align") */
|
15
33
|
@property({ type: String }) label = '';
|
16
34
|
|
@@ -26,18 +44,37 @@ export class NileRteSelect extends LitElement {
|
|
26
44
|
return map[v] || 'align-left';
|
27
45
|
}
|
28
46
|
|
29
|
-
private get parsedOptions():
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
+
}
|
40
75
|
}
|
76
|
+
|
77
|
+
return items;
|
41
78
|
}
|
42
79
|
|
43
80
|
private ensureDefault() {
|
@@ -48,57 +85,56 @@ export class NileRteSelect extends LitElement {
|
|
48
85
|
}
|
49
86
|
|
50
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;
|
91
|
+
}
|
51
92
|
this.selectedValue = value;
|
52
93
|
this.dispatchEvent(new CustomEvent('change', {
|
53
94
|
detail: value, bubbles: true, composed: true
|
54
95
|
}));
|
55
96
|
}
|
56
97
|
|
57
|
-
|
58
|
-
|
59
98
|
connectedCallback(): void {
|
60
99
|
super.connectedCallback();
|
61
|
-
this.injectLocalStyles();
|
100
|
+
this.injectLocalStyles();
|
62
101
|
}
|
63
102
|
|
64
103
|
private injectLocalStyles() {
|
65
|
-
|
66
104
|
if (this.querySelector('style[data-rte-select-style]')) return;
|
67
105
|
|
68
106
|
const style = document.createElement('style');
|
69
107
|
style.setAttribute('data-rte-select-style', 'true');
|
70
108
|
style.textContent = `
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
+
}
|
86
123
|
`;
|
87
|
-
|
88
|
-
this.insertBefore(style, this.firstChild);
|
124
|
+
this.insertBefore(style, this.firstChild);
|
89
125
|
}
|
90
126
|
|
91
|
-
|
92
127
|
render() {
|
93
128
|
const opts = this.parsedOptions;
|
94
129
|
this.ensureDefault();
|
95
130
|
const current = opts.find(o => o.value === this.selectedValue);
|
96
131
|
|
132
|
+
// ► Align: icon-only items + icon trigger
|
97
133
|
if (this.type === 'align') {
|
98
|
-
// ► Align: separate dropdown instance + icon-only items + icon trigger
|
99
134
|
const trigger = current?.icon
|
100
135
|
? html`<nile-icon name="${current.icon}"></nile-icon>`
|
101
136
|
: (this.label || 'Align');
|
137
|
+
|
102
138
|
return html`
|
103
139
|
<nile-dropdown class="rte-align-dd">
|
104
140
|
<nile-button slot="trigger" variant="tertiary" class="rte-align-trigger">
|
@@ -115,33 +151,36 @@ export class NileRteSelect extends LitElement {
|
|
115
151
|
`)}
|
116
152
|
</nile-menu>
|
117
153
|
</nile-dropdown>
|
154
|
+
`;
|
155
|
+
}
|
118
156
|
|
119
|
-
|
157
|
+
// ► Font: show labels, preview fonts in items and trigger
|
158
|
+
if (this.type === 'font') {
|
159
|
+
const triggerText = current?.label || this.label || 'Font';
|
160
|
+
return html`
|
161
|
+
<nile-dropdown class="rte-default-dd">
|
162
|
+
<nile-button
|
163
|
+
slot="trigger"
|
164
|
+
variant="tertiary"
|
165
|
+
class="rte-default-trigger"
|
166
|
+
style="font-family: ${current?.value || 'inherit'}">
|
167
|
+
${triggerText} <nile-icon name="arrowdown"></nile-icon>
|
168
|
+
</nile-button>
|
169
|
+
<nile-menu class="rte-default-menu">
|
170
|
+
${opts.map(o => html`
|
171
|
+
<nile-menu-item
|
172
|
+
style="font-family: ${o.value}"
|
173
|
+
?active=${o.value === this.selectedValue}
|
174
|
+
@click=${() => this.onSelect(o.value)}>
|
175
|
+
${o.label}
|
176
|
+
</nile-menu-item>
|
177
|
+
`)}
|
178
|
+
</nile-menu>
|
179
|
+
</nile-dropdown>
|
120
180
|
`;
|
121
|
-
if (this.type === 'font') {
|
122
|
-
const triggerText = current?.label || this.label || 'Font';
|
123
|
-
return html`
|
124
|
-
<nile-dropdown class="rte-default-dd">
|
125
|
-
<nile-button slot="trigger" variant="tertiary" class="rte-default-trigger"
|
126
|
-
style="font-family: ${current?.value || 'inherit'}">
|
127
|
-
${triggerText} <nile-icon name="arrowdown"></nile-icon>
|
128
|
-
</nile-button>
|
129
|
-
<nile-menu class="rte-default-menu">
|
130
|
-
${opts.map(o => html`
|
131
|
-
<nile-menu-item
|
132
|
-
style="font-family: ${o.value}"
|
133
|
-
?active=${o.value === this.selectedValue}
|
134
|
-
@click=${() => this.onSelect(o.value)}>
|
135
|
-
${o.label}
|
136
|
-
</nile-menu-item>
|
137
|
-
`)}
|
138
|
-
</nile-menu>
|
139
|
-
</nile-dropdown>
|
140
|
-
`;
|
141
|
-
}
|
142
181
|
}
|
143
182
|
|
144
|
-
// ►
|
183
|
+
// ► Default (e.g., heading): text items; heading values are validated already
|
145
184
|
const triggerText = current?.label || this.label || 'Select';
|
146
185
|
return html`
|
147
186
|
<nile-dropdown class="rte-default-dd">
|
@@ -102,6 +102,24 @@ export function closestBlock(node: Node | null, root: HTMLElement): HTMLElement
|
|
102
102
|
}
|
103
103
|
surroundInline(range, 'span', { style: `font-family:${family}` });
|
104
104
|
}
|
105
|
+
|
106
|
+
export function setBackColor(rootEl: HTMLElement, color: string) {
|
107
|
+
const sel = window.getSelection();
|
108
|
+
if (!sel || sel.rangeCount === 0) return;
|
109
|
+
const range = sel.getRangeAt(0);
|
110
|
+
if (!rootEl.contains(range.commonAncestorContainer) || range.collapsed) return;
|
111
|
+
|
112
|
+
const span = document.createElement('span');
|
113
|
+
span.style.backgroundColor = color;
|
114
|
+
span.appendChild(range.extractContents());
|
115
|
+
range.insertNode(span);
|
116
|
+
const after = document.createRange();
|
117
|
+
after.setStartAfter(span);
|
118
|
+
after.collapse(true);
|
119
|
+
sel.removeAllRanges();
|
120
|
+
sel.addRange(after);
|
121
|
+
}
|
122
|
+
|
105
123
|
|
106
124
|
export function setForeColor(root: HTMLElement, color: string) {
|
107
125
|
const sel = document.getSelection(); if (!sel || sel.rangeCount === 0) return;
|
@@ -2820,7 +2820,7 @@
|
|
2820
2820
|
},
|
2821
2821
|
{
|
2822
2822
|
"name": "nile-rich-text-editor",
|
2823
|
-
"description": "Events:\n\n * `content-changed` {`CustomEvent<{ content: string; }>`} - \n\nAttributes:\n\n * `value` {`string`} - Initial HTML content\n\n * `mentions` - Optional mentions config (can also be on <nile-rte-mentions mentions=\"...\">)\n\nProperties:\n\n * `value` {`string`} - Initial HTML content\n\n * `mentions` - Optional mentions config (can also be on <nile-rte-mentions mentions=\"...\">)\n\n * `content` {`string`} - \n\n * `editorEl` {`HTMLElement`} - \n\n * `previewEl` {`HTMLElement | null`} - \n\n * `toolbarEl` {`HTMLElement | null`} - \n\n * `lastRange` {`Range | null`} - \n\n * `buttonMap` {`Map<string, HTMLElement[]>`} - \n\n * `headingSelect` {`HTMLSelectElement | null`} - \n\n * `fontSelect` {`HTMLSelectElement | null`} - \n\n * `colorInput` {`HTMLInputElement | null`} - \n\n * `mentionsEl` {`HTMLElement | null`} - \n\n * `onSelectionChange` - ",
|
2823
|
+
"description": "Events:\n\n * `content-changed` {`CustomEvent<{ content: string; }>`} - \n\nAttributes:\n\n * `value` {`string`} - Initial HTML content\n\n * `mentions` - Optional mentions config (can also be on <nile-rte-mentions mentions=\"...\">)\n\nProperties:\n\n * `value` {`string`} - Initial HTML content\n\n * `mentions` - Optional mentions config (can also be on <nile-rte-mentions mentions=\"...\">)\n\n * `content` {`string`} - \n\n * `editorEl` {`HTMLElement`} - \n\n * `previewEl` {`HTMLElement | null`} - \n\n * `toolbarEl` {`HTMLElement | null`} - \n\n * `lastRange` {`Range | null`} - \n\n * `buttonMap` {`Map<string, HTMLElement[]>`} - \n\n * `headingSelect` {`HTMLSelectElement | null`} - \n\n * `fontSelect` {`HTMLSelectElement | null`} - \n\n * `colorInput` {`HTMLInputElement | null`} - \n\n * `bgColorInput` {`HTMLInputElement | null`} - \n\n * `colorSwatchEl` {`HTMLElement | null`} - \n\n * `bgSwatchEl` {`HTMLElement | null`} - \n\n * `mentionsEl` {`HTMLElement | null`} - \n\n * `onSelectionChange` - ",
|
2824
2824
|
"attributes": [
|
2825
2825
|
{
|
2826
2826
|
"name": "value",
|
@@ -2900,7 +2900,7 @@
|
|
2900
2900
|
},
|
2901
2901
|
{
|
2902
2902
|
"name": "nile-rte-select",
|
2903
|
-
"description": "Events:\n\n * `change` {`CustomEvent<string>`} - \n\nAttributes:\n\n * `type` {`string`} - 'heading' | 'font' | 'align'\n\n * `options` {`string`} - JSON: [{ value, label?, icon? }, ...]\n\n * `label` {`string`} - Fallback label for trigger (e.g., \"Align\")\n\nProperties:\n\n * `type` {`string`} - 'heading' | 'font' | 'align'\n\n * `options` {`string`} - JSON: [{ value, label?, icon? }, ...]\n\n * `label` {`string`} - Fallback label for trigger (e.g., \"Align\")\n\n * `selectedValue` {`string`} - \n\n * `parsedOptions` {`
|
2903
|
+
"description": "Events:\n\n * `change` {`CustomEvent<string>`} - \n\nAttributes:\n\n * `type` {`string`} - 'heading' | 'font' | 'align'\n\n * `options` {`string`} - JSON: [{ value, label?, icon? }, ...] (attribute-based; runtime-validated)\n\n * `label` {`string`} - Fallback label for trigger (e.g., \"Align\")\n\nProperties:\n\n * `type` {`string`} - 'heading' | 'font' | 'align'\n\n * `options` {`string`} - JSON: [{ value, label?, icon? }, ...] (attribute-based; runtime-validated)\n\n * `optionsObj` {`(GenericOption | HeadingOption)[] | undefined`} - Programmatic options (preferred for TS safety).\n\n * `label` {`string`} - Fallback label for trigger (e.g., \"Align\")\n\n * `selectedValue` {`string`} - \n\n * `parsedOptions` {`NormalizedOption[]`} - ",
|
2904
2904
|
"attributes": [
|
2905
2905
|
{
|
2906
2906
|
"name": "type",
|
@@ -2908,7 +2908,7 @@
|
|
2908
2908
|
},
|
2909
2909
|
{
|
2910
2910
|
"name": "options",
|
2911
|
-
"description": "`options` {`string`} - JSON: [{ value, label?, icon? }, ...]\n\nProperty: options\n\nDefault: []"
|
2911
|
+
"description": "`options` {`string`} - JSON: [{ value, label?, icon? }, ...] (attribute-based; runtime-validated)\n\nProperty: options\n\nDefault: []"
|
2912
2912
|
},
|
2913
2913
|
{
|
2914
2914
|
"name": "label",
|