kempo-ui 0.0.3 → 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 (137) hide show
  1. package/.github/workflows/publish-major.yml +39 -0
  2. package/.github/workflows/publish-minor.yml +39 -0
  3. package/.github/workflows/{publish-npm.yml → publish-patch.yml} +1 -1
  4. package/.vscode/settings.json +2 -0
  5. package/dist/kempo-hljs.css +1 -0
  6. package/dist/src/components/Accordion.js +36 -0
  7. package/dist/src/components/Card.js +37 -0
  8. package/dist/src/components/Collapsible.js +41 -0
  9. package/dist/src/components/ContentSlider.js +44 -0
  10. package/dist/src/components/Dialog.js +113 -0
  11. package/dist/src/components/Icon.js +2 -2
  12. package/dist/src/components/PersistantCollapsible.js +1 -0
  13. package/dist/src/components/PhotoViewer.js +135 -0
  14. package/dist/src/components/Resize.js +96 -0
  15. package/dist/src/components/ShowMore.js +42 -0
  16. package/dist/src/components/SideMenu.js +1 -1
  17. package/dist/src/components/Split.js +84 -0
  18. package/dist/src/components/Tabs.js +155 -0
  19. package/dist/src/components/Tags.js +69 -0
  20. package/dist/src/components/ThemeSwitcher.js +23 -0
  21. package/dist/src/components/Timestamp.js +1 -0
  22. package/dist/src/utils/debounce.js +1 -0
  23. package/dist/src/utils/drag.js +1 -0
  24. package/dist/src/utils/formatTimestamp.js +1 -0
  25. package/dist/src/utils/propConverters.js +1 -0
  26. package/dist/src/utils/watchWindowSize.js +1 -0
  27. package/docs/components/accordion.html +69 -25
  28. package/docs/components/card.html +6 -5
  29. package/docs/components/collapsible.html +198 -0
  30. package/docs/components/content-slider.html +56 -26
  31. package/docs/components/dialog.html +54 -48
  32. package/docs/components/focus-capture.html +2 -3
  33. package/docs/components/icon.html +5 -8
  34. package/docs/components/import.html +1 -6
  35. package/docs/components/persistant-collapsible.html +123 -0
  36. package/docs/components/photo-viewer.html +226 -0
  37. package/docs/components/resize.html +44 -18
  38. package/docs/components/show-more.html +12 -6
  39. package/docs/components/side-menu.html +3 -12
  40. package/docs/components/split.html +26 -5
  41. package/docs/components/tablePagination.html +4 -4
  42. package/docs/components/tableRecordFiltering.html +16 -3
  43. package/docs/components/tableRecordSelection.html +2 -2
  44. package/docs/components/tabs.html +24 -7
  45. package/docs/components/tags.html +24 -8
  46. package/docs/components/theme-switcher.html +3 -6
  47. package/docs/components/timestamp.html +20 -24
  48. package/docs/components/toast.html +23 -32
  49. package/docs/components/toggle.html +4 -3
  50. package/docs/kempo-hljs.css +1 -0
  51. package/docs/media/civic.jpg +0 -0
  52. package/docs/media/corvette.jpg +0 -0
  53. package/docs/media/evo.jpg +0 -0
  54. package/docs/media/gtr.jpg +0 -0
  55. package/docs/media/nsx.jpg +0 -0
  56. package/docs/nav.inc.html +11 -17
  57. package/docs/src/components/Accordion.js +36 -0
  58. package/docs/src/components/Card.js +37 -0
  59. package/docs/src/components/Collapsible.js +41 -0
  60. package/docs/src/components/ContentSlider.js +44 -0
  61. package/docs/src/components/Dialog.js +113 -0
  62. package/docs/src/components/Icon.js +2 -2
  63. package/docs/src/components/PersistantCollapsible.js +1 -0
  64. package/docs/src/components/PhotoViewer.js +135 -0
  65. package/docs/src/components/Resize.js +96 -0
  66. package/docs/src/components/ShowMore.js +42 -0
  67. package/docs/src/components/SideMenu.js +1 -1
  68. package/docs/src/components/Split.js +84 -0
  69. package/docs/src/components/Tabs.js +155 -0
  70. package/docs/src/components/Tags.js +69 -0
  71. package/docs/src/components/ThemeSwitcher.js +23 -0
  72. package/docs/src/components/Timestamp.js +1 -0
  73. package/docs/src/utils/debounce.js +1 -0
  74. package/docs/src/utils/drag.js +1 -0
  75. package/docs/src/utils/formatTimestamp.js +1 -0
  76. package/docs/src/utils/propConverters.js +1 -0
  77. package/docs/src/utils/watchWindowSize.js +1 -0
  78. package/docs/utils/debounce.html +78 -0
  79. package/docs/utils/drag.html +104 -0
  80. package/docs/utils/formatTimestamp.html +114 -0
  81. package/docs/utils/propConverters.html +132 -0
  82. package/docs/utils/watchWindowSize.html +166 -0
  83. package/icons/error.svg +1 -1
  84. package/icons/warning.svg +1 -1
  85. package/package.json +1 -1
  86. package/scripts/build.js +1 -1
  87. package/scripts/docs.js +4 -2
  88. package/src/components/Accordion.js +229 -0
  89. package/src/components/Collapsible.js +109 -0
  90. package/src/components/ContentSlider.js +205 -0
  91. package/src/components/Dialog.js +368 -0
  92. package/src/components/PersistantCollapsible.js +69 -0
  93. package/src/components/PhotoViewer.js +368 -0
  94. package/src/components/Resize.js +210 -0
  95. package/src/components/ShadowComponent.js +3 -1
  96. package/src/components/ShowMore.js +109 -0
  97. package/src/components/Sortable.js +193 -0
  98. package/src/components/Split.js +192 -0
  99. package/src/components/Table.js +1202 -0
  100. package/src/components/Tabs.js +428 -0
  101. package/src/components/Tags.js +253 -0
  102. package/src/components/Timestamp.js +31 -0
  103. package/src/components/Toast.js +454 -0
  104. package/src/components/Toggle.js +173 -0
  105. package/src/components/tableControls/DeleteRecord.js +29 -0
  106. package/src/components/tableControls/Edit.js +88 -0
  107. package/src/components/tableControls/ExportCSV.js +71 -0
  108. package/src/components/tableControls/ExportJson.js +55 -0
  109. package/src/components/tableControls/FieldSortHide.js +76 -0
  110. package/src/components/tableControls/Filters.js +114 -0
  111. package/src/components/tableControls/FirstPage.js +65 -0
  112. package/src/components/tableControls/HiddenCount.js +45 -0
  113. package/src/components/tableControls/Hide.js +34 -0
  114. package/src/components/tableControls/LastPage.js +65 -0
  115. package/src/components/tableControls/NextPage.js +65 -0
  116. package/src/components/tableControls/PageSelect.js +109 -0
  117. package/src/components/tableControls/PageSize.js +68 -0
  118. package/src/components/tableControls/PrevPage.js +65 -0
  119. package/src/components/tableControls/Search.js +58 -0
  120. package/src/components/tableControls/ShowAll.js +34 -0
  121. package/src/components/tableControls/TableControl.js +105 -0
  122. package/src/utils/debounce.js +9 -0
  123. package/src/utils/drag.js +80 -0
  124. package/src/utils/formatTimestamp.js +27 -0
  125. package/src/utils/toTitleCase.js +9 -0
  126. package/src/utils/watchWindowSize.js +16 -0
  127. package/dist/src/utils/cli.js +0 -1
  128. package/dist/src/utils/fs-utils.js +0 -1
  129. package/docs/components/component.html +0 -90
  130. package/docs/components/directory-viewer.html +0 -90
  131. package/docs/components/lazy-component.html +0 -84
  132. package/docs/components/reactive-component.html +0 -69
  133. package/docs/components/reactive-lazy-component.html +0 -69
  134. package/docs/components/search.html +0 -102
  135. package/docs/components/sortable.html +0 -96
  136. package/src/utils/cli.js +0 -43
  137. package/src/utils/fs-utils.js +0 -41
@@ -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);
@@ -0,0 +1,31 @@
1
+ import { html } from '../lit-all.min.js';
2
+ import ShadowComponent from './ShadowComponent.js';
3
+ import formatTimestamp from '../utils/formatTimestamp.js';
4
+
5
+ export default class Timestamp extends ShadowComponent {
6
+ static properties = {
7
+ timestamp: { type: Number, reflect: true },
8
+ format: { type: String, reflect: true },
9
+ locale: { type: String, reflect: true }
10
+ };
11
+
12
+ constructor() {
13
+ super();
14
+ this.timestamp = 0;
15
+ this.format = '';
16
+ this.locale = '';
17
+ }
18
+
19
+ /*
20
+ Rendering Logic
21
+ */
22
+ render() {
23
+ const formattedTime = this.timestamp
24
+ ? formatTimestamp(this.timestamp, this.format, this.locale || navigator.language)
25
+ : '';
26
+
27
+ return html`<span>${formattedTime}</span>`;
28
+ }
29
+ }
30
+
31
+ window.customElements.define('k-timestamp', Timestamp);