@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.
Files changed (223) hide show
  1. package/demo/index.html +24 -11
  2. package/dist/index.cjs.js +1 -1
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.js +130 -58
  5. package/dist/nile-avatar/nile-avatar.test.cjs.js +1 -1
  6. package/dist/nile-avatar/nile-avatar.test.cjs.js.map +1 -1
  7. package/dist/nile-avatar/nile-avatar.test.esm.js +1 -1
  8. package/dist/nile-badge/index.cjs.js +1 -1
  9. package/dist/nile-badge/index.esm.js +1 -1
  10. package/dist/nile-badge/nile-badge.cjs.js +1 -1
  11. package/dist/nile-badge/nile-badge.cjs.js.map +1 -1
  12. package/dist/nile-badge/nile-badge.esm.js +1 -1
  13. package/dist/nile-badge/nile-badge.test.cjs.js +1 -1
  14. package/dist/nile-badge/nile-badge.test.cjs.js.map +1 -1
  15. package/dist/nile-badge/nile-badge.test.esm.js +1 -1
  16. package/dist/nile-button/index.cjs.js +1 -1
  17. package/dist/nile-button/index.esm.js +1 -1
  18. package/dist/nile-button/nile-button.cjs.js +1 -1
  19. package/dist/nile-button/nile-button.cjs.js.map +1 -1
  20. package/dist/nile-button/nile-button.esm.js +1 -1
  21. package/dist/nile-button/nile-button.test.cjs.js +1 -1
  22. package/dist/nile-button/nile-button.test.cjs.js.map +1 -1
  23. package/dist/nile-button/nile-button.test.esm.js +1 -1
  24. package/dist/nile-calendar/nile-calendar.test.cjs.js +1 -1
  25. package/dist/nile-calendar/nile-calendar.test.cjs.js.map +1 -1
  26. package/dist/nile-calendar/nile-calendar.test.esm.js +1 -1
  27. package/dist/nile-chip/nile-chip.test.cjs.js +1 -1
  28. package/dist/nile-chip/nile-chip.test.cjs.js.map +1 -1
  29. package/dist/nile-chip/nile-chip.test.esm.js +1 -1
  30. package/dist/nile-dialog/index.cjs.js +1 -1
  31. package/dist/nile-dialog/index.esm.js +1 -1
  32. package/dist/nile-dialog/nile-dialog.cjs.js +1 -1
  33. package/dist/nile-dialog/nile-dialog.cjs.js.map +1 -1
  34. package/dist/nile-dialog/nile-dialog.esm.js +2 -2
  35. package/dist/nile-dialog/nile-dialog.test.cjs.js +1 -1
  36. package/dist/nile-dialog/nile-dialog.test.cjs.js.map +1 -1
  37. package/dist/nile-dialog/nile-dialog.test.esm.js +1 -1
  38. package/dist/nile-drawer/index.cjs.js +1 -1
  39. package/dist/nile-drawer/index.esm.js +1 -1
  40. package/dist/nile-drawer/nile-drawer.cjs.js +1 -1
  41. package/dist/nile-drawer/nile-drawer.cjs.js.map +1 -1
  42. package/dist/nile-drawer/nile-drawer.esm.js +1 -1
  43. package/dist/nile-drawer/nile-drawer.test.cjs.js +1 -1
  44. package/dist/nile-drawer/nile-drawer.test.cjs.js.map +1 -1
  45. package/dist/nile-drawer/nile-drawer.test.esm.js +1 -1
  46. package/dist/nile-icon/icons/svg/format_clear.cjs.js +2 -0
  47. package/dist/nile-icon/icons/svg/format_clear.cjs.js.map +1 -0
  48. package/dist/nile-icon/icons/svg/format_clear.esm.js +1 -0
  49. package/dist/nile-icon/icons/svg/format_list_bulleted.cjs.js +2 -0
  50. package/dist/nile-icon/icons/svg/format_list_bulleted.cjs.js.map +1 -0
  51. package/dist/nile-icon/icons/svg/format_list_bulleted.esm.js +1 -0
  52. package/dist/nile-icon/icons/svg/format_list_numbered.cjs.js +2 -0
  53. package/dist/nile-icon/icons/svg/format_list_numbered.cjs.js.map +1 -0
  54. package/dist/nile-icon/icons/svg/format_list_numbered.esm.js +1 -0
  55. package/dist/nile-icon/icons/svg/index.cjs.js +1 -1
  56. package/dist/nile-icon/icons/svg/index.esm.js +1 -1
  57. package/dist/nile-icon/index.cjs.js +1 -1
  58. package/dist/nile-icon/index.cjs.js.map +1 -1
  59. package/dist/nile-icon/index.esm.js +2 -2
  60. package/dist/nile-icon/nile-icon.test.cjs.js +1 -1
  61. package/dist/nile-icon/nile-icon.test.cjs.js.map +1 -1
  62. package/dist/nile-icon/nile-icon.test.esm.js +1 -1
  63. package/dist/nile-icon-button/index.cjs.js +1 -1
  64. package/dist/nile-icon-button/index.esm.js +1 -1
  65. package/dist/nile-icon-button/nile-icon-button.cjs.js +1 -1
  66. package/dist/nile-icon-button/nile-icon-button.cjs.js.map +1 -1
  67. package/dist/nile-icon-button/nile-icon-button.esm.js +1 -1
  68. package/dist/nile-input/index.cjs.js +1 -1
  69. package/dist/nile-input/index.esm.js +1 -1
  70. package/dist/nile-input/nile-input.cjs.js +1 -1
  71. package/dist/nile-input/nile-input.cjs.js.map +1 -1
  72. package/dist/nile-input/nile-input.esm.js +1 -1
  73. package/dist/nile-input/nile-input.test.cjs.js +1 -1
  74. package/dist/nile-input/nile-input.test.cjs.js.map +1 -1
  75. package/dist/nile-input/nile-input.test.esm.js +1 -1
  76. package/dist/nile-menu-item/index.cjs.js +1 -1
  77. package/dist/nile-menu-item/index.esm.js +1 -1
  78. package/dist/nile-menu-item/nile-menu-item.cjs.js +1 -1
  79. package/dist/nile-menu-item/nile-menu-item.cjs.js.map +1 -1
  80. package/dist/nile-menu-item/nile-menu-item.esm.js +1 -1
  81. package/dist/nile-option/index.cjs.js +1 -1
  82. package/dist/nile-option/index.esm.js +1 -1
  83. package/dist/nile-option/nile-option.cjs.js +1 -1
  84. package/dist/nile-option/nile-option.cjs.js.map +1 -1
  85. package/dist/nile-option/nile-option.esm.js +1 -1
  86. package/dist/nile-rich-text-editor/index.cjs.js +1 -1
  87. package/dist/nile-rich-text-editor/index.esm.js +1 -1
  88. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
  89. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
  90. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
  91. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
  92. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +14 -13
  93. package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
  94. package/dist/nile-rich-text-editor/nile-rte-color.cjs.js +1 -1
  95. package/dist/nile-rich-text-editor/nile-rte-color.cjs.js.map +1 -1
  96. package/dist/nile-rich-text-editor/nile-rte-color.esm.js +54 -1
  97. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js +2 -0
  98. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js.map +1 -0
  99. package/dist/nile-rich-text-editor/nile-rte-link.esm.js +19 -0
  100. package/dist/nile-rich-text-editor/nile-rte-select.cjs.js +1 -1
  101. package/dist/nile-rich-text-editor/nile-rte-select.cjs.js.map +1 -1
  102. package/dist/nile-rich-text-editor/nile-rte-select.esm.js +39 -39
  103. package/dist/nile-rich-text-editor/utils.cjs.js +1 -1
  104. package/dist/nile-rich-text-editor/utils.cjs.js.map +1 -1
  105. package/dist/nile-rich-text-editor/utils.esm.js +1 -1
  106. package/dist/nile-select/index.cjs.js +1 -1
  107. package/dist/nile-select/index.esm.js +1 -1
  108. package/dist/nile-select/nile-select.cjs.js +1 -1
  109. package/dist/nile-select/nile-select.cjs.js.map +1 -1
  110. package/dist/nile-select/nile-select.esm.js +1 -1
  111. package/dist/nile-select/nile-select.test.cjs.js +1 -1
  112. package/dist/nile-select/nile-select.test.cjs.js.map +1 -1
  113. package/dist/nile-select/nile-select.test.esm.js +1 -1
  114. package/dist/nile-tab/index.cjs.js +1 -1
  115. package/dist/nile-tab/index.esm.js +1 -1
  116. package/dist/nile-tab/nile-tab.cjs.js +1 -1
  117. package/dist/nile-tab/nile-tab.cjs.js.map +1 -1
  118. package/dist/nile-tab/nile-tab.esm.js +1 -1
  119. package/dist/nile-tab-group/index.cjs.js +1 -1
  120. package/dist/nile-tab-group/index.esm.js +1 -1
  121. package/dist/nile-tab-group/nile-tab-group.cjs.js +1 -1
  122. package/dist/nile-tab-group/nile-tab-group.cjs.js.map +1 -1
  123. package/dist/nile-tab-group/nile-tab-group.esm.js +1 -1
  124. package/dist/nile-tab-group/nile-tab-group.test.cjs.js +1 -1
  125. package/dist/nile-tab-group/nile-tab-group.test.cjs.js.map +1 -1
  126. package/dist/nile-tab-group/nile-tab-group.test.esm.js +1 -1
  127. package/dist/nile-tag/index.cjs.js +1 -1
  128. package/dist/nile-tag/index.esm.js +1 -1
  129. package/dist/nile-tag/nile-tag.cjs.js +1 -1
  130. package/dist/nile-tag/nile-tag.cjs.js.map +1 -1
  131. package/dist/nile-tag/nile-tag.esm.js +1 -1
  132. package/dist/nile-toast/index.cjs.js +1 -1
  133. package/dist/nile-toast/index.esm.js +1 -1
  134. package/dist/nile-toast/nile-toast.cjs.js +1 -1
  135. package/dist/nile-toast/nile-toast.cjs.js.map +1 -1
  136. package/dist/nile-toast/nile-toast.esm.js +1 -1
  137. package/dist/nile-tree/index.cjs.js +1 -1
  138. package/dist/nile-tree/index.esm.js +1 -1
  139. package/dist/nile-tree/nile-tree.cjs.js +1 -1
  140. package/dist/nile-tree/nile-tree.cjs.js.map +1 -1
  141. package/dist/nile-tree/nile-tree.esm.js +1 -1
  142. package/dist/nile-tree-item/index.cjs.js +1 -1
  143. package/dist/nile-tree-item/index.esm.js +1 -1
  144. package/dist/nile-tree-item/nile-tree-item.cjs.js +1 -1
  145. package/dist/nile-tree-item/nile-tree-item.cjs.js.map +1 -1
  146. package/dist/nile-tree-item/nile-tree-item.esm.js +1 -1
  147. package/dist/nile-virtual-select/index.cjs.js +1 -1
  148. package/dist/nile-virtual-select/index.esm.js +1 -1
  149. package/dist/nile-virtual-select/nile-virtual-select.cjs.js +1 -1
  150. package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
  151. package/dist/nile-virtual-select/nile-virtual-select.esm.js +1 -1
  152. package/dist/src/nile-icon/icons/svg/format_clear.d.ts +5 -0
  153. package/dist/src/nile-icon/icons/svg/format_clear.js +5 -0
  154. package/dist/src/nile-icon/icons/svg/format_clear.js.map +1 -0
  155. package/dist/src/nile-icon/icons/svg/format_list_bulleted.d.ts +5 -0
  156. package/dist/src/nile-icon/icons/svg/format_list_bulleted.js +5 -0
  157. package/dist/src/nile-icon/icons/svg/format_list_bulleted.js.map +1 -0
  158. package/dist/src/nile-icon/icons/svg/format_list_numbered.d.ts +5 -0
  159. package/dist/src/nile-icon/icons/svg/format_list_numbered.js +5 -0
  160. package/dist/src/nile-icon/icons/svg/format_list_numbered.js.map +1 -0
  161. package/dist/src/nile-icon/icons/svg/index.d.ts +3 -0
  162. package/dist/src/nile-icon/icons/svg/index.js +3 -0
  163. package/dist/src/nile-icon/icons/svg/index.js.map +1 -1
  164. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +14 -13
  165. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
  166. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +1 -0
  167. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +38 -90
  168. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
  169. package/dist/src/nile-rich-text-editor/nile-rte-color.d.ts +11 -3
  170. package/dist/src/nile-rich-text-editor/nile-rte-color.js +171 -6
  171. package/dist/src/nile-rich-text-editor/nile-rte-color.js.map +1 -1
  172. package/dist/src/nile-rich-text-editor/nile-rte-link.d.ts +19 -0
  173. package/dist/src/nile-rich-text-editor/nile-rte-link.js +172 -0
  174. package/dist/src/nile-rich-text-editor/nile-rte-link.js.map +1 -0
  175. package/dist/src/nile-rich-text-editor/nile-rte-select.js +62 -57
  176. package/dist/src/nile-rich-text-editor/nile-rte-select.js.map +1 -1
  177. package/dist/src/nile-rich-text-editor/rte-utils/content.d.ts +2 -0
  178. package/dist/src/nile-rich-text-editor/rte-utils/content.js +25 -0
  179. package/dist/src/nile-rich-text-editor/rte-utils/content.js.map +1 -0
  180. package/dist/src/nile-rich-text-editor/rte-utils/css.d.ts +1 -0
  181. package/dist/src/nile-rich-text-editor/rte-utils/css.js +9 -0
  182. package/dist/src/nile-rich-text-editor/rte-utils/css.js.map +1 -0
  183. package/dist/src/nile-rich-text-editor/rte-utils/dom.d.ts +2 -0
  184. package/dist/src/nile-rich-text-editor/rte-utils/dom.js +48 -0
  185. package/dist/src/nile-rich-text-editor/rte-utils/dom.js.map +1 -0
  186. package/dist/src/nile-rich-text-editor/rte-utils/formatting.d.ts +2 -0
  187. package/dist/src/nile-rich-text-editor/rte-utils/formatting.js +69 -0
  188. package/dist/src/nile-rich-text-editor/rte-utils/formatting.js.map +1 -0
  189. package/dist/src/nile-rich-text-editor/rte-utils/keys.d.ts +2 -0
  190. package/dist/src/nile-rich-text-editor/rte-utils/keys.js +38 -0
  191. package/dist/src/nile-rich-text-editor/rte-utils/keys.js.map +1 -0
  192. package/dist/src/nile-rich-text-editor/rte-utils/lists.d.ts +2 -0
  193. package/dist/src/nile-rich-text-editor/rte-utils/lists.js +28 -0
  194. package/dist/src/nile-rich-text-editor/rte-utils/lists.js.map +1 -0
  195. package/dist/src/nile-rich-text-editor/rte-utils/selection.d.ts +17 -0
  196. package/dist/src/nile-rich-text-editor/rte-utils/selection.js +39 -0
  197. package/dist/src/nile-rich-text-editor/rte-utils/selection.js.map +1 -0
  198. package/dist/src/nile-rich-text-editor/rte-utils/toolbar.d.ts +28 -0
  199. package/dist/src/nile-rich-text-editor/rte-utils/toolbar.js +161 -0
  200. package/dist/src/nile-rich-text-editor/rte-utils/toolbar.js.map +1 -0
  201. package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.d.ts +13 -0
  202. package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.js +119 -0
  203. package/dist/src/nile-rich-text-editor/rte-utils/toolbarState.js.map +1 -0
  204. package/dist/src/nile-rich-text-editor/rte-utils/vars.d.ts +1 -0
  205. package/dist/src/nile-rich-text-editor/rte-utils/vars.js +14 -0
  206. package/dist/src/nile-rich-text-editor/rte-utils/vars.js.map +1 -0
  207. package/dist/src/nile-rich-text-editor/sanatize.d.ts +1 -0
  208. package/dist/src/nile-rich-text-editor/sanatize.js +40 -0
  209. package/dist/src/nile-rich-text-editor/sanatize.js.map +1 -0
  210. package/dist/src/nile-rich-text-editor/utils.js.map +1 -1
  211. package/dist/tsconfig.tsbuildinfo +1 -1
  212. package/package.json +1 -1
  213. package/src/nile-icon/icons/svg/format_clear.ts +5 -0
  214. package/src/nile-icon/icons/svg/format_list_bulleted.ts +5 -0
  215. package/src/nile-icon/icons/svg/format_list_numbered.ts +5 -0
  216. package/src/nile-icon/icons/svg/index.ts +3 -0
  217. package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +14 -13
  218. package/src/nile-rich-text-editor/nile-rich-text-editor.ts +56 -90
  219. package/src/nile-rich-text-editor/nile-rte-color.ts +182 -6
  220. package/src/nile-rich-text-editor/nile-rte-link.ts +161 -0
  221. package/src/nile-rich-text-editor/nile-rte-select.ts +178 -173
  222. package/src/nile-rich-text-editor/utils.ts +342 -341
  223. 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
