kempo-ui 0.1.6 → 0.2.2

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 (119) hide show
  1. package/.github/skills/highlight-code/SKILL.md +22 -6
  2. package/.github/skills/new-component/SKILL.md +68 -4
  3. package/AGENTS.md +11 -0
  4. package/bin/highlight_code.js +7 -15
  5. package/dist/components/Aside.js +326 -0
  6. package/dist/components/Dialog.js +1 -1
  7. package/dist/components/Dropdown.js +1 -1
  8. package/dist/components/Main.js +2 -2
  9. package/dist/components/Nav.js +27 -0
  10. package/dist/components/PhotoViewer.js +1 -1
  11. package/dist/components/ShadowComponent.js +1 -1
  12. package/dist/components/ThemeSwitcher.js +1 -0
  13. package/dist/components/Toast.js +1 -1
  14. package/docs/components/accordion.html +4 -3
  15. package/docs/components/aside-items.html +43 -0
  16. package/docs/components/aside-menu.html +43 -0
  17. package/docs/components/aside-push.html +26 -0
  18. package/docs/components/aside.html +567 -0
  19. package/docs/components/card.html +4 -3
  20. package/docs/components/color-picker.html +4 -3
  21. package/docs/components/content-slider.html +4 -3
  22. package/docs/components/dialog.html +4 -3
  23. package/docs/components/dropdown.html +4 -3
  24. package/docs/components/filter-list.html +4 -3
  25. package/docs/components/focus-capture.html +6 -5
  26. package/docs/components/html-editor.html +5 -4
  27. package/docs/components/hybrid-component.html +5 -4
  28. package/docs/components/icon.html +4 -3
  29. package/docs/components/import.html +5 -4
  30. package/docs/components/light-component.html +4 -3
  31. package/docs/components/nav.html +117 -0
  32. package/docs/components/photo-viewer.html +5 -4
  33. package/docs/components/resize.html +5 -4
  34. package/docs/components/shadow-component.html +4 -3
  35. package/docs/components/show-more.html +5 -4
  36. package/docs/components/sortable.html +4 -3
  37. package/docs/components/spinner.html +4 -3
  38. package/docs/components/split.html +5 -4
  39. package/docs/components/table.html +4 -3
  40. package/docs/components/tableControls.html +4 -3
  41. package/docs/components/tableCustomFields.html +4 -3
  42. package/docs/components/tableFetchRecords.html +5 -4
  43. package/docs/components/tableFieldSortHide.html +5 -4
  44. package/docs/components/tablePagination.html +4 -3
  45. package/docs/components/tableRecordEditing.html +4 -3
  46. package/docs/components/tableRecordFiltering.html +5 -4
  47. package/docs/components/tableRecordHiding.html +4 -3
  48. package/docs/components/tableRecordSearching.html +5 -4
  49. package/docs/components/tableRecordSelection.html +4 -3
  50. package/docs/components/tableRowControls.html +4 -3
  51. package/docs/components/tableServerSync.html +5 -4
  52. package/docs/components/tableSorting.html +5 -4
  53. package/docs/components/tabs.html +4 -3
  54. package/docs/components/tags.html +4 -3
  55. package/docs/components/theme-select.html +4 -3
  56. package/docs/components/theme-switcher.html +5 -4
  57. package/docs/components/timestamp.html +4 -3
  58. package/docs/components/toast.html +4 -3
  59. package/docs/components/toggle.html +5 -4
  60. package/docs/components/tree.html +5 -4
  61. package/docs/index.html +22 -21
  62. package/docs/nav-1.inc.html +18 -12
  63. package/docs/nav.inc.html +20 -13
  64. package/docs/nav.inc.js +1 -1
  65. package/docs/src/components/Aside.js +326 -0
  66. package/docs/src/components/Dialog.js +1 -1
  67. package/docs/src/components/Dropdown.js +1 -1
  68. package/docs/src/components/Main.js +2 -2
  69. package/docs/src/components/Nav.js +27 -0
  70. package/docs/src/components/PhotoViewer.js +1 -1
  71. package/docs/src/components/ShadowComponent.js +1 -1
  72. package/docs/src/components/ThemeSwitcher.js +1 -0
  73. package/docs/src/components/Toast.js +1 -1
  74. package/docs/utils/context.html +4 -3
  75. package/docs/utils/cookie.html +4 -3
  76. package/docs/utils/debounce.html +4 -3
  77. package/docs/utils/drag.html +4 -3
  78. package/docs/utils/elevation.html +156 -0
  79. package/docs/utils/formatTimestamp.html +4 -3
  80. package/docs/utils/object.html +4 -3
  81. package/docs/utils/propConverters.html +4 -3
  82. package/docs/utils/string.html +4 -3
  83. package/docs/utils/theme.html +4 -3
  84. package/docs/utils/toTitleCase.html +4 -3
  85. package/docs/utils/type.html +4 -3
  86. package/docs/utils/wait.html +4 -3
  87. package/llm.txt +247 -0
  88. package/package.json +3 -4
  89. package/src/components/Aside.js +726 -0
  90. package/src/components/Dialog.js +1 -1
  91. package/src/components/Dropdown.js +1 -1
  92. package/src/components/Main.js +34 -30
  93. package/src/components/Nav.js +53 -0
  94. package/src/components/PhotoViewer.js +1 -1
  95. package/src/components/ShadowComponent.js +1 -1
  96. package/src/components/ThemeSwitcher.js +1 -0
  97. package/src/components/Toast.js +1 -1
  98. package/tests/bin/get_icon.node-test.js +26 -0
  99. package/tests/bin/highlight_code.node-test.js +108 -0
  100. package/tests/bin/list_icons.node-test.js +26 -0
  101. package/tests/components/Aside.browser-test.js +778 -0
  102. package/tests/components/Main.browser-test.js +115 -283
  103. package/dist/components/SideMenu.js +0 -97
  104. package/dist/components/SidePanel.js +0 -236
  105. package/docs/components/side-menu.html +0 -189
  106. package/docs/components/side-panel-basic.html +0 -44
  107. package/docs/components/side-panel-dual.html +0 -70
  108. package/docs/components/side-panel-menu.html +0 -50
  109. package/docs/components/side-panel-persistent.html +0 -90
  110. package/docs/components/side-panel-scroll.html +0 -176
  111. package/docs/components/side-panel.html +0 -245
  112. package/docs/src/components/SideMenu.js +0 -97
  113. package/docs/src/components/SidePanel.js +0 -236
  114. package/scripts/update-kempo-css.js +0 -53
  115. package/src/components/SideMenu.js +0 -178
  116. package/src/components/SidePanel.js +0 -513
  117. package/tests/components/SideMenu.browser-test.js +0 -667
  118. package/tests/components/SidePanel.browser-test.js +0 -520
  119. package/tools/highlight.js +0 -51
