@api-client/ui 0.0.9 → 0.0.11

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.
Files changed (56) hide show
  1. package/.eslintrc +8 -1
  2. package/demo/index.html +3 -0
  3. package/demo/layout/index.html +91 -0
  4. package/demo/layout/index.ts +182 -0
  5. package/dist/elements/layout/SplitItem.d.ts +1 -9
  6. package/dist/elements/layout/SplitItem.d.ts.map +1 -1
  7. package/dist/elements/layout/SplitItem.js +27 -20
  8. package/dist/elements/layout/SplitItem.js.map +1 -1
  9. package/dist/elements/layout/SplitLayout.d.ts +16 -14
  10. package/dist/elements/layout/SplitLayout.d.ts.map +1 -1
  11. package/dist/elements/layout/SplitLayout.js +47 -42
  12. package/dist/elements/layout/SplitLayout.js.map +1 -1
  13. package/dist/elements/layout/SplitPanel.d.ts +7 -2
  14. package/dist/elements/layout/SplitPanel.d.ts.map +1 -1
  15. package/dist/elements/layout/SplitPanel.js +130 -52
  16. package/dist/elements/layout/SplitPanel.js.map +1 -1
  17. package/dist/elements/layout/SplitView.d.ts.map +1 -1
  18. package/dist/elements/layout/SplitView.js +18 -14
  19. package/dist/elements/layout/SplitView.js.map +1 -1
  20. package/dist/elements/layout/type.d.ts +3 -3
  21. package/dist/elements/layout/type.d.ts.map +1 -1
  22. package/dist/elements/layout/type.js.map +1 -1
  23. package/dist/elements/schema-design/DataModelVisualizationElement.d.ts.map +1 -1
  24. package/dist/elements/schema-design/DataModelVisualizationElement.js +18 -1
  25. package/dist/elements/schema-design/DataModelVisualizationElement.js.map +1 -1
  26. package/dist/pages/http-project/HttpClientCommands.d.ts.map +1 -1
  27. package/dist/pages/http-project/HttpClientCommands.js +28 -12
  28. package/dist/pages/http-project/HttpClientCommands.js.map +1 -1
  29. package/package.json +2 -1
  30. package/src/elements/layout/SplitItem.ts +29 -21
  31. package/src/elements/layout/SplitLayout.ts +53 -43
  32. package/src/elements/layout/SplitPanel.ts +140 -57
  33. package/src/elements/layout/SplitView.ts +18 -15
  34. package/src/elements/layout/type.ts +3 -4
  35. package/src/elements/schema-design/DataModelVisualizationElement.ts +18 -1
  36. package/src/pages/http-project/HttpClientCommands.ts +28 -12
  37. package/test/elements/layout/SplitItem.test.ts +76 -75
  38. package/test/elements/layout/SplitLayoutManager.test.ts +70 -69
  39. package/test/elements/layout/SplitPanel.test.ts +10 -7
  40. package/tsconfig.eslint.json +8 -0
  41. package/web-test-runner.config.mjs +4 -1
  42. package/dist/define/layout/layout-panel.d.ts +0 -7
  43. package/dist/define/layout/layout-panel.d.ts.map +0 -1
  44. package/dist/define/layout/layout-panel.js +0 -3
  45. package/dist/define/layout/layout-panel.js.map +0 -1
  46. package/dist/elements/layout/LayoutManager.d.ts +0 -327
  47. package/dist/elements/layout/LayoutManager.d.ts.map +0 -1
  48. package/dist/elements/layout/LayoutManager.js +0 -747
  49. package/dist/elements/layout/LayoutManager.js.map +0 -1
  50. package/dist/elements/layout/LayoutPanelElement.d.ts +0 -62
  51. package/dist/elements/layout/LayoutPanelElement.d.ts.map +0 -1
  52. package/dist/elements/layout/LayoutPanelElement.js +0 -628
  53. package/dist/elements/layout/LayoutPanelElement.js.map +0 -1
  54. package/src/define/layout/layout-panel.ts +0 -9
  55. package/src/elements/layout/LayoutManager.ts +0 -930
  56. package/src/elements/layout/LayoutPanelElement.ts +0 -651
