@kubex/zinc 1.0.19 → 1.0.21

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.
@@ -0,0 +1,61 @@
1
+ ---
2
+ meta:
3
+ title: Input Group
4
+ description: A wrapper component that groups inputs and selects visually.
5
+ layout: component
6
+ ---
7
+
8
+ Input Groups allow you to group multiple form controls together into a single visual unit.
9
+
10
+ ```html:preview
11
+ <zn-input-group>
12
+ <zn-input placeholder="First Name"></zn-input>
13
+ <zn-input placeholder="Last Name"></zn-input>
14
+ </zn-input-group>
15
+ ```
16
+
17
+ ## Examples
18
+
19
+ ### Mixed Inputs and Selects
20
+
21
+ You can combine inputs and selects.
22
+
23
+ ```html:preview
24
+ <zn-input-group>
25
+ <zn-select value="http://" style="width: 125px;">
26
+ <zn-option value="http://">http://</zn-option>
27
+ <zn-option value="https://">https://</zn-option>
28
+ </zn-select>
29
+ <zn-input placeholder="example.com"></zn-input>
30
+ </zn-input-group>
31
+ ```
32
+
33
+ ### Multiple Inputs
34
+
35
+ ```html:preview
36
+ <zn-input-group>
37
+ <zn-input placeholder="City"></zn-input>
38
+ <zn-input placeholder="State"></zn-input>
39
+ <zn-input placeholder="Zip"></zn-input>
40
+ </zn-input-group>
41
+ ```
42
+
43
+ ### Labeled Group
44
+
45
+ Use the `label` attribute to add a label to the input group.
46
+
47
+ ```html:preview
48
+ <zn-input-group label="Personal Information">
49
+ <zn-input placeholder="First Name"></zn-input>
50
+ <zn-input placeholder="Last Name"></zn-input>
51
+ </zn-input-group>
52
+ ```
53
+
54
+ ### Input with Submit Button
55
+
56
+ ```html:preview
57
+ <zn-input-group>
58
+ <zn-input placeholder="Email Address" type="email"></zn-input>
59
+ <zn-button>Subscribe</zn-button>
60
+ </zn-input-group>
61
+ ```
@@ -0,0 +1,53 @@
1
+ ---
2
+ meta:
3
+ title: Translations
4
+ description: A component for managing multi-language text input.
5
+ layout: component
6
+ ---
7
+
8
+ The Translations component allows users to enter text for multiple languages, with a tabbed interface for switching
9
+ between languages.
10
+
11
+ ```html:preview
12
+ <zn-translations></zn-translations>
13
+ ```
14
+
15
+ ## Attributes
16
+
17
+ | Attribute | Type | Description |
18
+ |-------------|----------|---------------------------------------------------------------------------------------------|
19
+ | `languages` | `Object` | A key-value pair of language codes and names (e.g., `{ "en": "English", "fr": "French" }`). |
20
+ | `values` | `Object` | A key-value pair of language codes and translations. |
21
+ | `value` | `String` | A JSON string representing the current translations. |
22
+
23
+ ## Examples
24
+
25
+ ### With Label
26
+
27
+ ```html:preview
28
+
29
+ <zn-translations
30
+ label="Description"
31
+ languages='{&quot;en&quot;:&quot;English&quot;,&quot;fr&quot;:&quot;French&quot;,&quot;de&quot;:&quot;German&quot;}'>
32
+ </zn-translations>
33
+ ```
34
+
35
+ ### Custom Languages
36
+
37
+ You can provide a custom list of available languages via the `languages` property.
38
+
39
+ ```html:preview
40
+
41
+ <zn-translations
42
+ languages='{&quot;en&quot;:&quot;English&quot;,&quot;fr&quot;:&quot;French&quot;,&quot;de&quot;:&quot;German&quot;,&quot;es&quot;:&quot;Spanish&quot;,&quot;it&quot;:&quot;Italian&quot;,&quot;pt&quot;:&quot;Portuguese&quot;,&quot;ru&quot;:&quot;Russian&quot;,&quot;zh&quot;:&quot;Chinese&quot;,&quot;ja&quot;:&quot;Japanese&quot;,&quot;ar&quot;:&quot;Arabic&quot;}'></zn-translations>
43
+ ```
44
+
45
+ ### Pre-filled Values
46
+
47
+ ```html:preview
48
+
49
+ <zn-translations
50
+ languages='{&quot;en&quot;:&quot;English&quot;,&quot;fr&quot;:&quot;French&quot;}'
51
+ values='{&quot;en&quot;:&quot;Hello World&quot;,&quot;fr&quot;:&quot;Bonjour le monde&quot;}'
52
+ ></zn-translations>
53
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -1,4 +1,4 @@
1
- import {type CSSResultGroup, html, unsafeCSS} from 'lit';
1
+ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
2
2
  import {property, state} from "lit/decorators.js";
3
3
  import ZincElement from '../../internal/zinc-element';
4
4
  import type {ZnChangeEvent} from "../../events/zn-change";
@@ -27,13 +27,13 @@ interface AudioFile {
27
27
  export default class ZnAudioSelect extends ZincElement {
28
28
  static styles: CSSResultGroup = unsafeCSS(styles);
29
29
 
30
- private _selectedUrl: string = '';
31
30
  @state() private _isPlaying: boolean = false;
32
31
  private readonly _audio: HTMLAudioElement;
33
32
 
33
+ @property() value = '';
34
34
  @property() label = '';
35
35
  @property() placeholder = '';
36
- @property({type: Object}) files: AudioFile[];
36
+ @property({type: Object}) files: AudioFile[] = [];
37
37
 
38
38
  constructor() {
39
39
  super();
@@ -49,10 +49,21 @@ export default class ZnAudioSelect extends ZincElement {
49
49
 
50
50
  disconnectedCallback() {
51
51
  super.disconnectedCallback();
52
- this._stopAudio();
52
+ this.stopAudio();
53
53
  }
54
54
 
55
- _stopAudio() {
55
+ updated(changedProperties: PropertyValues) {
56
+ super.updated(changedProperties);
57
+
58
+ if (changedProperties.has('value')) {
59
+ this.stopAudio();
60
+ if (this.value) {
61
+ this._audio.src = this.value;
62
+ }
63
+ }
64
+ }
65
+
66
+ stopAudio() {
56
67
  if (this._audio) {
57
68
  this._audio.pause();
58
69
  this._audio.currentTime = 0;
@@ -65,20 +76,13 @@ export default class ZnAudioSelect extends ZincElement {
65
76
  return;
66
77
  }
67
78
 
68
- const url = (e.target as HTMLSelectElement).value;
69
- this._selectedUrl = url;
70
-
71
- this._stopAudio();
72
-
73
- if (url) {
74
- this._audio.src = url;
75
- }
79
+ this.value = (e.target as HTMLSelectElement).value;
76
80
  }
77
81
 
78
82
  togglePreview(e: CustomEvent) {
79
83
  e.stopPropagation();
80
84
 
81
- if (!this._selectedUrl) {
85
+ if (!this.value) {
82
86
  e.preventDefault();
83
87
  this._isPlaying = false;
84
88
  this.requestUpdate();
@@ -103,26 +107,25 @@ export default class ZnAudioSelect extends ZincElement {
103
107
 
104
108
  render() {
105
109
  return html`
