@kubex/zinc 1.0.104 → 1.0.106

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.
@@ -89,6 +89,31 @@ The group scales well with multiple translation inputs under a single toggle.
89
89
  </zn-translation-group>
90
90
  ```
91
91
 
92
+ ### Language Overflow
93
+
94
+ When there are more languages than will fit across the header, the extras collapse into a chevron dropdown beside the
95
+ visible buttons. The dropdown sits before the `+` add-language dropdown and selects the same way — picking a language
96
+ switches every child translation input in the group.
97
+
98
+ ```html:preview
99
+ <div style="max-width: 480px;">
100
+ <zn-translation-group
101
+ label="Release Notes"
102
+ languages='{"en":"English","ar":"Arabic","de":"German","es":"Spanish","fr":"French","it":"Italian","ja":"Japanese","ko":"Korean","pt":"Portuguese","ru":"Russian","tr":"Turkish","zh-hans":"Simplified Chinese"}'>
103
+ <zn-translations
104
+ label="Headline"
105
+ name="headline"
106
+ values='{"en":"Now shipping","ar":"متاح الآن","de":"Jetzt verfügbar","es":"Ya disponible","fr":"Disponible dès maintenant","it":"Disponibile ora","ja":"発売開始","ko":"출시됨","pt":"Disponível agora","ru":"Уже в продаже","tr":"Şimdi mevcut","zh-hans":"现已发布"}'
107
+ ></zn-translations>
108
+ <zn-translations
109
+ label="Summary"
110
+ name="summary"
111
+ values='{"en":"Tap through the release highlights.","ar":"تصفح أبرز ميزات الإصدار.","de":"Highlights der Version ansehen.","es":"Consulta lo más destacado.","fr":"Découvrez les nouveautés.","it":"Scopri le novità.","ja":"リリースのハイライトをご覧ください。","ko":"업데이트 주요 내용 보기.","pt":"Veja os destaques.","ru":"Ознакомьтесь с обновлениями.","tr":"Sürüm önemli noktaları.","zh-hans":"浏览版本亮点。"}'
112
+ ></zn-translations>
113
+ </zn-translation-group>
114
+ </div>
115
+ ```
116
+
92
117
  ### Flush Layout
93
118
 
94
119
  Remove body padding for a more compact appearance using the `flush` attribute.
@@ -141,7 +141,8 @@ Use the `expand` slot to add custom buttons or actions.
141
141
 
142
142
  ### Many Languages
143
143
 
144
- The component handles many languages gracefully with scrollable tabs.
144
+ When the language buttons don't all fit on one line, the overflow collapses into a chevron dropdown so the row stays a
145
+ single line. Resizing the container re-measures and expands the buttons back out when space allows.
145
146
 
146
147
  ```html:preview
147
148
  <zn-translations
@@ -151,6 +152,21 @@ The component handles many languages gracefully with scrollable tabs.
151
152
  ></zn-translations>