@@ -1,651 +0,0 @@
1
- /* eslint-disable class-methods-use-this */
2
- import { LitElement, html, TemplateResult, CSSResult, css } from 'lit';
3
- import { property, state, eventOptions } from 'lit/decorators.js';
4
- import { ifDefined } from 'lit/directives/if-defined.js';
5
- import { classMap } from 'lit/directives/class-map.js';
6
- // eslint-disable-next-line import/no-cycle
7
- import { LayoutPanel, DropRegion, ILayoutItem } from './LayoutManager.js';
8
- import surfaceStyles from '../../styles/m3/surface.module.js';
9
- import '../../define/ui/ui-icon.js';
10
-
11
- export default class LayoutPanelElement extends LitElement {
12
- static override get styles(): CSSResult[] {
13
- return [surfaceStyles, css`
14
- :host {
15
- display: flex;
16
- position: relative;
17
- flex-direction: column;
18
- /* border: 1px red solid; */
19
- }
20
-
21
- .content {
22
- display: flex;
23
- flex: 1;
24
- }
25
-
26
- :host([constrain]) .content {
27
- overflow: hidden;
28
- }
29
-
30
- :host([layout=horizontal]) .content {
31
- flex-direction: row;
32
- }
33
-
34
- :host([layout=vertical]) .content {
35
- flex-direction: column;
36
- }
37
-
38
- :host ::slotted(*) {
39
- flex: 1;
40
- /* width: 100%;
41
- height: 100%; */
42
- }
43
-
44
- .drag-region {
45
- position: absolute;
46
- background-color: rgba(0,0,0,0.12);
47
- }
48
-
49
- .drag-region.center {
50
- top: 0;
51
- bottom: 0;
52
- left: 0;
53
- right:0;
54
- }
55
-
56
- .drag-region.west {
57
- top: 0;
58
- bottom: 0;
59
- left: 0;
60
- right: 50%;
61
- }
62
-
63
- .drag-region.east {
64
- top: 0;
65
- bottom: 0;
66
- left: 50%;
67
- right: 0;
68
- }
69
-
70
- .drag-region.north {
71
- top: 0;
72
- left: 0;
73
- right: 0;
74
- bottom: 50%;
75
- }
76
-
77
- .drag-region.south {
78
- top: 50%;
79
- left: 0;
80
- right: 0;
81
- bottom: 0;
82
- }
83
-
84
- .layout-tabs {
85
- display: flex;
86
- align-items: center;
87
- background: var(--layout-tabs-background, var(--secondary-background-color));
88
- }
89
-
90
- .layout-tab {
91
- position: relative;
92
- display: inline-flex;
93
- align-items: center;
94
- flex: 1 1 200px;
95
- max-width: 200px;
96
- min-width: 40px;
97
- width: 200px;
98
- height: 100%;
99
- font-size: 0.94rem;
100
- padding: 0px 12px;
101
- border-radius: var(--md-sys-shape-corner-small-top);
102
- height: 40px;
103
- outline: none;
104
- justify-content: flex-start;
105
- text-transform: none;
106
- color: var(--md-sys-color-on-surface);
107
-
108
- font-family: var(--md-sys-typescale-body-medium-font-family-name);
109
- font-style: var(--md-sys-typescale-body-medium-font-family-style);
110
- font-weight: var(--md-sys-typescale-body-medium-font-weight);
111
- font-size: var(--md-sys-typescale-body-medium-font-size);
112
- letter-spacing: var(--md-sys-typescale-body-medium-tracking);
113
- line-height: var(--md-sys-typescale-body-medium-height);
114
- text-transform: var(--md-sys-typescale-body-medium-text-transform);
115
- text-decoration: var(--md-sys-typescale-body-medium-text-decoration);
116
- }
117
-
118
- .tab-label {
119
- overflow: hidden;
120
- white-space: nowrap;
121
- text-overflow: ellipsis;
122
- }
123
-
124
- .is-dirty .tab-label {
125
- font-style: italic;
126
- }
127
-
128
- .is-dirty .tab-label::after {
129
- content: '*';
130
- user-select: none;
131
- }
132
-
133
- .layout-tab.selected {
134
- /* z-index: 2; */
135
- background: var(--layout-tab-select-background, var(--primary-background-color));
136
- }
137
-
138
- .layout-tab:not(.selected):hover::before {
139
- background-color: var(--md-sys-color-primary);
140
- opacity: var(--md-sys-state-hover-state-layer-opacity);
141
- position: absolute;
142
- inset: 0;
143
- content: '';
144
- border-radius: inherit;
145
- }
146
-
147
- .layout-tab:not(.selected):focus::before {
148
- background-color: var(--md-sys-color-primary);
149
- opacity: var(--md-sys-state-focus-state-layer-opacity);
150
- position: absolute;
151
- inset: 0;
152
- content: '';
153
- border-radius: inherit;
154
- }
155
-
156
- /* .layout-tab:focus {
157
- border-top-color: var(--primary-color);
158
- } */
159
-
160
- .tab-favicon {
161
- width: 16px;
162
- height: 16px;
163
- margin-right: 8px;
164
- }
165
-
166
- .close-icon {
167
- width: 16px;
168
- height: 16px;
169
- margin-left: auto;
170
- }
171
-
172
- .tab-divider {
173
- width: 1px;
174
- min-width: 1px;
175
- height: 20px;
176
- background-color: var(--md-sys-color-outline-variant);
177
- }
178
-
179
- .tab-divider.hidden {
180
- background-color: transparent;
181
- }
182
- `];
183
- }
184
-
185
- /**
186
- * @attribute
187
- */
188
- @property({ type: String, reflect: true }) layout: 'horizontal' | 'vertical' = 'horizontal';
189
-
190
- /**
191
- * @attribute
192
- */
193
- @property({ type: Number, reflect: true }) layoutId?: number;
194
-
195
- @property({ type: Array }) dragTypes?: string[];
196
-
197
- /**
198
- * Whether dragging is occurring over the element
199
- */
200
- @state() protected inDrag = false;
201
-
202
- /**
203
- * The region the drag is leaning to.
204
- */
205
- @state() protected dragRegion?: DropRegion = 'center';
206
-
207
- @property({ type: Object }) panel?: LayoutPanel;
208
-
209
- /**
210
- * When set it adds the `overflow` hidden on the container that holds the tab contents.
211
- * @attribute
212
- */
213
- @property({ type: Boolean, reflect: true }) constrain = false;
214
-
215
- constructor() {
216
- super();
217
- this._dragEnterHandler = this._dragEnterHandler.bind(this);
218
- this._dragOverHandler = this._dragOverHandler.bind(this);
219
- this._dropHandler = this._dropHandler.bind(this);
220
- this._dragleaveHandler = this._dragleaveHandler.bind(this);
221
- this._dragendHandler = this._dragendHandler.bind(this);
222
- }
223
-
224
- override connectedCallback(): void {
225
- super.connectedCallback();
226
- this.addEventListener('dragenter', this._dragEnterHandler);
227
- this.addEventListener('dragover', this._dragOverHandler);
228
- this.addEventListener('drop', this._dropHandler);
229
- document.body.addEventListener('dragleave', this._dragleaveHandler);
230
- document.body.addEventListener('dragend', this._dragendHandler);
231
- }
232
-
233
- override disconnectedCallback(): void {
234
- super.disconnectedCallback();
235
- this.removeEventListener('dragenter', this._dragEnterHandler);
236
- this.removeEventListener('dragover', this._dragOverHandler);
237
- this.removeEventListener('drop', this._dropHandler);
238
- document.body.removeEventListener('dragleave', this._dragleaveHandler);
239
- document.body.removeEventListener('dragend', this._dragendHandler);
240
- }
241
-
242
- protected hasDropTypes(dt: DataTransfer): boolean {
243
- const { dragTypes } = this;
244
- if (!Array.isArray(dragTypes)) {
245
- return true;
246
- }
247
- const eventTypes = [...dt.types];
248
- const allowedTypes = dragTypes.map(i => i.toLowerCase());
249
- return !allowedTypes.some(type => !eventTypes.includes(type));
250
- }
251
-
252
- protected panelCanDrop(e: DragEvent): boolean {
253
- if (e.defaultPrevented) {
254
- return false;
255
- }
256
- if (e.dataTransfer && !this.hasDropTypes(e.dataTransfer)) {
257
- return false;
258
- }
259
- const { panel } = this;
260
- if (!panel) {
261
- return true;
262
- }
263
- return panel.canDrop();
264
- }
265
-
266
- protected _dragEnterHandler(e: DragEvent): void {
267
- const { dataTransfer } = e;
268
- if (!dataTransfer || !this.panelCanDrop(e)) {
269
- return;
270
- }
271
- e.preventDefault();
272
- e.stopPropagation();
273
- dataTransfer.dropEffect = 'copy';
274
- }
275
-
276
- protected _dragOverHandler(e: DragEvent): void {
277
- const { dataTransfer } = e;
278
- if (!dataTransfer || !this.panelCanDrop(e)) {
279
- return;
280
- }
281
- e.preventDefault();
282
- e.stopPropagation();
283
- const region = this.getDropRegionFromEvent(e);
284
- if (!region) {
285
- this.inDrag = false;
286
- return;
287
- }
288
- dataTransfer.dropEffect = 'copy';
289
- this.inDrag = true;
290
- this.dragRegion = region;
291
- }
292
-
293
- protected _dropHandler(e: DragEvent): void {
294
- const { dataTransfer } = e;
295
- if (!dataTransfer || !this.panelCanDrop(e)) {
296
- return;
297
- }
298
- e.preventDefault();
299
- e.stopPropagation();
300
- const kind = dataTransfer.getData('text/kind');
301
- const key = dataTransfer.getData('text/key');
302
- if (!kind || !key) {
303
- return;
304
- }
305
- this.inDrag = false;
306
- const { panel } = this;
307
- let dispatch = true;
308
- if (panel) {
309
- dispatch = panel.addItem({ key, kind, label: 'New tab' }, { region: this.dragRegion });
310
- }
311
- if (dispatch) {
312
- this.dispatchEvent(new CustomEvent('datadrop', {
313
- detail: { kind, key, region: this.dragRegion },
314
- composed: true,
315
- bubbles: true,
316
- cancelable: true,
317
- }));
318
- }
319
- this.requestUpdate();
320
- }
321
-
322
- protected _dragleaveHandler(e: DragEvent): void {
323
- const elm = this.findLayout(e);
324
- if (!elm || elm !== this) {
325
- this.inDrag = false;
326
- this.dragRegion = 'center';
327
- }
328
- }
329
-
330
- protected _dragendHandler(): void {
331
- this.inDrag = false;
332
- this.dragRegion = 'center';
333
- }
334
-
335
- protected getDropRegionFromEvent(e: DragEvent): DropRegion | undefined {
336
- const layout = this.findLayout(e);
337
- if (!layout) {
338
- return undefined;
339
- }
340
- return this.getDropRegion(layout, e);
341
- }
342
-
343
- getDropRegion(element: HTMLElement, e: DragEvent): DropRegion | undefined {
344
- const { pageX, pageY } = e;
345
- const rect = element.getBoundingClientRect();
346
- const quarterWidth = rect.width / 4;
347
- const quarterHeight = rect.height / 4;
348
- if (pageX < rect.left + quarterWidth) {
349
- return 'west';
350
- }
351
- if (pageX > rect.right - quarterWidth) {
352
- return 'east';
353
- }
354
- if (pageY < rect.top + quarterHeight) {
355
- return 'north';
356
- }
357
- if (pageY > rect.bottom - quarterHeight) {
358
- return 'south';
359
- }
360
- const withingCenterX = (pageX >= rect.left + quarterWidth) && (pageX <= rect.right - quarterWidth);
361
- const withingCenterY = (pageY >= rect.top + quarterHeight) && (pageY <= rect.bottom - quarterHeight);
362
- if (withingCenterX && withingCenterY) {
363
- return 'center';
364
- }
365
- return undefined;
366
- }
367
-
368
- protected findLayout(e: Event): LayoutPanelElement | undefined {
369
- const path = e.composedPath();
370
- while (path.length) {
371
- const node = path.shift() as Element;
372
- if (node.nodeType !== Node.ELEMENT_NODE) {
373
- continue;
374
- }
375
- if (node.localName === this.localName) {
376
- return node as LayoutPanelElement;
377
- }
378
- }
379
- return undefined;
380
- }
381
-
382
- protected async _tabSelectHandler(e: Event): Promise<void> {
383
- if (!this.panel) {
384
- return;
385
- }
386
- const key = (e.currentTarget as HTMLElement).dataset.key as string;
387
- if (!key) {
388
- return;
389
- }
390
- if (this.panel.selected === key) {
391
- return;
392
- }
393
- this.panel.selected = key;
394
- this.panel.manager.changed();
395
- this.requestUpdate();
396
- await this.updateComplete;
397
- const child = this.querySelector(`[data-key="${key}"]`);
398
- if (child) {
399
- child.dispatchEvent(new Event('resize'));
400
- }
401
- }
402
-
403
- /**
404
- * Closes a panel with right pointer configuration
405
- */
406
- protected _tabPointerDownHandler(e: PointerEvent): void {
407
- // the configuration of a middle button click which is
408
- // equal to 3 fingers click on a track pad.
409
- if (e.button === 1 && e.buttons === 4) {
410
- e.preventDefault();
411
- e.stopPropagation();
412
- const key = (e.target as HTMLElement).dataset.key as string;
413
- this.closeTab(key);
414
- }
415
- }
416
-
417
- protected _tabCloseHandler(e: Event): void {
418
- const icon = e.target as HTMLElement;
419
- const button = icon.parentElement as HTMLElement;
420
- const key = button.dataset.key as string;
421
- if (key) {
422
- e.preventDefault();
423
- e.stopPropagation();
424
- this.closeTab(key);
425
- }
426
- }
427
-
428
- @eventOptions({ passive: true })
429
- protected _tabTouchStartHandler(e: TouchEvent): void {
430
- if (e.targetTouches.length === 3) {
431
- e.preventDefault();
432
- e.stopPropagation();
433
- const key = (e.target as HTMLElement).dataset.key as string;
434
- this.closeTab(key);
435
- }
436
- }
437
-
438
- protected closeTab(key: string): void {
439
- const { panel } = this;
440
- if (!panel) {
441
- return;
442
- }
443
- const item = panel.items.find(i => i.key === key);
444
- if (!item) {
445
- return;
446
- }
447
- if (item.persistent && item.pinned) {
448
- return;
449
- }
450
- panel.removeItem(key);
451
- this.requestUpdate();
452
- this.dispatchEvent(new CustomEvent('closetab', {
453
- bubbles: true,
454
- cancelable: true,
455
- composed: true,
456
- detail: key,
457
- }));
458
- }
459
-
460
- protected _tabKeydownHandler(e: KeyboardEvent): void {
461
- if (e.key === 'Enter') {
462
- this._tabSelectHandler(e);
463
- }
464
- }
465
-
466
- protected _tabDragStart(e: DragEvent): void {
467
- const dt = e.dataTransfer;
468
- if (!dt || this.layoutId === undefined) {
469
- return;
470
- }
471
-
472
- dt.effectAllowed = 'copyMove';
473
- dt.dropEffect = 'move';
474
- const node = e.target as HTMLElement;
475
- dt.setData('text/kind', node.dataset.kind as string);
476
- dt.setData('text/key', node.dataset.key as string);
477
- dt.setData('text/source', this.localName);
478
- dt.setData('layout/id', String(this.layoutId));
479
- }
480
-
481
- protected _tabsDragEnterHandler(e: DragEvent): void {
482
- const dt = e.dataTransfer;
483
- if (!dt || !this.panelCanDrop(e)) {
484
- return;
485
- }
486
- e.stopPropagation();
487
- e.preventDefault();
488
- dt.effectAllowed = 'move';
489
- dt.dropEffect = 'move';
490
- }
491
-
492
- protected _tabsDragoverHandler(e: DragEvent): void {
493
- const dt = e.dataTransfer;
494
- if (!dt || !this.panelCanDrop(e)) {
495
- return;
496
- }
497
- e.stopPropagation();
498
- e.preventDefault();
499
- dt.effectAllowed = 'move';
500
- dt.dropEffect = 'move';
501
- }
502
-
503
- protected _tabsDrop(e: DragEvent): void {
504
- const dt = e.dataTransfer;
505
- if (!dt || !this.panelCanDrop(e) || this.layoutId === undefined) {
506
- return;
507
- }
508
- e.stopPropagation();
509
- e.preventDefault();
510
- const src = dt.getData('text/source');
511
- const key = dt.getData('text/key');
512
- const kind = dt.getData('text/kind');
513
- const lid = dt.getData('layout/id');
514
- const movingTab = src === this.localName;
515
-
516
- const overTab = this.findTab(e);
517
- const toIndex = overTab ? Number(overTab.dataset.index) : undefined;
518
-
519
- if (movingTab) {
520
- if (!lid) {
521
- return;
522
- }
523
- const srcPanel = Number(lid);
524
- if (Number.isNaN(srcPanel)) {
525
- return;
526
- }
527
- this.moveTab(srcPanel, this.layoutId, key, toIndex);
528
- } else {
529
- let dispatch = true;
530
- if (this.panel) {
531
- dispatch = this.panel.addItem({ key, kind, label: 'New tab' }, { index: toIndex });
532
- }
533
- if (dispatch) {
534
- this.dispatchEvent(new CustomEvent('datadrop', {
535
- detail: { kind, key, index: toIndex },
536
- composed: true,
537
- bubbles: true,
538
- cancelable: true,
539
- }));
540
- }
541
- this.requestUpdate();
542
- }
543
- }
544
-
545
- protected moveTab(fromLayout: number, toLayout: number, key: string, toIndex?: number): void {
546
- const { panel } = this;
547
- if (panel) {
548
- panel.manager.moveTab(fromLayout, toLayout, key, toIndex);
549
- }
550
- }
551
-
552
- protected findTab(e: Event): HTMLElement | undefined {
553
- const path = e.composedPath();
554
- while (path.length) {
555
- const node = path.shift() as Element;
556
- if (node.nodeType !== Node.ELEMENT_NODE) {
557
- continue;
558
- }
559
- if (node.classList.contains('layout-tab')) {
560
- return node as HTMLElement;
561
- }
562
- }
563
- return undefined;
564
- }
565
-
566
- override render(): TemplateResult {
567
- return html`
568
- ${this.dragRegionTemplate()}
569
- ${this.tabsTemplate()}
570
- <div class="content">
571
- <slot></slot>
572
- </div>
573
- `;
574
- }
575
-
576
- protected dragRegionTemplate(): TemplateResult | string {
577
- const { inDrag, dragRegion } = this;
578
- if (!inDrag) {
579
- return '';
580
- }
581
- return html`
582
- <div class="drag-region ${dragRegion}"></div>
583
- `;
584
- }
585
-
586
- protected tabsTemplate(): TemplateResult | string {
587
- const { panel } = this;
588
- if (!panel) {
589
- return '';
590
- }
591
- const items = panel.sortedItems();
592
- if (!items) {
593
- return '';
594
- }
595
- const size = items.length;
596
- return html`
597
- <div
598
- class="layout-tabs"
599
- role="tablist"
600
- @dragenter="${this._tabsDragEnterHandler}"
601
- @dragover="${this._tabsDragoverHandler}"
602
- @drop="${this._tabsDrop}"
603
- >
604
- ${items.map((tab, i) => this.tabTemplate(tab, i + 1 === size, items[i + 1]?.key))}
605
- </div>
606
- `;
607
- }
608
-
609
- protected tabTemplate(item: ILayoutItem, last: boolean, nextKey?: string): TemplateResult {
610
- const { key, kind, label = '', index=0, icon, isDirty = false, parent } = item;
611
- const { panel } = this;
612
- const selected = !!panel && panel.selected === key;
613
- const nextSelected = !!nextKey && !!panel && panel.selected === nextKey;
614
- const closable = !item.persistent && !item.pinned;
615
- const classes = {
616
- 'layout-tab': true,
617
- 'is-dirty': isDirty,
618
- selected,
619
- surface1: selected,
620
- };
621
- let title = label;
622
- if (isDirty) {
623
- title = 'Unsaved changes';
624
- }
625
-
626
- const dividerHidden = last || selected || nextSelected;
627
- return html`
628
- <div
629
- data-key="${key}"
630
- data-kind="${kind}"
631
- data-index="${index}"
632
- data-parent="${ifDefined(parent)}"
633
- data-dirty="${isDirty}"
634
- role="tab"
635
- class="${classMap(classes)}"
636
- draggable="true"
637
- @dragstart="${this._tabDragStart}"
638
- @click="${this._tabSelectHandler}"
639
- @pointerdown="${this._tabPointerDownHandler}"
640
- @touchstart="${this._tabTouchStartHandler}"
641
- @keydown="${this._tabKeydownHandler}"
642
- tabindex="0"
643
- >
644
- ${icon ? html`<ui-icon icon="${icon}" class="tab-favicon"></ui-icon>` : ''}
645
- <span class="tab-label" title="${title}">${label}</span>
646
- ${closable ? html`<ui-icon icon="close" class="close-icon" @click="${this._tabCloseHandler}"></ui-icon>` : ''}
647
- </div>
648
- <div class="tab-divider ${dividerHidden ? 'hidden' : ''}"></div>
649
- `;
650
- }
651
- }