106
- <div class="container">
107
- <div class="select-wrapper">
108
- <zn-select label="${this.label}" @zn-change="${this.handleSelectChange}">
109
- <zn-checkbox
110
- slot="prefix"
111
- checked-icon="pause"
112
- unchecked-icon="play_arrow"
113
- checked-color="primary"
114
- unchecked-color="secondary"
115
- ?checked="${this._isPlaying}"
116
- @zn-change="${this.togglePreview}">
117
- </zn-checkbox>
118
-
119
- <zn-option value="" disabled selected>${this.placeholder || 'Select audio'}</zn-option>
120
- ${this.files.map(file => html`
121
- <zn-option value="${file.url}">${file.name}</zn-option>
122
- `)}
123
- </zn-select>
124
- </div>
125
- </div>
110
+ <zn-select
111
+ label="${this.label}"
112
+ value="${this.value}"
113
+ @zn-change="${this.handleSelectChange}">
114
+ <zn-checkbox
115
+ slot="prefix"
116
+ checked-icon="pause"
117
+ unchecked-icon="play_arrow"
118
+ checked-color="primary"
119
+ unchecked-color="secondary"
120
+ ?checked="${this._isPlaying}"
121
+ @zn-change="${this.togglePreview}">
122
+ </zn-checkbox>
123
+
124
+ <zn-option value="" disabled>${this.placeholder || 'Select audio'}</zn-option>
125
+ ${this.files.map(file => html`
126
+ <zn-option value="${file.url}">${file.name}</zn-option>
127
+ `)}
128
+ </zn-select>
126
129
  `;
127
130
  }
128
131
  }
@@ -49,7 +49,7 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
49
49
  static dependencies = {'zn-icon': ZnIcon};
50
50
 
51
51
  private readonly formControlController = new FormControlController(this, {
52
- value: (control: ZnCheckbox) => (control.checked ? control.value || 'on' : undefined),
52
+ value: (control: ZnCheckbox) => (control.checked ? control.value || 'on' : control.uncheckedValue || undefined),
53
53
  defaultValue: (control: ZnCheckbox) => control.defaultChecked,
54
54
  setValue: (control: ZnCheckbox, checked: boolean) => (control.checked = checked)
55
55
  });
@@ -67,6 +67,9 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
67
67
  /** The current value of the checkbox, submitted as a name/value pair with form data. */
68
68
  @property() value: string;
69
69
 
70
+ /** The unchecked value of the checkbox, submitted as a name/value pair with form data. */
71
+ @property({attribute: 'unchecked-value', reflect: true}) uncheckedValue: string;
72
+
70
73
  /** The checkbox's size. */
71
74
  @property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium';
72
75
 
@@ -101,6 +104,9 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
101
104
  /** Makes the checkbox a required field. */
102
105
  @property({type: Boolean, reflect: true}) required = false;
103
106
 
107
+ /** Submits the form when checkbox is clicked. */
108
+ @property({attribute: 'submit-on-click', type: Boolean, reflect: true}) submitOnClick: boolean = false;
109
+
104
110
  /** The checkbox's help text. If you need to display HTML, use the `description` slot instead. */
105
111
  @property({attribute: 'description'}) description = '';
106
112
 
@@ -149,6 +155,11 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
149
155
  this.checked = !this.checked;
150
156
  this.indeterminate = false;
151
157
  this.emit('zn-change');
158
+
159
+ const form = this.getForm();
160
+ if (this.submitOnClick && form) {
161
+ form.requestSubmit();
162
+ }
152
163
  }
153
164
 
154
165
  private handleBlur() {
@@ -44,6 +44,7 @@ interface Cell {
44
44
  sortValue?: string;
45
45
  uri?: string;
46
46
  target?: string;
47
+ copyable?: boolean;
47
48
  }
48
49
 
49
50
  interface Row {
@@ -925,6 +926,12 @@ export default class ZnDataTable extends ZincElement {
925
926
  <zn-icon src="${src}" color="${color}"></zn-icon> ${content}`;