152
153
  ```
153
154
 
155
+ ### Constrained Width
156
+
157
+ Placing the component in a narrow container forces the chevron overflow to kick in. Selecting a language from the
158
+ dropdown switches the active language just like clicking a visible button.
159
+
160
+ ```html:preview
161
+ <div style="max-width: 320px;">
162
+ <zn-translations
163
+ label="Narrow Container"
164
+ languages='{"en":"English","fr":"French","de":"German","es":"Spanish","it":"Italian","pt":"Portuguese","ja":"Japanese","ko":"Korean","zh-hans":"Simplified Chinese"}'
165
+ values='{"en":"Hello","fr":"Bonjour","de":"Hallo","es":"Hola","it":"Ciao","pt":"Olá","ja":"こんにちは","ko":"안녕하세요","zh-hans":"你好"}'
166
+ ></zn-translations>
167
+ </div>
168
+ ```
169
+
154
170
  ### RTL Language Support
155
171
 
156
172
  The component automatically detects and applies right-to-left text direction for Arabic and Hebrew languages.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.0.104",
3
+ "version": "1.0.106",
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",
@@ -79,6 +79,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
79
79
  @property() step: number | 'any';
80
80
 
81
81
  @property({ attribute: "input-type" }) inputType: 'select' | 'text' | 'data-select' | 'number' | 'textarea' = 'text';
82
+ @property({ attribute: "textarea-rows", type: Number }) textareaRows: 1;
82
83
 
83
84
  @property({ type: Object }) options: { [key: string]: string } = {};
84
85
 
@@ -387,7 +388,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
387
388
  name="${this.name}"
388
389
  size="${this.size}"
389
390
  resize="auto"
390
- rows="1"
391
+ rows="${this.textareaRows}"
391
392
  .value="${this.value}"
392
393
  placeholder="${this.placeholder}"
393
394
  pattern=${ifDefined(this.pattern)}
@@ -14,7 +14,7 @@
14
14
  gap: 10px;
15
15
 
16
16
  &:not(.ai--editing) {
17
- .ai__input::part(base), .ai__input::part(combobox) {
17
+ .ai__input::part(base), .ai__input::part(combobox), .ai__input::part(textarea) {
18
18
  background-color: transparent;
19
19
  border-color: transparent;
20
20
  box-shadow: none;
@@ -30,7 +30,7 @@
30
30
  color: rgb(var(--zn-text));
31
31
  }
32
32
 
33
- .ai__input::part(input), .ai__input::part(combobox) {
33
+ .ai__input::part(input), .ai__input::part(combobox), .ai__input::part(textarea) {
34
34
  padding: 0;
35
35
  color: rgb(var(--zn-text));
36
36
  text-decoration: underline;
@@ -39,6 +39,12 @@
39
39
  text-underline-offset: 3px;
40
40
  }
41
41
 
42
+ .ai__input::part(textarea) {
43
+ padding-top: 8px;
44
+ box-shadow: none;
45
+ text-decoration:none;
46
+ }
47
+
42
48
  .ai__input::part(expand-icon) {
43
49
  display: none;
44
50
  }
@@ -150,7 +156,7 @@
150
156
  }
151
157
 
152
158
  &--disabled:not(.ai--editing) {
153
- .ai__input::part(input), .ai__input::part(combobox) {
159
+ .ai__input::part(input), .ai__input::part(combobox), .ai__input::part(textarea) {
154
160
  text-decoration: none;
155
161
  caret-color: transparent;
156
162
  border-radius: 0;
@@ -170,12 +176,4 @@ zn-textarea::part(textarea) {
170
176
  resize: none;
171
177
  }
172
178
 
173
- .ai:not(.ai--editing) {
174
- zn-textarea::part(textarea) {
175
- box-shadow: none;
176
- padding-left: 0;
177
- padding-right: 0;
178
- min-height: 35px;
179
- }
180
- }
181
179
 
@@ -3,18 +3,19 @@ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
3
  import {defaultValue} from "../../internal/default-value";
4
4
  import {FormControlController} from "../../internal/form";
5
5
  import {HasSlotController} from "../../internal/slot";
6
+ import {ifDefined} from "lit/directives/if-defined.js";
6
7
  import {marked} from "marked";
7
- import {property, query, state} from 'lit/decorators.js';
8
+ import {property, query} from 'lit/decorators.js';
8
9
  import {watch} from "../../internal/watch";
9
- import ZincElement from '../../internal/zinc-element';
10
10
  import type {ZincFormControl} from '../../internal/zinc-element';
11
11
  import type ZnTextarea from "../textarea";
12
12
 
13
13
  import styles from './markdown-editor.scss';
14
+ import ZnPanel from "../panel/panel.component";
14
15
 
15
16
  type ViewMode = 'editor' | 'split' | 'preview';
16
17
 
17
- const VIEW_MODES: {mode: ViewMode; icon: string; label: string}[] = [
18
+ const VIEW_MODES: { mode: ViewMode; icon: string; label: string }[] = [
18
19
  {mode: 'editor', icon: 'edit_note', label: 'Editor'},
19
20
  {mode: 'split', icon: 'vertical_split', label: 'Split'},
20
21
  {mode: 'preview', icon: 'visibility', label: 'Preview'},
@@ -42,13 +43,13 @@ const VIEW_MODES: {mode: ViewMode; icon: string; label: string}[] = [
42
43
  * @csspart editor - The textarea wrapper.
43
44
  * @csspart preview - The rendered markdown preview.
44
45
  */
45
- export default class ZnMarkdownEditor extends ZincElement implements ZincFormControl {
46
- static styles: CSSResultGroup = unsafeCSS(styles);
46
+ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl {
47
+ static styles: CSSResultGroup = [ZnPanel.styles as CSSResultGroup, unsafeCSS(styles)];
47
48
 
48
49
  private readonly formControlController = new FormControlController(this, {
49
50
  assumeInteractionOn: ['zn-input', 'zn-change'],
50
51
  });
51
- private readonly hasSlotController = new HasSlotController(this, 'label', 'help-text');
52
+ private readonly markdownSlotController = new HasSlotController(this, 'label', 'help-text', 'actions', 'footer');
52
53
 
53
54
  private debounceTimer: ReturnType<typeof setTimeout> | null = null;
54
55
 
@@ -95,7 +96,7 @@ export default class ZnMarkdownEditor extends ZincElement implements ZincFormCon
95
96
  @property({type: Boolean, reflect: true}) disabled = false;
96
97
 
97
98
  /** Whether the editor is currently expanded to cover its containing positioned ancestor. */
98
- @state() expanded = false;
99
+ @property({type: Boolean, reflect: true}) expanded = false;
99
100
 
100
101
  get validity(): ValidityState {
101
102
  return this.textarea?.validity;
@@ -151,15 +152,18 @@ export default class ZnMarkdownEditor extends ZincElement implements ZincFormCon
151
152
 
152
153
  const stored = this.readStoredViewMode();
153
154
  if (stored) this.viewMode = stored;
155
+
156
+ this.addEventListener('toggle', this.handleExpandToggle as EventListener);
154
157
  }
155
158
 
156
159
  disconnectedCallback() {
157
160
  super.disconnectedCallback();
158
161
  if (this.debounceTimer) clearTimeout(this.debounceTimer);
162
+ this.removeEventListener('toggle', this.handleExpandToggle as EventListener);
159
163
  }
160
164
 
161
165
  protected firstUpdated(_changedProperties: PropertyValues) {
162
- void _changedProperties;
166
+ super.firstUpdated(_changedProperties);
163
167
  this.formControlController.updateValidity();
164
168
  if (this.viewMode !== 'editor') this.renderPreview();
165
169
  }
@@ -240,70 +244,109 @@ export default class ZnMarkdownEditor extends ZincElement implements ZincFormCon
240
244
  }
241
245
 
242
246
  render() {
243
- const hasLabelSlot = this.hasSlotController.test('label');
244
- const hasHelpSlot = this.hasSlotController.test('help-text');
247
+ const hasLabelSlot = this.markdownSlotController.test('label');
248
+ const hasHelpSlot = this.markdownSlotController.test('help-text');
249
+ const hasActionSlot = this.markdownSlotController.test('actions');
250
+ const hasFooterSlot = this.markdownSlotController.test('footer');
245
251
  const hasLabel = !!this.label || hasLabelSlot;
246
252
  const hasHelpText = !!this.helpText || hasHelpSlot;
253
+ const hasHeader = !!this.caption || hasActionSlot;
247
254
  const showEditor = this.viewMode === 'editor' || this.viewMode === 'split';
248
255
  const showPreview = this.viewMode === 'preview' || this.viewMode === 'split';
249
256
 
250
257
  return html`
