kempo-ui 0.0.2 → 0.0.4

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 (278) hide show
  1. package/.github/copilot-instructions.md +149 -149
  2. package/.github/workflows/publish-major.yml +39 -0
  3. package/.github/workflows/publish-minor.yml +39 -0
  4. package/.github/workflows/{publish-npm.yml → publish-patch.yml} +44 -44
  5. package/.vscode/settings.json +2 -0
  6. package/CONTRIBUTING.md +149 -149
  7. package/README.md +0 -0
  8. package/dist/kempo-hljs.css +1 -0
  9. package/dist/kempo-vars.css +0 -0
  10. package/dist/src/components/Accordion.js +36 -0
  11. package/dist/src/components/Card.js +37 -0
  12. package/dist/src/components/Collapsible.js +41 -0
  13. package/dist/src/components/ContentSlider.js +44 -0
  14. package/dist/src/components/Dialog.js +113 -0
  15. package/dist/src/components/FocusCapture.js +7 -0
  16. package/dist/src/components/Icon.js +10 -0
  17. package/dist/src/components/Import.js +1 -0
  18. package/dist/src/components/PersistantCollapsible.js +1 -0
  19. package/dist/src/components/PhotoViewer.js +135 -0
  20. package/dist/src/components/Resize.js +96 -0
  21. package/dist/src/components/ShadowComponent.js +1 -0
  22. package/dist/src/components/ShowMore.js +42 -0
  23. package/dist/src/components/SideMenu.js +97 -0
  24. package/dist/src/components/Split.js +84 -0
  25. package/dist/src/components/Tabs.js +155 -0
  26. package/dist/src/components/Tags.js +69 -0
  27. package/dist/src/components/ThemeSwitcher.js +23 -0
  28. package/dist/src/components/Timestamp.js +1 -0
  29. package/dist/src/lit-all.min.js +120 -0
  30. package/dist/src/utils/debounce.js +1 -0
  31. package/dist/src/utils/drag.js +1 -0
  32. package/dist/src/utils/formatTimestamp.js +1 -0
  33. package/dist/src/utils/propConverters.js +1 -0
  34. package/dist/src/utils/watchWindowSize.js +1 -0
  35. package/docs/components/accordion.html +166 -0
  36. package/docs/components/card.html +92 -0
  37. package/docs/components/collapsible.html +198 -0
  38. package/docs/components/content-slider.html +223 -0
  39. package/docs/components/dialog.html +251 -0
  40. package/docs/components/focus-capture.html +67 -0
  41. package/docs/components/icon.html +158 -0
  42. package/docs/components/import.html +73 -0
  43. package/docs/components/persistant-collapsible.html +123 -0
  44. package/docs/components/photo-viewer.html +226 -0
  45. package/docs/components/resize.html +178 -0
  46. package/docs/components/show-more.html +129 -0
  47. package/docs/components/side-menu.html +159 -0
  48. package/docs/components/split.html +147 -0
  49. package/docs/components/table.html +312 -0
  50. package/docs/components/tableControls.html +77 -0
  51. package/docs/components/tableCustomFields.html +113 -0
  52. package/docs/components/tableFetchRecords.html +114 -0
  53. package/docs/components/tableFieldSortHide.html +75 -0
  54. package/docs/components/tablePagination.html +100 -0
  55. package/docs/components/tableRecordEditing.html +144 -0
  56. package/docs/components/tableRecordFiltering.html +88 -0
  57. package/docs/components/tableRecordHiding.html +80 -0
  58. package/docs/components/tableRecordSearching.html +75 -0
  59. package/docs/components/tableRecordSelection.html +75 -0
  60. package/docs/components/tableRowControls.html +78 -0
  61. package/docs/components/tableSorting.html +73 -0
  62. package/docs/components/tabs.html +180 -0
  63. package/docs/components/tags.html +110 -0
  64. package/docs/components/theme-switcher.html +126 -0
  65. package/docs/components/timestamp.html +82 -0
  66. package/docs/components/toast.html +319 -0
  67. package/docs/components/toggle.html +156 -0
  68. package/docs/dev.config.json +20 -0
  69. package/docs/icons/add.svg +1 -0
  70. package/docs/icons/arrow-back.svg +1 -0
  71. package/docs/icons/arrow-down-double.svg +1 -0
  72. package/docs/icons/arrow-down.svg +1 -0
  73. package/docs/icons/arrow-forward.svg +1 -0
  74. package/docs/icons/arrow-up-double.svg +1 -0
  75. package/docs/icons/arrow-up.svg +1 -0
  76. package/docs/icons/cards.svg +1 -0
  77. package/docs/icons/check.svg +1 -0
  78. package/docs/icons/chevron-left.svg +1 -0
  79. package/docs/icons/chevron-right.svg +1 -0
  80. package/docs/icons/close.svg +2 -0
  81. package/docs/icons/compare.svg +1 -0
  82. package/docs/icons/delete.svg +1 -0
  83. package/docs/icons/drag-handle.svg +1 -0
  84. package/docs/icons/drawing2.svg +124 -0
  85. package/docs/icons/edit.svg +1 -0
  86. package/docs/icons/error.svg +1 -0
  87. package/docs/icons/export-file.svg +1 -0
  88. package/docs/icons/file-text.svg +1 -0
  89. package/docs/icons/file.svg +1 -0
  90. package/docs/icons/filter-off.svg +1 -0
  91. package/docs/icons/filter.svg +1 -0
  92. package/docs/icons/first.svg +1 -0
  93. package/docs/icons/folder-clear.svg +1 -0
  94. package/docs/icons/folder-create.svg +1 -0
  95. package/docs/icons/folder-open.svg +1 -0
  96. package/docs/icons/folder.svg +1 -0
  97. package/docs/icons/hide.svg +1 -0
  98. package/docs/icons/image.svg +1 -0
  99. package/docs/icons/label-add.svg +1 -0
  100. package/docs/icons/label.svg +1 -0
  101. package/docs/icons/last.svg +1 -0
  102. package/docs/icons/menu.svg +1 -0
  103. package/docs/icons/mode-auto.svg +1 -0
  104. package/docs/icons/mode-dark.svg +1 -0
  105. package/docs/icons/mode-light.svg +1 -0
  106. package/docs/icons/open-in-browser.svg +1 -0
  107. package/docs/icons/pause.svg +1 -0
  108. package/docs/icons/play.svg +1 -0
  109. package/docs/icons/refresh.svg +1 -0
  110. package/docs/icons/remove.svg +1 -0
  111. package/docs/icons/save.svg +1 -0
  112. package/docs/icons/search.svg +1 -0
  113. package/docs/icons/settings.svg +1 -0
  114. package/docs/icons/show.svg +1 -0
  115. package/docs/icons/storage.svg +1 -0
  116. package/docs/icons/table-visibility.svg +1 -0
  117. package/docs/icons/tag.svg +1 -0
  118. package/docs/icons/tools.svg +1 -0
  119. package/docs/icons/trash-x.svg +1 -0
  120. package/docs/icons/warning.svg +1 -0
  121. package/docs/index.html +28 -21
  122. package/docs/kempo-hljs.css +1 -0
  123. package/docs/media/civic.jpg +0 -0
  124. package/docs/media/corvette.jpg +0 -0
  125. package/docs/media/evo.jpg +0 -0
  126. package/docs/media/gtr.jpg +0 -0
  127. package/docs/media/nsx.jpg +0 -0
  128. package/docs/nav.inc.html +67 -0
  129. package/docs/nav.inc.js +4 -0
  130. package/{config/production.json → docs/prod.config.json} +20 -16
  131. package/docs/src/components/Accordion.js +36 -0
  132. package/docs/src/components/Card.js +37 -0
  133. package/docs/src/components/Collapsible.js +41 -0
  134. package/docs/src/components/ContentSlider.js +44 -0
  135. package/docs/src/components/Dialog.js +113 -0
  136. package/docs/src/components/FocusCapture.js +7 -0
  137. package/docs/src/components/HybridComponent.js +1 -0
  138. package/docs/src/components/Icon.js +10 -0
  139. package/docs/src/components/Import.js +1 -0
  140. package/docs/src/components/LightComponent.js +1 -0
  141. package/docs/src/components/PersistantCollapsible.js +1 -0
  142. package/docs/src/components/PhotoViewer.js +135 -0
  143. package/docs/src/components/Resize.js +96 -0
  144. package/docs/src/components/ShadowComponent.js +1 -0
  145. package/docs/src/components/ShowMore.js +42 -0
  146. package/docs/src/components/SideMenu.js +97 -0
  147. package/docs/src/components/Split.js +84 -0
  148. package/docs/src/components/Tabs.js +155 -0
  149. package/docs/src/components/Tags.js +69 -0
  150. package/docs/src/components/ThemeSwitcher.js +23 -0
  151. package/docs/src/components/Timestamp.js +1 -0
  152. package/docs/src/lit-all.min.js +120 -0
  153. package/docs/src/utils/debounce.js +1 -0
  154. package/docs/src/utils/drag.js +1 -0
  155. package/docs/src/utils/formatTimestamp.js +1 -0
  156. package/docs/src/utils/propConverters.js +1 -0
  157. package/docs/src/utils/watchWindowSize.js +1 -0
  158. package/docs/styles.css +6 -0
  159. package/docs/utils/debounce.html +78 -0
  160. package/docs/utils/drag.html +104 -0
  161. package/docs/utils/formatTimestamp.html +114 -0
  162. package/docs/utils/propConverters.html +132 -0
  163. package/docs/utils/watchWindowSize.html +166 -0
  164. package/icons/add.svg +1 -0
  165. package/icons/arrow-back.svg +1 -0
  166. package/icons/arrow-down-double.svg +1 -0
  167. package/icons/arrow-down.svg +1 -0
  168. package/icons/arrow-forward.svg +1 -0
  169. package/icons/arrow-up-double.svg +1 -0
  170. package/icons/arrow-up.svg +1 -0
  171. package/icons/cards.svg +1 -0
  172. package/icons/check.svg +1 -0
  173. package/icons/chevron-left.svg +1 -0
  174. package/icons/chevron-right.svg +1 -0
  175. package/icons/close.svg +2 -0
  176. package/icons/compare.svg +1 -0
  177. package/icons/delete.svg +1 -0
  178. package/icons/drag-handle.svg +1 -0
  179. package/icons/drawing2.svg +124 -0
  180. package/icons/edit.svg +1 -0
  181. package/icons/error.svg +1 -0
  182. package/icons/export-file.svg +1 -0
  183. package/icons/file-text.svg +1 -0
  184. package/icons/file.svg +1 -0
  185. package/icons/filter-off.svg +1 -0
  186. package/icons/filter.svg +1 -0
  187. package/icons/first.svg +1 -0
  188. package/icons/folder-clear.svg +1 -0
  189. package/icons/folder-create.svg +1 -0
  190. package/icons/folder-open.svg +1 -0
  191. package/icons/folder.svg +1 -0
  192. package/icons/hide.svg +1 -0
  193. package/icons/image.svg +1 -0
  194. package/icons/label-add.svg +1 -0
  195. package/icons/label.svg +1 -0
  196. package/icons/last.svg +1 -0
  197. package/icons/menu.svg +1 -0
  198. package/icons/mode-auto.svg +1 -0
  199. package/icons/mode-dark.svg +1 -0
  200. package/icons/mode-light.svg +1 -0
  201. package/icons/open-in-browser.svg +1 -0
  202. package/icons/pause.svg +1 -0
  203. package/icons/play.svg +1 -0
  204. package/icons/refresh.svg +1 -0
  205. package/icons/remove.svg +1 -0
  206. package/icons/save.svg +1 -0
  207. package/icons/search.svg +1 -0
  208. package/icons/settings.svg +1 -0
  209. package/icons/show.svg +1 -0
  210. package/icons/storage.svg +1 -0
  211. package/icons/table-visibility.svg +1 -0
  212. package/icons/tag.svg +1 -0
  213. package/icons/tools.svg +1 -0
  214. package/icons/trash-x.svg +1 -0
  215. package/icons/warning.svg +1 -0
  216. package/package.json +2 -2
  217. package/scripts/build.js +109 -138
  218. package/scripts/docs.js +62 -60
  219. package/src/components/Accordion.js +229 -0
  220. package/src/components/Card.js +66 -0
  221. package/src/components/Collapsible.js +109 -0
  222. package/src/components/ContentSlider.js +205 -0
  223. package/src/components/Dialog.js +368 -0
  224. package/src/components/FocusCapture.js +23 -0
  225. package/src/components/HybridComponent.js +40 -40
  226. package/src/components/Icon.js +155 -0
  227. package/src/components/Import.js +104 -0
  228. package/src/components/LightComponent.js +32 -32
  229. package/src/components/PersistantCollapsible.js +69 -0
  230. package/src/components/PhotoViewer.js +368 -0
  231. package/src/components/Resize.js +210 -0
  232. package/src/components/ShadowComponent.js +32 -17
  233. package/src/components/ShowMore.js +109 -0
  234. package/src/components/SideMenu.js +167 -0
  235. package/src/components/Sortable.js +193 -0
  236. package/src/components/Split.js +192 -0
  237. package/src/components/Table.js +1202 -0
  238. package/src/components/Tabs.js +428 -0
  239. package/src/components/Tags.js +253 -0
  240. package/src/components/ThemeSwitcher.js +108 -0
  241. package/src/components/Timestamp.js +31 -0
  242. package/src/components/Toast.js +454 -0
  243. package/src/components/Toggle.js +173 -0
  244. package/src/components/tableControls/DeleteRecord.js +29 -0
  245. package/src/components/tableControls/Edit.js +88 -0
  246. package/src/components/tableControls/ExportCSV.js +71 -0
  247. package/src/components/tableControls/ExportJson.js +55 -0
  248. package/src/components/tableControls/FieldSortHide.js +76 -0
  249. package/src/components/tableControls/Filters.js +114 -0
  250. package/src/components/tableControls/FirstPage.js +65 -0
  251. package/src/components/tableControls/HiddenCount.js +45 -0
  252. package/src/components/tableControls/Hide.js +34 -0
  253. package/src/components/tableControls/LastPage.js +65 -0
  254. package/src/components/tableControls/NextPage.js +65 -0
  255. package/src/components/tableControls/PageSelect.js +109 -0
  256. package/src/components/tableControls/PageSize.js +68 -0
  257. package/src/components/tableControls/PrevPage.js +65 -0
  258. package/src/components/tableControls/Search.js +58 -0
  259. package/src/components/tableControls/ShowAll.js +34 -0
  260. package/src/components/tableControls/TableControl.js +105 -0
  261. package/src/kempo-hljs.css +146 -0
  262. package/src/utils/debounce.js +9 -0
  263. package/src/utils/drag.js +80 -0
  264. package/src/utils/formatTimestamp.js +27 -0
  265. package/src/utils/propConverters.js +11 -0
  266. package/src/utils/toTitleCase.js +9 -0
  267. package/src/utils/watchWindowSize.js +16 -0
  268. package/tests/HybridComponent.browser-test.js +214 -214
  269. package/tests/LightComponent.browser-test.js +169 -169
  270. package/tests/ShadowComponent.browser-test.js +130 -130
  271. package/config/development.json +0 -14
  272. package/docs/components/ShadowComponent.js +0 -1
  273. package/src/utils/cli.js +0 -43
  274. package/src/utils/fs-utils.js +0 -41
  275. /package/{docs → dist/src}/components/HybridComponent.js +0 -0
  276. /package/{docs → dist/src}/components/LightComponent.js +0 -0
  277. /package/docs/{utils → src/utils}/cli.js +0 -0
  278. /package/docs/{utils → src/utils}/fs-utils.js +0 -0