926
927
  }
927
928
 
929
+ if (data.copyable) {
930
+ return html`
931
+ ${content}
932
+ <zn-copy-button value="${data.text}"></zn-copy-button>`;
933
+ }
934
+
928
935
  return content;
929
936
  }
930
937
 
@@ -140,7 +140,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
140
140
  mouseEventHandler = (e: MouseEvent) => {
141
141
  if (this.isEditing && !this.contains(e.target as Node)) {
142
142
  this.isEditing = false;
143
- this.value = this.defaultValue;
143
+ this.emit('zn-change');
144
144
  this.input.blur();
145
145
  }
146
146
  };
@@ -156,6 +156,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
156
156
  submitKeyHandler = (e: KeyboardEvent) => {
157
157
  if (e.key === 'Enter' && this.isEditing) {
158
158
  this.isEditing = false;
159
+ this.emit('zn-submit', {detail: {value: this.value, element: this}});
159
160
  this.formControlController.submit();
160
161
  this.input.blur();
161
162
  }
@@ -186,6 +187,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
186
187
  handleSubmitClick = (e: MouseEvent) => {
187
188
  e.preventDefault();
188
189
  this.isEditing = false;
190
+ this.emit('zn-submit', {detail: {value: this.value, element: this}});
189
191
  this.formControlController.submit();
190
192
  };
191
193
 
@@ -204,6 +206,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
204
206
  return;
205
207
  }