251
- <div part="base"
252
- class=${classMap({
253
- 'markdown-editor': true,
254
- 'markdown-editor--expanded': this.expanded,
255
- 'markdown-editor--split': this.viewMode === 'split',
256
- 'markdown-editor--preview-only': this.viewMode === 'preview',
257
- })}>
258
- ${hasLabel ? html`
259
- <label class="markdown-editor__label">
260
- <slot name="label">${this.label}</slot>
261
- </label>` : ''}
262
-
263
- <div part="toolbar" class="markdown-editor__toolbar">
264
- <zn-button-group class="markdown-editor__view-toggle" @click=${this.handleViewToggle}>
265
- ${VIEW_MODES.map(v => this.renderViewButton(v.mode, v.icon, v.label))}
266
- </zn-button-group>
267
- <zn-button
268
- class="markdown-editor__expand-btn"
269
- type="button"
270
- size="medium"
271
- color="secondary"
272
- icon=${this.expanded ? 'close_fullscreen' : 'open_in_full'}
273
- icon-size="18"
274
- tooltip=${this.expanded ? 'Collapse' : 'Expand'}
275
- @click=${this.handleExpandToggle}
276
- ></zn-button>
277
- </div>
278
-
279
- <div class="markdown-editor__body">
280
- <div part="editor"
281
- class="markdown-editor__editor"
282
- ?hidden=${!showEditor}>
283
- <zn-textarea
284
- .value=${this.value}
285
- name=${this.name || ''}
286
- placeholder=${this.placeholder}
287
- rows=${this.rows}
288
- resize="auto"
289
- ?required=${this.required}
290
- ?readonly=${this.readonly}
291
- ?disabled=${this.disabled}
292
- @zn-input=${this.handleInput}
293
- @zn-change=${this.handleChange}
294
- ></zn-textarea>
258
+ <div part="base" class=${classMap({
259
+ panel: true,
260
+ 'panel--flush': this.flush || this.tabbed,
261
+ 'panel--flush-x': this.flushX,
262
+ 'panel--flush-y': this.flushY,
263
+ 'panel--flush-footer': this.flushFooter,
264
+ 'panel--tabbed': this.tabbed,
265
+ 'panel--transparent': this.transparent,
266
+ 'panel--has-actions': hasActionSlot,
267
+ 'panel--has-footer': hasFooterSlot,
268
+ 'panel--has-header': hasHeader,
269
+ 'panel--cosmic': this.cosmic,
270
+ 'panel--shadow': this.shadow,
271
+ })}>
272
+ <div class="panel__inner">
273
+ ${hasHeader ? html`
274
+ <zn-header class=${classMap({
275
+ panel__header: true,
276
+ 'panel__header--underline': this.underlineHeader,
277
+ })}
278
+ icon=${this.icon}
279
+ caption=${this.caption}
280
+ description=${ifDefined(this.description)}
281
+ transparent>
282
+ ${hasActionSlot ? html`
283
+ <slot name="actions" slot="actions" class="panel__header__actions"></slot>` : null}
284
+ </zn-header>` : null}
285
+
286
+ <div class="panel__content">
287
+ <div class="panel__body">
288
+ <div class=${classMap({
289
+ 'markdown-editor': true,
290
+ 'markdown-editor--split': this.viewMode === 'split',
291
+ 'markdown-editor--preview-only': this.viewMode === 'preview',
292
+ 'markdown-editor--expanded': this.expanded,
293
+ })}>
294
+ ${hasLabel ? html`
295
+ <label class="markdown-editor__label">
296
+ <slot name="label">${this.label}</slot>
297
+ </label>` : ''}
298
+
299
+ <div part="toolbar" class="markdown-editor__toolbar">
300
+ <zn-button-group class="markdown-editor__view-toggle" @click=${this.handleViewToggle}>
301
+ ${VIEW_MODES.map(v => this.renderViewButton(v.mode, v.icon, v.label))}
302
+ </zn-button-group>
303
+ <zn-button
304
+ class="markdown-editor__expand-btn"
305
+ type="button"
306
+ size="medium"
307
+ color="secondary"
308
+ icon=${this.expanded ? 'close_fullscreen' : 'open_in_full'}
309
+ icon-size="18"
310
+ tooltip=${this.expanded ? 'Collapse' : 'Expand'}
311
+ @click=${this.handleExpandToggle}
312
+ ></zn-button>
313
+ </div>
314
+
315
+ <div class="markdown-editor__body">
316
+ <div part="editor"
317
+ class="markdown-editor__editor"
318
+ ?hidden=${!showEditor}>
319
+ <zn-textarea
320
+ .value=${this.value}
321
+ name=${this.name || ''}
322
+ placeholder=${this.placeholder}
323
+ rows=${this.rows}
324
+ resize="auto"
325
+ ?required=${this.required}
326
+ ?readonly=${this.readonly}
327
+ ?disabled=${this.disabled}
328
+ @zn-input=${this.handleInput}
329
+ @zn-change=${this.handleChange}
330
+ ></zn-textarea>
331
+ </div>
332
+ <div part="preview"
333
+ class="markdown-editor__preview-wrapper"
334
+ ?hidden=${!showPreview}>
335
+ <div class="markdown-editor__preview"></div>
336
+ </div>
337
+ </div>
338
+
339
+ ${hasHelpText ? html`
340
+ <div class="markdown-editor__help-text">
341
+ <slot name="help-text">${this.helpText}</slot>
342
+ </div>` : ''}
343
+ </div>
344
+ </div>
295
345
  </div>
