@kubex/zinc 2.0.1 → 2.0.3
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 +373 -320
- package/dist/vscode.html-custom-data.json +30 -30
- package/dist/web-types.json +78 -78
- package/dist/zn.d.ts +11 -6
- package/dist/zn.min.js +323 -308
- package/package.json +1 -1
- package/src/components/button/button.component.ts +2 -2
- package/src/components/button/button.test.ts +7 -0
- package/src/components/chart/chart.component.ts +1 -1
- package/src/components/data-table/data-table.component.ts +48 -21
- package/src/components/data-table/data-table.scss +26 -8
- package/src/components/data-table/data-table.test.ts +54 -1
- package/src/components/editor/modules/dialog/dialog.test.ts +1 -1
- package/src/components/expanding-action/expanding-action.component.ts +1 -1
- package/src/components/filter-wrapper/filter-wrapper.component.ts +1 -1
- package/src/components/inline-edit/inline-edit.component.ts +1 -2
- package/src/components/inline-edit/inline-edit.scss +20 -20
- package/src/components/input/input.component.ts +38 -2
- package/src/components/input/input.scss +68 -4
- package/src/components/input/input.test.ts +1 -1
- package/src/components/nav-list/nav-list.test.ts +1 -1
- package/src/components/navbar/navbar.component.ts +14 -8
- package/src/components/panel/panel.component.ts +1 -1
- package/src/components/priority-list/priority-list.test.ts +1 -1
- package/src/components/split-pane/split-pane.component.ts +1 -1
- package/src/components/tabs/tabs.component.ts +2 -2
package/package.json
CHANGED
|
@@ -12,8 +12,8 @@ import ZnDropdown from "../dropdown";
|
|
|
12
12
|
import ZnIcon from "../icon";
|
|
13
13
|
import ZnTooltip from "../tooltip";
|
|
14
14
|
import type {IconColor, IconLibrary} from "../icon";
|
|
15
|
-
import type ZnMenu from "../menu";
|
|
16
15
|
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
16
|
+
import type ZnMenu from "../menu";
|
|
17
17
|
|
|
18
18
|
import sharedStyles from '../../../scss/shared/index.scss';
|
|
19
19
|
import styles from './button.scss';
|
|
@@ -641,7 +641,7 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
|
|
|
641
641
|
'button--icon-button-small': this.iconButton === 'small',
|
|
642
642
|
'button--icon-button-round': this.iconButton === 'round',
|
|
643
643
|
'button--grow': this.grow,
|
|
644
|
-
'button--standard': !this.outline && !this.text,
|
|
644
|
+
'button--standard': !this.outline && (!this.text || this.solid),
|
|
645
645
|
'button--with-icon': this.icon,
|
|
646
646
|
'button--icon-left': this.iconPosition === 'left',
|
|
647
647
|
'button--icon-right': this.iconPosition === 'right',
|
|
@@ -66,6 +66,13 @@ describe('<zn-button>', () => {
|
|
|
66
66
|
expect(base.classList.contains('button--large')).to.be.true;
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
it('keeps the filled style when solid overrides text', async () => {
|
|
70
|
+
const el = await fixture<ZnButton>(html`<zn-button text solid color="primary">Create</zn-button>`);
|
|
71
|
+
const base = el.shadowRoot!.querySelector('[part~="base"]')!;
|
|
72
|
+
expect(base.classList.contains('button--standard')).to.be.true;
|
|
73
|
+
expect(base.classList.contains('button--text')).to.be.false;
|
|
74
|
+
});
|
|
75
|
+
|
|
69
76
|
it('applies the ai color', async () => {
|
|
70
77
|
const el = await fixture<ZnButton>(html`<zn-button color="ai">Summarise</zn-button>`);
|
|
71
78
|
const base = el.shadowRoot!.querySelector('[part~="base"]')!;
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
type RadarIndicator,
|
|
17
17
|
type SeriesItem,
|
|
18
18
|
} from './builders';
|
|
19
|
-
import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
|
|
20
19
|
import { classMap } from 'lit/directives/class-map.js';
|
|
20
|
+
import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
|
|
21
21
|
import { type EChartsModule, loadECharts } from './echarts-loader';
|
|
22
22
|
import { LiveTransport, LiveWindow } from './live';
|
|
23
23
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -4,10 +4,11 @@ import {HasSlotController} from "../../internal/slot";
|
|
|
4
4
|
import {ifDefined} from "lit/directives/if-defined.js";
|
|
5
5
|
import {property, state} from 'lit/decorators.js';
|
|
6
6
|
import {ref} from "lit/directives/ref.js";
|
|
7
|
-
import {styleMap} from "lit/directives/style-map.js";
|
|
8
7
|
import {renderSortIcon, sortKeyHandler} from "../../internal/table-sort";
|
|
9
8
|
import {ReorderController} from "../../internal/reorder";
|
|
10
9
|
import {ResizeController} from '@lit-labs/observers/resize-controller.js';
|
|
10
|
+
import {slideTabIndicator, squishTabIndicator} from "../../internal/tab-indicator";
|
|
11
|
+
import {styleMap} from "lit/directives/style-map.js";
|
|
11
12
|
import {type TableTab, TabRowController} from "../../internal/table-tabs";
|
|
12
13
|
import {Task} from "@lit/task";
|
|
13
14
|
import {unsafeHTML} from "lit/directives/unsafe-html.js";
|
|
@@ -24,18 +25,18 @@ import ZnDataTableSearch from "../data-table-search";
|
|
|
24
25
|
import ZnDropdown from "../dropdown";
|
|
25
26
|
import ZnEmptyState from "../empty-state";
|
|
26
27
|
import ZnHoverContainer from "../hover-container";
|
|
28
|
+
import ZnIcon from "../icon";
|
|
29
|
+
import ZnInput from "../input";
|
|
27
30
|
import ZnMenu from "../menu";
|
|
28
31
|
import ZnMenuItem from "../menu-item";
|
|
32
|
+
import ZnOptGroup from "../opt-group";
|
|
33
|
+
import ZnOption from "../option";
|
|
29
34
|
import ZnPanel from "../panel";
|
|
35
|
+
import ZnSelect from "../select";
|
|
30
36
|
import ZnSkeleton from "../skeleton";
|
|
31
37
|
import ZnStyle from "../style";
|
|
32
38
|
import type ZnDataSelect from "../data-select";
|
|
33
|
-
import ZnIcon from "../icon";
|
|
34
|
-
import ZnInput from "../input";
|
|
35
|
-
import ZnOptGroup from "../opt-group";
|
|
36
|
-
import ZnOption from "../option";
|
|
37
39
|
import type ZnQueryBuilder from "../query-builder";
|
|
38
|
-
import ZnSelect from "../select";
|
|
39
40
|
|
|
40
41
|
// Tables load in such a way that the global sheet has to be re-applied inside the shadow root.
|
|
41
42
|
import bootStyles from '../../../scss/boot.scss';
|
|
@@ -920,6 +921,34 @@ export default class ZnDataTable extends ZincElement {
|
|
|
920
921
|
protected updated(changed: PropertyValues) {
|
|
921
922
|
super.updated(changed);
|
|
922
923
|
this.publishFilterSuggestions();
|
|
924
|
+
void this.syncPageIndicator();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Slides the shared active-page pill to the current page the way navbar pill tabs do
|
|
928
|
+
private async syncPageIndicator() {
|
|
929
|
+
const indicator = this.shadowRoot?.querySelector<HTMLElement>('.table__footer__indicator');
|
|
930
|
+
const active = indicator?.parentElement?.querySelector<ZnButton>('.table__footer__page--active');
|
|
931
|
+
if (!indicator || !active) return;
|
|
932
|
+
|
|
933
|
+
// The button has no size until it has rendered
|
|
934
|
+
await active.updateComplete;
|
|
935
|
+
if (!active.isConnected || !active.classList.contains('table__footer__page--active')) return;
|
|
936
|
+
|
|
937
|
+
const origin = indicator.parentElement!.getBoundingClientRect().left;
|
|
938
|
+
const tab = active.getBoundingClientRect();
|
|
939
|
+
const target = {left: tab.left - origin, right: tab.right - origin};
|
|
940
|
+
const left = `${target.left}px`; const width = `${target.right - target.left}px`;
|
|
941
|
+
if (indicator.style.left === left && indicator.style.width === width) return;
|
|
942
|
+
|
|
943
|
+
// A freshly rendered indicator has no width yet and jumps straight into place
|
|
944
|
+
const placed = indicator.style.width !== '';
|
|
945
|
+
const drawn = indicator.getBoundingClientRect();
|
|
946
|
+
indicator.style.left = left;
|
|
947
|
+
indicator.style.width = width;
|
|
948
|
+
if (placed) {
|
|
949
|
+
squishTabIndicator(indicator);
|
|
950
|
+
slideTabIndicator(indicator, {left: drawn.left - origin, right: drawn.right - origin}, target);
|
|
951
|
+
}
|
|
923
952
|
}
|
|
924
953
|
|
|
925
954
|
// The filter bar can't see the data, so hand it the values already on screen to type against
|
|
@@ -1459,28 +1488,26 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1459
1488
|
</div>`;
|
|
1460
1489
|
}
|
|
1461
1490
|
|
|
1462
|
-
// A window of PAGE_WINDOW pages around the current one, plus the first and last pages;
|
|
1463
|
-
//
|
|
1491
|
+
// A window of PAGE_WINDOW pages around the current one, plus the first and last pages; null marks a gap.
|
|
1492
|
+
// Always PAGE_WINDOW + 4 slots once there are that many pages, so the prev/next buttons never move.
|
|
1464
1493
|
private getPageRange(): (number | null)[] {
|
|
1465
|
-
const
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
return
|
|
1473
|
-
|
|
1474
|
-
if (gap === 2) return [p - 1, p];
|
|
1475
|
-
return gap > 2 ? [null, p] : [p];
|
|
1476
|
-
});
|
|
1494
|
+
const n = this.totalPages;
|
|
1495
|
+
const slots = PAGE_WINDOW + 4;
|
|
1496
|
+
const range = (from: number, count: number) => Array.from({length: count}, (_, i) => from + i);
|
|
1497
|
+
if (n <= slots) return range(1, n);
|
|
1498
|
+
|
|
1499
|
+
const half = Math.floor(PAGE_WINDOW / 2);
|
|
1500
|
+
if (this.page - half <= 3) return [...range(1, slots - 2), null, n];
|
|
1501
|
+
if (this.page + half >= n - 2) return [1, null, ...range(n - slots + 3, slots - 2)];
|
|
1502
|
+
return [1, null, ...range(this.page - half, PAGE_WINDOW), null, n];
|
|
1477
1503
|
}
|
|
1478
1504
|
|
|
1479
1505
|
getPagination() {
|
|
1480
1506
|
if (this.hidePagination || this.totalPages <= 1) return null;
|
|
1481
1507
|
|
|
1482
1508
|
return html`
|
|
1483
|
-
<div class="table__footer__pages">
|
|
1509
|
+
<div class="table__footer__pages" style="--page-digits: ${String(this.totalPages).length}">
|
|
1510
|
+
<span class="table__footer__indicator"></span>
|
|
1484
1511
|
<zn-button class="table__footer__page"
|
|
1485
1512
|
color="transparent"
|
|
1486
1513
|
size="small"
|
|
@@ -311,7 +311,9 @@ table {
|
|
|
311
311
|
padding: 0;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
// Equal shares either side keep the page buttons centred however wide the range label gets
|
|
314
315
|
&__left, &__rows-per-page {
|
|
316
|
+
flex: 1 1 0;
|
|
315
317
|
display: flex;
|
|
316
318
|
flex-direction: row;
|
|
317
319
|
align-items: center;
|
|
@@ -320,14 +322,33 @@ table {
|
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
&__pages {
|
|
325
|
+
// Every slot sized for the widest page number, so buttons don't resize as the window slides
|
|
326
|
+
--page-slot: max(var(--zn-button-height-small, 28px), calc(var(--page-digits, 1) * 1ch + 12px));
|
|
327
|
+
|
|
328
|
+
position: relative;
|
|
329
|
+
isolation: isolate;
|
|
323
330
|
display: flex;
|
|
324
331
|
align-items: center;
|
|
325
332
|
gap: 2px;
|
|
326
333
|
margin-inline: auto;
|
|
327
334
|
}
|
|
328
335
|
|
|
336
|
+
// Placed by left and width, slid with transform only
|
|
337
|
+
&__indicator {
|
|
338
|
+
@include wc.pill-tab-indicator-motion;
|
|
339
|
+
|
|
340
|
+
position: absolute;
|
|
341
|
+
inset-block: 0;
|
|
342
|
+
left: 0;
|
|
343
|
+
z-index: -1;
|
|
344
|
+
transform-origin: left center;
|
|
345
|
+
border-radius: var(--zn-border-radius);
|
|
346
|
+
background-color: var(--zn-table-page-active-background, var(--zn-color-neutral-300));
|
|
347
|
+
pointer-events: none;
|
|
348
|
+
}
|
|
349
|
+
|
|
329
350
|
&__rows-per-page {
|
|
330
|
-
|
|
351
|
+
justify-content: flex-end;
|
|
331
352
|
|
|
332
353
|
zn-select[name="rowPerPage"] {
|
|
333
354
|
--zn-input-background-color: rgb(var(--zn-panel));
|
|
@@ -346,13 +367,10 @@ table {
|
|
|
346
367
|
}
|
|
347
368
|
}
|
|
348
369
|
|
|
349
|
-
&__pages + &__rows-per-page {
|
|
350
|
-
margin-inline-start: 0;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
370
|
&__page::part(base) {
|
|
354
|
-
min-width: var(--
|
|
371
|
+
min-width: var(--page-slot);
|
|
355
372
|
padding: 0 5px;
|
|
373
|
+
font-variant-numeric: tabular-nums;
|
|
356
374
|
justify-content: center;
|
|
357
375
|
color: rgb(var(--zn-color-muted-text, var(--zn-text)));
|
|
358
376
|
font-size: 12px;
|
|
@@ -360,14 +378,14 @@ table {
|
|
|
360
378
|
}
|
|
361
379
|
|
|
362
380
|
&__page--active::part(base) {
|
|
363
|
-
background-color:
|
|
381
|
+
background-color: transparent;
|
|
364
382
|
color: var(--zn-table-page-active-color, rgb(var(--zn-text)));
|
|
365
383
|
}
|
|
366
384
|
|
|
367
385
|
&__gap {
|
|
368
386
|
display: inline-flex;
|
|
369
387
|
justify-content: center;
|
|
370
|
-
min-width: var(--
|
|
388
|
+
min-width: var(--page-slot);
|
|
371
389
|
font-weight: var(--zn-font-weight-semibold, 600);
|
|
372
390
|
}
|
|
373
391
|
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
+
import { centre, dragTo, drop, startDrag } from '../../internal/test-drag';
|
|
2
3
|
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
|
3
|
-
import { centre, drop, dragTo, startDrag } from '../../internal/test-drag';
|
|
4
4
|
import { render } from 'lit';
|
|
5
5
|
import type ZnDataTable from './data-table.component';
|
|
6
6
|
|
|
7
|
+
// WebKit reports a benign "ResizeObserver loop" error when paging re-lays out the footer's zn-select
|
|
8
|
+
window.addEventListener('error', (e: ErrorEvent) => {
|
|
9
|
+
if (typeof e.message === 'string' && e.message.includes('ResizeObserver loop')) {
|
|
10
|
+
e.stopImmediatePropagation();
|
|
11
|
+
e.preventDefault();
|
|
12
|
+
}
|
|
13
|
+
}, true);
|
|
14
|
+
|
|
7
15
|
describe('<zn-data-table>', () => {
|
|
8
16
|
it('should render a component', async () => {
|
|
9
17
|
const el = await fixture(html` <zn-data-table></zn-data-table> `);
|
|
@@ -67,6 +75,51 @@ describe('<zn-data-table>', () => {
|
|
|
67
75
|
}
|
|
68
76
|
});
|
|
69
77
|
|
|
78
|
+
it('renders the same number of page slots on every page', async () => {
|
|
79
|
+
const originalFetch = window.fetch;
|
|
80
|
+
window.fetch = (_url, init) => {
|
|
81
|
+
const {page} = JSON.parse(String(init!.body)) as {page: number};
|
|
82
|
+
return Promise.resolve(new Response(JSON.stringify({
|
|
83
|
+
rows: [{id: '1', cells: [{text: 'Row', column: 'name'}]}],
|
|
84
|
+
page,
|
|
85
|
+
perPage: 10,
|
|
86
|
+
total: 1234,
|
|
87
|
+
}), {status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const el = await fixture<ZnDataTable>(html`
|
|
92
|
+
<zn-data-table data-uri="/test-data" method="POST" headers='{"name": {"key": "name", "label": "Name"}}'></zn-data-table>`);
|
|
93
|
+
const slots = () => [...el.shadowRoot!.querySelectorAll('.table__footer__page, .table__footer__gap')]
|
|
94
|
+
.slice(1, -1).map(s => s.textContent!.trim()).join(',');
|
|
95
|
+
|
|
96
|
+
const seen: Record<number, string> = {};
|
|
97
|
+
for (const page of [1, 4, 5, 60, 120, 121, 124]) {
|
|
98
|
+
(el as unknown as {goToPage: (p: number) => void}).goToPage(page);
|
|
99
|
+
await waitUntil(() => el.shadowRoot!.querySelector('[aria-current="page"]')?.textContent?.trim() === String(page));
|
|
100
|
+
seen[page] = slots();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
expect(seen).to.deep.equal({
|
|
104
|
+
1: '1,2,3,4,5,…,124',
|
|
105
|
+
4: '1,2,3,4,5,…,124',
|
|
106
|
+
5: '1,…,4,5,6,…,124',
|
|
107
|
+
60: '1,…,59,60,61,…,124',
|
|
108
|
+
120: '1,…,119,120,121,…,124',
|
|
109
|
+
121: '1,…,120,121,122,123,124',
|
|
110
|
+
124: '1,…,120,121,122,123,124',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const indicator = el.shadowRoot!.querySelector<HTMLElement>('.table__footer__indicator')!;
|
|
114
|
+
const active = el.shadowRoot!.querySelector<HTMLElement>('.table__footer__page--active')!;
|
|
115
|
+
await waitUntil(() => indicator.getAnimations().length === 0);
|
|
116
|
+
expect(Math.round(indicator.getBoundingClientRect().left)).to.equal(Math.round(active.getBoundingClientRect().left));
|
|
117
|
+
expect(Math.round(indicator.getBoundingClientRect().width)).to.equal(Math.round(active.getBoundingClientRect().width));
|
|
118
|
+
} finally {
|
|
119
|
+
window.fetch = originalFetch;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
70
123
|
it('hides the rows-per-page selector when the dataset fits the smallest page size', async () => {
|
|
71
124
|
const originalFetch = window.fetch;
|
|
72
125
|
window.fetch = () => Promise.resolve(new Response(JSON.stringify({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../../../../../dist/zn.min.js';
|
|
2
2
|
import {expect, fixture, html, oneEvent} from '@open-wc/testing';
|
|
3
|
-
import type DialogComponent from './dialog.component';
|
|
4
3
|
import type {ZnEditorInsertEvent} from '../events/zn-editor-insert';
|
|
4
|
+
import type DialogComponent from './dialog.component';
|
|
5
5
|
|
|
6
6
|
describe('<zn-editor-dialog> content inserts', () => {
|
|
7
7
|
const dialog = () => fixture<DialogComponent>(html`
|
|
@@ -3,10 +3,10 @@ import {type CSSResultGroup, html, nothing, type PropertyValues, unsafeCSS} from
|
|
|
3
3
|
import {deepQuerySelectorAll} from "../../utilities/query";
|
|
4
4
|
import {md5} from "../../utilities/md5";
|
|
5
5
|
import {MutationController} from '@lit-labs/observers/mutation-controller.js';
|
|
6
|
+
import {onHoverIntent} from '../../internal/hover-intent';
|
|
6
7
|
import {property} from 'lit/decorators.js';
|
|
7
8
|
import {styleMap} from "lit/directives/style-map.js";
|
|
8
9
|
import ZincElement from '../../internal/zinc-element';
|
|
9
|
-
import {onHoverIntent} from '../../internal/hover-intent';
|
|
10
10
|
|
|
11
11
|
import styles from './expanding-action.scss';
|
|
12
12
|
|
|
@@ -3,8 +3,8 @@ import {property, state} from "lit/decorators.js";
|
|
|
3
3
|
import {trimControlValue} from '../../internal/form';
|
|
4
4
|
import ZincElement from '../../internal/zinc-element';
|
|
5
5
|
|
|
6
|
-
import type ZnDataSelect from "../data-select";
|
|
7
6
|
import type ZnButton from "../button";
|
|
7
|
+
import type ZnDataSelect from "../data-select";
|
|
8
8
|
import type ZnDataTable from "../data-table";
|
|
9
9
|
import type ZnInput from "../input";
|
|
10
10
|
import type ZnQueryBuilder from "../query-builder";
|
|
@@ -86,7 +86,7 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
|
|
|
86
86
|
|
|
87
87
|
@property({ type: Boolean }) inline: boolean
|
|
88
88
|
|
|
89
|
-
@property({ type: Boolean }) padded: boolean
|
|
89
|
+
@property({ type: Boolean, reflect: true }) padded: boolean
|
|
90
90
|
|
|
91
91
|
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
|
92
92
|
|
|
@@ -436,7 +436,6 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
|
|
|
436
436
|
'ai--focused': this.hasFocus,
|
|
437
437
|
'ai--disabled': this.disabled,
|
|
438
438
|
'ai--inline': this.inline,
|
|
439
|
-
'ai--padded': this.padded,
|
|
440
439
|
'ai--masked': !!this.displayValue,
|
|
441
440
|
})}" dir="ltr">
|
|
442
441
|
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
@use "../../wc";
|
|
2
2
|
|
|
3
3
|
:host {
|
|
4
|
+
--ai-input-height: var(--zn-input-height-medium);
|
|
5
|
+
|
|
4
6
|
display: block;
|
|
7
|
+
box-sizing: border-box;
|
|
5
8
|
container-type: inline-size;
|
|
6
9
|
width: 100%;
|
|
7
10
|
}
|
|
8
11
|
|
|
12
|
+
// On the host rather than the row, so the hover fill and edit actions stay hugging the field
|
|
13
|
+
:host([padded]) {
|
|
14
|
+
padding: var(--zn-spacing-large);
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
.ai {
|
|
10
18
|
--bleed: var(--zn-inline-edit-bleed, 10px);
|
|
11
19
|
display: flex;
|
|
12
|
-
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
// The hover fill hangs out past the value's left edge, so the text stays aligned with the column
|
|
22
|
+
width: calc(100% + var(--bleed));
|
|
23
|
+
margin-inline-start: calc(-1 * var(--bleed));
|
|
24
|
+
padding-inline-start: var(--bleed);
|
|
13
25
|
flex-direction: row;
|
|
14
26
|
gap: 10px;
|
|
15
27
|
border-radius: var(--zn-border-radius-medium);
|
|
16
28
|
transition: background-color var(--zn-transition-fast) ease;
|
|
17
29
|
|
|
18
|
-
// The hover fill hangs out past the value's left edge, so the text stays aligned with the column
|
|
19
|
-
&:not(.ai--padded) {
|
|
20
|
-
box-sizing: border-box;
|
|
21
|
-
width: calc(100% + var(--bleed));
|
|
22
|
-
margin-inline-start: calc(-1 * var(--bleed));
|
|
23
|
-
padding-inline-start: var(--bleed);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
30
|
// The input's own padding takes over from the bleed, keeping its text roughly where the value was
|
|
27
|
-
&--editing
|
|
31
|
+
&--editing {
|
|
28
32
|
padding-inline-start: 0;
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -116,10 +120,6 @@
|
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
&--padded {
|
|
120
|
-
padding: var(--zn-spacing-large);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
123
|
&--inline {
|
|
124
124
|
display: flex;
|
|
125
125
|
flex-direction: row;
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
position: absolute;
|
|
161
161
|
inset-block-start: 0;
|
|
162
162
|
inset-inline-end: var(--action-inset);
|
|
163
|
-
height: var(--
|
|
163
|
+
height: var(--ai-input-height);
|
|
164
164
|
gap: 2px;
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -238,7 +238,7 @@
|
|
|
238
238
|
|
|
239
239
|
&__reveal {
|
|
240
240
|
// Matches the height of the input it stands in for, so entering edit mode doesn't resize the row
|
|
241
|
-
line-height: var(--
|
|
241
|
+
line-height: var(--ai-input-height);
|
|
242
242
|
color: rgb(var(--zn-text));
|
|
243
243
|
|
|
244
244
|
// Fill the row so hovering anywhere over the field reveals the value
|
|
@@ -312,10 +312,10 @@ zn-textarea::part(textarea) {
|
|
|
312
312
|
resize: none;
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
:host([size="small"])
|
|
316
|
-
|
|
315
|
+
:host([size="small"]) {
|
|
316
|
+
--ai-input-height: var(--zn-input-height-small);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
:host([size="large"])
|
|
320
|
-
|
|
319
|
+
:host([size="large"]) {
|
|
320
|
+
--ai-input-height: var(--zn-input-height-large);
|
|
321
321
|
}
|
|
@@ -192,7 +192,7 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
|
|
|
192
192
|
/** Determines whether or no the password is currently visible. Only applies to password types **/
|
|
193
193
|
@property({attribute: 'password-visible', type: Boolean}) passwordVisible: boolean = false;
|
|
194
194
|
|
|
195
|
-
/** Hides the
|
|
195
|
+
/** Hides the increment/decrement buttons on number inputs **/
|
|
196
196
|
@property({attribute: 'no-spin-buttons', type: Boolean}) noSpinButtons: boolean = false;
|
|
197
197
|
|
|
198
198
|
/**
|
|
@@ -505,6 +505,27 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
|
|
|
505
505
|
}
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
/** Whether the value has reached `limit`; `direction` 1 checks a maximum, -1 a minimum. */
|
|
509
|
+
private isAtLimit(limit: number | string | undefined, direction: 1 | -1): boolean {
|
|
510
|
+
const value = parseFloat(String(this.value));
|
|
511
|
+
const bound = parseFloat(String(limit));
|
|
512
|
+
return !isNaN(value) && !isNaN(bound) && (value - bound) * direction >= 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
private handleStep(direction: 'up' | 'down') {
|
|
516
|
+
const previous: unknown = this.value;
|
|
517
|
+
if (direction === 'up') {
|
|
518
|
+
this.stepUp();
|
|
519
|
+
} else {
|
|
520
|
+
this.stepDown();
|
|
521
|
+
}
|
|
522
|
+
if (this.value === previous) return;
|
|
523
|
+
// stepUp()/stepDown() don't fire events, unlike the native spinner they replace
|
|
524
|
+
this.formControlController.updateValidity();
|
|
525
|
+
this.emit('zn-input');
|
|
526
|
+
this.emit('zn-change');
|
|
527
|
+
}
|
|
528
|
+
|
|
508
529
|
private handlePasswordToggle() {
|
|
509
530
|
this.passwordVisible = !this.passwordVisible;
|
|
510
531
|
}
|
|
@@ -861,7 +882,6 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
|
|
|
861
882
|
'input--focused': this.hasFocus,
|
|
862
883
|
'input--empty': !this.value,
|
|
863
884
|
'input--range': isRange,
|
|
864
|
-
'input--no-spin-buttons': this.noSpinButtons && this.type !== 'currency',
|
|
865
885
|
'input--color': this.type === 'color',
|
|
866
886
|
'input--color-invalid': this.colorInvalid,
|
|
867
887
|
})}>
|
|
@@ -973,6 +993,22 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
|
|
|
973
993
|
</button>`
|
|
974
994
|
: ''}
|
|
975
995
|
|
|
996
|
+
${this.type === 'currency' || (this.type === 'number' && !this.noSpinButtons)
|
|
997
|
+
? html`
|
|
998
|
+
<span part="stepper" class="input__stepper">
|
|
999
|
+
<button class="input__stepper-button" type="button" tabindex="-1" aria-label="Decrement"
|
|
1000
|
+
?disabled=${this.disabled || this.readonly || this.isAtLimit(this.min, -1)}
|
|
1001
|
+
@click=${() => this.handleStep('down')}>
|
|
1002
|
+
<zn-icon src="minus@lu" size="16"></zn-icon>
|
|
1003
|
+
</button>
|
|
1004
|
+
<button class="input__stepper-button" type="button" tabindex="-1" aria-label="Increment"
|
|
1005
|
+
?disabled=${this.disabled || this.readonly || this.isAtLimit(this.max, 1)}
|
|
1006
|
+
@click=${() => this.handleStep('up')}>
|
|
1007
|
+
<zn-icon src="plus@lu" size="16"></zn-icon>
|
|
1008
|
+
</button>
|
|
1009
|
+
</span>`
|
|
1010
|
+
: ''}
|
|
1011
|
+
|
|
976
1012
|
<span part="suffix" class="input__suffix">
|
|
977
1013
|
<slot name="suffix"></slot>
|
|
978
1014
|
</span>
|
|
@@ -386,17 +386,81 @@ input[type='text'].input__control[data-color-input] {
|
|
|
386
386
|
display: none;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
/*
|
|
390
|
-
|
|
391
|
-
|
|
389
|
+
/* The built-in spinner is replaced by .input__stepper */
|
|
390
|
+
input[type='number']::-webkit-outer-spin-button,
|
|
391
|
+
input[type='number']::-webkit-inner-spin-button {
|
|
392
392
|
-webkit-appearance: none;
|
|
393
393
|
display: none;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
input[type='number'] {
|
|
397
397
|
-moz-appearance: textfield;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
+
.input__stepper {
|
|
401
|
+
--stepper-button-size: calc(var(--zn-input-height-medium) - 10px);
|
|
402
|
+
display: flex;
|
|
403
|
+
flex: 0 0 auto;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 4px;
|
|
406
|
+
padding-inline: 4px 5px;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.input--x-small .input__stepper {
|
|
410
|
+
--stepper-button-size: calc(var(--zn-input-height-x-small) - 6px);
|
|
411
|
+
gap: 2px;
|
|
412
|
+
padding-inline: 2px 3px;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.input--small .input__stepper {
|
|
416
|
+
--stepper-button-size: calc(var(--zn-input-height-small) - 8px);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.input--large .input__stepper {
|
|
420
|
+
--stepper-button-size: calc(var(--zn-input-height-large) - 10px);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.input__stepper-button {
|
|
424
|
+
display: grid;
|
|
425
|
+
place-content: center;
|
|
426
|
+
flex: 0 0 auto;
|
|
427
|
+
width: var(--stepper-button-size);
|
|
428
|
+
height: var(--stepper-button-size);
|
|
429
|
+
padding: 0;
|
|
430
|
+
border: none;
|
|
431
|
+
border-radius: var(--zn-border-radius-small);
|
|
432
|
+
background: none;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
transition: var(--zn-transition-fast) background-color,
|
|
435
|
+
transform 160ms var(--zn-ease-out, cubic-bezier(0.23, 1, 0.32, 1));
|
|
436
|
+
|
|
437
|
+
zn-icon {
|
|
438
|
+
--icon-color: var(--zn-input-icon-color);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
&:hover:not(:disabled) {
|
|
442
|
+
background-color: var(--zn-color-neutral-100);
|
|
443
|
+
|
|
444
|
+
zn-icon {
|
|
445
|
+
--icon-color: var(--zn-input-icon-color-hover);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Deeper than zn-button's 0.97, which is imperceptible at this size
|
|
450
|
+
&:active:not(:disabled) {
|
|
451
|
+
background-color: var(--zn-color-neutral-200);
|
|
452
|
+
transform: scale(0.9);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
&:disabled {
|
|
456
|
+
cursor: not-allowed;
|
|
457
|
+
|
|
458
|
+
zn-icon {
|
|
459
|
+
--icon-color: var(--zn-input-border-color);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
400
464
|
/* Replace the -webkit-calendar-picker icon */
|
|
401
465
|
input[type='date']::-webkit-calendar-picker-indicator {
|
|
402
466
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="14" width="12.25" viewBox="0 0 448 512"><path fill="%236d7176" d="M128 0c13.3 0 24 10.7 24 24V64H296V24c0-13.3 10.7-24 24-24s24 10.7 24 24V64h40c35.3 0 64 28.7 64 64v16 48V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V192 144 128C0 92.7 28.7 64 64 64h40V24c0-13.3 10.7-24 24-24zM400 192H48V448c0 8.8 7.2 16 16 16H384c8.8 0 16-7.2 16-16V192zM112 256h96c8.8 0 16 7.2 16 16v96c0 8.8-7.2 16-16 16H112c-8.8 0-16-7.2-16-16V272c0-8.8 7.2-16 16-16z"/></svg>');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
-
import {expect, fixture, html, waitUntil} from '@open-wc/testing';
|
|
3
2
|
import {clearFormStoreValues} from '../../utilities/form';
|
|
3
|
+
import {expect, fixture, html, waitUntil} from '@open-wc/testing';
|
|
4
4
|
import type ZnInput from './input.component';
|
|
5
5
|
import type ZnSlashMenu from '../slash-menu/slash-menu.component';
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
2
|
import {aTimeout, expect, fixture, html} from '@open-wc/testing';
|
|
3
|
-
import {centre,
|
|
3
|
+
import {centre, dragTo, drop, startDrag} from '../../internal/test-drag';
|
|
4
4
|
import type ZnNavItem from '../nav-item';
|
|
5
5
|
import type ZnNavList from './nav-list.component';
|
|
6
6
|
|