@kubex/zinc 1.0.105 → 1.0.107

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.
@@ -1,204 +1,96 @@
1
- import { property } from 'lit/decorators.js';
1
+ import * as echarts from 'echarts/core';
2
+ import { BarChart } from 'echarts/charts';
3
+ import { CanvasRenderer } from 'echarts/renderers';
2
4
  import { type CSSResultGroup, html, unsafeCSS } from 'lit';
3
- import { Chart, ChartConfiguration, registerables } from "chart.js";
5
+ import { GridComponent, TooltipComponent } from 'echarts/components';
6
+ import { property } from 'lit/decorators.js';
7
+ import styles from './simple-chart.scss';
4
8
  import ZincElement from '../../internal/zinc-element';
9
+ import type { ECharts } from 'echarts/core';
5
10
 
6
- import styles from './simple-chart.scss';
11
+ echarts.use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
12
+
13
+ const DEFAULT_LABELS = [
14
+ 'Jun 2016', 'Jul 2016', 'Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016',
15
+ 'Dec 2016', 'Jan 2017', 'Feb 2017', 'Mar 2017', 'Apr 2017', 'May 2017',
16
+ ];
17
+ const DEFAULT_DATA = [56.4, 39.8, 66.8, 66.4, 40.6, 55.2, 77.4, 69.8, 57.8, 76, 110.8, 142.6];
7
18
 
8
19
  /**
9
- * @summary Short summary of the component's intended use.
20
+ * @summary A simple, pre-styled bar chart.
10
21
  * @documentation https://zinc.style/components/simple-chart
11
22
  * @status experimental
12
23
  * @since 1.0
13
- *
14
- * @dependency zn-example
15
- *
16
- * @event zn-event-name - Emitted as an example.
17
- *
18
- * @slot - The default slot.
19
- * @slot example - An example slot.
20
- *
21
- * @csspart base - The component's base wrapper.
22
- *
23
- * @cssproperty --example - An example CSS custom property.
24
24
  */