206
208
  this.value = (e.target as (HTMLInputElement | HTMLSelectElement)).value;
209
+ this.emit('zn-input');
207
210
  };
208
211
 
209
212
  protected render() {
@@ -0,0 +1,12 @@
1
+ import ZnInputGroup from './input-group.component';
2
+
3
+ export * from './input-group.component';
4
+ export default ZnInputGroup;
5
+
6
+ ZnInputGroup.define('zn-input-group');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-input-group': ZnInputGroup;
11
+ }
12
+ }
@@ -0,0 +1,73 @@
1
+ import {classMap} from 'lit/directives/class-map.js';
2
+ import {type CSSResultGroup, html, unsafeCSS} from 'lit';
3
+ import {HasSlotController} from '../../internal/slot';
4
+ import {property, query} from 'lit/decorators.js';
5
+ import ZincElement from '../../internal/zinc-element';
6
+
7
+ import styles from './input-group.scss';
8
+
9
+ /**
10
+ * @summary A wrapper component that groups inputs and selects visually.
11
+ * @documentation https://zinc.style/components/input-group
12
+ * @status experimental
13
+ * @since 1.0
14
+ *
15
+ * @slot - The default slot for inputs and selects.
16
+ * @slot label - The input group's label. Alternatively, you can use the `label` attribute.
17
+ *
18
+ * @csspart base - The component's base wrapper.
19
+ * @csspart form-control - The form control wrapper.
20
+ * @csspart form-control-label - The label's wrapper.
21
+ * @csspart form-control-input - The input group container.
22
+ */
23
+ export default class ZnInputGroup extends ZincElement {
24
+ static styles: CSSResultGroup = unsafeCSS(styles);
25
+
26
+ private readonly hasSlotController = new HasSlotController(this, 'label');
27
+
28
+ @query('slot:not([name])') defaultSlot: HTMLSlotElement;
29
+
30
+ /** The input group's label. If you need to display HTML, use the `label` slot. */
31
+ @property() label = '';
32
+
33
+ private handleSlotChange() {
34
+ const slottedElements = [...this.defaultSlot.assignedElements({flatten: true})] as HTMLElement[];
35
+
36
+ // Filter for inputs and selects
37
+ const supportedTags = ['ZN-INPUT', 'ZN-SELECT', 'ZN-BUTTON'];
38
+ const controls = slottedElements.filter(el => supportedTags.includes(el.tagName));
39
+
40
+ controls.forEach(el => {
41
+ const index = controls.indexOf(el);
42
+
43
+ el.toggleAttribute('data-zn-input-group__input', true);
44
+ el.toggleAttribute('data-zn-input-group__input--first', index === 0);
45
+ el.toggleAttribute('data-zn-input-group__input--inner', index > 0 && index < controls.length - 1);
46
+ el.toggleAttribute('data-zn-input-group__input--last', index === controls.length - 1);
47
+ });
48
+ }
49
+
50
+ render() {
51
+ const hasLabelSlot = this.hasSlotController.test('label');
52
+ const hasLabel = this.label ? true : hasLabelSlot;
53
+
54
+ return html`
55
+ <div part="form-control"
56
+ class="${classMap({
57
+ 'form-control': true,
58
+ 'form-control--has-label': hasLabel,
59
+ })}">
60
+
61
+ <label part="form-control-label"
62
+ id="label"
63
+ class="form-control__label"
64
+ aria-hidden=${hasLabel ? 'false' : 'true'}>
65
+ <slot name="label">${this.label}</slot>
66
+ </label>
67
+
68
+ <div part="base form-control-input" class="input-group" role="group" aria-labelledby="label">
69
+ <slot @slotchange=${this.handleSlotChange}></slot>
70
+ </div>
71
+ </div>`;
72
+ }
73
+ }
@@ -0,0 +1,65 @@
1
+ @use "../../wc";
2
+ @use "../../form-control";
3
+
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .input-group {
9
+ display: flex;
10
+ flex-wrap: nowrap;
11
+ align-items: center;
12
+ width: 100%;
13
+ }
14
+
15
+ /* Common styles for slotted inputs */
16
+ ::slotted([data-zn-input-group__input]) {
17
+ /* Overlap borders */
18
+ margin-inline-end: -1px;
19
+
20
+ /* Ensure focus ring is above */
21
+ position: relative;
22
+ z-index: 1;
23
+ }
24
+
25
+ ::slotted([data-zn-input-group__input]:focus-within) {
26
+ z-index: 2;
27
+ }
28
+
29
+ ::slotted([data-zn-input-group__input--first]) {
30
+ --zn-input-border-radius-x-small: var(--zn-border-radius-medium) 0 0 var(--zn-border-radius-medium);
31
+ --zn-input-border-radius-small: var(--zn-border-radius-medium) 0 0 var(--zn-border-radius-medium);
32
+ --zn-input-border-radius-medium: var(--zn-border-radius-medium) 0 0 var(--zn-border-radius-medium);
33
+ --zn-input-border-radius-large: var(--zn-border-radius-medium) 0 0 var(--zn-border-radius-medium);
34
+ }
35
+
36
+ ::slotted([data-zn-input-group__input--inner]) {
37
+ --zn-input-border-radius-x-small: 0;
38
+ --zn-input-border-radius-small: 0;
39
+ --zn-input-border-radius-medium: 0;
40
+ --zn-input-border-radius-large: 0;
41
+ }
42
+
43
+ ::slotted([data-zn-input-group__input--last]) {
44
+ --zn-input-border-radius-x-small: 0 var(--zn-border-radius-medium) var(--zn-border-radius-medium) 0;
45
+ --zn-input-border-radius-small: 0 var(--zn-border-radius-medium) var(--zn-border-radius-medium) 0;
46
+ --zn-input-border-radius-medium: 0 var(--zn-border-radius-medium) var(--zn-border-radius-medium) 0;
47
+ --zn-input-border-radius-large: 0 var(--zn-border-radius-medium) var(--zn-border-radius-medium) 0;
48
+
49
+ margin-inline-end: 0;
50
+ }
51
+
52
+ /* Button overrides */
53
+ ::slotted(zn-button[data-zn-input-group__input])::part(base) {
54
+ border-radius: 0;
55
+ }
56
+
57
+ ::slotted(zn-button[data-zn-input-group__input--first])::part(base) {
58
+ border-start-start-radius: var(--zn-border-radius-medium);
59
+ border-end-start-radius: var(--zn-border-radius-medium);
60
+ }
61
+
62
+ ::slotted(zn-button[data-zn-input-group__input--last])::part(base) {
63
+ border-start-end-radius: var(--zn-border-radius-medium);
64
+ border-end-end-radius: var(--zn-border-radius-medium);
65
+ }
@@ -0,0 +1,98 @@
1
+ import '../../components/input/index';
2
+ import '../../components/select/index';
3
+ import '../../components/button/index';
4
+ import './index';
5
+ import {expect, fixture, html} from '@open-wc/testing';
6
+ import type ZnInputGroup from './input-group.component';
7
+
8
+ // We need to ensure components are defined for the test
9
+ customElements.define('zn-input-group-test-input', class extends HTMLElement {
10
+ });
11
+
12
+ describe('<zn-input-group>', () => {
13
+ it('should render a component', async () => {
14
+ const el = await fixture(html`
15
+ <zn-input-group></zn-input-group> `);
16
+ expect(el).to.exist;
17
+ });
18
+
19
+ it('should apply data attributes to slotted inputs', async () => {
20
+ const el = await fixture<ZnInputGroup>(html`
21
+ <zn-input-group>
22
+ <zn-input></zn-input>
23
+ <zn-input></zn-input>
24
+ <zn-input></zn-input>
25
+ </zn-input-group>
26
+ `);
27
+
28
+ await el.updateComplete;
29
+ await new Promise(r => setTimeout(r, 100));
30
+
31
+ const inputs = el.querySelectorAll('zn-input');
32
+ expect(inputs[0]).to.have.attribute('data-zn-input-group__input');
33
+ expect(inputs[0]).to.have.attribute('data-zn-input-group__input--first');
34
+
35
+ expect(inputs[1]).to.have.attribute('data-zn-input-group__input');
36
+ expect(inputs[1]).to.have.attribute('data-zn-input-group__input--inner');
37
+
38
+ expect(inputs[2]).to.have.attribute('data-zn-input-group__input');
39
+ expect(inputs[2]).to.have.attribute('data-zn-input-group__input--last');
40
+ });
41
+
42
+ it('should handle mixed inputs and selects', async () => {
43
+ const el = await fixture<ZnInputGroup>(html`
44
+ <zn-input-group>
45
+ <zn-input></zn-input>
46
+ <zn-select></zn-select>
47
+ </zn-input-group>
48
+ `);
49
+
50
+ await new Promise(r => setTimeout(r, 100));
51
+
52
+ const controls = el.querySelectorAll('zn-input, zn-select');
53
+ expect(controls[0]).to.have.attribute('data-zn-input-group__input--first');
54
+ expect(controls[1]).to.have.attribute('data-zn-input-group__input--last');
55
+ });
56
+
57
+ it('should handle button at the end', async () => {
58
+ const el = await fixture<ZnInputGroup>(html`
59
+ <zn-input-group>
60
+ <zn-input></zn-input>
61
+ <zn-button>Submit</zn-button>
62
+ </zn-input-group>
63
+ `);
64
+
65
+ await new Promise(r => setTimeout(r, 100));
66
+
67
+ const input = el.querySelector('zn-input');
68
+ const button = el.querySelector('zn-button');
69
+
70
+ expect(input).to.have.attribute('data-zn-input-group__input--first');
71
+ expect(button).to.have.attribute('data-zn-input-group__input--last');
72
+ expect(button).to.have.attribute('data-zn-input-group__input');
73
+ });
74
+
75
+ it('should render a label when the label attribute is set', async () => {
76
+ const el = await fixture<ZnInputGroup>(html`
77
+ <zn-input-group label="Test Label"></zn-input-group>
78
+ `);
79
+ await el.updateComplete;
80
+ const label = el.shadowRoot?.querySelector('.form-control__label');
81
+ expect(label).to.exist;
82
+ expect(label).to.contain.text('Test Label');
83
+ });
84
+
85
+ it('should render a label from the label slot', async () => {
86
+ const el = await fixture<ZnInputGroup>(html`
87
+ <zn-input-group>
88
+ <span slot="label">Slot Label</span>
89
+ </zn-input-group>
90
+ `);
91
+ await el.updateComplete;
92
+ const label = el.shadowRoot?.querySelector('.form-control__label');
93
+ expect(label).to.exist;
94
+ const slot = label?.querySelector('slot');
95
+ expect(slot).to.exist;
96
+ // Note: checking slot content in shadow DOM is tricky, but existence of label wrapper is enough to prove logic works
97
+ });
98
+ });
@@ -51,6 +51,9 @@ export default class ZnItem extends ZincElement {
51
51
  // align items to the right
52
52
  @property({type: Boolean, attribute: 'align-end'}) alignEnd: boolean;
53
53
 
54
+ // align items in the center
55
+ @property({type: Boolean, attribute: 'align-center'}) alignCenter: boolean;
56
+
54
57
  connectedCallback() {
55
58
  super.connectedCallback();
56
59
  this.setAttribute('role', 'listitem');
@@ -95,7 +98,8 @@ export default class ZnItem extends ZincElement {
95
98
  'item--large': this.size === 'large',
96
99
  'item--has-icon': hasIcon,
97
100
  'item--no-padding': this.noPadding,
98
- 'item--align-end': this.alignEnd
101
+ 'item--align-end': this.alignEnd,
102
+ 'item--align-center': this.alignCenter
99
103
  })}"
100
104
  part="base">
101
105
 
@@ -41,6 +41,10 @@
41
41
  text-align: right;
42
42
  }
43
43
 
44
+ &--align-center {
45
+ align-items: center;
46
+ }
47
+
44
48
  &__header {
45
49
  display: flex;
46
50
  align-items: center;