- // 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);
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
- @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>;
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
- /** Fallback label for trigger (e.g., "Align") */
33
- @property({ type: String }) label = '';
77
+ return items;
78
+ }
34
79
 
35
- @state() private selectedValue = '';
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
- 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] || '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
- 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
- }
98
+ connectedCallback(): void {
99
+ super.connectedCallback();
100
+ this.injectLocalStyles();
101
+ }
76
102
 
77
- return items;
78
- }
103
+ private injectLocalStyles() {
104
+ if (this.querySelector('style[data-rte-select-style]')) return;
79
105
 
80
- private ensureDefault() {
81
- if (!this.selectedValue) {
82
- const first = this.parsedOptions[0];
83
- if (first) this.selectedValue = first.value;
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
- private onSelect(value: string) {
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
- this.selectedValue = value;
93
- this.dispatchEvent(new CustomEvent('change', {
94
- detail: value, bubbles: true, composed: true
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
- connectedCallback(): void {
99
- super.connectedCallback();
100
- this.injectLocalStyles();
101
- }
126
+ border: none;
127
+ }
128
+ `;
129
+ this.insertBefore(style, this.firstChild);
130
+ }
102
131
 
103
- private injectLocalStyles() {
104
- if (this.querySelector('style[data-rte-select-style]')) return;
105
-
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;
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
- render() {
128
- const opts = this.parsedOptions;
129
- this.ensureDefault();
130
- const current = opts.find(o => o.value === this.selectedValue);
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
- // ► 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>
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
- // ► Default (e.g., heading): text items; heading values are validated already
184
- const triggerText = current?.label || this.label || 'Select';
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 slot="trigger" variant="tertiary" class="rte-default-trigger">
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
- declare global {
205
- interface HTMLElementTagNameMap {
206
- 'nile-rte-select': NileRteSelect;
207
- }
209
+ declare global {
210
+ interface HTMLElementTagNameMap {
211
+ 'nile-rte-select': NileRteSelect;
208
212
  }
213
+ }