25
25
  export default class ZnSimpleChart extends ZincElement {
26
26
  static styles: CSSResultGroup = unsafeCSS(styles);
27
27
 
28
- @property({ attribute: 'datasets', type: Array }) datasets: any[];
29
- @property({ attribute: 'labels', type: Array }) labels: any[];
30
-
31
- myChart: Chart;
28
+ @property({ attribute: 'datasets', type: Array }) datasets?: { data: number[] }[];
29
+ @property({ attribute: 'labels', type: Array }) labels?: string[];
30
+ @property({
31
+ attribute: 'enable-animations',
32
+ converter: {
33
+ fromAttribute: (value: string | null) => {
34
+ if (value === null) return false;
35
+ if (value === '' || value === 'true') return true;
36
+ const num = parseFloat(value);
37
+ return Number.isNaN(num) ? true : num;
38
+ },
39
+ },
40
+ }) enableAnimations: boolean | number = false;
32
41
 
33
- constructor() {
34
- super();
35
- Chart.register(...registerables);
36
- }
42
+ private chart?: ECharts;
43
+ private resizeObserver?: ResizeObserver;
37
44
 
38
45
  firstUpdated() {
39
- const ctx = (this.renderRoot.querySelector('#myChart') as HTMLCanvasElement).getContext('2d');
40
-
41
- const config = {
42
- type: 'bar',
43
- data: {
44
- labels: [
45
- "Jun 2016",
46
- "Jul 2016",
47
- "Aug 2016",
48
- "Sep 2016",
49
- "Oct 2016",
50
- "Nov 2016",
51
- "Dec 2016",
52
- "Jan 2017",
53
- "Feb 2017",
54
- "Mar 2017",
55
- "Apr 2017",
56
- "May 2017"
57
- ],
58
- datasets: [
59
- {
60
- data: [
61
- 56.4,
62
- 39.8,
63
- 66.8,
64
- 66.4,
65
- 40.6,
66
- 55.2,
67
- 77.4,
68
- 69.8,
69
- 57.8,
70
- 76,
71
- 110.8,
72
- 142.6
73
- ],
74
- }
75
- ]
46
+ const host = this.shadowRoot?.getElementById('chart') as HTMLElement | null;
47
+ if (!host) return;
48
+ this.chart = echarts.init(host);
49
+ const data = this.datasets?.[0]?.data ?? DEFAULT_DATA;
50
+ const labels = this.labels ?? DEFAULT_LABELS;
51
+ const animEnabled = this.enableAnimations !== false && this.enableAnimations !== 0;
52
+ const animDuration = typeof this.enableAnimations === 'number' ? this.enableAnimations : 1500;
53
+ this.chart.setOption({
54
+ animation: animEnabled,
55
+ animationDuration: animDuration,
56
+ animationEasing: 'cubicOut',
57
+ tooltip: {
58
+ trigger: 'axis',
59
+ backgroundColor: 'rgba(0,0,0,0.7)',
60
+ borderWidth: 0,
61
+ textStyle: { color: '#fff' },
76
62
  },
77
- options: {
78
- responsive: true,
79
- maintainAspectRatio: false,
80
- plugins: {
81
- legend: {
82
- display: false
83
- },
84
- tooltip: {
85
- enabled: false,
86
- external: function (context: any) {
87
- // Tooltip Element
88
- let tooltipEl = document.getElementById("chartjs-tooltip");
89
-
90
- // Create element on first render
91
- if (!tooltipEl) {
92
- tooltipEl = document.createElement("div");
93
- tooltipEl.id = "chartjs-tooltip";
94
- tooltipEl.innerHTML = "<table></table>";
95
- document.body.appendChild(tooltipEl);
96
- }
97
-
98
- // Hide if no tooltip
99
- const tooltipModel = context.tooltip;
100
- if (tooltipModel.opacity === 0) {
101
- tooltipEl.style.opacity = "0";
102
- return;
103
- }
104
-
105
- // Set caret Position
106
- tooltipEl.classList.remove("above", "below", "no-transform");
107
- if (tooltipModel.yAlign) {
108
- tooltipEl.classList.add(tooltipModel.yAlign);
109
- } else {
110
- tooltipEl.classList.add("no-transform");
111
- }
112
-
113
- function getBody(bodyItem: any) {
114
- return bodyItem.lines;
115
- }
116
-
117
- // Set Text
118
- if (tooltipModel.body) {
119
- const titleLines = tooltipModel.title || [];
120
- const bodyLines = tooltipModel.body.map(getBody);
121
-
122
- let innerHtml = "<thead>";
123
-
124
- titleLines.forEach(function (title: any) {
125
- innerHtml += "<tr><th>" + title + "</th></tr>";
126
- });
127
- innerHtml += "</thead><tbody>";
128
-
129
- bodyLines.forEach(function (body: any, i: any) {
130
- const colors = tooltipModel.labelColors[i];
131
- let style = "background:" + colors.backgroundColor;
132
- style += "; border-color:" + colors.borderColor;
133
- style += "; border-width: 2px";
134
- const span = '<span style="' + style + '"></span>';
135
- innerHtml += "<tr><td>" + span + body + "</td></tr>";
136
- });
137
- innerHtml += "</tbody>";
138
-
139
- const tableRoot = tooltipEl.querySelector("table");
140
- if (tableRoot) {
141
- tableRoot.innerHTML = innerHtml;
142
- }
143
- }
144
-
145
- const position = context.chart.canvas.getBoundingClientRect();
146
- const bodyFont = Chart.defaults.font;
147
-
148
- tooltipModel.padding = 3;
149
-
150
- // Display, position, and set styles for font
151
- tooltipEl.style.opacity = "1";
152
- tooltipEl.style.position = "absolute";
153
- tooltipEl.style.left =
154
- position.left +
155
- window.pageXOffset +
156
- tooltipModel.caretX +
157
- 5 +
158
- "px";
159
- tooltipEl.style.top =
160
- position.top +
161
- window.pageYOffset +
162
- tooltipModel.caretY +
163
- 10 +
164
- "px";
165
- tooltipEl.style.font = bodyFont.family ?? "Helvetica Neue";
166
- tooltipEl.style.color = "white";
167
- tooltipEl.style.padding = tooltipModel.padding + "px " + tooltipModel.padding + "px";
168
- tooltipEl.style.pointerEvents = "none";
169
- tooltipEl.style.background = "rgba(0, 0, 0, 0.7)";
170
- tooltipEl.style.borderRadius = "10px";
171
- }
172
- }
63
+ xAxis: { type: 'category', data: labels, show: false },
64
+ yAxis: { type: 'value', show: false },
65
+ grid: { left: 0, right: 0, top: 0, bottom: 0 },
66
+ series: [{
67
+ type: 'bar',
68
+ data,
69
+ barWidth: 7,
70
+ itemStyle: {
71
+ color: '#29C1BC',
72
+ borderRadius: 10,
173
73
  },
174
- elements: {
175
- bar: {
176
- borderRadius: 10,
177
- barThickness: 2,
178
- maxBarThickness: 2,
179
- backgroundColor: "#29C1BC",
180
- hoverBackgroundColor: "#19837f",
181
- }
74
+ emphasis: {
75
+ itemStyle: { color: '#19837f' },
182
76
  },
183
- scales: {
184
- x: {
185
- display: false
186
- },
187
- y: {
188
- display: false
189
- }
190
- }
191
- }
192
- };
77
+ }],
78
+ });
79
+ let firstResize = true;
80
+ this.resizeObserver = new ResizeObserver(() => {
81
+ if (firstResize) { firstResize = false; return; }
82
+ this.chart?.resize();
83
+ });
84
+ this.resizeObserver.observe(host);
85
+ }
193
86
 
194
- this.myChart = new Chart(ctx as CanvasRenderingContext2D, config as ChartConfiguration);
87
+ disconnectedCallback() {
88
+ super.disconnectedCallback();
89
+ this.resizeObserver?.disconnect();
90
+ this.chart?.dispose();
195
91
  }
196
92
 
197
93
  render() {
198
- return html`
199
- <div>
200
- <canvas id="myChart"></canvas>
201
- </div>
202
- `;
94
+ return html`<div id="chart"></div>`;
203
95
  }
204
96
  }
@@ -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"
@@ -2,6 +2,7 @@ import {classMap} from 'lit/directives/class-map.js';
2
2
  import {FormControlController, validValidityState} from '../../internal/form';
3
3
  import {HasSlotController} from '../../internal/slot';
4
4
  import {html, nothing, unsafeCSS} from 'lit';
5
+ import {ifDefined} from 'lit/directives/if-defined.js';
5
6
  import {keyed} from 'lit/directives/keyed.js';
6
7
  import {live} from 'lit/directives/live.js';
7
8
  import {property, state} from 'lit/decorators.js';
@@ -39,6 +40,7 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
39
40
  @property({type: Boolean, reflect: true}) required = false;
40
41
  @property({type: Boolean, reflect: true}) flush = false;
41
42
  @property({attribute: "input-type"}) inputType: 'select' | 'text' | 'number' | 'textarea' = 'text';
43
+ @property({attribute: "textarea-rows", type: Number}) textareaRows: number | undefined;
42
44
 
43
45
  /** When true, hides the individual language navbar and defers language control to a parent zn-translation-group. */
44
46
  @property({type: Boolean, reflect: true}) grouped = false;
@@ -49,6 +51,11 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
49
51
  @property({type: Object}) values: Record<string, string> = {};
50
52
 
51
53
  @state() private _activeLanguage = 'en';
54
+ @state() private _overflowIndex = -1;
55
+
56
+ private _resizeObserver?: ResizeObserver;
57
+ private _lastObservedWidth = 0;
58
+ private _measureRafId = 0;
52
59
 
53
60
  get validity(): ValidityState {
54
61
  return validValidityState;
@@ -99,8 +106,82 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
99
106
  return Object.keys(this.values);
100
107
  }
101
108
 
109
+ connectedCallback() {
110
+ super.connectedCallback();
111
+ this._resizeObserver = new ResizeObserver(entries => {
112
+ const width = entries[0]?.contentRect.width ?? 0;
113
+ if (Math.abs(width - this._lastObservedWidth) < 1) return;
114
+ this._lastObservedWidth = width;
115
+ if (this._overflowIndex !== -1) {
116
+ this._overflowIndex = -1;
117
+ } else {
118
+ this._scheduleLangOverflow();
119
+ }
120
+ });
121
+ }
122
+
123
+ disconnectedCallback() {
124
+ super.disconnectedCallback();
125
+ this._resizeObserver?.disconnect();
126
+ if (this._measureRafId) {
127
+ cancelAnimationFrame(this._measureRafId);
128
+ this._measureRafId = 0;
129
+ }
130
+ }
131
+
102
132
  protected firstUpdated() {
103
133
  this.formControlController.updateValidity();
134
+ this._resizeObserver?.observe(this);
135
+ }
136
+
137
+ protected updated(changedProperties: PropertyValues) {
138
+ super.updated(changedProperties);
139
+ this._scheduleLangOverflow();
140
+ }
141
+
142
+ private _scheduleLangOverflow() {
143
+ if (this._measureRafId) return;
144
+ this._measureRafId = requestAnimationFrame(() => {
145
+ this._measureRafId = 0;
146
+ this._computeLangOverflow();
147
+ });
148
+ }
149
+
150
+ private _computeLangOverflow() {
151
+ const group = this.shadowRoot?.querySelector<HTMLElement>('.translations__language-group');
152
+ if (!group) return;
153
+
154
+ const buttons = Array.from(group.querySelectorAll<HTMLElement>('zn-button[data-lang-btn]'));
155
+ if (buttons.length < 2) {
156
+ if (this._overflowIndex !== -1) this._overflowIndex = -1;
157
+ return;
158
+ }
159
+
160
+ const firstTop = buttons[0].getBoundingClientRect().top;
161
+ let wrapAt = -1;
162
+ for (let i = 1; i < buttons.length; i++) {
163
+ if (buttons[i].getBoundingClientRect().top > firstTop + 1) {
164
+ wrapAt = i;
165
+ break;
166
+ }
167
+ }
168
+
169
+ if (wrapAt === -1) {
170
+ const trailing = group.querySelector<HTMLElement>('[data-lang-overflow], [data-lang-add]');
171
+ if (trailing && trailing.getBoundingClientRect().top > firstTop + 1) {
172
+ wrapAt = buttons.length;
173
+ }
174
+ }
175
+
176
+ if (wrapAt === -1) return;
177
+
178
+ const newIndex = this._overflowIndex === -1
179
+ ? wrapAt
180
+ : Math.max(1, Math.min(wrapAt, this._overflowIndex - 1));
181
+
182
+ if (newIndex !== this._overflowIndex) {
183
+ this._overflowIndex = newIndex;
184
+ }
104
185
  }
105
186
 
106
187
  willUpdate(changedProperties: PropertyValues) {
@@ -164,6 +245,15 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
164
245
  }
165
246
  };
166
247
 
248
+ private handleOverflowSelect = (e: ZnMenuSelectEvent) => {
249
+ e.stopPropagation();
250
+ const element = e.detail.element as HTMLElement;
251
+ const languageCode = element.getAttribute('data-path');
252
+ if (languageCode) {
253
+ this.switchLanguage(languageCode);
254
+ }
255
+ };
256
+
167
257
  private switchLanguage = (lang: string) => {
168
258
  this._activeLanguage = lang;
169
259
  this.requestUpdate();
@@ -223,6 +313,16 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
223
313
  visibleTabs.unshift('en');
224
314
  }
225
315
 
316
+ const overflowCutoff = this._overflowIndex === -1 ? visibleTabs.length : this._overflowIndex;
317
+ const visibleLangTabs = visibleTabs.slice(0, overflowCutoff);
318
+ const overflowLangTabs = visibleTabs.slice(overflowCutoff);
319
+ const overflowActions = overflowLangTabs.map(code => ({
320
+ title: code.toUpperCase(),
321
+ type: 'dropdown',
322
+ path: code,
323
+ icon: code === this._activeLanguage ? 'check' : ''
324
+ }));
325
+
226
326
  const currentTranslation = this.values[this._activeLanguage] ?? '';
227
327
  const isRTL = this.isRTLLanguage(this._activeLanguage);
228
328
 
@@ -252,9 +352,10 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
252
352
  <div class="translations__actions">
253
353
  ${hasExpandSlot ? html`<slot name="expand"></slot>` : nothing}
254
354
  ${hasMultipleLanguages ? html`
255
- <zn-button-group>
256
- ${visibleTabs.map(code => html`
355
+ <zn-button-group class="translations__language-group">
356
+ ${visibleLangTabs.map(code => html`
257
357
  <zn-button
358
+ data-lang-btn
258
359
  size="x-small"
259
360
  color="default"
260
361
  ?outline="${code !== this._activeLanguage}"
@@ -262,8 +363,23 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
262
363
  >${code.toUpperCase()}
263
364
  </zn-button>
264
365
  `)}
366
+ ${overflowActions.length > 0 ? html`
367
+ <zn-dropdown placement="bottom-end" data-lang-overflow>
368
+ <zn-button
369
+ slot="trigger"
370
+ size="x-small"
371
+ color="default"
372
+ icon="keyboard_arrow_down"
373
+ ?outline="${!overflowLangTabs.includes(this._activeLanguage)}"
374
+ ></zn-button>
375
+ <zn-menu
376
+ .actions=${overflowActions}
377
+ @zn-menu-select="${this.handleOverflowSelect}"
378
+ ></zn-menu>
379
+ </zn-dropdown>
380
+ ` : nothing}
265
381
  ${availableLanguages.length > 0 ? html`
266
- <zn-dropdown placement="bottom-end">
382
+ <zn-dropdown placement="bottom-end" data-lang-add>
267
383
  <zn-button
268
384
  slot="trigger"
269
385
  size="x-small"
@@ -287,6 +403,7 @@ export default class ZnTranslations extends ZincElement implements ZincFormContr
287
403
  ${keyed(this._activeLanguage, html`
288
404
  <zn-inline-edit
289
405
  input-type=${this.inputType}
406
+ textarea-rows=${ifDefined(this.textareaRows)}
290
407
  .value=${live(currentTranslation)}
291
408
  name="${this.name}"
292
409
  placeholder="Enter translation..."