@@ -0,0 +1,428 @@
1
+ import { html, css } from '../lit-all.min.js';
2
+ import ShadowComponent from './ShadowComponent.js';
3
+ import { boolExists } from '../utils/propConverters.js';
4
+
5
+ /*
6
+ Tabs Component
7
+ */
8
+ export class Tabs extends ShadowComponent {
9
+ static properties = {
10
+ active: { type: String, reflect: true },
11
+ fixedHeight: { type: Boolean, reflect: true, attribute: 'fixed-height', converter: boolExists }
12
+ };
13
+
14
+ constructor() {
15
+ super();
16
+ this.active = '';
17
+ this.fixedHeight = false;
18
+ }
19
+
20
+ /*
21
+ Lifecycle Callbacks
22
+ */
23
+ firstUpdated() {
24
+ super.firstUpdated();
25
+
26
+ // Set initial active tab if not specified
27
+ if(!this.active) {
28
+ const firstContent = this.querySelector('k-tab-content');
29
+ if(firstContent) {
30
+ this.active = firstContent.name;
31
+ }
32
+ }
33
+
34
+ this.setupScrollListeners();
35
+ this.updateScrollIndicators();
36
+ new ResizeObserver(() => this.updateScrollIndicators()).observe(this.shadowRoot.getElementById('tabs'));
37
+ }
38
+
39
+ updated(changedProperties) {
40
+ super.updated(changedProperties);
41
+
42
+ if(changedProperties.has('active')) {
43
+ this.updateActiveElements();
44
+ }
45
+ }
46
+
47
+ /*
48
+ Event Handlers
49
+ */
50
+ setupScrollListeners() {
51
+ const tabsContainer = this.shadowRoot.getElementById('tabs');
52
+ const leftButton = this.shadowRoot.getElementById('scroll-left');
53
+ const rightButton = this.shadowRoot.getElementById('scroll-right');
54
+
55
+ tabsContainer.addEventListener('scroll', () => this.updateScrollIndicators());
56
+
57
+ leftButton.addEventListener('click', () => {
58
+ tabsContainer.scrollBy({ left: -200, behavior: 'smooth' });
59
+ });
60
+ rightButton.addEventListener('click', () => {
61
+ tabsContainer.scrollBy({ left: 200, behavior: 'smooth' });
62
+ });
63
+ }
64
+
65
+ updateScrollIndicators() {
66
+ const tabsContainer = this.shadowRoot.getElementById('tabs');
67
+ const leftIndicator = this.shadowRoot.getElementById('scroll-left');
68
+ const rightIndicator = this.shadowRoot.getElementById('scroll-right');
69
+
70
+ const hasLeftScroll = tabsContainer.scrollLeft > 0;
71
+ const hasRightScroll = tabsContainer.scrollLeft < (tabsContainer.scrollWidth - tabsContainer.clientWidth);
72
+
73
+ leftIndicator.classList.toggle('visible', hasLeftScroll);
74
+ rightIndicator.classList.toggle('visible', hasRightScroll);
75
+ }
76
+
77
+ /*
78
+ Public Methods
79
+ */
80
+ updateActiveElements() {
81
+ // Update tabs
82
+ const activeTab = this.getActiveTab();
83
+ if(activeTab) activeTab.active = false;
84
+
85
+ const activeContent = this.getActiveContent();
86
+ if(activeContent) activeContent.active = false;
87
+
88
+ const newTab = this.getTab(this.active);
89
+ if(newTab) newTab.active = true;
90
+
91
+ const newContent = this.getContent(this.active);
92
+ if(newContent) newContent.active = true;
93
+
94
+ // Dispatch tab change event
95
+ this.dispatchEvent(new CustomEvent('tab', {
96
+ detail: { tab: this.active },
97
+ bubbles: true
98
+ }));
99
+ }
100
+
101
+ get contents() {
102
+ return [...this.querySelectorAll(':scope > k-tab-content')];
103
+ }
104
+
105
+ get tabs() {
106
+ return [...this.querySelectorAll(':scope > k-tab')];
107
+ }
108
+
109
+ getTab(id) {
110
+ let tab;
111
+ if(typeof(id) === 'string') {
112
+ tab = this.querySelector(`k-tab[for="${id}"]`);
113
+ }
114
+ if(!tab) {
115
+ let index = parseInt(id);
116
+ if(!index) index = 0;
117
+ tab = this.querySelectorAll('k-tab')[index];
118
+ }
119
+ return tab;
120
+ }
121
+
122
+ getActiveTab() {
123
+ return this.querySelector(':scope > k-tab[active]');
124
+ }
125
+
126
+ getContent(id) {
127
+ let content;
128
+ if(typeof(id) === 'string') {
129
+ content = this.querySelector(`k-tab-content[name="${id}"]`);
130
+ }
131
+ if(!content) {
132
+ let index = parseInt(id);
133
+ if(!index) index = 0;
134
+ content = this.querySelectorAll('k-tab-content')[index];
135
+ }
136
+ return content;
137
+ }
138
+
139
+ getActiveContent() {
140
+ return this.querySelector(':scope > k-tab-content[active]');
141
+ }
142
+
143
+ /*
144
+ Styles
145
+ */
146
+ static styles = css`
147
+ :host {
148
+ display: block;
149
+ width: 100%;
150
+ }
151
+
152
+ #wrapper {
153
+ display: flex;
154
+ flex-direction: column;
155
+ width: 100%;
156
+ min-width: 0;
157
+ }
158
+
159
+ #tabs-container {
160
+ position: relative;
161
+ border-bottom: 1px solid var(--c_border);
162
+ }
163
+
164
+ #tabs {
165
+ display: flex;
166
+ overflow-x: auto;
167
+ overflow-y: hidden;
168
+ }
169
+
170
+ #tabs ::slotted(*) {
171
+ flex: 0 0 auto;
172
+ }
173
+
174
+ .scroll-indicator {
175
+ position: absolute;
176
+ top: 0;
177
+ bottom: 0;
178
+ width: 72px;
179
+ pointer-events: none;
180
+ opacity: 0;
181
+ transition: opacity 0.2s;
182
+ display: flex;
183
+ align-items: center;
184
+ justify-content: flex-start;
185
+ padding-bottom: 2px;
186
+ }
187
+
188
+ .scroll-indicator .arrow {
189
+ color: var(--tc_base);
190
+ z-index: 1;
191
+ }
192
+
193
+ .scroll-indicator.visible {
194
+ opacity: 1;
195
+ }
196
+
197
+ #scroll-left {
198
+ left: 0;
199
+ background: linear-gradient(90deg,
200
+ var(--c_bg) 0%,
201
+ var(--c_bg) 30%,
202
+ transparent 100%
203
+ );
204
+ }
205
+
206
+ #scroll-right {
207
+ right: 0;
208
+ justify-content: flex-end;
209
+ background: linear-gradient(-90deg,
210
+ var(--c_bg) 0%,
211
+ var(--c_bg) 30%,
212
+ transparent 100%
213
+ );
214
+ }
215
+
216
+ :host([fixed-height]) #wrapper {
217
+ height: 100%;
218
+ }
219
+
220
+ :host([fixed-height]) #contents {
221
+ height: 100%;
222
+ flex: 1;
223
+ min-height: 1.35rem;
224
+ overflow: auto;
225
+ }
226
+ `;
227
+
228
+ /*
229
+ Rendering
230
+ */
231
+ render() {
232
+ return html`
233
+ <div id="wrapper">
234
+ <div id="tabs-container">
235
+ <div id="scroll-left" class="scroll-indicator">
236
+ <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">
237
+ <path d="M12 15L7 10L12 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
238
+ </svg>
239
+ </div>
240
+ <div id="tabs">
241
+ <slot name="tabs"></slot>
242
+ </div>
243
+ <div id="scroll-right" class="scroll-indicator">
244
+ <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">
245
+ <path d="M8 15L13 10L8 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
246
+ </svg>
247
+ </div>
248
+ </div>
249
+ <div id="contents">
250
+ <slot></slot>
251
+ </div>
252
+ </div>
253
+ `;
254
+ }
255
+ }
256
+
257
+ /*
258
+ Tab Component
259
+ */
260
+ export class Tab extends ShadowComponent {
261
+ static properties = {
262
+ active: { type: Boolean, reflect: true, converter: boolExists },
263
+ for: { type: String, reflect: true }
264
+ };
265
+
266
+ constructor() {
267
+ super();
268
+ this.active = false;
269
+ this.for = '';
270
+ this.slot = 'tabs';
271
+ }
272
+
273
+ /*
274
+ Event Handlers
275
+ */
276
+ handleClick = () => {
277
+ if(!this.active) {
278
+ const tabs = this.parentElement;
279
+ if(tabs && tabs.tagName === 'K-TABS') {
280
+ tabs.active = this.for || tabs.tabs.indexOf(this).toString();
281
+ }
282
+ }
283
+ };
284
+
285
+ /*
286
+ Public Methods
287
+ */
288
+ get tabs() {
289
+ return this.parentElement?.tagName === 'K-TABS' ? this.parentElement : null;
290
+ }
291
+
292
+ /*
293
+ Styles
294
+ */
295
+ static styles = css`
296
+ :host {
297
+ margin-bottom: -1px;
298
+ flex: 0 0 auto;
299
+ }
300
+
301
+ #button {
302
+ padding: var(--spacer_h);
303
+ background-color: transparent;
304
+ border: none;
305
+ cursor: inherit;
306
+ box-shadow: none;
307
+ color: inherit;
308
+ white-space: nowrap;
309
+ }
310
+
311
+ :host(:not([active])) #button {
312
+ cursor: pointer;
313
+ }
314
+
315
+ :host([active]) {
316
+ border-bottom: 2px solid var(--c_primary);
317
+ margin-bottom: -1px;
318
+ }
319
+
320
+ :host([active]) #button {
321
+ color: var(--tc_primary);
322
+ }
323
+ `;
324
+
325
+ /*
326
+ Rendering
327
+ */
328
+ render() {
329
+ return html`
330
+ <button id="button" @click=${this.handleClick}>
331
+ <slot></slot>
332
+ </button>
333
+ `;
334
+ }
335
+ }
336
+
337
+ /*
338
+ TabContent Component
339
+ */
340
+ export class TabContent extends ShadowComponent {
341
+ static properties = {
342
+ active: { type: Boolean, reflect: true, converter: boolExists },
343
+ name: { type: String, reflect: true }
344
+ };
345
+
346
+ constructor() {
347
+ super();
348
+ this.active = false;
349
+ this.name = '';
350
+ }
351
+
352
+ /*
353
+ Public Methods
354
+ */
355
+ get tabs() {
356
+ return this.parentElement?.tagName === 'K-TABS' ? this.parentElement : null;
357
+ }
358
+
359
+ /*
360
+ Styles
361
+ */
362
+ static styles = css`
363
+ :host {
364
+ display: block;
365
+ height: 100%;
366
+ max-height: 100%;
367
+ flex: 1 1 auto;
368
+ overflow: auto;
369
+ }
370
+
371
+ :host([active]) {
372
+ display: block;
373
+ }
374
+
375
+ :host(:not([active])) {
376
+ display: none;
377
+ }
378
+ `;
379
+
380
+ /*
381
+ Rendering
382
+ */
383
+ render() {
384
+ return html`<slot></slot>`;
385
+ }
386
+ }
387
+
388
+ /*
389
+ TabSpacer Component
390
+ */
391
+ export class TabSpacer extends ShadowComponent {
392
+ constructor() {
393
+ super();
394
+ this.slot = 'tabs';
395
+ }
396
+
397
+ /*
398
+ Styles
399
+ */
400
+ static styles = css`
401
+ :host {
402
+ flex: 1 1 auto !important;
403
+ height: 1px;
404
+ }
405
+ `;
406
+
407
+ /*
408
+ Rendering
409
+ */
410
+ render() {
411
+ return html`<slot></slot>`;
412
+ }
413
+ }
414
+
415
+ /*
416
+ Define Custom Elements
417
+ */
418
+ customElements.define('k-tabs', Tabs);
419
+ customElements.define('k-tab', Tab);
420
+ customElements.define('k-tab-content', TabContent);
421
+ customElements.define('k-tab-spacer', TabSpacer);
422
+
423
+ export default {
424
+ Tab,
425
+ TabContent,
426
+ Tabs,
427
+ TabSpacer
428
+ };
@@ -0,0 +1,253 @@
1
+ import { html, css } from '../lit-all.min.js';
2
+ import ShadowComponent from './ShadowComponent.js';
3
+
4
+ /*
5
+ Tags Component
6
+ */
7
+ export default class Tags extends ShadowComponent {
8
+ static properties = {
9
+ value: { type: String, reflect: true },
10
+ allowedTags: { type: String, reflect: true, attribute: 'allowed-tags' },
11
+ disallowedTags: { type: String, reflect: true, attribute: 'disallowed-tags' }
12
+ };
13
+
14
+ constructor() {
15
+ super();
16
+ this.value = '';
17
+ this.allowedTags = '';
18
+ this.disallowedTags = '';
19
+ }
20
+
21
+ /*
22
+ Lifecycle Callbacks
23
+ */
24
+ firstUpdated() {
25
+ super.firstUpdated();
26
+ this.renderTags();
27
+ }
28
+
29
+ updated(changedProperties) {
30
+ super.updated(changedProperties);
31
+
32
+ if(changedProperties.has('value')) {
33
+ const oldValue = changedProperties.get('value');
34
+ const validTags = this.validateTags();
35
+ if(validTags !== this.value) {
36
+ this.value = validTags;
37
+ } else {
38
+ this.dispatchEvent(new CustomEvent('change', {
39
+ detail: { oldValue, newValue: this.value },
40
+ bubbles: true
41
+ }));
42
+ this.renderTags();
43
+ }
44
+ }
45
+
46
+ if(changedProperties.has('allowedTags') || changedProperties.has('disallowedTags')) {
47
+ const oldValue = changedProperties.get('allowedTags') || changedProperties.get('disallowedTags');
48
+ const newValue = this.allowedTags || this.disallowedTags;
49
+ const validTags = this.validateTags();
50
+ if(validTags !== this.value) {
51
+ this.value = validTags;
52
+ } else {
53
+ const eventName = changedProperties.has('allowedTags') ? 'allowedtagschange' : 'disallowedtagschange';
54
+ this.dispatchEvent(new CustomEvent(eventName, {
55
+ detail: { oldValue, newValue },
56
+ bubbles: true
57
+ }));
58
+ this.renderTags();
59
+ }
60
+ }
61
+ }
62
+
63
+ /*
64
+ Event Handlers
65
+ */
66
+ handleInputChange = () => {
67
+ const tagsInput = this.shadowRoot.getElementById('tagsInput');
68
+ const tag = tagsInput.value.trim();
69
+ if(tag) {
70
+ this.addTag(tag);
71
+ tagsInput.value = '';
72
+ }
73
+ };
74
+
75
+ handleInputInput = (event) => {
76
+ if(event.data === ',' || event.inputType === 'insertFromPaste') {
77
+ const tagsInput = this.shadowRoot.getElementById('tagsInput');
78
+ const tags = tagsInput.value.split(',').filter(tag => !!tag.trim());
79
+ if(tags.length) {
80
+ tags.forEach(tag => this.addTag(tag.trim()));
81
+ tagsInput.value = '';
82
+ }
83
+ }
84
+ };
85
+
86
+ /*
87
+ Public Methods
88
+ */
89
+ async renderTags() {
90
+ await this.updateComplete;
91
+ const tagsContainer = this.shadowRoot.getElementById('tags');
92
+ tagsContainer.innerHTML = '';
93
+
94
+ if(this.value) {
95
+ this.value.split(',').forEach(tag => {
96
+ const tagElement = new Tag(tag.trim(), this);
97
+ tagsContainer.appendChild(tagElement);
98
+ });
99
+ }
100
+ }
101
+
102
+ addTag(tag) {
103
+ const tags = new Set(this.value.split(',').filter(t => !!t.trim()));
104
+ tags.add(tag.trim());
105
+ this.value = [...tags].filter(t => !!t).join(',');
106
+ this.dispatchEvent(new CustomEvent('addtag', {
107
+ detail: { tag },
108
+ bubbles: true
109
+ }));
110
+ }
111
+
112
+ removeTag(tag) {
113
+ const tags = new Set(this.value.split(',').filter(t => !!t.trim()));
114
+ tags.delete(tag);
115
+ this.value = [...tags].join(',');
116
+ this.dispatchEvent(new CustomEvent('removetag', {
117
+ detail: { tag },
118
+ bubbles: true
119
+ }));
120
+ }
121
+
122
+ validateTags() {
123
+ return this.value
124
+ .split(',')
125
+ .map(t => t.trim())
126
+ .map(tag => {
127
+ const allowed = new Set(this.allowedTags.split(',').filter(t => !!t.trim()));
128
+ if(allowed.size) return allowed.has(tag) ? tag : '';
129
+ const disallowed = new Set(this.disallowedTags.split(',').filter(t => !!t.trim()));
130
+ if(disallowed.size) return disallowed.has(tag) ? '' : tag;
131
+ return tag;
132
+ })
133
+ .filter(t => !!t)
134
+ .join(',');
135
+ }
136
+
137
+ /*
138
+ Styles
139
+ */
140
+ static styles = css`
141
+ :host {
142
+ display: block;
143
+
144
+ #tagsHolder {
145
+ display: flex;
146
+ flex-wrap: wrap;
147
+ align-items: center;
148
+ width: 100%;
149
+ background-color: var(--input_bg);
150
+ color: var(--input_tc);
151
+ border: var(--input_border_width) solid var(--c_border);
152
+ padding: var(--spacer_q);
153
+ margin-bottom: var(--spacer);
154
+ border-radius: var(--radius);
155
+ outline: none;
156
+ transition: box-shadow var(--animation_ms);
157
+ cursor: default;
158
+ }
159
+
160
+ #tagsHolder:focus-within {
161
+ box-shadow: var(--focus_shadow);
162
+ }
163
+
164
+ #tags {
165
+ display: contents;
166
+ }
167
+
168
+ #tagsInput {
169
+ display: inline-block;
170
+ min-width: 5rem;
171
+ width: auto;
172
+ max-width: 100%;
173
+ background-color: transparent;
174
+ color: inherit;
175
+ border: 0 solid transparent;
176
+ margin: var(--spacer_q);
177
+ padding: var(--spacer_q) 0;
178
+ border-radius: 0;
179
+ transition: none;
180
+ box-shadow: 0 0 0 transparent;
181
+ }
182
+ `;
183
+
184
+ /*
185
+ Rendering
186
+ */
187
+ render() {
188
+ return html`
189
+ <label for="tagsInput">
190
+ <slot></slot>
191
+ <div id="tagsHolder">
192
+ <span id="tags"></span>
193
+ <input
194
+ id="tagsInput"
195
+ @change=${this.handleInputChange}
196
+ @input=${this.handleInputInput}
197
+ />
198
+ </div>
199
+ </label>
200
+ `;
201
+ }
202
+ }
203
+
204
+ /*
205
+ Tag Component
206
+ */
207
+ class Tag extends ShadowComponent {
208
+ constructor(tag, tagsComponent) {
209
+ super();
210
+ this.tag = tag;
211
+ this.tagsComponent = tagsComponent;
212
+ this.innerHTML = tag;
213
+ }
214
+
215
+ /*
216
+ Event Handlers
217
+ */
218
+ handleClick = () => {
219
+ this.tagsComponent.removeTag(this.tag);
220
+ };
221
+
222
+ /*
223
+ Styles
224
+ */
225
+ static styles = css`
226
+ :host {
227
+ display: inline-block;
228
+ width: min-content;
229
+ margin: var(--spacer_q);
230
+ padding: var(--spacer_q) var(--spacer_h);
231
+ border: 1px solid var(--c_border);
232
+ border-radius: var(--radius);
233
+ cursor: pointer;
234
+ }
235
+
236
+ :host(:hover) {
237
+ text-decoration: line-through;
238
+ }
239
+ `;
240
+
241
+ /*
242
+ Rendering
243
+ */
244
+ render() {
245
+ return html`<span @click=${this.handleClick}><slot></slot></span>`;
246
+ }
247
+ }
248
+
249
+ /*
250
+ Define Custom Elements
251
+ */
252
+ customElements.define('k-tags', Tags);
253
+ customElements.define('k-tag', Tag);