@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.
- package/dist/custom-elements.json +604 -115
- package/dist/vscode.html-custom-data.json +33 -13
- package/dist/web-types.json +73 -26
- package/dist/zn.d.ts +24 -3
- package/dist/zn.min.js +347 -294
- package/docs/pages/components/translation-group.md +25 -0
- package/docs/pages/components/translations.md +17 -1
- package/package.json +1 -1
- package/src/components/inline-edit/inline-edit.component.ts +2 -1
- package/src/components/inline-edit/inline-edit.scss +9 -11
- package/src/components/markdown-editor/markdown-editor.component.ts +107 -64
- package/src/components/markdown-editor/markdown-editor.scss +23 -24
- package/src/components/textarea/textarea.component.ts +1 -1
- package/src/components/translation-group/translation-group.component.ts +113 -2
- package/src/components/translations/translations.component.ts +120 -3
|
@@ -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
|
-
|
|
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
|
@@ -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="
|
|
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
|
|
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
|
|
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
|
|
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
|
-
@
|
|
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
|
-
|
|
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.
|
|
244
|
-
const hasHelpSlot = this.
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
20
|
-
position:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
${
|
|
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"
|