@kubex/zinc 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/custom-elements.json +302 -16
  2. package/dist/vscode.html-custom-data.json +13 -2
  3. package/dist/web-types.json +43 -5
  4. package/dist/zn.d.ts +81 -40
  5. package/dist/zn.min.js +333 -270
  6. package/package.json +1 -1
  7. package/src/components/bulk-actions/bulk-actions.component.ts +152 -84
  8. package/src/components/bulk-actions/bulk-actions.scss +2 -0
  9. package/src/components/chart/chart.component.ts +14 -12
  10. package/src/components/cols/cols.component.ts +7 -3
  11. package/src/components/cols/cols.scss +22 -14
  12. package/src/components/content-block/content-block.component.ts +164 -30
  13. package/src/components/data-select/data-select.component.ts +51 -10
  14. package/src/components/data-table/data-table.component.ts +58 -38
  15. package/src/components/data-table/data-table.scss +74 -7
  16. package/src/components/data-table-filter/data-table-filter.component.ts +1 -1
  17. package/src/components/editor/modules/attachment/attachment.ts +53 -41
  18. package/src/components/editor/modules/toolbar/toolbar.ts +42 -0
  19. package/src/components/empty-state/empty-state.component.ts +16 -8
  20. package/src/components/icon/icon.component.ts +2 -0
  21. package/src/components/icon/icon.scss +13 -0
  22. package/src/components/inline-edit/inline-edit.component.ts +24 -3
  23. package/src/components/inline-edit/inline-edit.scss +11 -1
  24. package/src/components/item/item.component.ts +11 -3
  25. package/src/components/item/item.scss +14 -19
  26. package/src/components/panel/panel.component.ts +2 -0
  27. package/src/components/panel/panel.scss +6 -0
  28. package/src/components/settings-container/settings-container.scss +11 -4
  29. package/src/components/style/style.component.ts +7 -1
@@ -21,7 +21,6 @@ table {
21
21
  &.table--standalone {
22
22
  background-color: rgb(var(--zn-panel));
23
23
  border-radius: var(--zn-border-radius-large);
24
- box-shadow: var(--zn-shadow-medium);
25
24
  border: 1px solid rgb(var(--zn-border-color));
26
25
  overflow: hidden;
27
26
  }
@@ -35,7 +34,7 @@ table {
35
34
  border-bottom: 1px solid rgb(var(--zn-border-color)) !important;
36
35
  }
37
36
 
38
- .table--with-checkboxes tbody tr:hover {
37
+ &.with-hover tbody tr:hover {
39
38
  background-color: rgba(var(--zn-primary), 0.05) !important;
40
39
  cursor: pointer;
41
40
  }
@@ -115,10 +114,10 @@ table {
115
114
  }
116
115
  }
117
116
 
118
- &:first-of-type > div:has(input[type="checkbox"]) {
117
+ &:first-of-type > div:has(zn-checkbox) {
119
118
  justify-content: center;
120
119
 
121
- input[type="checkbox"] {
120
+ zn-checkbox {
122
121
  margin: 0;
123
122
  }
124
123
  }
@@ -240,7 +239,7 @@ table {
240
239
  flex-direction: row;
241
240
  justify-content: space-between;
242
241
  align-items: baseline;
243
- padding: var(--zn-spacing-small);
242
+ padding-block: var(--zn-spacing-small);
244
243
  gap: var(--zn-spacing-small);
245
244
 
246
245
  &__actions,
@@ -306,9 +305,7 @@ table {
306
305
  }
307
306
  }
308
307
  }
309
-
310
308
  }
311
-
312
309
  }
313
310
 