296
- <div part="preview"
297
- class="markdown-editor__preview-wrapper"
298
- ?hidden=${!showPreview}>
299
- <div class="markdown-editor__preview"></div>
300
- </div>
301
- </div>
302
346
 
303
- ${hasHelpText ? html`
304
- <div class="markdown-editor__help-text">
305
- <slot name="help-text">${this.helpText}</slot>
306
- </div>` : ''}
347
+ ${hasFooterSlot ? html`
348
+ <slot name="footer" class="panel__footer"></slot>` : null}
349
+ </div>
307
350
  </div>
308
351
  `;
309
352
  }
@@ -1,10 +1,9 @@
1
1
  @use "../../wc";
2
2
 
3
- :host {
4
- display: flex;
5
- flex-direction: column;
6
- width: 100%;
7
- min-height: 0;
3
+ .panel {
4
+ border: 1px solid rgb(var(--zn-border-color));
5
+ background-color: rgba(var(--zn-panel), var(--zn-panel-opacity));
6
+ border-radius: var(--zn-border-radius-large);
8
7
  }
9
8
 
10
9
  .markdown-editor {
@@ -16,15 +15,25 @@
16
15
  min-height: 0;
17
16
  }
18
17
 
19
- .markdown-editor--expanded {
20
- position: fixed;
21
- inset: 10px;
22
- z-index: 9999;
23
- background: var(--zn-panel-background-color, var(--zn-color-neutral-0, #fff));
24
- border: 1px solid var(--zn-color-neutral-300);
25
- border-radius: var(--zn-border-radius-medium);
26
- padding: var(--zn-spacing-medium);
27
- box-shadow: var(--zn-shadow-large);
18
+ :host([expanded]) {
19
+ position: absolute;
20
+ top: 10px;
21
+ bottom: 10px;
22
+ left: var(--zn-sp-padding);
23
+ right: var(--zn-sp-padding);
24
+ z-index: var(--zn-z-index-editor-action, 1000000);
25
+ }
26
+
27
+ :host([expanded]) .panel,
28
+ :host([expanded]) .panel__inner,
29
+ :host([expanded]) .panel__content,
30
+ :host([expanded]) .panel__body {
31
+ height: 100%;
32
+ max-height: 100%;
33
+ }
34
+
35
+ :host([expanded]) .markdown-editor {
36
+ height: 100%;
28
37
  }
29
38
 
30
39
  .markdown-editor__label {
@@ -200,13 +209,3 @@
200
209
  font-size: var(--zn-input-help-text-font-size-medium);
201
210
  color: var(--zn-input-help-text-color);
202
211
  }
203
-
204
- .markdown-editor--expanded .markdown-editor__body {
205
- min-height: 0;
206
- flex: 1;
207
- }
208
-
209
- .markdown-editor--expanded .markdown-editor__editor zn-textarea,
210
- .markdown-editor--expanded .markdown-editor__preview {
211
- min-height: calc(100vh - 200px);
212
- }
@@ -78,7 +78,7 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
78
78
  @property() placeholder = '';
79
79
 
80
80
  /** The number of rows to display by default. */
81
- @property({type: Number}) rows = 4;
81
+ @property({attribute: 'rows',type: Number}) rows = 4;
82
82
 
83
83
  /** Controls how the textarea can be resized. */
84
84
  @property() resize: 'none' | 'vertical' | 'auto' = 'vertical';
@@ -54,9 +54,39 @@ export default class ZnTranslationGroup extends ZnPanel {
54
54
  /** Tracks all language codes that have been activated across children. */
55
55
  @state() private _activatedLanguages: string[] = ['en'];
56
56
 
57
+ @state() private _overflowIndex = -1;
58
+
59
+ private _resizeObserver?: ResizeObserver;
60
+ private _lastObservedWidth = 0;
61
+ private _measureRafId = 0;
62
+
63
+ connectedCallback() {
64
+ super.connectedCallback();
65
+ this._resizeObserver = new ResizeObserver(entries => {
66
+ const width = entries[0]?.contentRect.width ?? 0;
67
+ if (Math.abs(width - this._lastObservedWidth) < 1) return;
68
+ this._lastObservedWidth = width;
69
+ if (this._overflowIndex !== -1) {
70
+ this._overflowIndex = -1;
71
+ } else {
72
+ this._scheduleLangOverflow();
73
+ }
74
+ });
75
+ }
76
+
77
+ disconnectedCallback() {
78
+ super.disconnectedCallback();
79
+ this._resizeObserver?.disconnect();
80
+ if (this._measureRafId) {
81
+ cancelAnimationFrame(this._measureRafId);
82
+ this._measureRafId = 0;
83
+ }
84
+ }
85
+
57
86
  protected firstUpdated(_changedProperties: PropertyValues) {
58
87
  super.firstUpdated(_changedProperties);
59
88
  this.syncChildren();
89
+ this._resizeObserver?.observe(this);
60
90
  }
61
91
 
62
92
  protected updated(changedProperties: PropertyValues) {
@@ -64,6 +94,52 @@ export default class ZnTranslationGroup extends ZnPanel {
64
94
  if (changedProperties.has('languages')) {
65
95
  this.syncChildLanguages();
66
96
  }
97
+ this._scheduleLangOverflow();
98
+ }
99
+
100
+ private _scheduleLangOverflow() {
101
+ if (this._measureRafId) return;
102
+ this._measureRafId = requestAnimationFrame(() => {
103
+ this._measureRafId = 0;
104
+ this._computeLangOverflow();
105
+ });
106
+ }
107
+
108
+ private _computeLangOverflow() {
109
+ const group = this.shadowRoot?.querySelector<HTMLElement>('.translation-group__languages zn-button-group');
110
+ if (!group) return;
111
+
112
+ const buttons = Array.from(group.querySelectorAll<HTMLElement>('zn-button[data-lang-btn]'));
113
+ if (buttons.length < 2) {
114
+ if (this._overflowIndex !== -1) this._overflowIndex = -1;
115
+ return;
116
+ }
117
+
118
+ const firstTop = buttons[0].getBoundingClientRect().top;
119
+ let wrapAt = -1;
120
+ for (let i = 1; i < buttons.length; i++) {
121
+ if (buttons[i].getBoundingClientRect().top > firstTop + 1) {
122
+ wrapAt = i;
123
+ break;
124
+ }
125
+ }
126
+
127
+ if (wrapAt === -1) {
128
+ const trailing = group.querySelector<HTMLElement>('[data-lang-overflow], [data-lang-add]');
129
+ if (trailing && trailing.getBoundingClientRect().top > firstTop + 1) {
130
+ wrapAt = buttons.length;
131
+ }
132
+ }
133
+
134
+ if (wrapAt === -1) return;
135
+
136
+ const newIndex = this._overflowIndex === -1
137
+ ? wrapAt
138
+ : Math.max(1, Math.min(wrapAt, this._overflowIndex - 1));
139
+
140
+ if (newIndex !== this._overflowIndex) {
141
+ this._overflowIndex = newIndex;
142
+ }
67
143
  }
68
144
 
69
145
  private getAllTranslations(): ZnTranslations[] {
@@ -124,6 +200,15 @@ export default class ZnTranslationGroup extends ZnPanel {
124
200
  }
125
201
  };
126
202
 
203
+ private handleOverflowSelect = (e: ZnMenuSelectEvent) => {
204
+ e.stopPropagation();
205
+ const element = e.detail.element as HTMLElement;
206
+ const languageCode = element.getAttribute('data-path');
207
+ if (languageCode) {
208
+ this.switchLanguage(languageCode);
209
+ }
210
+ };
211
+
127
212
  render() {
128
213
  const hasActionSlot = this._slotController.test('actions');
129
214
  const hasFooterSlot = this._slotController.test('footer');
@@ -142,6 +227,16 @@ export default class ZnTranslationGroup extends ZnPanel {
142
227
  visibleTabs.unshift('en');
143
228
  }
144
229
 
230
+ const overflowCutoff = this._overflowIndex === -1 ? visibleTabs.length : this._overflowIndex;
231
+ const visibleLangTabs = visibleTabs.slice(0, overflowCutoff);
232
+ const overflowLangTabs = visibleTabs.slice(overflowCutoff);
233
+ const overflowActions = overflowLangTabs.map(code => ({
234
+ title: code.toUpperCase(),
235
+ type: 'dropdown',
236
+ path: code,
237
+ icon: code === this._activeLanguage ? 'check' : ''
238
+ }));
239
+
145
240
  const hasMultipleLanguages = Object.keys(this.languages).length > 1;
146
241
  const hasHeader = headerCaption || hasMultipleLanguages || hasActionSlot;
147
242
 
@@ -163,8 +258,9 @@ export default class ZnTranslationGroup extends ZnPanel {
163
258
  ${hasMultipleLanguages ? html`