@@ -0,0 +1,726 @@
1
+ import ShadowComponent from './ShadowComponent.js';
2
+ import { html, css, nothing } from '../lit-all.min.js';
3
+ import { boolTrueFalse } from '../utils/propConverters.js';
4
+ import './FocusCapture.js';
5
+ import './Icon.js';
6
+
7
+ export default class Aside extends ShadowComponent {
8
+ static properties = {
9
+ state: { type: String, reflect: true },
10
+ side: { type: String, reflect: true },
11
+ main: { type: String, reflect: true },
12
+ overlayClose: { type: Boolean, reflect: true, attribute: 'overlay-close', converter: boolTrueFalse },
13
+ escClose: { type: Boolean, reflect: true, attribute: 'esc-close', converter: boolTrueFalse }
14
+ };
15
+
16
+ constructor() {
17
+ super();
18
+ this.state = 'offscreen';
19
+ this.side = 'left';
20
+ this.main = 'overlay';
21
+ this.overlayClose = true;
22
+ this.escClose = true;
23
+ }
24
+
25
+ /*
26
+ Event Handlers
27
+ */
28
+ overlayClick = () => {
29
+ if(this.overlayClose) this.hide();
30
+ }
31
+
32
+ handleKeyDown = (e) => {
33
+ if(this.escClose && e.key === 'Escape') this.hide();
34
+ }
35
+
36
+ /*
37
+ Lifecycle Callbacks
38
+ */
39
+ updated(changedProperties) {
40
+ super.updated(changedProperties);
41
+
42
+ if(changedProperties.has('state')) {
43
+ const prev = changedProperties.get('state');
44
+
45
+ if(this.state !== 'offscreen' && (prev === 'offscreen' || prev === undefined)) {
46
+ document.addEventListener('keydown', this.handleKeyDown);
47
+ } else if(this.state === 'offscreen' && prev !== undefined && prev !== 'offscreen') {
48
+ document.removeEventListener('keydown', this.handleKeyDown);
49
+ }
50
+
51
+ if(this.main === 'overlay') {
52
+ if(this.state === 'expanded') {
53
+ document.body.classList.add('no-scroll');
54
+ } else {
55
+ document.body.classList.remove('no-scroll');
56
+ }
57
+ }
58
+
59
+ const targetWidth = this.state === 'offscreen' ? 0 : this.getTargetWidth(this.state);
60
+ const detail = { aside: this, state: this.state, main: this.main, width: targetWidth };
61
+ this.dispatchEvent(new CustomEvent('aside_state_change', { detail }));
62
+ window.dispatchEvent(new CustomEvent('aside_state_change', { detail }));
63
+ this.inert = this.state === 'offscreen';
64
+ }
65
+ }
66
+
67
+ disconnectedCallback() {
68
+ super.disconnectedCallback();
69
+ document.removeEventListener('keydown', this.handleKeyDown);
70
+ document.body.classList.remove('no-scroll');
71
+ const detail = { aside: this, state: 'offscreen', main: this.main, width: 0 };
72
+ this.dispatchEvent(new CustomEvent('aside_state_change', { detail }));
73
+ window.dispatchEvent(new CustomEvent('aside_state_change', { detail }));
74
+ }
75
+
76
+ /*
77
+ Methods
78
+ */
79
+ expand = () => this.state = 'expanded';
80
+ collapse = () => this.state = 'collapsed';
81
+ hide = () => this.state = 'offscreen';
82
+
83
+ toggle = () => {
84
+ this.state = this.state === 'expanded' ? 'offscreen' : 'expanded';
85
+ }
86
+
87
+ getTargetWidth(forState) {
88
+ const rootFs = parseFloat(getComputedStyle(document.documentElement).fontSize);
89
+ const varName = forState === 'collapsed' ? '--collapsed-width' : '--width';
90
+ const raw = getComputedStyle(this).getPropertyValue(varName).trim();
91
+ const rem = raw ? parseFloat(raw) : (forState === 'collapsed' ? 3.5 : 20);
92
+ return rem * rootFs;
93
+ }
94
+
95
+ /*
96
+ Rendering
97
+ */
98
+ render() {
99
+ const aside = html`<aside><slot></slot></aside>`;
100
+ if(this.main === 'overlay') {
101
+ return html`
102
+ <k-focus-capture>
103
+ <div id="container">
104
+ <button id="overlay-btn" @click=${this.overlayClick}>
105
+ <div id="overlay-x"><k-icon name="close"></k-icon></div>
106
+ </button>
107
+ ${aside}
108
+ </div>
109
+ </k-focus-capture>
110
+ `;
111
+ }
112
+ return aside;
113
+ }
114
+
115
+ /*
116
+ Styles
117
+ */
118
+ static styles = css`
119
+ :host {
120
+ --bg: var(--c_bg);
121
+ --border: var(--c_border);
122
+ --width: 20rem;
123
+ --collapsed-width: 3.5rem;
124
+ position: fixed;
125
+ top: 0;
126
+ height: 100vh;
127
+ pointer-events: none;
128
+ }
129
+ :host([main="push"]) {
130
+ left: 0;
131
+ z-index: 30;
132
+ transition: width var(--animation_ms, 256ms);
133
+ }
134
+ :host([main="push"][side="right"]) {
135
+ left: auto;
136
+ right: 0;
137
+ }
138
+ :host([main="push"][state="collapsed"]) {
139
+ width: var(--collapsed-width);
140
+ pointer-events: auto;
141
+ }
142
+ :host([main="push"][state="expanded"]) {
143
+ width: var(--width);
144
+ pointer-events: auto;
145
+ }
146
+ :host([main="overlay"]) {
147
+ left: 0;
148
+ width: 100vw;
149
+ max-width: 100%;
150
+ z-index: 60;
151
+ overflow: hidden;
152
+ }
153
+ :host([main="overlay"][state="expanded"]) {
154
+ pointer-events: auto;
155
+ }
156
+ k-focus-capture {
157
+ width: 100%;
158
+ height: 100%;
159
+ }
160
+ #container {
161
+ position: relative;
162
+ width: 100%;
163
+ height: 100%;
164
+ }
165
+ #overlay-btn {
166
+ position: absolute;
167
+ width: 100%;
168
+ height: 100%;
169
+ left: 0;
170
+ top: 0;
171
+ background: var(--overlay, rgba(0, 0, 0, 0.5));
172
+ border: none;
173
+ padding: 0;
174
+ cursor: pointer;
175
+ z-index: 1;
176
+ opacity: 0;
177
+ transition: opacity var(--animation_ms, 256ms);
178
+ }
179
+ :host([state="expanded"]) #overlay-btn {
180
+ opacity: 1;
181
+ }
182
+ #overlay-x {
183
+ position: absolute;
184
+ top: var(--spacer_h);
185
+ right: var(--spacer_h);
186
+ font-size: 1.75rem;
187
+ cursor: pointer;
188
+ color: var(--tc_light);
189
+ }
190
+ :host([overlay-close="false"]) #overlay-x {
191
+ display: none;
192
+ }
193
+ :host([overlay-close="false"]) #overlay-btn {
194
+ cursor: default;
195
+ }
196
+ :host([side="right"]) #overlay-x {
197
+ right: auto;
198
+ left: var(--spacer_h);
199
+ }
200
+ aside {
201
+ position: fixed;
202
+ top: 0;
203
+ left: 0;
204
+ height: 100vh;
205
+ overflow-y: auto;
206
+ overflow-x: hidden;
207
+ background: var(--bg);
208
+ padding: var(--aside_padding, var(--spacer));
209
+ z-index: 2;
210
+ box-sizing: border-box;
211
+ border-right: 1px solid var(--border);
212
+ transition: width var(--animation_ms, 256ms), transform var(--animation_ms, 256ms);
213
+ }
214
+ :host([side="right"]) aside {
215
+ left: auto;
216
+ right: 0;
217
+ border-right: none;
218
+ border-left: 1px solid var(--border);
219
+ }
220
+ :host([main="push"]) aside {
221
+ position: absolute;
222
+ width: var(--width);
223
+ transform: translateX(-100%);
224
+ }
225
+ :host([main="push"][side="right"]) aside {
226
+ transform: translateX(100%);
227
+ }
228
+ :host([main="push"][state="collapsed"]) aside {
229
+ width: var(--collapsed-width);
230
+ transform: translateX(0);
231
+ }
232
+ :host([main="push"][state="expanded"]) aside {
233
+ transform: translateX(0);
234
+ }
235
+ :host([main="overlay"]) aside {
236
+ width: var(--width);
237
+ max-width: calc(100vw - 6rem);
238
+ border: none;
239
+ transform: translateX(-100%);
240
+ }
241
+ :host([main="overlay"][state="expanded"]) aside {
242
+ transform: translateX(0);
243
+ }
244
+ :host([main="overlay"][side="right"]) aside {
245
+ transform: translateX(100%);
246
+ }
247
+ :host([main="overlay"][side="right"][state="expanded"]) aside {
248
+ transform: translateX(0);
249
+ }
250
+ `;
251
+ }
252
+
253
+ customElements.define('k-aside', Aside);
254
+
255
+ /*
256
+ AsideItem
257
+ */
258
+ class AsideItem extends ShadowComponent {
259
+ static properties = {
260
+ icon: { type: String },
261
+ href: { type: String },
262
+ active: { type: Boolean, reflect: true },
263
+ collapsed: { type: Boolean, reflect: true },
264
+ 'no-expand': { type: Boolean, attribute: 'no-expand' },
265
+ 'hide-when-collapsed': { type: Boolean, attribute: 'hide-when-collapsed' }
266
+ };
267
+
268
+ constructor() {
269
+ super();
270
+ this.icon = '';
271
+ this.href = '#';
272
+ this.active = false;
273
+ this.collapsed = false;
274
+ this['no-expand'] = false;
275
+ this['hide-when-collapsed'] = false;
276
+ }
277
+
278
+ /*
279
+ Lifecycle Callbacks
280
+ */
281
+ connectedCallback() {
282
+ super.connectedCallback();
283
+ this.aside = this.closest('k-aside');
284
+ if(this.aside) {
285
+ this.collapsed = this.aside.state === 'collapsed';
286
+ this.aside.addEventListener('aside_state_change', this.handleStateChange);
287
+ }
288
+ }
289
+
290
+ disconnectedCallback() {
291
+ super.disconnectedCallback();
292
+ if(this.aside) {
293
+ this.aside.removeEventListener('aside_state_change', this.handleStateChange);
294
+ }
295
+ }
296
+
297
+ /*
298
+ Event Handlers
299
+ */
300
+ handleStateChange = (e) => {
301
+ const { state } = e.detail;
302
+ if(state === 'collapsed') this.collapsed = true;
303
+ else if(state === 'expanded') this.collapsed = false;
304
+ };
305
+
306
+ handleClick = (e) => {
307
+ if(this.collapsed && !this['no-expand'] && this.aside) {
308
+ e.preventDefault();
309
+ this.aside.expand();
310
+ }
311
+ };
312
+
313
+ /*
314
+ Rendering
315
+ */
316
+ render() {
317
+ return html`
318
+ <a href="${this.href}" class="item ${this.active ? 'active bg-primary' : ''}" @click=${this.handleClick}>
319
+ ${this.icon ? html`<k-icon name="${this.icon}"></k-icon>` : this.collapsed ? html`<k-icon name="dot"></k-icon>` : nothing}
320
+ ${this.collapsed ? nothing : html`<span class="label"><slot></slot></span>`}
321
+ </a>
322
+ `;
323
+ }
324
+
325
+ /*
326
+ Styles
327
+ */
328
+ static styles = css`
329
+ :host {
330
+ display: block;
331
+ }
332
+ :host([collapsed][hide-when-collapsed]) {
333
+ display: none;
334
+ }
335
+ .item {
336
+ display: flex;
337
+ align-items: center;
338
+ gap: var(--spacer_h);
339
+ padding: var(--spacer_h);
340
+ color: var(--tc);
341
+ text-decoration: none;
342
+ border-radius: var(--radius);
343
+ margin: 0 var(--spacer_h);
344
+ transition: background var(--animation_ms), color var(--animation_ms);
345
+ white-space: nowrap;
346
+ }
347
+ .item:hover {
348
+ background: var(--c_bg_hover);
349
+ }
350
+ .item.active {
351
+ color: var(--tc_on_primary);
352
+ }
353
+ .item.active:hover {
354
+ background: var(--c_primary);
355
+ filter: brightness(1.1);
356
+ }
357
+ k-icon {
358
+ flex-shrink: 0;
359
+ }
360
+ .label {
361
+ min-width: 0;
362
+ overflow: hidden;
363
+ text-overflow: ellipsis;
364
+ }
365
+ `;
366
+ }
367
+
368
+ /*
369
+ AsideLabel
370
+ */
371
+ class AsideLabel extends ShadowComponent {
372
+ static properties = {
373
+ collapsed: { type: Boolean, reflect: true }
374
+ };
375
+
376
+ constructor() {
377
+ super();
378
+ this.collapsed = false;
379
+ }
380
+
381
+ /*
382
+ Lifecycle Callbacks
383
+ */
384
+ connectedCallback() {
385
+ super.connectedCallback();
386
+ this.aside = this.closest('k-aside');
387
+ if(this.aside) {
388
+ this.collapsed = this.aside.state === 'collapsed';
389
+ this.aside.addEventListener('aside_state_change', this.handleStateChange);
390
+ }
391
+ }
392
+
393
+ disconnectedCallback() {
394
+ super.disconnectedCallback();
395
+ if(this.aside) {
396
+ this.aside.removeEventListener('aside_state_change', this.handleStateChange);
397
+ }
398
+ }
399
+
400
+ /*
401
+ Event Handlers
402
+ */
403
+ handleStateChange = (e) => {
404
+ const { state } = e.detail;
405
+ if(state === 'collapsed') this.collapsed = true;
406
+ else if(state === 'expanded') this.collapsed = false;
407
+ };
408
+
409
+ /*
410
+ Rendering
411
+ */
412
+ render() {
413
+ return this.collapsed ? html`<hr>` : html`<div class="label"><slot></slot></div>`;
414
+ }
415
+
416
+ /*
417
+ Styles
418
+ */
419
+ static styles = css`
420
+ :host {
421
+ display: block;
422
+ margin: var(--spacer_h) 0;
423
+ }
424
+ :host([collapsed]) {
425
+ margin: 0 var(--spacer_h);
426
+ align-self: stretch;
427
+ }
428
+ .label {
429
+ padding: 0 var(--spacer);
430
+ font-size: 0.75rem;
431
+ font-weight: 600;
432
+ text-transform: uppercase;
433
+ color: var(--c_text_muted);
434
+ letter-spacing: 0.05em;
435
+ }
436
+ hr {
437
+ border: none;
438
+ border-top: 1px solid var(--c_border);
439
+ margin: var(--spacer_h) var(--spacer);
440
+ }
441
+ :host([collapsed]) hr {
442
+ margin: var(--spacer_h) 0;
443
+ }
444
+ `;
445
+ }
446
+
447
+ /*
448
+ AsideMenu
449
+ */
450
+ class AsideMenu extends ShadowComponent {
451
+ static properties = {
452
+ icon: { type: String },
453
+ label: { type: String },
454
+ open: { type: Boolean, reflect: true },
455
+ collapsed: { type: Boolean, reflect: true },
456
+ 'no-expand': { type: Boolean, attribute: 'no-expand' },
457
+ 'hide-when-collapsed': { type: Boolean, attribute: 'hide-when-collapsed' }
458
+ };
459
+
460
+ constructor() {
461
+ super();
462
+ this.icon = '';
463
+ this.label = '';
464
+ this.open = false;
465
+ this.collapsed = false;
466
+ this['no-expand'] = false;
467
+ this['hide-when-collapsed'] = false;
468
+ }
469
+
470
+ /*
471
+ Lifecycle Callbacks
472
+ */
473
+ connectedCallback() {
474
+ super.connectedCallback();
475
+ this.aside = this.closest('k-aside');
476
+ if(this.aside) {
477
+ this.collapsed = this.aside.state === 'collapsed';
478
+ this.aside.addEventListener('aside_state_change', this.handleStateChange);
479
+ }
480
+ }
481
+
482
+ disconnectedCallback() {
483
+ super.disconnectedCallback();
484
+ if(this.aside) {
485
+ this.aside.removeEventListener('aside_state_change', this.handleStateChange);
486
+ }
487
+ }
488
+
489
+ /*
490
+ Event Handlers
491
+ */
492
+ handleStateChange = (e) => {
493
+ const { state } = e.detail;
494
+ if(state === 'collapsed') {
495
+ this.collapsed = true;
496
+ this.open = false;
497
+ } else if(state === 'expanded') {
498
+ this.collapsed = false;
499
+ }
500
+ };
501
+
502
+ toggleMenu = () => {
503
+ if(this.collapsed && !this['no-expand'] && this.aside) {
504
+ this.aside.expand();
505
+ } else if(!this.collapsed) {
506
+ this.open = !this.open;
507
+ }
508
+ };
509
+
510
+ /*
511
+ Rendering
512
+ */
513
+ render() {
514
+ return html`
515
+ <div class="menu-container">
516
+ <button class="no-btn menu-header ${this.open ? 'open' : ''}" @click=${this.toggleMenu}>
517
+ ${this.icon ? html`<k-icon name="${this.icon}"></k-icon>` : this.collapsed ? html`<k-icon name="dot"></k-icon>` : nothing}
518
+ ${this.collapsed ? nothing : html`<span class="label">${this.label}</span>`}
519
+ ${!this.collapsed ? html`<k-icon class="chevron" name="chevron" direction="${this.open ? 'down' : 'right'}"></k-icon>` : nothing}
520
+ </button>
521
+ <div class="menu-content ${this.open && !this.collapsed ? 'open' : ''}">
522
+ <slot></slot>
523
+ </div>
524
+ </div>
525
+ `;
526
+ }
527
+
528
+ /*
529
+ Styles
530
+ */
531
+ static styles = css`
532
+ :host {
533
+ display: block;
534
+ }
535
+ :host([collapsed][hide-when-collapsed]) {
536
+ display: none;
537
+ }
538
+ .menu-header {
539
+ display: flex !important;
540
+ align-items: center;
541
+ gap: var(--spacer_h);
542
+ padding: var(--spacer_h) !important;
543
+ color: var(--tc);
544
+ background: transparent;
545
+ border: none;
546
+ border-radius: var(--radius);
547
+ margin: 0 var(--spacer_h);
548
+ width: calc(100% - var(--spacer));
549
+ cursor: pointer;
550
+ text-align: left;
551
+ white-space: nowrap;
552
+ transition: background var(--animation_ms);
553
+ }
554
+ :host([collapsed]) .menu-header {
555
+ width: auto;
556
+ }
557
+ .menu-header:hover {
558
+ background: var(--c_bg_hover);
559
+ }
560
+ k-icon {
561
+ flex-shrink: 0;
562
+ }
563
+ .label {
564
+ flex: 1;
565
+ min-width: 0;
566
+ overflow: hidden;
567
+ text-overflow: ellipsis;
568
+ }
569
+ .chevron {
570
+ transition: transform var(--animation_ms);
571
+ }
572
+ .menu-content {
573
+ max-height: 0;
574
+ overflow: hidden;
575
+ transition: max-height var(--animation_ms);
576
+ padding-left: calc(var(--spacer_h) * 2);
577
+ }
578
+ :host([collapsed]) .menu-content {
579
+ padding-left: 0;
580
+ }
581
+ .menu-content.open {
582
+ max-height: 500px;
583
+ }
584
+ ::slotted(*) {
585
+ margin: 0.25rem 0;
586
+ }
587
+ `;
588
+ }
589
+
590
+ /*
591
+ AsideSpacer
592
+ */
593
+ class AsideSpacer extends ShadowComponent {
594
+ render() {
595
+ return html`<div></div>`;
596
+ }
597
+
598
+ static styles = css`
599
+ :host {
600
+ display: block;
601
+ flex: 1;
602
+ }
603
+ `;
604
+ }
605
+
606
+ /*
607
+ AsideToggle
608
+ */
609
+ class AsideToggle extends ShadowComponent {
610
+ static properties = {
611
+ collapsed: { type: Boolean, reflect: true },
612
+ direction: { type: String }
613
+ };
614
+
615
+ constructor() {
616
+ super();
617
+ this.collapsed = false;
618
+ this.direction = 'left';
619
+ }
620
+
621
+ /*
622
+ Lifecycle Callbacks
623
+ */
624
+ connectedCallback() {
625
+ super.connectedCallback();
626
+ this.aside = this.closest('k-aside');
627
+ if(this.aside) {
628
+ this.collapsed = this.aside.state === 'collapsed';
629
+ this.updateDirection();
630
+ this.aside.addEventListener('aside_state_change', this.handleStateChange);
631
+ }
632
+ }
633
+
634
+ disconnectedCallback() {
635
+ super.disconnectedCallback();
636
+ if(this.aside) {
637
+ this.aside.removeEventListener('aside_state_change', this.handleStateChange);
638
+ }
639
+ }
640
+
641
+ /*
642
+ Event Handlers
643
+ */
644
+ handleStateChange = (e) => {
645
+ const { state } = e.detail;
646
+ if(state === 'collapsed') this.collapsed = true;
647
+ else if(state === 'expanded') this.collapsed = false;
648
+ this.updateDirection();
649
+ };
650
+
651
+ handleClick = () => {
652
+ if(!this.aside) return;
653
+ this.aside.state = this.aside.state === 'collapsed' ? 'expanded' : 'collapsed';
654
+ };
655
+
656
+ /*
657
+ Methods
658
+ */
659
+ updateDirection = () => {
660
+ const rightSide = this.aside?.side === 'right';
661
+ this.direction = (this.collapsed !== rightSide) ? 'right' : 'left';
662
+ };
663
+
664
+ /*
665
+ Rendering
666
+ */
667
+ render() {
668
+ return html`
669
+ <div id="header">
670
+ <slot></slot>
671
+ <button id="toggle" @click=${this.handleClick}>
672
+ <k-icon name="arrow-line" direction=${this.direction}></k-icon>
673
+ </button>
674
+ </div>
675
+ `;
676
+ }
677
+
678
+ /*
679
+ Styles
680
+ */
681
+ static styles = css`
682
+ :host {
683
+ display: block;
684
+ }
685
+ #header {
686
+ display: flex;
687
+ align-items: center;
688
+ justify-content: flex-end;
689
+ gap: var(--spacer_h);
690
+ padding: var(--spacer_h);
691
+ border-bottom: 1px solid var(--c_border);
692
+ flex-shrink: 0;
693
+ }
694
+ ::slotted(*) {
695
+ margin-right: auto;
696
+ flex: 1;
697
+ min-width: 0;
698
+ }
699
+ :host([collapsed]) ::slotted(*) {
700
+ display: none;
701
+ }
702
+ #toggle {
703
+ flex-shrink: 0;
704
+ width: 2rem;
705
+ height: 2rem;
706
+ border: none;
707
+ background: transparent;
708
+ color: var(--c_text);
709
+ cursor: pointer;
710
+ display: flex;
711
+ align-items: center;
712
+ justify-content: center;
713
+ border-radius: var(--radius);
714
+ padding: 0;
715
+ }
716
+ #toggle:hover {
717
+ background: var(--c_bg_hover);
718
+ }
719
+ `;
720
+ }
721
+
722
+ customElements.define('k-aside-item', AsideItem);
723
+ customElements.define('k-aside-label', AsideLabel);
724
+ customElements.define('k-aside-menu', AsideMenu);
725
+ customElements.define('k-aside-spacer', AsideSpacer);
726
+ customElements.define('k-aside-toggle', AsideToggle);
@@ -165,7 +165,7 @@ export default class Dialog extends ShadowComponent {
165
165
  left: 0;
166
166
  width: 100vw;
167
167
  height: 100vh;
168
- z-index: 100;
168
+ z-index: 80;
169
169
  display: none;
170
170
  justify-content: center;
171
171
  align-items: center;