@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.
@@ -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..."