314
311
  .reduced-opacity .table tbody {
@@ -333,3 +330,73 @@ table {
333
330
  filter: drop-shadow(0 5px 4px rgba(0, 0, 0, 0.04)) drop-shadow(0 4px 3px rgba(0, 0, 0, 0.1));
334
331
  background-color: rgb(var(--zn-panel, 255, 255, 255));
335
332
  }
333
+
334
+ /* Table Select Styles */
335
+ table tbody tr.table__row--selected {
336
+ background-color: var(--zn-color-purple-100);
337
+
338
+ &:nth-of-type(even) {
339
+ background-color: hsla(from var(--zn-color-purple-200) h s l / 0.8);
340
+ }
341
+
342
+ &:hover {
343
+ background-color: var(--zn-color-purple-200) !important;
344
+ }
345
+
346
+ td {
347
+ border-top: 1px solid var(--zn-color-purple-600) !important;
348
+ border-bottom: 1px solid var(--zn-color-purple-600) !important;
349
+ }
350
+
351
+ td:first-of-type {
352
+ border-left: 1px solid var(--zn-color-purple-600);
353
+ }
354
+
355
+ td:last-of-type {
356
+ border-right: 1px solid var(--zn-color-purple-600);
357
+ }
358
+
359
+ &:last-of-type td:first-of-type {
360
+ border-bottom-left-radius: var(--zn-border-radius-large);
361
+ }
362
+
363
+ &:last-of-type td:last-of-type {
364
+ border-bottom-right-radius: var(--zn-border-radius-large);
365
+ }
366
+
367
+ & + tr td {
368
+ border-top: 0 !important;
369
+ }
370
+
371
+ & + tr.table__row--selected td {
372
+ &:first-of-type {
373
+ border-top-left-radius: 0;
374
+ }
375
+
376
+ &:last-of-type {
377
+ border-top-right-radius: 0;
378
+ }
379
+ }
380
+
381
+ &:has(+ tr.table__row--selected) td {
382
+ border-bottom: 0 !important;
383
+ }
384
+
385
+ &:first-of-type td {
386
+ border-top: 0 !important;
387
+ }
388
+
389
+ &:has(+ tr.table__row--selected) {
390
+ td:first-of-type {
391
+ border-bottom-left-radius: 0;
392
+ }
393
+
394
+ td:last-of-type {
395
+ border-bottom-right-radius: 0;
396
+ }
397
+ }
398
+ }
399
+
400
+ table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
401
+ border-bottom-color: var(--zn-color-purple-600) !important;
402
+ }
@@ -106,7 +106,7 @@ export default class ZnDataTableFilter extends ZincElement implements ZincFormCo
106
106
  return html`
107
107
  <zn-button id="slideout-trigger" color="transparent" size="x-small" icon="filter_alt" icon-size="22"
108
108
  slot="trigger"
109
- tooltip="Open Filter">Filter
109
+ tooltip="Filter">
110
110
  </zn-button>
111
111
  <zn-slideout class="slideout-basic" trigger="slideout-trigger" label="Filters">
112
112
 
@@ -2,7 +2,7 @@ import type Quill from 'quill';
2
2
  import type Toolbar from "../toolbar/toolbar";
3
3
 
4
4
  interface AttachmentOptions {
5
- upload: (file: File) => Promise<{ path: any, url: any, filename: any }>;
5
+ upload: (file: File) => Promise<{ path: any; url: any; filename: any }>;
6
6
  onFileUploaded?: (node: HTMLElement, {url}: { url: string }) => void;
7
7
  attachmentInput?: HTMLInputElement;
8
8
  }
@@ -26,6 +26,9 @@ export default class Attachment {
26
26
  if (typeof (this._options.upload) !== "function") {
27
27
  console.warn("[Quill Attachment Module] No upload function provided");
28
28
  }
29
+ if (typeof (this._options.onFileUploaded) !== "function") {
30
+ console.warn("[Quill Attachment Module] No file uploaded function provided");
31
+ }
29
32
 
30
33
  (this._quill
31
34
  .getModule('toolbar') as Toolbar)
@@ -48,24 +51,34 @@ export default class Attachment {
48
51
  }
49
52
 
50
53
  const file = this._fileHolder.files[0];
51
- const attachmentId = generateId();
52
- const fileReader = new FileReader();
54
+ this.addAttachment(file);
55
+ }
53
56
 
54
- fileReader.addEventListener('load', () => {
55
- const base64Content = fileReader.result as string;
56
- this._insertAttachment({dataUrl: base64Content, file, id: attachmentId});
57
- }, false);
57
+ public addAttachment(file: File, dataUrl?: string) {
58
+ const attachmentId = generateId();
59
+ const insertWithDataUrl = (base64: string) => {
60
+ this._insertAttachment({ dataUrl: base64, file, id: attachmentId });
61
+ };
58
62
 
59
- if (file) {
63
+ if (dataUrl) {
64
+ insertWithDataUrl(dataUrl);
65
+ } else {
66
+ const fileReader = new FileReader();
67
+ fileReader.addEventListener('load', () => {
68
+ const base64Content = fileReader.result as string;
69
+ insertWithDataUrl(base64Content);
70
+ }, false);
60
71
  fileReader.readAsDataURL(file);
61
72
  }
62
73
 
63
- this._options.upload(file).then(({path, url}) => {
64
- this._uploadAttachment(file, url);
65
- this._updateAttachment(attachmentId, url, path);
66
- }).catch(err => {
67
- console.warn(err.message);
68
- });
74
+ if (typeof this._options.upload === 'function') {
75
+ this._options.upload(file).then(({ path, url }: { path: string; url: string }) => {
76
+ this._uploadAttachment(file, url);
77
+ this._updateAttachment(attachmentId, url, path);
78
+ }).catch((err: { message: string }) => {
79
+ console.warn(err.message);
80
+ });
81
+ }
69
82
  }
70
83
 
71
84
  private _attachmentContainer = document.createElement('div');
@@ -85,37 +98,36 @@ export default class Attachment {
85
98
  this._quill.container.appendChild(attachmentContainer);
86
99
  }
87
100
 
88
- private _insertAttachment({dataUrl, file, id}: { dataUrl: string, file: File, id: string }) {
101
+ private _insertAttachment({dataUrl, file, id}: { dataUrl: string; file: File; id: string }) {
89
102
  this._attachmentContainer.appendChild(this._createAttachment(dataUrl, file, id));
90
103
  }
91
104
 
92
105
  private _updateAttachment(id: string, url: string, filename: string) {
93
- const element = this._quill.container.querySelector(`#${id}`) as HTMLAnchorElement;
94
- if (element) {
95
- element.setAttribute('href', url);
96
- let attachmentName = element.querySelector('.attachment-name');
97
- if (attachmentName) {
98
- if (filename) {
99
- attachmentName.textContent = filename;
100
- } else {
101
- attachmentName.textContent = 'Error uploading file';
102
- }
103
- }
104
-
105
- if (typeof this._options.onFileUploaded === 'function') {
106
- this._options.onFileUploaded(element, {url});
106
+ const element = this._quill.container.querySelector(`#${id}`);
107
+ if (!element) return;
108
+
109
+ element.setAttribute('href', url);
110
+ const attachmentName = element.querySelector('.attachment-name');
111
+ if (attachmentName) {
112
+ if (filename) {
113
+ attachmentName.textContent = filename;
114
+ } else {
115
+ attachmentName.textContent = 'Error uploading file';
107
116
  }
117
+ }
108
118
 
119
+ if (this._options.onFileUploaded) {
120
+ this._options.onFileUploaded(element as HTMLElement, {url});
121
+ }
109
122
 
110
- // add the url to the hidden input
111
- const attachments = this._options.attachmentInput;
112
- if (attachments && filename) {
113
- // value should be an array of attachment names
114
- const value = attachments.value;
115
- const data = value ? JSON.parse(value) : [];
116
- data.push(filename);
117
- attachments.value = JSON.stringify(data);
118
- }
123
+ // add the url to the hidden input
124
+ const attachments = this._options.attachmentInput;
125
+ if (attachments && filename) {
126
+ // value should be an array of attachment names
127
+ const value = attachments.value;
128
+ const data: string[] = value ? JSON.parse(value) as string[] : [];
129
+ data.push(filename);
130
+ attachments.value = JSON.stringify(data);
119
131
  }
120
132
  }
121
133
 
@@ -162,9 +174,9 @@ export default class Attachment {
162
174
  if (attachments) {
163
175
  // value should be an array of attachment names
164
176
  const value = attachments.value;
165
- let attachmentName = attachment?.querySelector('.attachment-name');
166
- const data = value ? JSON.parse(value) : [];
167
- const index = data.indexOf(attachmentName ? attachmentName.textContent : '');
177
+ const attachmentName = attachment?.querySelector('.attachment-name');
178
+ const data: string[] = value ? JSON.parse(value) as string[] : [];
179
+ const index = data.indexOf(attachmentName?.textContent ? attachmentName.textContent : '');
168
180
  if (index > -1) {
169
181
  data.splice(index, 1);
170
182
  attachments.value = JSON.stringify(data);
@@ -1,6 +1,7 @@
1
1
  import './toolbar.component';
2
2
  import Quill from "quill";
3
3
  import QuillToolbar from "quill/modules/toolbar";
4
+ import type Attachment from "../attachment/attachment";
4
5
  import type DialogComponent from "../dialog/dialog.component";
5
6
  import type Emoji from "../emoji/emoji";
6
7
  import type ToolbarComponent from "./toolbar.component";
@@ -26,6 +27,7 @@ class Toolbar extends QuillToolbar {
26
27
  this.addHandler('redo', () => quill.history.redo());
27
28
  this.addHandler('undo', () => quill.history.undo());
28
29
  this.addHandler('dialog', (value: string) => this._openDialog(value));
30
+ this.addHandler('image', () => this._addImage());
29
31
 
30
32
  this._quill = quill;
31
33
  this._component = options.container;
@@ -323,6 +325,46 @@ class Toolbar extends QuillToolbar {
323
325
  const emoji = this._quill.getModule('emoji') as Emoji;
324
326
  emoji?.initPicker();
325
327
  }
328
+
329
+ private _addImage() {
330
+ const input = document.createElement('input')
331
+ input.setAttribute('type', 'file')
332
+ input.setAttribute('accept', 'image/*')
333
+ input.click()
334
+
335
+ input.onchange = () => {
336
+ const file = input.files?.[0];
337
+ if (!file?.type || !file.type.startsWith('image/')) return;
338
+
339
+ const reader = new window.FileReader();
340
+ reader.onload = () => {
341
+ const dataUrl = reader.result as string;
342
+ const selection = this._quill.getSelection(true);
343
+ const index = selection ? selection.index + selection.length : this._quill.getLength();
344
+
345
+ this._quill.insertEmbed(index, 'image', dataUrl, Quill.sources.USER);
346
+
347
+ const root = this._quill.root as HTMLElement;
348
+ const images = Array.from(root.querySelectorAll('img')) as HTMLImageElement[];
349
+ const inserted = images.reverse().find(img => img.getAttribute('src') === dataUrl) || null;
350
+ if (inserted) {
351
+ inserted.setAttribute('alt', file.name.replace(/\.[^/.]+$/, ''));
352
+ inserted.setAttribute('title', file.name);
353
+ }
354
+
355
+ this._quill.setSelection(index + 1, 0, Quill.sources.USER);
356
+ this._syncToolbarState();
357
+
358
+ try {
359
+ const attachmentModule = this._quill.getModule('attachment') as Attachment | undefined;
360
+ attachmentModule?.addAttachment(file, dataUrl);
361
+ } catch (err) {
362
+ console.warn('[Toolbar] Failed to process image with attachment handler', err);
363
+ }
364
+ };
365
+ reader.readAsDataURL(file);
366
+ }
367
+ }
326
368
  }
327
369
 
328
370
  export default Toolbar;
@@ -1,7 +1,8 @@
1
- import {property} from 'lit/decorators.js';
1
+ import {classMap} from "lit/directives/class-map.js";
2
2
  import {type CSSResultGroup, html, unsafeCSS} from 'lit';
3
+ import {HasSlotController} from "../../internal/slot";
4
+ import {property} from 'lit/decorators.js';
3
5
  import ZincElement from '../../internal/zinc-element';
4
- import {classMap} from "lit/directives/class-map.js";
5
6
 
6
7
  import styles from './empty-state.scss';
7
8
 
@@ -35,6 +36,8 @@ export default class ZnEmptyState extends ZincElement {
35
36
 
36
37
  @property({type: Boolean}) padded: boolean = false;
37
38
 
39
+ private readonly hasSlotController = new HasSlotController(this, '[default]', 'caption', 'description');
40
+
38
41
  render() {
39
42
  return html`
40
43
  <div class="${classMap({
@@ -45,16 +48,21 @@ export default class ZnEmptyState extends ZincElement {
45
48
  'empty-state--padded': this.padded
46
49
  })}">
47
50
  <div class="empty-state__wrapper">
48
- ${this.icon
49
- ? html`<zn-icon src="${this.icon}" size="48" color="${this.type ? this.type : 'primary'}"></zn-icon>`
51
+ ${this.icon || this.hasSlotController.test('icon')
52
+ ? html`
53
+ <zn-icon src="${this.icon}" size="48" color="${this.type ? this.type : 'primary'}"></zn-icon>`
50
54
  : ''}
51
- ${this.caption
55
+ ${this.caption || this.hasSlotController.test('caption')
52
56
  ? html`
53
- <div class="caption">${this.caption}</div>`
57
+ <div class="caption">
58
+ <slot name="caption">${this.caption}</slot>
59
+ </div>`
54
60
  : ''}
55
- ${this.description
61
+ ${this.description || this.hasSlotController.test('description')
56
62
  ? html`
57
- <div class="description">${this.description}</div>`
63
+ <div class="description">
64
+ <slot name="description">${this.description}</slot>
65
+ </div>`
58
66
  : ''}
59
67
  <slot></slot>
60
68
  </div>
@@ -64,6 +64,7 @@ export default class ZnIcon extends ZincElement {
64
64
  @property({reflect: true}) color: IconColor;
65
65
 
66
66
  @property({type: Boolean}) padded: boolean = false;
67
+ @property({type: Boolean}) blink: boolean = false;
67
68
 
68
69
  @property({type: Boolean}) squared: boolean = false;
69
70
 
@@ -192,6 +193,7 @@ export default class ZnIcon extends ZincElement {
192
193
  'icon--grey': this.color === "grey",
193
194
  'icon--avatar': this.library === "avatar",
194
195
  'icon--padded': this.padded,
196
+ 'icon--blink': this.blink,
195
197
  'icon--squared': this.squared,
196
198
  })}" style="${styleMap({
197
199
  '--icon-size': this.size + "px",
@@ -119,6 +119,10 @@
119
119
  border-radius: 50%;
120
120
  }
121
121
 
122
+ &--blink {
123
+ animation: blink-animation 1.5s steps(5, start) infinite;
124
+ }
125
+
122
126
  &--avatar {
123
127
  background-color: var(--avatar-color, var(--icon-color));
124
128
  color: hsl(from var(--avatar-color, var(--icon-color)) h s 95);
@@ -197,3 +201,12 @@
197
201
  vertical-align: middle;
198
202
  }
199
203
  }
204
+
205
+ @keyframes blink-animation {
206
+ 0%, 100% {
207
+ opacity: 1;
208
+ }
209
+ 50% {
210
+ opacity: 0.5;
211
+ }
212
+ }
@@ -105,6 +105,8 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
105
105
  document.addEventListener('keydown', this.escKeyHandler);
106
106
  document.addEventListener('keydown', this.submitKeyHandler);
107
107
  document.addEventListener('click', this.mouseEventHandler);
108
+ this.addEventListener('mousedown', this.captureMouseDown, {capture: true});
109
+ this.addEventListener('keydown', this.captureKeyDown, {capture: true});
108
110
  }
109
111
 
110
112
  disconnectedCallback() {
@@ -112,6 +114,8 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
112
114
  document.removeEventListener('keydown', this.escKeyHandler);
113
115
  document.removeEventListener('keydown', this.submitKeyHandler);
114
116
  document.removeEventListener('click', this.mouseEventHandler);
117
+ this.removeEventListener('mousedown', this.captureMouseDown, true);
118
+ this.removeEventListener('keydown', this.captureKeyDown, true);
115
119
  }
116
120
 
117
121
  async firstUpdated() {
@@ -157,6 +161,19 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
157
161
  }
158
162
  }
159
163
 
164
+ captureMouseDown = (e: MouseEvent) => {
165
+ if (this.disabled && !this.isEditing) {
166
+ e.stopPropagation();
167
+ }
168
+ }
169
+
170
+ captureKeyDown = (e: KeyboardEvent) => {
171
+ if (this.disabled && !this.isEditing) {
172
+ e.stopPropagation();
173
+ e.preventDefault();
174
+ }
175
+ }
176
+
160
177
  handleEditClick = (e: MouseEvent) => {
161
178
  e.preventDefault();
162
179
  e.stopPropagation();
@@ -183,6 +200,9 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
183
200
  };
184
201
 
185
202
  handleInput = (e: Event) => {
203
+ if (this.disabled || !this.isEditing) {
204
+ return;
205
+ }
186
206
  this.value = (e.target as (HTMLInputElement | HTMLSelectElement)).value;
187
207
  };
188
208
 
@@ -224,7 +244,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
224
244
  'ai--padded': this.padded,
225
245
  })}">
226
246
 
227
- <div class="ai__left" @click="${this.handleEditClick}">
247
+ <div class="ai__left" @click="${this.disabled ? undefined : this.handleEditClick}">
228
248
  ${input}
229
249
  </div>
230
250
 
@@ -232,10 +252,11 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
232
252
  ${!this.isEditing ?
233
253
  html`
234
254
  ${hasEditText ? html`
235
- <zn-button @click="${this.handleEditClick}" size="x-small" color="secondary">
255
+ <zn-button @click="${this.disabled ? undefined : this.handleEditClick}" size="x-small"
256
+ color="secondary">
236
257
  ${this.editText}
237
258
  </zn-button>` : html`
238
- <zn-button @click="${this.handleEditClick}"
259
+ <zn-button @click="${this.disabled ? undefined : this.handleEditClick}"
239
260
  class="button--edit"
240
261
  icon="edit"
241
262
  size="x-small"
@@ -133,7 +133,17 @@
133
133
  }
134
134
  }
135
135
 
136
- &--disabled {
136
+ &--disabled:not(.ai--editing) {
137
+ .ai__input::part(input), .ai__input::part(combobox) {
138
+ text-decoration: none;
139
+ caret-color: transparent;
140
+ border-radius: 0;
141
+ }
142
+
143
+ .ai__input::part(display-input) {
144
+ cursor: text;
145
+ }
146
+
137
147
  .ai__right {
138
148
  display: none !important;
139
149
  }
@@ -63,6 +63,12 @@ export default class ZnItem extends ZincElement {
63
63
  if (inlineEdit) {
64
64
  inlineEdit.setAttribute('size', this.size);
65
65
  }
66
+ this.shadowRoot?.querySelector('.item')?.classList.toggle('item--with-content', this._hasContent());
67
+ }
68
+
69
+ protected _hasContent(): boolean {
70
+ const slotContent = this.querySelectorAll(":scope > :not([slot])")?.length || 0;
71
+ return slotContent > 0 || this.innerText.length > 0 || !(this.value === undefined || this.value === null)
66
72
  }
67
73
 
68
74
  render() {
@@ -71,13 +77,15 @@ export default class ZnItem extends ZincElement {
71
77
  const headings = html`
72
78
  <div class="item__headings">
73
79
  <div class="item__caption" part="caption">${this.caption}</div>
74
- ${this.description ? html`<div class="item__description">${this.description}</div>` : ''}
80
+ ${this.description ? html`
81
+ <div class="item__description">${this.description}</div>` : ''}
75
82
  </div>`;
76
83
 
77
84
  return html`
78
85
  <div
79
- class=${classMap({
86
+ class="${classMap({
80
87
  'item': true,
88
+ 'item--with-content': this._hasContent(),
81
89
  'item--stacked': this.stacked,
82
90
  'item--edit-on-hover': this.editOnHover,
83
91
  'item--inline': this.inline,
@@ -88,7 +96,7 @@ export default class ZnItem extends ZincElement {
88
96
  'item--has-icon': hasIcon,
89
97
  'item--no-padding': this.noPadding,
90
98
  'item--align-end': this.alignEnd
91
- })}
99
+ })}"
92
100
  part="base">
93
101
 
94
102
  ${this.icon ? html`
@@ -4,7 +4,7 @@
4
4
  display: block;
5
5
  container-type: inline-size;
6
6
 
7
- --zn-item-label-max-width: 300px;
7
+ --zn-item-label-max-width: 450px;
8
8
  }
9
9
 
10
10
  .item {
@@ -14,11 +14,11 @@
14
14
  gap: var(--zn-spacing-2x-small);
15
15
 
16
16
  @include wc.container-query(null, md) {
17
- --zn-item-label-max-width: 200px;
17
+ --zn-item-label-max-width: 220px;
18
18
  }
19
19
 
20
20
  @include wc.container-query(null, smp) {
21
- --zn-item-label-max-width: 150px;
21
+ --zn-item-label-max-width: 180px;
22
22
  }
23
23
 
24
24
  @include wc.container-query(null, sm) {
@@ -29,7 +29,7 @@
29
29
  &--has-icon {
30
30
  .item__icon {
31
31
  position: relative;
32
- line-height: 33px;
32
+ line-height: var(--zn-line-height-loose);
33
33
  display: flex;
34
34
  justify-content: center;
35
35
  margin-right: var(--zn-spacing-small);
@@ -44,8 +44,6 @@
44
44
  &__header {
45
45
  display: flex;
46
46
  align-items: center;
47
- max-width: var(--zn-item-label-max-width);
48
- width: 100%;
49
47
 
50
48
  .item__caption {
51
49
  width: unset;
@@ -56,16 +54,22 @@
56
54
  &__headings {
57
55
  display: flex;
58
56
  flex-direction: column;
59
- max-width: var(--zn-item-label-max-width);
60
- width: 100%;
61
57
  flex-shrink: 0;
62
58
  }
63
59
 
60
+ &--with-content &__headings {
61
+ max-width: 45%;
62
+ @include wc.container-query(null, sm) {
63
+ max-width: unset;
64
+ }
65
+ width: var(--zn-item-label-max-width);
66
+ }
67
+
64
68
  &__content {
65
69
  width: 100%;
66
70
  display: flex;
67
71
  justify-content: space-between;
68
- line-height: 33px;
72
+ line-height: var(--zn-line-height-loose);
69
73
  }
70
74
 
71
75
  &__content-inner {
@@ -84,14 +88,9 @@
84
88
  &__caption {
85
89
  font-weight: 500;
86
90
  color: rgb(var(--zn-text-heading));
87
- line-height: var(--zn-line-height-normal);
88
- max-width: var(--zn-item-label-max-width);
91
+ line-height: var(--zn-line-height-loose);
89
92
  width: 100%;
90
93
  flex-shrink: 0;
91
-
92
- @include wc.container-query(null, sm) {
93
- line-height: var(--zn-line-height-normal);
94
- }
95
94
  }
96
95
 
97
96
  &__description {
@@ -100,9 +99,6 @@
100
99
  margin: var(--zn-spacing-2x-small) 0 0;
101
100
  font-size: var(--zn-font-size-small);
102
101
  color: rgb(var(--zn-text-color));
103
- overflow: hidden;
104
- white-space: nowrap;
105
- text-overflow: ellipsis;
106
102
  }
107
103
 
108
104
  &__action-wrapper {
@@ -159,7 +155,6 @@
159
155
  }
160
156
 
161
157
  &--inline {
162
-
163
158
  gap: var(--zn-spacing-small);
164
159
 
165
160
  .item__header {
@@ -34,6 +34,7 @@ export default class ZnPanel extends ZincElement {
34
34
  @property({type: Boolean}) flush: boolean;
35
35
  @property({attribute: 'flush-x', type: Boolean}) flushX: boolean;
36
36
  @property({attribute: 'flush-y', type: Boolean}) flushY: boolean;
37
+ @property({attribute: 'flush-footer', type: Boolean}) flushFooter: boolean;
37
38
  @property({type: Boolean}) transparent: boolean;
38
39
 
39
40
  protected firstUpdated(_changedProperties: PropertyValues) {
@@ -81,6 +82,7 @@ export default class ZnPanel extends ZincElement {
81
82
  'panel--flush': this.flush || this.tabbed,
82
83
  'panel--flush-x': this.flushX,
83
84
  'panel--flush-y': this.flushY,
85
+ 'panel--flush-footer': this.flushFooter,
84
86
  'panel--tabbed': this.tabbed,
85
87
  'panel--transparent': this.transparent,
86
88
  'panel--has-actions': hasActionSlot,
@@ -120,6 +120,12 @@
120
120
  }
121
121
  }
122
122
 
123
+ &--flush-footer {
124
+ .panel__footer {
125
+ padding: 0;
126
+ }
127
+ }
128
+
123
129
  &--transparent {
124
130
  .panel__header {
125
131
  padding-left: 0;