@kubex/zinc 1.1.44 → 1.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom-elements.json +157 -81
- package/dist/vscode.html-custom-data.json +13 -13
- package/dist/web-types.json +31 -25
- package/dist/zn.d.ts +17 -4
- package/dist/zn.min.js +107 -94
- package/docs/pages/components/content-block.md +20 -0
- package/package.json +1 -1
- package/src/components/chat-message-attachment/chat-message-attachment.component.ts +6 -6
- package/src/components/content-block/content-block.component.ts +33 -1
- package/src/components/content-block/content-block.scss +35 -9
- package/src/components/data-table/data-table.component.ts +6 -1
- package/src/components/menu/menu.component.ts +14 -9
- package/src/components/menu/menu.scss +8 -3
- package/src/components/navbar/navbar.component.ts +46 -16
- package/src/components/navbar/navbar.scss +2 -2
- package/src/components/navbar/navbar.test.ts +35 -0
|
@@ -52,6 +52,26 @@ layout: component
|
|
|
52
52
|
</zn-content-block>
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Attachments
|
|
56
|
+
|
|
57
|
+
Attachments render in a row at the bottom of the content and are only visible while the block
|
|
58
|
+
is expanded. Use `zn-chat-message-attachment`, which assigns itself to the `attachments` slot.
|
|
59
|
+
When a block has attachments, a paperclip icon and count are shown in the header after the
|
|
60
|
+
preview text.
|
|
61
|
+
|
|
62
|
+
```html:preview
|
|
63
|
+
|
|
64
|
+
<zn-content-block
|
|
65
|
+
time="16 Jun 2025, 13:00"
|
|
66
|
+
sender="John Smith"
|
|
67
|
+
avatar="JS"
|
|
68
|
+
>
|
|
69
|
+
<div slot="text">Text formatted message goes here.</div>
|
|
70
|
+
<zn-chat-message-attachment href="/" name="filename1.txt" download></zn-chat-message-attachment>
|
|
71
|
+
<zn-chat-message-attachment href="/" name="filename2.png" download></zn-chat-message-attachment>
|
|
72
|
+
</zn-content-block>
|
|
73
|
+
```
|
|
74
|
+
|
|
55
75
|
### Content Footer
|
|
56
76
|
|
|
57
77
|
Basic Footer
|
package/package.json
CHANGED
|
@@ -6,10 +6,10 @@ import ZnIcon from "../icon";
|
|
|
6
6
|
import styles from './chat-message-attachment.scss';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @summary A single file or link attachment for a `zn-chat-message
|
|
10
|
-
* icon and a label as a link, styled to match the message's attachments row. It is
|
|
11
|
-
* intended to be used only inside a `zn-chat-message` and is automatically
|
|
12
|
-
* that component's `attachments` slot.
|
|
9
|
+
* @summary A single file or link attachment for a `zn-chat-message` or `zn-content-block`.
|
|
10
|
+
* Renders an icon and a label as a link, styled to match the message's attachments row. It is
|
|
11
|
+
* intended to be used only inside a `zn-chat-message` or `zn-content-block` and is automatically
|
|
12
|
+
* placed in that component's `attachments` slot.
|
|
13
13
|
* @documentation https://zinc.style/components/chat-message-attachment
|
|
14
14
|
* @status experimental
|
|
15
15
|
* @since 1.0
|
|
@@ -51,12 +51,12 @@ export default class ZnChatMessageAttachment extends ZincElement {
|
|
|
51
51
|
connectedCallback() {
|
|
52
52
|
super.connectedCallback();
|
|
53
53
|
|
|
54
|
-
if (this.closest('zn-chat-message')) {
|
|
54
|
+
if (this.closest('zn-chat-message, zn-content-block')) {
|
|
55
55
|
if (!this.slot) {
|
|
56
56
|
this.slot = 'attachments';
|
|
57
57
|
}
|
|
58
58
|
} else {
|
|
59
|
-
console.warn('<zn-chat-message-attachment> can only be used inside a <zn-chat-message>.', this);
|
|
59
|
+
console.warn('<zn-chat-message-attachment> can only be used inside a <zn-chat-message> or <zn-content-block>.', this);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -3,7 +3,7 @@ import {deepQuerySelectorAll} from "../../utilities/query";
|
|
|
3
3
|
import {HasSlotController} from "../../internal/slot";
|
|
4
4
|
import {html, unsafeCSS} from 'lit';
|
|
5
5
|
import {MutationController} from '@lit-labs/observers/mutation-controller.js';
|
|
6
|
-
import {property, queryAssignedNodes, queryAsync} from 'lit/decorators.js';
|
|
6
|
+
import {property, queryAssignedNodes, queryAsync, state} from 'lit/decorators.js';
|
|
7
7
|
import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';
|
|
8
8
|
import ZincElement from "../../internal/zinc-element";
|
|
9
9
|
import type {PropertyValues} from 'lit';
|
|
@@ -21,6 +21,12 @@ interface TextRow {
|
|
|
21
21
|
* @documentation https://zinc.style/components/content-block
|
|
22
22
|
* @status experimental
|
|
23
23
|
* @since 1.0
|
|
24
|
+
*
|
|
25
|
+
* @slot attachments - Attachments displayed at the bottom of the content when the block is
|
|
26
|
+
* expanded. Use `zn-chat-message-attachment`, which auto-assigns itself to this slot.
|
|
27
|
+
*
|
|
28
|
+
* @csspart attachments - The attachments row at the bottom of the content.
|
|
29
|
+
* @csspart header-attachments - The attachment count indicator in the header.
|
|
24
30
|
*/
|
|
25
31
|
export default class ContentBlock extends ZincElement {
|
|
26
32
|
static styles = unsafeCSS(styles);
|
|
@@ -43,6 +49,8 @@ export default class ContentBlock extends ZincElement {
|
|
|
43
49
|
|
|
44
50
|
private readonly hasSlotController = new HasSlotController(this, 'text', 'html');
|
|
45
51
|
|
|
52
|
+
@state() private attachmentCount = 0;
|
|
53
|
+
|
|
46
54
|
private _textRows: TextRow[] = [];
|
|
47
55
|
|
|
48
56
|
private readonly _footerObserver = new MutationController(this, {
|
|
@@ -163,9 +171,21 @@ export default class ContentBlock extends ZincElement {
|
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
|
|
174
|
+
private handleAttachmentsSlotChange() {
|
|
175
|
+
this.syncAttachmentCount();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private syncAttachmentCount() {
|
|
179
|
+
const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name="attachments"]');
|
|
180
|
+
this.attachmentCount = slot?.assignedElements().length ?? 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
166
183
|
protected firstUpdated(_changedProperties: PropertyValues) {
|
|
167
184
|
super.firstUpdated(_changedProperties);
|
|
168
185
|
|
|
186
|
+
this.syncAttachmentCount();
|
|
187
|
+
Promise.resolve().then(() => this.syncAttachmentCount());
|
|
188
|
+
|
|
169
189
|
const textContent = this.shadowRoot?.querySelectorAll('.text-section');
|
|
170
190
|
if (textContent && textContent.length > 0) {
|
|
171
191
|
textContent.forEach((content) => {
|
|
@@ -196,6 +216,7 @@ export default class ContentBlock extends ZincElement {
|
|
|
196
216
|
<slot name="icon">
|
|
197
217
|
<zn-icon src="${this.avatar}"
|
|
198
218
|
library="avatar"
|
|
219
|
+
size="36"
|
|
199
220
|
round></zn-icon>
|
|
200
221
|
</slot>
|
|
201
222
|
</div>
|
|
@@ -205,6 +226,10 @@ export default class ContentBlock extends ZincElement {
|
|
|
205
226
|
<slot name="caption">${this.sender}</slot>
|
|
206
227
|
</span>
|
|
207
228
|
<span class="content-block-header__description">${this.truncateText()}</span>
|
|
229
|
+
${this.attachmentCount > 0 ? html`
|
|
230
|
+
<span class="content-block-header__attachments" part="header-attachments">
|
|
231
|
+
<zn-icon src="paperclip@lu" size="14"></zn-icon>${this.attachmentCount}
|
|
232
|
+
</span>` : ''}
|
|
208
233
|
</div>
|
|
209
234
|
|
|
210
235
|
<div class="content-block-header__actions">
|
|
@@ -245,6 +270,13 @@ export default class ContentBlock extends ZincElement {
|
|
|
245
270
|
</div>
|
|
246
271
|
</zn-sp>
|
|
247
272
|
|
|
273
|
+
<slot
|
|
274
|
+
name="attachments"
|
|
275
|
+
part="attachments"
|
|
276
|
+
class="content-block__attachments ${this.attachmentCount > 0 ? 'content-block__attachments--visible' : ''}"
|
|
277
|
+
@slotchange=${this.handleAttachmentsSlotChange}
|
|
278
|
+
></slot>
|
|
279
|
+
|
|
248
280
|
${hasFooter ? html`
|
|
249
281
|
<slot slot="footer" name="footer" @slotchange="${this._handleSlotChange}"></slot>` : ''}
|
|
250
282
|
</div>
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
.content-block-header {
|
|
17
17
|
display: flex;
|
|
18
18
|
align-items: center;
|
|
19
|
-
gap:
|
|
19
|
+
gap: var(--zn-spacing-small);
|
|
20
20
|
width: 100%;
|
|
21
21
|
max-width: 100%;
|
|
22
|
-
padding: var(--zn-
|
|
22
|
+
padding-block: var(--zn-spacing-small);
|
|
23
23
|
position: relative;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
display: flex;
|
|
38
38
|
flex-direction: row;
|
|
39
39
|
align-items: center;
|
|
40
|
-
gap:
|
|
40
|
+
gap: var(--zn-spacing-small);
|
|
41
41
|
min-width: 0;
|
|
42
42
|
flex-grow: 1;
|
|
43
43
|
overflow: hidden;
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
margin: 0;
|
|
52
52
|
display: flex;
|
|
53
53
|
align-items: center;
|
|
54
|
-
gap: var(--zn-spacing-
|
|
54
|
+
gap: var(--zn-spacing-small);
|
|
55
55
|
flex-shrink: 0;
|
|
56
56
|
overflow: hidden;
|
|
57
57
|
white-space: nowrap;
|
|
@@ -69,16 +69,43 @@
|
|
|
69
69
|
display: none;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
.content-block-header__attachments {
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--zn-spacing-2x-small);
|
|
76
|
+
font-size: var(--zn-font-size-small);
|
|
77
|
+
color: rgb(var(--zn-text-muted));
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
margin-left: auto;
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
.content-block-header__actions {
|
|
73
83
|
display: flex;
|
|
74
84
|
align-items: center;
|
|
75
|
-
gap:
|
|
85
|
+
gap: var(--zn-spacing-small);
|
|
76
86
|
margin-left: auto;
|
|
77
87
|
flex-shrink: 0;
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
.content-block__attachments {
|
|
91
|
+
display: none;
|
|
92
|
+
flex-direction: row;
|
|
93
|
+
flex-wrap: wrap;
|
|
94
|
+
align-items: center;
|
|
95
|
+
gap: var(--zn-spacing-small);
|
|
96
|
+
padding-block-end: var(--zn-spacing-small);
|
|
97
|
+
|
|
98
|
+
&--visible {
|
|
99
|
+
display: flex;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
::slotted(zn-chat-message-attachment) {
|
|
103
|
+
min-width: 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
80
107
|
.content-block--short {
|
|
81
|
-
zn-sp {
|
|
108
|
+
zn-sp, .content-block__attachments {
|
|
82
109
|
display: none;
|
|
83
110
|
}
|
|
84
111
|
|
|
@@ -92,11 +119,10 @@
|
|
|
92
119
|
}
|
|
93
120
|
|
|
94
121
|
.text-section {
|
|
95
|
-
|
|
96
|
-
line-height: 19px;
|
|
122
|
+
@include wc.text-style(paragraph);
|
|
97
123
|
|
|
98
124
|
a {
|
|
99
|
-
font-size:
|
|
125
|
+
font-size: var(--zn-font-size-small);
|
|
100
126
|
opacity: 0.8;
|
|
101
127
|
}
|
|
102
128
|
}
|
|
@@ -245,6 +245,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
245
245
|
|
|
246
246
|
// Data Table Properties
|
|
247
247
|
private _initialLoad = true;
|
|
248
|
+
private _hasLoadedData = false;
|
|
248
249
|
private _lastTableContent: TemplateResult = html``;
|
|
249
250
|
|
|
250
251
|
private readonly resizeObserver = new ResizeController(this, {
|
|
@@ -363,7 +364,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
363
364
|
} else if (this.dataUri) {
|
|
364
365
|
tableBody = this._dataTask.render({
|
|
365
366
|
pending: () => {
|
|
366
|
-
|
|
367
|
+
// Show the skeleton until data has rendered at least once, so the
|
|
368
|
+
// first fetch on a no-initial-load table doesn't render blank space
|
|
369
|
+
if (this._initialLoad || !this._hasLoadedData) {
|
|
367
370
|
return html`
|
|
368
371
|
<div>${this.loadingTable()}</div>`;
|
|
369
372
|
}
|
|
@@ -372,6 +375,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
372
375
|
},
|
|
373
376
|
complete: (data) => {
|
|
374
377
|
this._initialLoad = false;
|
|
378
|
+
this._hasLoadedData = true;
|
|
375
379
|
this._lastTableContent = html`
|
|
376
380
|
<div>${this.renderTable(data as Response)}</div>`;
|
|
377
381
|
return this._lastTableContent;
|
|
@@ -401,6 +405,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
401
405
|
page: 1,
|
|
402
406
|
};
|
|
403
407
|
this._initialLoad = false;
|
|
408
|
+
this._hasLoadedData = true;
|
|
404
409
|
this._lastTableContent = html`
|
|
405
410
|
<div>${this.renderTable(response)}</div>`;
|
|
406
411
|
tableBody = this._lastTableContent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {type CSSResultGroup, html, unsafeCSS} from 'lit';
|
|
2
|
-
import {ifDefined} from "lit/directives/if-defined.js";
|
|
3
|
-
import {property, query} from 'lit/decorators.js';
|
|
1
|
+
import { type CSSResultGroup, html, unsafeCSS } from 'lit';
|
|
2
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
3
|
+
import { property, query } from 'lit/decorators.js';
|
|
4
4
|
import ZincElement from '../../internal/zinc-element';
|
|
5
5
|
import ZnConfirm from "../confirm";
|
|
6
6
|
import ZnDropdown from "../dropdown";
|
|
@@ -9,6 +9,7 @@ import ZnMenuItem from "../menu-item";
|
|
|
9
9
|
import ZnTooltip from "../tooltip";
|
|
10
10
|
|
|
11
11
|
import styles from './menu.scss';
|
|
12
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
12
13
|
|
|
13
14
|
interface NavItem {
|
|
14
15
|
title: string;
|
|
@@ -56,12 +57,12 @@ export default class ZnMenu extends ZincElement {
|
|
|
56
57
|
|
|
57
58
|
@query('slot') defaultSlot: HTMLSlotElement;
|
|
58
59
|
|
|
59
|
-
@property({attribute: 'actions', type: Array}) actions = [];
|
|
60
|
+
@property({ attribute: 'actions', type: Array }) actions = [];
|
|
60
61
|
|
|
61
62
|
/** The menu's visual style. `shell` renders the app-shell header dropdown
|
|
62
63
|
* look: a padded panel with floating rounded items. Propagated to the
|
|
63
64
|
* menu's items. */
|
|
64
|
-
@property({reflect: true}) variant: 'default' | 'shell' = 'default';
|
|
65
|
+
@property({ reflect: true }) variant: 'default' | 'shell' = 'default';
|
|
65
66
|
|
|
66
67
|
connectedCallback() {
|
|
67
68
|
super.connectedCallback();
|
|
@@ -70,7 +71,7 @@ export default class ZnMenu extends ZincElement {
|
|
|
70
71
|
|
|
71
72
|
/** @internal Gets all slotted menu items, ignoring dividers, headers, and other elements. */
|
|
72
73
|
getAllItems() {
|
|
73
|
-
return [...this.defaultSlot.assignedElements({flatten: true})].filter((el: HTMLElement) => {
|
|
74
|
+
return [...this.defaultSlot.assignedElements({ flatten: true })].filter((el: HTMLElement) => {
|
|
74
75
|
return !(el.inert || !this.isMenuItem(el));
|
|
75
76
|
|
|
76
77
|
}) as ZnMenuItem[];
|
|
@@ -100,15 +101,19 @@ export default class ZnMenu extends ZincElement {
|
|
|
100
101
|
render() {
|
|
101
102
|
return html`
|
|
102
103
|
<div
|
|
104
|
+
class="${classMap({
|
|
105
|
+
'menu': true,
|
|
106
|
+
'menu--shell': this.variant === 'shell',
|
|
107
|
+
})}"
|
|
103
108
|
@slotchange=${this.handleSlotChange}
|
|
104
109
|
@keydown=${this.handleKeyDown}
|
|
105
110
|
@mousedown=${this.handleMouseDown}>
|
|
106
111
|
<slot></slot>
|
|
107
112
|
${this.actions.map((item: NavItem) => {
|
|
108
|
-
if(
|
|
113
|
+
if (item === null || item === undefined) {
|
|
109
114
|
return html``; // Skip null or undefined items
|
|
110
115
|
}
|
|
111
|
-
if (
|
|
116
|
+
if (item?.confirm) {
|
|
112
117
|
return html`
|
|
113
118
|
<zn-confirm trigger="${ifDefined(item.confirm?.trigger)}"
|
|
114
119
|
type="${ifDefined(item.confirm?.type)}"
|
|
@@ -177,7 +182,7 @@ export default class ZnMenu extends ZincElement {
|
|
|
177
182
|
// get the parent dropdown and close it
|
|
178
183
|
(closestMenu?.closest('zn-dropdown') as ZnDropdown | null)?.hide();
|
|
179
184
|
|
|
180
|
-
this.emit('zn-select', {detail: {item}});
|
|
185
|
+
this.emit('zn-select', { detail: { item } });
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
private handleKeyDown(event: KeyboardEvent) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
@use "../../wc";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
.menu {
|
|
4
4
|
display: block;
|
|
5
5
|
position: relative;
|
|
6
6
|
padding: 1px;
|
|
7
|
-
border: 1px solid rgb(var(--zn-border-color));
|
|
7
|
+
border: 1px solid rgb(var(--zn-border-color)) !important;
|
|
8
8
|
border-radius: var(--zn-border-radius);
|
|
9
9
|
width: fit-content;
|
|
10
10
|
min-width: var(--zn-size-spanel);
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
// Shell variant — the app-shell header dropdown look (as used by Rubix):
|
|
21
21
|
// padded panel with floating rounded items, translucent blurred background
|
|
22
|
-
|
|
22
|
+
.menu--shell {
|
|
23
23
|
display: flex;
|
|
24
24
|
flex-direction: column;
|
|
25
25
|
gap: var(--zn-spacing-2x-small);
|
|
@@ -35,3 +35,8 @@
|
|
|
35
35
|
background-color: rgba(var(--zn-color-navigation, 255, 255, 255), 0.95);
|
|
36
36
|
backdrop-filter: blur(3px);
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
::slotted(zn-menu-item + zn-menu-item) {
|
|
41
|
+
border-top: 1px solid rgb(var(--zn-border-color));
|
|
42
|
+
}
|
|
@@ -62,8 +62,8 @@ export default class ZnNavbar extends ZincElement {
|
|
|
62
62
|
@property({attribute: 'store-ttl', type: Number, reflect: true}) storeTtl = 0;
|
|
63
63
|
@property({attribute: 'local-storage', type: Boolean, reflect: true}) localStorage: boolean;
|
|
64
64
|
|
|
65
|
-
private _preItems:
|
|
66
|
-
private _postItems:
|
|
65
|
+
private _preItems: HTMLElement[] = [];
|
|
66
|
+
private _postItems: HTMLElement[] = [];
|
|
67
67
|
@property()
|
|
68
68
|
private _appended: Element[];
|
|
69
69
|
private _expanding: Element[] = [];
|
|
@@ -79,6 +79,7 @@ export default class ZnNavbar extends ZincElement {
|
|
|
79
79
|
private _expandable: HTMLElement | null = null;
|
|
80
80
|
private _extendedMenu: HTMLElement | null = null;
|
|
81
81
|
private readonly _cloneSources = new WeakMap<HTMLElement, HTMLElement>();
|
|
82
|
+
private readonly _lightDomClones = new WeakMap<HTMLElement, HTMLElement>();
|
|
82
83
|
private _navItemsGap: number = 0;
|
|
83
84
|
private _expandableMargin: number = 0;
|
|
84
85
|
private _totalItemWidth: number = 0;
|
|
@@ -91,6 +92,40 @@ export default class ZnNavbar extends ZincElement {
|
|
|
91
92
|
this._appended = [...(this._appended || []), item];
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
private _cloneLightItem(item: HTMLElement): HTMLElement {
|
|
96
|
+
const existing = this._lightDomClones.get(item);
|
|
97
|
+
if (existing) {
|
|
98
|
+
return existing;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const clone = item.cloneNode(true) as HTMLElement;
|
|
102
|
+
this._cloneSources.set(clone, item);
|
|
103
|
+
this._lightDomClones.set(item, clone);
|
|
104
|
+
clone.addEventListener('click', () => item.click());
|
|
105
|
+
return clone;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private _syncLightDomItems() {
|
|
109
|
+
const preItems: HTMLElement[] = [];
|
|
110
|
+
const postItems: HTMLElement[] = [];
|
|
111
|
+
|
|
112
|
+
Array.from(this.children).forEach(child => {
|
|
113
|
+
if (!(child instanceof HTMLElement) || child.tagName !== 'LI') {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const clone = this._cloneLightItem(child);
|
|
118
|
+
if (child.hasAttribute('suffix')) {
|
|
119
|
+
postItems.push(clone);
|
|
120
|
+
} else {
|
|
121
|
+
preItems.push(clone);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this._preItems = preItems;
|
|
126
|
+
this._postItems = postItems;
|
|
127
|
+
}
|
|
128
|
+
|
|
94
129
|
addExpandingAction(action: Element) {
|
|
95
130
|
if (!this._expanding.includes(action)) {
|
|
96
131
|
this._expanding = [...this._expanding, action];
|
|
@@ -141,31 +176,26 @@ export default class ZnNavbar extends ZincElement {
|
|
|
141
176
|
}
|
|
142
177
|
|
|
143
178
|
private _adoptNewLightItems(mutations: MutationRecord[]) {
|
|
144
|
-
const ul = this.shadowRoot?.querySelector('ul');
|
|
145
|
-
if (!ul) return;
|
|
146
|
-
const moreItem = ul.querySelector('li.more');
|
|
147
179
|
for (const m of mutations) {
|
|
148
180
|
for (const node of Array.from(m.addedNodes)) {
|
|
149
181
|
if (node instanceof Element && node.tagName === 'ZN-EXPANDING-ACTION') {
|
|
150
182
|
this.addExpandingAction(node);
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!(node instanceof HTMLLIElement)) continue;
|
|
155
|
-
if (node.hasAttribute('suffix')) continue;
|
|
156
|
-
if (moreItem) {
|
|
157
|
-
ul.insertBefore(node, moreItem);
|
|
158
|
-
} else {
|
|
159
|
-
ul.appendChild(node);
|
|
160
183
|
}
|
|
161
184
|
}
|
|
162
185
|
}
|
|
186
|
+
|
|
187
|
+
if (mutations.some(mutation =>
|
|
188
|
+
Array.from(mutation.addedNodes).some(node => node instanceof HTMLLIElement) ||
|
|
189
|
+
Array.from(mutation.removedNodes).some(node => node instanceof HTMLLIElement)
|
|
190
|
+
)) {
|
|
191
|
+
this._syncLightDomItems();
|
|
192
|
+
this.requestUpdate();
|
|
193
|
+
}
|
|
163
194
|
}
|
|
164
195
|
|
|
165
196
|
connectedCallback() {
|
|
166
197
|
super.connectedCallback();
|
|
167
|
-
this.
|
|
168
|
-
this._postItems = this.querySelectorAll('li[suffix]');
|
|
198
|
+
this._syncLightDomItems();
|
|
169
199
|
this._expanding = Array.from(this.querySelectorAll('zn-expanding-action'));
|
|
170
200
|
|
|
171
201
|
if (!this.masterId) {
|
|
@@ -242,7 +242,7 @@ ul.navbar.navbar--stacked.navbar--icon-bar {
|
|
|
242
242
|
|
|
243
243
|
:host(:not([stacked]):not([icon-bar]):not([icon-only])) {
|
|
244
244
|
ul.navbar {
|
|
245
|
-
min-width:
|
|
245
|
+
min-width: 200px;
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -279,7 +279,7 @@ ul.navbar.navbar--stacked.navbar--icon-bar {
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
> li.last-visible {
|
|
283
283
|
border-top-right-radius: 6px;
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
2
|
import {aTimeout, expect, fixture, html, waitUntil} from '@open-wc/testing';
|
|
3
|
+
import {render} from 'lit';
|
|
3
4
|
import type ZnNavbar from './navbar.component';
|
|
4
5
|
|
|
5
6
|
describe('<zn-navbar>', () => {
|
|
@@ -9,6 +10,40 @@ describe('<zn-navbar>', () => {
|
|
|
9
10
|
expect(el).to.exist;
|
|
10
11
|
});
|
|
11
12
|
|
|
13
|
+
it('does not move Lit-owned light DOM items when the parent rerenders', async () => {
|
|
14
|
+
const host = document.createElement('div');
|
|
15
|
+
document.body.append(host);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
render(html`
|
|
19
|
+
<zn-navbar>
|
|
20
|
+
<li tab="one">One</li>
|
|
21
|
+
</zn-navbar>
|
|
22
|
+
`, host);
|
|
23
|
+
await aTimeout(20);
|
|
24
|
+
|
|
25
|
+
render(html`
|
|
26
|
+
<zn-navbar>
|
|
27
|
+
<li tab="one">One</li>
|
|
28
|
+
<li tab="two">Two</li>
|
|
29
|
+
</zn-navbar>
|
|
30
|
+
`, host);
|
|
31
|
+
await aTimeout(20);
|
|
32
|
+
|
|
33
|
+
const navbar = host.querySelector<ZnNavbar>('zn-navbar')!;
|
|
34
|
+
const shadowItems = navbar.shadowRoot!.querySelectorAll('li[tab]');
|
|
35
|
+
const lightItems = navbar.querySelectorAll('li[tab]');
|
|
36
|
+
|
|
37
|
+
expect(lightItems.length).to.equal(2);
|
|
38
|
+
expect(shadowItems.length).to.equal(2);
|
|
39
|
+
expect(shadowItems[0]).to.not.equal(lightItems[0]);
|
|
40
|
+
expect(shadowItems[1].textContent?.trim()).to.equal('Two');
|
|
41
|
+
} finally {
|
|
42
|
+
render(html``, host);
|
|
43
|
+
host.remove();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
12
47
|
it('reserves at least 200px for visible nav items when expandables need space', async () => {
|
|
13
48
|
const el = await fixture<ZnNavbar>(html`
|
|
14
49
|
<zn-navbar>
|