164
259
  <div slot="actions" class="translation-group__languages">
165
260
  <zn-button-group>
166
- ${visibleTabs.map(code => html`
261
+ ${visibleLangTabs.map(code => html`
167
262
  <zn-button
263
+ data-lang-btn
168
264
  size="x-small"
169
265
  color="default"
170
266
  ?outline="${code !== this._activeLanguage}"
@@ -172,8 +268,23 @@ export default class ZnTranslationGroup extends ZnPanel {
172
268
  >${code.toUpperCase()}
173
269
  </zn-button>
174
270
  `)}
271
+ ${overflowActions.length > 0 ? html`
272
+ <zn-dropdown placement="bottom-end" data-lang-overflow>
273
+ <zn-button
274
+ slot="trigger"
275
+ size="x-small"
276
+ color="default"
277
+ icon="keyboard_arrow_down"
278
+ ?outline="${!overflowLangTabs.includes(this._activeLanguage)}"
279
+ ></zn-button>
280
+ <zn-menu
281
+ .actions=${overflowActions}
282
+ @zn-menu-select="${this.handleOverflowSelect}"
283
+ ></zn-menu>
284
+ </zn-dropdown>
285
+ ` : nothing}
175
286
  ${availableLanguages.length > 0 ? html`
176
- <zn-dropdown placement="bottom-end">
287
+ <zn-dropdown placement="bottom-end" data-lang-add>
177
288
  <zn-button
178
289
  slot="trigger"
179
290
  size="x-small"