@exmg/exm-chip-input 1.2.8 → 1.2.9
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/.rollup.cache/root/repo/packages/exm-chip-input/dist/dropdown/dropdown-container.d.ts +1 -1
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-input-dropdown.d.ts +1 -1
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-input.d.ts +22 -8
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-input.js +87 -3
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-input.stories.d.ts +38 -0
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-input.stories.js +139 -0
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-set.d.ts +14 -0
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip-set.js +121 -0
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip.d.ts +2 -13
- package/.rollup.cache/root/repo/packages/exm-chip-input/dist/exm-chip.js +7 -36
- package/dist/dropdown/dropdown-container.d.ts +1 -1
- package/dist/dropdown/dropdown-container.js +1 -1
- package/dist/exm-chip-input-dropdown.d.ts +1 -1
- package/dist/exm-chip-input-dropdown.js +1 -1
- package/dist/exm-chip-input.d.ts +22 -8
- package/dist/exm-chip-input.js +88 -4
- package/dist/exm-chip-input.stories.d.ts +38 -0
- package/dist/exm-chip-set.d.ts +14 -0
- package/dist/exm-chip-set.js +124 -0
- package/dist/exm-chip.d.ts +2 -13
- package/dist/exm-chip.js +8 -37
- package/dist/node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js +31 -0
- package/package.json +2 -2
- package/dist/validator/chip-validator.js +0 -68
|
@@ -30,6 +30,6 @@ export declare class ExmChipInputDropdown extends ChipSet {
|
|
|
30
30
|
private onMenuClosed;
|
|
31
31
|
private _removeSelected;
|
|
32
32
|
private onKeydown;
|
|
33
|
-
protected render(): import("lit
|
|
33
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
34
34
|
private updateTabIndicesOverride;
|
|
35
35
|
}
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
interface HTMLElementTagNameMap {
|
|
4
|
-
'exm-chip-input': ExmChipInput;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
2
|
+
import { ExmChipSet } from './exm-chip-set.js';
|
|
7
3
|
/**
|
|
8
4
|
*
|
|
9
5
|
* @final
|
|
10
6
|
* @suppress {visibility}
|
|
11
7
|
*/
|
|
12
|
-
export declare class ExmChipInput extends
|
|
8
|
+
export declare class ExmChipInput extends ExmChipSet {
|
|
9
|
+
static formAssociated: boolean;
|
|
13
10
|
label: string;
|
|
14
11
|
supportingText: string;
|
|
12
|
+
required: boolean;
|
|
13
|
+
value: string;
|
|
14
|
+
min: number;
|
|
15
|
+
max?: number;
|
|
15
16
|
chipCount: number;
|
|
16
17
|
static styles: import("lit").CSSResult[];
|
|
17
|
-
|
|
18
|
+
checkValidity(): boolean;
|
|
19
|
+
reportValidity(): boolean;
|
|
20
|
+
protected firstUpdated(): void;
|
|
21
|
+
updated(changedProperties: PropertyValues): void;
|
|
22
|
+
private validate;
|
|
23
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
24
|
+
fireChange(name: string): void;
|
|
25
|
+
valuesToString(): string;
|
|
26
|
+
onChanges(): void;
|
|
18
27
|
private updateTabIndicesOverride;
|
|
19
28
|
}
|
|
29
|
+
declare global {
|
|
30
|
+
interface HTMLElementTagNameMap {
|
|
31
|
+
'exm-chip-input': ExmChipInput;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,24 +1,76 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
3
3
|
import { style } from './styles/exm-chip-input-css.js';
|
|
4
|
-
import { ChipSet } from '@material/web/chips/internal/chip-set.js';
|
|
5
4
|
import { styles } from '@material/web/chips/internal/chip-set-styles.js';
|
|
6
5
|
import { html, nothing } from 'lit';
|
|
7
6
|
import { property, state } from 'lit/decorators.js';
|
|
8
7
|
import { sharedChipStyle } from './styles/exm-chips-shared-css.js';
|
|
9
8
|
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
import { ExmChipSet } from './exm-chip-set.js';
|
|
10
10
|
/**
|
|
11
11
|
*
|
|
12
12
|
* @final
|
|
13
13
|
* @suppress {visibility}
|
|
14
14
|
*/
|
|
15
|
-
let ExmChipInput = class ExmChipInput extends
|
|
15
|
+
let ExmChipInput = class ExmChipInput extends ExmChipSet {
|
|
16
16
|
constructor() {
|
|
17
17
|
super(...arguments);
|
|
18
18
|
this.label = '';
|
|
19
19
|
this.supportingText = '';
|
|
20
|
+
this.required = false;
|
|
21
|
+
this.value = '';
|
|
22
|
+
this.min = 1;
|
|
23
|
+
this.max = undefined;
|
|
20
24
|
this.chipCount = 0;
|
|
21
25
|
}
|
|
26
|
+
checkValidity() {
|
|
27
|
+
var _a;
|
|
28
|
+
return (_a = this.internals) === null || _a === void 0 ? void 0 : _a.checkValidity();
|
|
29
|
+
}
|
|
30
|
+
reportValidity() {
|
|
31
|
+
var _a;
|
|
32
|
+
return (_a = this.internals) === null || _a === void 0 ? void 0 : _a.reportValidity();
|
|
33
|
+
}
|
|
34
|
+
firstUpdated() {
|
|
35
|
+
this.validate();
|
|
36
|
+
}
|
|
37
|
+
updated(changedProperties) {
|
|
38
|
+
var _a;
|
|
39
|
+
if (changedProperties.has('value')) {
|
|
40
|
+
(_a = this.internals) === null || _a === void 0 ? void 0 : _a.setFormValue(this.value);
|
|
41
|
+
this.validate();
|
|
42
|
+
}
|
|
43
|
+
if (changedProperties.has('required')) {
|
|
44
|
+
this.validate();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
validate() {
|
|
48
|
+
var _a, _b, _c;
|
|
49
|
+
const { chips } = this;
|
|
50
|
+
this.chipCount = chips.length;
|
|
51
|
+
let selectedCount = 0;
|
|
52
|
+
for (const chip of chips) {
|
|
53
|
+
selectedCount = chip.selected ? selectedCount + 1 : selectedCount;
|
|
54
|
+
}
|
|
55
|
+
const isInvalid = this.min && this.max !== undefined && this.max !== 0
|
|
56
|
+
? selectedCount < this.min || selectedCount > this.max
|
|
57
|
+
: selectedCount < this.min;
|
|
58
|
+
if (isInvalid) {
|
|
59
|
+
if (this.max && (this.max !== undefined || this.max !== 0) && selectedCount > this.max) {
|
|
60
|
+
(_a = this.internals) === null || _a === void 0 ? void 0 : _a.setValidity({
|
|
61
|
+
rangeOverflow: true,
|
|
62
|
+
}, `Please select a maximum of ${this.max}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
(_b = this.internals) === null || _b === void 0 ? void 0 : _b.setValidity({
|
|
66
|
+
rangeUnderflow: true,
|
|
67
|
+
}, `Please select a minimum of ${this.min}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
(_c = this.internals) === null || _c === void 0 ? void 0 : _c.setValidity({});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
22
74
|
render() {
|
|
23
75
|
const containerClasses = { 'has-label': !!this.label };
|
|
24
76
|
const labelClasses = { 'not-empty': this.chipCount > 0 };
|
|
@@ -27,11 +79,30 @@ let ExmChipInput = class ExmChipInput extends ChipSet {
|
|
|
27
79
|
<div class="background"></div>
|
|
28
80
|
<div class="state-layer"></div>
|
|
29
81
|
<div class="label ${classMap(labelClasses)}">${this.label}</div>
|
|
30
|
-
<div class="items"
|
|
82
|
+
<div class="items">
|
|
83
|
+
<slot @slotchange=${this.updateTabIndicesOverride} @change=${this.onChanges}></slot>
|
|
84
|
+
</div>
|
|
31
85
|
${this.supportingText ? html `<div class="supporting-text">${this.supportingText}</div>` : nothing}
|
|
32
86
|
</div>
|
|
33
87
|
`;
|
|
34
88
|
}
|
|
89
|
+
fireChange(name) {
|
|
90
|
+
this.dispatchEvent(new CustomEvent(name, {
|
|
91
|
+
detail: this.value,
|
|
92
|
+
bubbles: true,
|
|
93
|
+
composed: true,
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
valuesToString() {
|
|
97
|
+
const values = this.chips.map((value) => (value.selected ? value.value : null));
|
|
98
|
+
return values.filter((value) => value !== null).join();
|
|
99
|
+
}
|
|
100
|
+
onChanges() {
|
|
101
|
+
this.value = this.valuesToString();
|
|
102
|
+
this.required && this.validate();
|
|
103
|
+
this.fireChange('change');
|
|
104
|
+
this.fireChange('input');
|
|
105
|
+
}
|
|
35
106
|
updateTabIndicesOverride() {
|
|
36
107
|
// The chip that should be focusable is either the chip that currently has
|
|
37
108
|
// focus or the first chip that can be focused.
|
|
@@ -59,6 +130,7 @@ let ExmChipInput = class ExmChipInput extends ChipSet {
|
|
|
59
130
|
}
|
|
60
131
|
}
|
|
61
132
|
};
|
|
133
|
+
ExmChipInput.formAssociated = true;
|
|
62
134
|
ExmChipInput.styles = [styles, sharedChipStyle, style];
|
|
63
135
|
__decorate([
|
|
64
136
|
property({ type: String })
|
|
@@ -66,6 +138,18 @@ __decorate([
|
|
|
66
138
|
__decorate([
|
|
67
139
|
property({ type: String, attribute: 'supporting-text' })
|
|
68
140
|
], ExmChipInput.prototype, "supportingText", void 0);
|
|
141
|
+
__decorate([
|
|
142
|
+
property({ type: Boolean, reflect: true })
|
|
143
|
+
], ExmChipInput.prototype, "required", void 0);
|
|
144
|
+
__decorate([
|
|
145
|
+
property({ type: String, reflect: true })
|
|
146
|
+
], ExmChipInput.prototype, "value", void 0);
|
|
147
|
+
__decorate([
|
|
148
|
+
property({ type: Number, reflect: true })
|
|
149
|
+
], ExmChipInput.prototype, "min", void 0);
|
|
150
|
+
__decorate([
|
|
151
|
+
property({ type: Number, reflect: true })
|
|
152
|
+
], ExmChipInput.prototype, "max", void 0);
|
|
69
153
|
__decorate([
|
|
70
154
|
state()
|
|
71
155
|
], ExmChipInput.prototype, "chipCount", void 0);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StoryObj } from '@storybook/web-components-vite';
|
|
2
|
+
import { ExmChipInputDropdown } from './exm-chip-input-dropdown.js';
|
|
3
|
+
import { ExmChipInput } from './exm-chip-input.js';
|
|
4
|
+
import { ExmChip } from './exm-chip.js';
|
|
5
|
+
import './exm-chip-input-dropdown.js';
|
|
6
|
+
import './exm-chip-input.js';
|
|
7
|
+
import '@exmg/exm-form';
|
|
8
|
+
import './exm-chip.js';
|
|
9
|
+
declare const meta: {
|
|
10
|
+
title: string;
|
|
11
|
+
tags: string[];
|
|
12
|
+
render: (args: Partial<ExmChip>) => import("lit").TemplateResult<1>;
|
|
13
|
+
argTypes: {
|
|
14
|
+
name: {
|
|
15
|
+
control: string;
|
|
16
|
+
};
|
|
17
|
+
value: {
|
|
18
|
+
control: "text";
|
|
19
|
+
};
|
|
20
|
+
label: {
|
|
21
|
+
control: "text";
|
|
22
|
+
};
|
|
23
|
+
ariaLabel: {
|
|
24
|
+
control: "text";
|
|
25
|
+
};
|
|
26
|
+
required: {
|
|
27
|
+
control: "boolean";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export default meta;
|
|
32
|
+
export declare const Chip: StoryObj<ExmChip>;
|
|
33
|
+
export declare const ChipInput: StoryObj<ExmChipInput>;
|
|
34
|
+
export declare const ChipInputDropdown: StoryObj<ExmChipInputDropdown>;
|
|
35
|
+
type ExmChipInputExtended = ExmChipInput & {
|
|
36
|
+
formSubmit: (e: any) => void;
|
|
37
|
+
};
|
|
38
|
+
export declare const ChipInputValidation: StoryObj<ExmChipInputExtended>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import './exm-chip-input-dropdown.js';
|
|
4
|
+
import './exm-chip-input.js';
|
|
5
|
+
import '@exmg/exm-form';
|
|
6
|
+
import './exm-chip.js';
|
|
7
|
+
const supportingText = 'Supporting text option';
|
|
8
|
+
const chipTemplate = (args) => html `
|
|
9
|
+
<exm-chip
|
|
10
|
+
name="${args.name || ''}"
|
|
11
|
+
value="${args.value || ''}"
|
|
12
|
+
label="${args.label || ''}"
|
|
13
|
+
aria-label="${args.ariaLabel || ''}"
|
|
14
|
+
?required="${args.required}"
|
|
15
|
+
?selected="${args.selected}"
|
|
16
|
+
></exm-chip>
|
|
17
|
+
`;
|
|
18
|
+
const meta = {
|
|
19
|
+
title: 'Packages/Chip Input',
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
render: chipTemplate,
|
|
22
|
+
argTypes: {
|
|
23
|
+
name: { control: 'text' },
|
|
24
|
+
value: { control: 'text' },
|
|
25
|
+
label: { control: 'text' },
|
|
26
|
+
ariaLabel: { control: 'text' },
|
|
27
|
+
required: { control: 'boolean' },
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default meta;
|
|
31
|
+
const lorem = faker.lorem.word();
|
|
32
|
+
export const Chip = {
|
|
33
|
+
args: {
|
|
34
|
+
name: lorem,
|
|
35
|
+
value: lorem,
|
|
36
|
+
label: lorem,
|
|
37
|
+
ariaLabel: lorem,
|
|
38
|
+
required: false,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
|
|
42
|
+
const ChipInputTemplate = (args) => html `
|
|
43
|
+
<exm-chip-input label="${args.label}" supporting-text="${args.supportingText}"> ${args.children} </exm-chip-input>
|
|
44
|
+
`;
|
|
45
|
+
const selectedWeekDay = faker.number.int({ max: weekdays.length - 1 });
|
|
46
|
+
const days = html `${weekdays.map((d, i) => chipTemplate({
|
|
47
|
+
name: d.substring(0, 3),
|
|
48
|
+
value: d.substring(0, 3),
|
|
49
|
+
label: d.substring(0, 3),
|
|
50
|
+
ariaLabel: d,
|
|
51
|
+
selected: i == selectedWeekDay,
|
|
52
|
+
}))}`;
|
|
53
|
+
export const ChipInput = {
|
|
54
|
+
render: ChipInputTemplate,
|
|
55
|
+
argTypes: {
|
|
56
|
+
label: { control: 'text' },
|
|
57
|
+
supportingText: { control: 'text' },
|
|
58
|
+
},
|
|
59
|
+
args: {
|
|
60
|
+
label: 'Dates (Chip input)',
|
|
61
|
+
supportingText,
|
|
62
|
+
children: days,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
// eslint-disable-next-line prefer-spread
|
|
66
|
+
const fruitsArray = Array.apply(null, Array(5)).map((_unused) => faker.food.fruit());
|
|
67
|
+
const fruits = (preSelected) => html `${fruitsArray.map((f, i) => chipTemplate({
|
|
68
|
+
name: `${f}-name`,
|
|
69
|
+
value: f,
|
|
70
|
+
label: f,
|
|
71
|
+
ariaLabel: f,
|
|
72
|
+
selected: i === preSelected,
|
|
73
|
+
}))}`;
|
|
74
|
+
const ChipInputDropdownTemplate = (args) => html `
|
|
75
|
+
<exm-chip-input-dropdown
|
|
76
|
+
label="${args.label}"
|
|
77
|
+
aria-labelledby="fruit-label"
|
|
78
|
+
dropdown-title="${args.dropdownTitle}"
|
|
79
|
+
supporting-text="${args.supportingText}"
|
|
80
|
+
>
|
|
81
|
+
${args.children}
|
|
82
|
+
</exm-chip-input-dropdown>
|
|
83
|
+
`;
|
|
84
|
+
export const ChipInputDropdown = {
|
|
85
|
+
render: ChipInputDropdownTemplate,
|
|
86
|
+
argTypes: {
|
|
87
|
+
label: { control: 'text' },
|
|
88
|
+
supportingText: { control: 'text' },
|
|
89
|
+
dropdownTitle: { control: 'text' },
|
|
90
|
+
},
|
|
91
|
+
args: {
|
|
92
|
+
label: 'Preferred Fruit (Chip input dropdown)',
|
|
93
|
+
dropdownTitle: 'Select fruits',
|
|
94
|
+
supportingText,
|
|
95
|
+
children: fruits(1),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const ChipInputTemplateWithForm = (args) => html `
|
|
99
|
+
<exm-form
|
|
100
|
+
hide-cancel-button
|
|
101
|
+
submitBtn="valid-submit"
|
|
102
|
+
@form-success=${args.formSuccess}
|
|
103
|
+
@form-error=${args.formError}
|
|
104
|
+
@form-submit-finished=${args.formSubmitFinished}
|
|
105
|
+
@form-submit=${args.formSubmit}
|
|
106
|
+
@form-cancel=${args.formCancel}
|
|
107
|
+
>
|
|
108
|
+
<form>
|
|
109
|
+
<exm-chip-input
|
|
110
|
+
name="fruit"
|
|
111
|
+
label="${args.label}"
|
|
112
|
+
supporting-text="${args.supportingText}"
|
|
113
|
+
min="${args.min}"
|
|
114
|
+
max="${args.max}"
|
|
115
|
+
required
|
|
116
|
+
>
|
|
117
|
+
${args.children}
|
|
118
|
+
</exm-chip-input>
|
|
119
|
+
</form>
|
|
120
|
+
</exm-form>
|
|
121
|
+
`;
|
|
122
|
+
export const ChipInputValidation = {
|
|
123
|
+
render: ChipInputTemplateWithForm,
|
|
124
|
+
argTypes: {
|
|
125
|
+
label: { control: 'text' },
|
|
126
|
+
supportingText: { control: 'text' },
|
|
127
|
+
min: { control: 'number' },
|
|
128
|
+
max: { control: 'number' },
|
|
129
|
+
},
|
|
130
|
+
args: {
|
|
131
|
+
min: 2,
|
|
132
|
+
max: undefined,
|
|
133
|
+
label: 'Preferred Fruit (min required)',
|
|
134
|
+
supportingText,
|
|
135
|
+
children: fruits(-1),
|
|
136
|
+
formSubmit: (e) => console.log(e),
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=exm-chip-input.stories.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { ExmChip } from './exm-chip.js';
|
|
3
|
+
/**
|
|
4
|
+
* A chip set component.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ExmChipSet extends LitElement {
|
|
7
|
+
get chips(): ExmChip[];
|
|
8
|
+
private readonly childElements;
|
|
9
|
+
internals: ElementInternals;
|
|
10
|
+
constructor();
|
|
11
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
12
|
+
private handleKeyDown;
|
|
13
|
+
updateTabIndices(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, isServer, LitElement } from 'lit';
|
|
3
|
+
import { queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
import { ExmChip } from './exm-chip.js';
|
|
5
|
+
/**
|
|
6
|
+
* A chip set component.
|
|
7
|
+
*/
|
|
8
|
+
export class ExmChipSet extends LitElement {
|
|
9
|
+
get chips() {
|
|
10
|
+
return this.childElements.filter((child) => child instanceof ExmChip);
|
|
11
|
+
}
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.internals = this.attachInternals();
|
|
15
|
+
if (!isServer) {
|
|
16
|
+
this.addEventListener('focusin', this.updateTabIndices.bind(this));
|
|
17
|
+
this.addEventListener('update-focus', this.updateTabIndices.bind(this));
|
|
18
|
+
this.addEventListener('keydown', this.handleKeyDown.bind(this));
|
|
19
|
+
this.internals.role = 'toolbar';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
return html `<slot @slotchange=${this.updateTabIndices}></slot>`;
|
|
24
|
+
}
|
|
25
|
+
handleKeyDown(event) {
|
|
26
|
+
const isLeft = event.key === 'ArrowLeft';
|
|
27
|
+
const isRight = event.key === 'ArrowRight';
|
|
28
|
+
const isHome = event.key === 'Home';
|
|
29
|
+
const isEnd = event.key === 'End';
|
|
30
|
+
// Ignore non-navigation keys
|
|
31
|
+
if (!isLeft && !isRight && !isHome && !isEnd) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const { chips } = this;
|
|
35
|
+
// Don't try to select another chip if there aren't any.
|
|
36
|
+
if (chips.length < 2) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Prevent default interactions, such as scrolling.
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
if (isHome || isEnd) {
|
|
42
|
+
const index = isHome ? 0 : chips.length - 1;
|
|
43
|
+
chips[index].focus({ trailing: isEnd });
|
|
44
|
+
this.updateTabIndices();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Check if moving forwards or backwards
|
|
48
|
+
const isRtl = getComputedStyle(this).direction === 'rtl';
|
|
49
|
+
const forwards = isRtl ? isLeft : isRight;
|
|
50
|
+
const focusedChip = chips.find((chip) => chip.matches(':focus-within'));
|
|
51
|
+
if (!focusedChip) {
|
|
52
|
+
// If there is not already a chip focused, select the first or last chip
|
|
53
|
+
// based on the direction we're traveling.
|
|
54
|
+
const nextChip = forwards ? chips[0] : chips[chips.length - 1];
|
|
55
|
+
nextChip.focus({ trailing: !forwards });
|
|
56
|
+
this.updateTabIndices();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const currentIndex = chips.indexOf(focusedChip);
|
|
60
|
+
let nextIndex = forwards ? currentIndex + 1 : currentIndex - 1;
|
|
61
|
+
// Search for the next sibling that is not disabled to select.
|
|
62
|
+
// If we return to the host index, there is nothing to select.
|
|
63
|
+
while (nextIndex !== currentIndex) {
|
|
64
|
+
if (nextIndex >= chips.length) {
|
|
65
|
+
// Return to start if moving past the last item.
|
|
66
|
+
nextIndex = 0;
|
|
67
|
+
}
|
|
68
|
+
else if (nextIndex < 0) {
|
|
69
|
+
// Go to end if moving before the first item.
|
|
70
|
+
nextIndex = chips.length - 1;
|
|
71
|
+
}
|
|
72
|
+
// Check if the next sibling is disabled. If so,
|
|
73
|
+
// move the index and continue searching.
|
|
74
|
+
//
|
|
75
|
+
// Some toolbar items may be focusable when disabled for increased
|
|
76
|
+
// visibility.
|
|
77
|
+
const nextChip = chips[nextIndex];
|
|
78
|
+
if (nextChip.disabled && !nextChip.alwaysFocusable) {
|
|
79
|
+
if (forwards) {
|
|
80
|
+
nextIndex++;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
nextIndex--;
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
nextChip.focus({ trailing: !forwards });
|
|
88
|
+
this.updateTabIndices();
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
updateTabIndices() {
|
|
93
|
+
// The chip that should be focusable is either the chip that currently has
|
|
94
|
+
// focus or the first chip that can be focused.
|
|
95
|
+
const { chips } = this;
|
|
96
|
+
let chipToFocus;
|
|
97
|
+
for (const chip of chips) {
|
|
98
|
+
const isChipFocusable = chip.alwaysFocusable || !chip.disabled;
|
|
99
|
+
const chipIsFocused = chip.matches(':focus-within');
|
|
100
|
+
if (chipIsFocused && isChipFocusable) {
|
|
101
|
+
// Found the first chip that is actively focused. This overrides the
|
|
102
|
+
// first focusable chip found.
|
|
103
|
+
chipToFocus = chip;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (isChipFocusable && !chipToFocus) {
|
|
107
|
+
chipToFocus = chip;
|
|
108
|
+
}
|
|
109
|
+
// Disable non-focused chips. If we disable all of them, we'll grant focus
|
|
110
|
+
// to the first focusable child that was found.
|
|
111
|
+
chip.tabIndex = -1;
|
|
112
|
+
}
|
|
113
|
+
if (chipToFocus) {
|
|
114
|
+
chipToFocus.tabIndex = 0;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
__decorate([
|
|
119
|
+
queryAssignedElements()
|
|
120
|
+
], ExmChipSet.prototype, "childElements", void 0);
|
|
121
|
+
//# sourceMappingURL=exm-chip-set.js.map
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { FilterChip } from '@material/web/chips/internal/filter-chip.js';
|
|
2
|
-
import { createValidator, getValidityAnchor } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
3
|
-
import { getFormState, getFormValue } from '@material/web/labs/behaviors/form-associated.js';
|
|
4
|
-
import { ChipValidator } from './validator/chip-validator.js';
|
|
5
2
|
declare global {
|
|
6
3
|
interface HTMLElementTagNameMap {
|
|
7
4
|
'exm-chip': ExmChip;
|
|
8
5
|
}
|
|
9
6
|
}
|
|
10
7
|
declare const CHECKED: unique symbol;
|
|
11
|
-
declare const FilterChipBaseClass: import("@material/web/labs/behaviors/mixin.js").MixinReturn<
|
|
8
|
+
declare const FilterChipBaseClass: import("@material/web/labs/behaviors/mixin.js").MixinReturn<typeof FilterChip, import("@material/web/labs/behaviors/focusable.js").Focusable>;
|
|
12
9
|
/**
|
|
13
10
|
*
|
|
14
11
|
* @final
|
|
@@ -36,16 +33,8 @@ export declare class ExmChip extends FilterChipBaseClass {
|
|
|
36
33
|
value: string;
|
|
37
34
|
private readonly container;
|
|
38
35
|
disabled: boolean;
|
|
39
|
-
name: string;
|
|
40
|
-
[getFormValue](): string | null;
|
|
41
|
-
[getFormState](): string;
|
|
42
36
|
constructor();
|
|
43
|
-
|
|
44
|
-
formStateRestoreCallback(state: string): void;
|
|
45
|
-
protected updated(): void;
|
|
46
|
-
protected renderPrimaryAction(content: unknown): import("lit-html").TemplateResult<1>;
|
|
37
|
+
protected renderPrimaryAction(content: unknown): import("lit").TemplateResult<1>;
|
|
47
38
|
private _handleClick;
|
|
48
|
-
[createValidator](): ChipValidator;
|
|
49
|
-
[getValidityAnchor](): HTMLElement;
|
|
50
39
|
}
|
|
51
40
|
export {};
|
|
@@ -8,17 +8,13 @@ import { styles as selectableStyles } from '@material/web/chips/internal/selecta
|
|
|
8
8
|
import { styles as sharedStyles } from '@material/web/chips/internal/shared-styles.js';
|
|
9
9
|
import { styles as trailingIconStyles } from '@material/web/chips/internal/trailing-icon-styles.js';
|
|
10
10
|
import { redispatchEvent } from '@material/web/internal/events/redispatch-event.js';
|
|
11
|
-
import { createValidator, getValidityAnchor, mixinConstraintValidation, } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
12
|
-
import { getFormState, getFormValue, mixinFormAssociated } from '@material/web/labs/behaviors/form-associated.js';
|
|
13
|
-
import { internals, mixinElementInternals } from '@material/web/labs/behaviors/element-internals.js';
|
|
14
11
|
import { mixinFocusable } from '@material/web/labs/behaviors/focusable.js';
|
|
15
|
-
import { ChipValidator } from './validator/chip-validator.js';
|
|
16
12
|
import { observer } from '@exmg/lit-base';
|
|
17
13
|
import { SelectionController } from './selection-controller.js';
|
|
18
14
|
import { html, nothing } from 'lit';
|
|
19
15
|
const CHECKED = Symbol('checked');
|
|
20
16
|
// Separate variable needed for closure.
|
|
21
|
-
const FilterChipBaseClass =
|
|
17
|
+
const FilterChipBaseClass = mixinFocusable(FilterChip);
|
|
22
18
|
/**
|
|
23
19
|
*
|
|
24
20
|
* @final
|
|
@@ -39,12 +35,6 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
39
35
|
this[CHECKED] = checked;
|
|
40
36
|
this.requestUpdate('checked', wasChecked);
|
|
41
37
|
}
|
|
42
|
-
[(_a = CHECKED, getFormValue)]() {
|
|
43
|
-
return this.checked ? this.value : null;
|
|
44
|
-
}
|
|
45
|
-
[getFormState]() {
|
|
46
|
-
return String(this.checked);
|
|
47
|
-
}
|
|
48
38
|
constructor() {
|
|
49
39
|
super();
|
|
50
40
|
this.removable = false;
|
|
@@ -61,18 +51,6 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
61
51
|
*/
|
|
62
52
|
this.value = 'on';
|
|
63
53
|
this.addController(this.selectionController);
|
|
64
|
-
this[internals].role = 'radio';
|
|
65
|
-
}
|
|
66
|
-
formResetCallback() {
|
|
67
|
-
// The checked property does not reflect, so the original attribute set by
|
|
68
|
-
// the user is used to determine the default value.
|
|
69
|
-
this.checked = this.hasAttribute('checked');
|
|
70
|
-
}
|
|
71
|
-
formStateRestoreCallback(state) {
|
|
72
|
-
this.checked = state === 'true';
|
|
73
|
-
}
|
|
74
|
-
updated() {
|
|
75
|
-
this[internals].ariaChecked = String(this.checked);
|
|
76
54
|
}
|
|
77
55
|
renderPrimaryAction(content) {
|
|
78
56
|
const { ariaLabel } = this;
|
|
@@ -97,6 +75,11 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
97
75
|
// event listener.
|
|
98
76
|
const prevValue = this.selected;
|
|
99
77
|
this.selected = !this.selected;
|
|
78
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
79
|
+
detail: this.selected,
|
|
80
|
+
bubbles: true,
|
|
81
|
+
composed: true,
|
|
82
|
+
}));
|
|
100
83
|
const preventDefault = !redispatchEvent(this, event);
|
|
101
84
|
if (preventDefault) {
|
|
102
85
|
// We should not do `this.selected = !this.selected`, since a client
|
|
@@ -106,20 +89,8 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
106
89
|
return;
|
|
107
90
|
}
|
|
108
91
|
}
|
|
109
|
-
[createValidator]() {
|
|
110
|
-
return new ChipValidator(() => {
|
|
111
|
-
if (!this.selectionController) {
|
|
112
|
-
// Validation runs on superclass construction, so selection controller
|
|
113
|
-
// might not actually be ready until this class constructs.
|
|
114
|
-
return [this];
|
|
115
|
-
}
|
|
116
|
-
return this.selectionController.controls;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
[getValidityAnchor]() {
|
|
120
|
-
return this.container;
|
|
121
|
-
}
|
|
122
92
|
};
|
|
93
|
+
_a = CHECKED;
|
|
123
94
|
ExmChip.styles = [sharedStyles, elevatedStyles, trailingIconStyles, selectableStyles, styles];
|
|
124
95
|
__decorate([
|
|
125
96
|
property({ type: Boolean })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __decorate } from 'tslib';
|
|
1
|
+
import { __decorate } from '../node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js';
|
|
2
2
|
import { css, LitElement, html } from 'lit';
|
|
3
3
|
import { property, customElement } from 'lit/decorators.js';
|
|
4
4
|
|
|
@@ -30,6 +30,6 @@ export declare class ExmChipInputDropdown extends ChipSet {
|
|
|
30
30
|
private onMenuClosed;
|
|
31
31
|
private _removeSelected;
|
|
32
32
|
private onKeydown;
|
|
33
|
-
protected render(): import("lit
|
|
33
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
34
34
|
private updateTabIndicesOverride;
|
|
35
35
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __decorate } from 'tslib';
|
|
1
|
+
import { __decorate } from './node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js';
|
|
2
2
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
3
3
|
import '@material/web/iconbutton/icon-button.js';
|
|
4
4
|
import '@material/web/button/text-button.js';
|
package/dist/exm-chip-input.d.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
interface HTMLElementTagNameMap {
|
|
4
|
-
'exm-chip-input': ExmChipInput;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
2
|
+
import { ExmChipSet } from './exm-chip-set.js';
|
|
7
3
|
/**
|
|
8
4
|
*
|
|
9
5
|
* @final
|
|
10
6
|
* @suppress {visibility}
|
|
11
7
|
*/
|
|
12
|
-
export declare class ExmChipInput extends
|
|
8
|
+
export declare class ExmChipInput extends ExmChipSet {
|
|
9
|
+
static formAssociated: boolean;
|
|
13
10
|
label: string;
|
|
14
11
|
supportingText: string;
|
|
12
|
+
required: boolean;
|
|
13
|
+
value: string;
|
|
14
|
+
min: number;
|
|
15
|
+
max?: number;
|
|
15
16
|
chipCount: number;
|
|
16
17
|
static styles: import("lit").CSSResult[];
|
|
17
|
-
|
|
18
|
+
checkValidity(): boolean;
|
|
19
|
+
reportValidity(): boolean;
|
|
20
|
+
protected firstUpdated(): void;
|
|
21
|
+
updated(changedProperties: PropertyValues): void;
|
|
22
|
+
private validate;
|
|
23
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
24
|
+
fireChange(name: string): void;
|
|
25
|
+
valuesToString(): string;
|
|
26
|
+
onChanges(): void;
|
|
18
27
|
private updateTabIndicesOverride;
|
|
19
28
|
}
|
|
29
|
+
declare global {
|
|
30
|
+
interface HTMLElementTagNameMap {
|
|
31
|
+
'exm-chip-input': ExmChipInput;
|
|
32
|
+
}
|
|
33
|
+
}
|
package/dist/exm-chip-input.js
CHANGED
|
@@ -1,25 +1,77 @@
|
|
|
1
|
-
import { __decorate } from 'tslib';
|
|
1
|
+
import { __decorate } from './node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js';
|
|
2
2
|
import { customElement } from 'lit/decorators/custom-element.js';
|
|
3
3
|
import { style } from './styles/exm-chip-input-css.js';
|
|
4
|
-
import { ChipSet } from '@material/web/chips/internal/chip-set.js';
|
|
5
4
|
import { styles } from '@material/web/chips/internal/chip-set-styles.js';
|
|
6
5
|
import { nothing, html } from 'lit';
|
|
7
6
|
import { property, state } from 'lit/decorators.js';
|
|
8
7
|
import { sharedChipStyle } from './styles/exm-chips-shared-css.js';
|
|
9
8
|
import { classMap } from 'lit/directives/class-map.js';
|
|
9
|
+
import { ExmChipSet } from './exm-chip-set.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
*
|
|
13
13
|
* @final
|
|
14
14
|
* @suppress {visibility}
|
|
15
15
|
*/
|
|
16
|
-
let ExmChipInput = class ExmChipInput extends
|
|
16
|
+
let ExmChipInput = class ExmChipInput extends ExmChipSet {
|
|
17
17
|
constructor() {
|
|
18
18
|
super(...arguments);
|
|
19
19
|
this.label = '';
|
|
20
20
|
this.supportingText = '';
|
|
21
|
+
this.required = false;
|
|
22
|
+
this.value = '';
|
|
23
|
+
this.min = 1;
|
|
24
|
+
this.max = undefined;
|
|
21
25
|
this.chipCount = 0;
|
|
22
26
|
}
|
|
27
|
+
checkValidity() {
|
|
28
|
+
var _a;
|
|
29
|
+
return (_a = this.internals) === null || _a === void 0 ? void 0 : _a.checkValidity();
|
|
30
|
+
}
|
|
31
|
+
reportValidity() {
|
|
32
|
+
var _a;
|
|
33
|
+
return (_a = this.internals) === null || _a === void 0 ? void 0 : _a.reportValidity();
|
|
34
|
+
}
|
|
35
|
+
firstUpdated() {
|
|
36
|
+
this.validate();
|
|
37
|
+
}
|
|
38
|
+
updated(changedProperties) {
|
|
39
|
+
var _a;
|
|
40
|
+
if (changedProperties.has('value')) {
|
|
41
|
+
(_a = this.internals) === null || _a === void 0 ? void 0 : _a.setFormValue(this.value);
|
|
42
|
+
this.validate();
|
|
43
|
+
}
|
|
44
|
+
if (changedProperties.has('required')) {
|
|
45
|
+
this.validate();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
validate() {
|
|
49
|
+
var _a, _b, _c;
|
|
50
|
+
const { chips } = this;
|
|
51
|
+
this.chipCount = chips.length;
|
|
52
|
+
let selectedCount = 0;
|
|
53
|
+
for (const chip of chips) {
|
|
54
|
+
selectedCount = chip.selected ? selectedCount + 1 : selectedCount;
|
|
55
|
+
}
|
|
56
|
+
const isInvalid = this.min && this.max !== undefined && this.max !== 0
|
|
57
|
+
? selectedCount < this.min || selectedCount > this.max
|
|
58
|
+
: selectedCount < this.min;
|
|
59
|
+
if (isInvalid) {
|
|
60
|
+
if (this.max && (this.max !== undefined || this.max !== 0) && selectedCount > this.max) {
|
|
61
|
+
(_a = this.internals) === null || _a === void 0 ? void 0 : _a.setValidity({
|
|
62
|
+
rangeOverflow: true,
|
|
63
|
+
}, `Please select a maximum of ${this.max}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
(_b = this.internals) === null || _b === void 0 ? void 0 : _b.setValidity({
|
|
67
|
+
rangeUnderflow: true,
|
|
68
|
+
}, `Please select a minimum of ${this.min}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
(_c = this.internals) === null || _c === void 0 ? void 0 : _c.setValidity({});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
23
75
|
render() {
|
|
24
76
|
const containerClasses = { 'has-label': !!this.label };
|
|
25
77
|
const labelClasses = { 'not-empty': this.chipCount > 0 };
|
|
@@ -28,11 +80,30 @@ let ExmChipInput = class ExmChipInput extends ChipSet {
|
|
|
28
80
|
<div class="background"></div>
|
|
29
81
|
<div class="state-layer"></div>
|
|
30
82
|
<div class="label ${classMap(labelClasses)}">${this.label}</div>
|
|
31
|
-
<div class="items"
|
|
83
|
+
<div class="items">
|
|
84
|
+
<slot @slotchange=${this.updateTabIndicesOverride} @change=${this.onChanges}></slot>
|
|
85
|
+
</div>
|
|
32
86
|
${this.supportingText ? html `<div class="supporting-text">${this.supportingText}</div>` : nothing}
|
|
33
87
|
</div>
|
|
34
88
|
`;
|
|
35
89
|
}
|
|
90
|
+
fireChange(name) {
|
|
91
|
+
this.dispatchEvent(new CustomEvent(name, {
|
|
92
|
+
detail: this.value,
|
|
93
|
+
bubbles: true,
|
|
94
|
+
composed: true,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
valuesToString() {
|
|
98
|
+
const values = this.chips.map((value) => (value.selected ? value.value : null));
|
|
99
|
+
return values.filter((value) => value !== null).join();
|
|
100
|
+
}
|
|
101
|
+
onChanges() {
|
|
102
|
+
this.value = this.valuesToString();
|
|
103
|
+
this.required && this.validate();
|
|
104
|
+
this.fireChange('change');
|
|
105
|
+
this.fireChange('input');
|
|
106
|
+
}
|
|
36
107
|
updateTabIndicesOverride() {
|
|
37
108
|
// The chip that should be focusable is either the chip that currently has
|
|
38
109
|
// focus or the first chip that can be focused.
|
|
@@ -60,6 +131,7 @@ let ExmChipInput = class ExmChipInput extends ChipSet {
|
|
|
60
131
|
}
|
|
61
132
|
}
|
|
62
133
|
};
|
|
134
|
+
ExmChipInput.formAssociated = true;
|
|
63
135
|
ExmChipInput.styles = [styles, sharedChipStyle, style];
|
|
64
136
|
__decorate([
|
|
65
137
|
property({ type: String })
|
|
@@ -67,6 +139,18 @@ __decorate([
|
|
|
67
139
|
__decorate([
|
|
68
140
|
property({ type: String, attribute: 'supporting-text' })
|
|
69
141
|
], ExmChipInput.prototype, "supportingText", void 0);
|
|
142
|
+
__decorate([
|
|
143
|
+
property({ type: Boolean, reflect: true })
|
|
144
|
+
], ExmChipInput.prototype, "required", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
property({ type: String, reflect: true })
|
|
147
|
+
], ExmChipInput.prototype, "value", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
property({ type: Number, reflect: true })
|
|
150
|
+
], ExmChipInput.prototype, "min", void 0);
|
|
151
|
+
__decorate([
|
|
152
|
+
property({ type: Number, reflect: true })
|
|
153
|
+
], ExmChipInput.prototype, "max", void 0);
|
|
70
154
|
__decorate([
|
|
71
155
|
state()
|
|
72
156
|
], ExmChipInput.prototype, "chipCount", void 0);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StoryObj } from '@storybook/web-components-vite';
|
|
2
|
+
import { ExmChipInputDropdown } from './exm-chip-input-dropdown.js';
|
|
3
|
+
import { ExmChipInput } from './exm-chip-input.js';
|
|
4
|
+
import { ExmChip } from './exm-chip.js';
|
|
5
|
+
import './exm-chip-input-dropdown.js';
|
|
6
|
+
import './exm-chip-input.js';
|
|
7
|
+
import '@exmg/exm-form';
|
|
8
|
+
import './exm-chip.js';
|
|
9
|
+
declare const meta: {
|
|
10
|
+
title: string;
|
|
11
|
+
tags: string[];
|
|
12
|
+
render: (args: Partial<ExmChip>) => import("lit").TemplateResult<1>;
|
|
13
|
+
argTypes: {
|
|
14
|
+
name: {
|
|
15
|
+
control: string;
|
|
16
|
+
};
|
|
17
|
+
value: {
|
|
18
|
+
control: "text";
|
|
19
|
+
};
|
|
20
|
+
label: {
|
|
21
|
+
control: "text";
|
|
22
|
+
};
|
|
23
|
+
ariaLabel: {
|
|
24
|
+
control: "text";
|
|
25
|
+
};
|
|
26
|
+
required: {
|
|
27
|
+
control: "boolean";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export default meta;
|
|
32
|
+
export declare const Chip: StoryObj<ExmChip>;
|
|
33
|
+
export declare const ChipInput: StoryObj<ExmChipInput>;
|
|
34
|
+
export declare const ChipInputDropdown: StoryObj<ExmChipInputDropdown>;
|
|
35
|
+
type ExmChipInputExtended = ExmChipInput & {
|
|
36
|
+
formSubmit: (e: any) => void;
|
|
37
|
+
};
|
|
38
|
+
export declare const ChipInputValidation: StoryObj<ExmChipInputExtended>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { ExmChip } from './exm-chip.js';
|
|
3
|
+
/**
|
|
4
|
+
* A chip set component.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ExmChipSet extends LitElement {
|
|
7
|
+
get chips(): ExmChip[];
|
|
8
|
+
private readonly childElements;
|
|
9
|
+
internals: ElementInternals;
|
|
10
|
+
constructor();
|
|
11
|
+
protected render(): import("lit").TemplateResult<1>;
|
|
12
|
+
private handleKeyDown;
|
|
13
|
+
updateTabIndices(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { __decorate } from './node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import { LitElement, isServer, html } from 'lit';
|
|
3
|
+
import { queryAssignedElements } from 'lit/decorators.js';
|
|
4
|
+
import { ExmChip } from './exm-chip.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A chip set component.
|
|
8
|
+
*/
|
|
9
|
+
class ExmChipSet extends LitElement {
|
|
10
|
+
get chips() {
|
|
11
|
+
return this.childElements.filter((child) => child instanceof ExmChip);
|
|
12
|
+
}
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
this.internals = this.attachInternals();
|
|
16
|
+
if (!isServer) {
|
|
17
|
+
this.addEventListener('focusin', this.updateTabIndices.bind(this));
|
|
18
|
+
this.addEventListener('update-focus', this.updateTabIndices.bind(this));
|
|
19
|
+
this.addEventListener('keydown', this.handleKeyDown.bind(this));
|
|
20
|
+
this.internals.role = 'toolbar';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
render() {
|
|
24
|
+
return html `<slot @slotchange=${this.updateTabIndices}></slot>`;
|
|
25
|
+
}
|
|
26
|
+
handleKeyDown(event) {
|
|
27
|
+
const isLeft = event.key === 'ArrowLeft';
|
|
28
|
+
const isRight = event.key === 'ArrowRight';
|
|
29
|
+
const isHome = event.key === 'Home';
|
|
30
|
+
const isEnd = event.key === 'End';
|
|
31
|
+
// Ignore non-navigation keys
|
|
32
|
+
if (!isLeft && !isRight && !isHome && !isEnd) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const { chips } = this;
|
|
36
|
+
// Don't try to select another chip if there aren't any.
|
|
37
|
+
if (chips.length < 2) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Prevent default interactions, such as scrolling.
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
if (isHome || isEnd) {
|
|
43
|
+
const index = isHome ? 0 : chips.length - 1;
|
|
44
|
+
chips[index].focus({ trailing: isEnd });
|
|
45
|
+
this.updateTabIndices();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Check if moving forwards or backwards
|
|
49
|
+
const isRtl = getComputedStyle(this).direction === 'rtl';
|
|
50
|
+
const forwards = isRtl ? isLeft : isRight;
|
|
51
|
+
const focusedChip = chips.find((chip) => chip.matches(':focus-within'));
|
|
52
|
+
if (!focusedChip) {
|
|
53
|
+
// If there is not already a chip focused, select the first or last chip
|
|
54
|
+
// based on the direction we're traveling.
|
|
55
|
+
const nextChip = forwards ? chips[0] : chips[chips.length - 1];
|
|
56
|
+
nextChip.focus({ trailing: !forwards });
|
|
57
|
+
this.updateTabIndices();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const currentIndex = chips.indexOf(focusedChip);
|
|
61
|
+
let nextIndex = forwards ? currentIndex + 1 : currentIndex - 1;
|
|
62
|
+
// Search for the next sibling that is not disabled to select.
|
|
63
|
+
// If we return to the host index, there is nothing to select.
|
|
64
|
+
while (nextIndex !== currentIndex) {
|
|
65
|
+
if (nextIndex >= chips.length) {
|
|
66
|
+
// Return to start if moving past the last item.
|
|
67
|
+
nextIndex = 0;
|
|
68
|
+
}
|
|
69
|
+
else if (nextIndex < 0) {
|
|
70
|
+
// Go to end if moving before the first item.
|
|
71
|
+
nextIndex = chips.length - 1;
|
|
72
|
+
}
|
|
73
|
+
// Check if the next sibling is disabled. If so,
|
|
74
|
+
// move the index and continue searching.
|
|
75
|
+
//
|
|
76
|
+
// Some toolbar items may be focusable when disabled for increased
|
|
77
|
+
// visibility.
|
|
78
|
+
const nextChip = chips[nextIndex];
|
|
79
|
+
if (nextChip.disabled && !nextChip.alwaysFocusable) {
|
|
80
|
+
if (forwards) {
|
|
81
|
+
nextIndex++;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
nextIndex--;
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
nextChip.focus({ trailing: !forwards });
|
|
89
|
+
this.updateTabIndices();
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
updateTabIndices() {
|
|
94
|
+
// The chip that should be focusable is either the chip that currently has
|
|
95
|
+
// focus or the first chip that can be focused.
|
|
96
|
+
const { chips } = this;
|
|
97
|
+
let chipToFocus;
|
|
98
|
+
for (const chip of chips) {
|
|
99
|
+
const isChipFocusable = chip.alwaysFocusable || !chip.disabled;
|
|
100
|
+
const chipIsFocused = chip.matches(':focus-within');
|
|
101
|
+
if (chipIsFocused && isChipFocusable) {
|
|
102
|
+
// Found the first chip that is actively focused. This overrides the
|
|
103
|
+
// first focusable chip found.
|
|
104
|
+
chipToFocus = chip;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (isChipFocusable && !chipToFocus) {
|
|
108
|
+
chipToFocus = chip;
|
|
109
|
+
}
|
|
110
|
+
// Disable non-focused chips. If we disable all of them, we'll grant focus
|
|
111
|
+
// to the first focusable child that was found.
|
|
112
|
+
chip.tabIndex = -1;
|
|
113
|
+
}
|
|
114
|
+
if (chipToFocus) {
|
|
115
|
+
chipToFocus.tabIndex = 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
__decorate([
|
|
120
|
+
queryAssignedElements()
|
|
121
|
+
], ExmChipSet.prototype, "childElements", void 0);
|
|
122
|
+
|
|
123
|
+
export { ExmChipSet };
|
|
124
|
+
//# sourceMappingURL=exm-chip-set.js.map
|
package/dist/exm-chip.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { FilterChip } from '@material/web/chips/internal/filter-chip.js';
|
|
2
|
-
import { createValidator, getValidityAnchor } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
3
|
-
import { getFormState, getFormValue } from '@material/web/labs/behaviors/form-associated.js';
|
|
4
|
-
import { ChipValidator } from './validator/chip-validator.js';
|
|
5
2
|
declare global {
|
|
6
3
|
interface HTMLElementTagNameMap {
|
|
7
4
|
'exm-chip': ExmChip;
|
|
8
5
|
}
|
|
9
6
|
}
|
|
10
7
|
declare const CHECKED: unique symbol;
|
|
11
|
-
declare const FilterChipBaseClass: import("@material/web/labs/behaviors/mixin.js").MixinReturn<
|
|
8
|
+
declare const FilterChipBaseClass: import("@material/web/labs/behaviors/mixin.js").MixinReturn<typeof FilterChip, import("@material/web/labs/behaviors/focusable.js").Focusable>;
|
|
12
9
|
/**
|
|
13
10
|
*
|
|
14
11
|
* @final
|
|
@@ -36,16 +33,8 @@ export declare class ExmChip extends FilterChipBaseClass {
|
|
|
36
33
|
value: string;
|
|
37
34
|
private readonly container;
|
|
38
35
|
disabled: boolean;
|
|
39
|
-
name: string;
|
|
40
|
-
[getFormValue](): string | null;
|
|
41
|
-
[getFormState](): string;
|
|
42
36
|
constructor();
|
|
43
|
-
|
|
44
|
-
formStateRestoreCallback(state: string): void;
|
|
45
|
-
protected updated(): void;
|
|
46
|
-
protected renderPrimaryAction(content: unknown): import("lit-html").TemplateResult<1>;
|
|
37
|
+
protected renderPrimaryAction(content: unknown): import("lit").TemplateResult<1>;
|
|
47
38
|
private _handleClick;
|
|
48
|
-
[createValidator](): ChipValidator;
|
|
49
|
-
[getValidityAnchor](): HTMLElement;
|
|
50
39
|
}
|
|
51
40
|
export {};
|
package/dist/exm-chip.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __decorate } from 'tslib';
|
|
1
|
+
import { __decorate } from './node_modules/.bun/@rollup_plugin-typescript@12.3.0_8b7a8dd02ef2c96b/node_modules/tslib/tslib.es6.js';
|
|
2
2
|
import { property, query, customElement } from 'lit/decorators.js';
|
|
3
3
|
import { styles as styles$1 } from '@material/web/chips/internal/elevated-styles.js';
|
|
4
4
|
import { FilterChip } from '@material/web/chips/internal/filter-chip.js';
|
|
@@ -7,11 +7,7 @@ import { styles as styles$3 } from '@material/web/chips/internal/selectable-styl
|
|
|
7
7
|
import { styles } from '@material/web/chips/internal/shared-styles.js';
|
|
8
8
|
import { styles as styles$2 } from '@material/web/chips/internal/trailing-icon-styles.js';
|
|
9
9
|
import { redispatchEvent } from '@material/web/internal/events/redispatch-event.js';
|
|
10
|
-
import { mixinConstraintValidation, createValidator, getValidityAnchor } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
11
|
-
import { mixinFormAssociated, getFormValue, getFormState } from '@material/web/labs/behaviors/form-associated.js';
|
|
12
|
-
import { mixinElementInternals, internals } from '@material/web/labs/behaviors/element-internals.js';
|
|
13
10
|
import { mixinFocusable } from '@material/web/labs/behaviors/focusable.js';
|
|
14
|
-
import { ChipValidator } from './validator/chip-validator.js';
|
|
15
11
|
import { observer } from '@exmg/lit-base';
|
|
16
12
|
import { SelectionController } from './selection-controller.js';
|
|
17
13
|
import { nothing, html } from 'lit';
|
|
@@ -19,7 +15,7 @@ import { nothing, html } from 'lit';
|
|
|
19
15
|
var _a;
|
|
20
16
|
const CHECKED = Symbol('checked');
|
|
21
17
|
// Separate variable needed for closure.
|
|
22
|
-
const FilterChipBaseClass =
|
|
18
|
+
const FilterChipBaseClass = mixinFocusable(FilterChip);
|
|
23
19
|
/**
|
|
24
20
|
*
|
|
25
21
|
* @final
|
|
@@ -40,12 +36,6 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
40
36
|
this[CHECKED] = checked;
|
|
41
37
|
this.requestUpdate('checked', wasChecked);
|
|
42
38
|
}
|
|
43
|
-
[(_a = CHECKED, getFormValue)]() {
|
|
44
|
-
return this.checked ? this.value : null;
|
|
45
|
-
}
|
|
46
|
-
[getFormState]() {
|
|
47
|
-
return String(this.checked);
|
|
48
|
-
}
|
|
49
39
|
constructor() {
|
|
50
40
|
super();
|
|
51
41
|
this.removable = false;
|
|
@@ -62,18 +52,6 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
62
52
|
*/
|
|
63
53
|
this.value = 'on';
|
|
64
54
|
this.addController(this.selectionController);
|
|
65
|
-
this[internals].role = 'radio';
|
|
66
|
-
}
|
|
67
|
-
formResetCallback() {
|
|
68
|
-
// The checked property does not reflect, so the original attribute set by
|
|
69
|
-
// the user is used to determine the default value.
|
|
70
|
-
this.checked = this.hasAttribute('checked');
|
|
71
|
-
}
|
|
72
|
-
formStateRestoreCallback(state) {
|
|
73
|
-
this.checked = state === 'true';
|
|
74
|
-
}
|
|
75
|
-
updated() {
|
|
76
|
-
this[internals].ariaChecked = String(this.checked);
|
|
77
55
|
}
|
|
78
56
|
renderPrimaryAction(content) {
|
|
79
57
|
const { ariaLabel } = this;
|
|
@@ -98,6 +76,11 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
98
76
|
// event listener.
|
|
99
77
|
const prevValue = this.selected;
|
|
100
78
|
this.selected = !this.selected;
|
|
79
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
80
|
+
detail: this.selected,
|
|
81
|
+
bubbles: true,
|
|
82
|
+
composed: true,
|
|
83
|
+
}));
|
|
101
84
|
const preventDefault = !redispatchEvent(this, event);
|
|
102
85
|
if (preventDefault) {
|
|
103
86
|
// We should not do `this.selected = !this.selected`, since a client
|
|
@@ -107,20 +90,8 @@ let ExmChip = class ExmChip extends FilterChipBaseClass {
|
|
|
107
90
|
return;
|
|
108
91
|
}
|
|
109
92
|
}
|
|
110
|
-
[createValidator]() {
|
|
111
|
-
return new ChipValidator(() => {
|
|
112
|
-
if (!this.selectionController) {
|
|
113
|
-
// Validation runs on superclass construction, so selection controller
|
|
114
|
-
// might not actually be ready until this class constructs.
|
|
115
|
-
return [this];
|
|
116
|
-
}
|
|
117
|
-
return this.selectionController.controls;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
[getValidityAnchor]() {
|
|
121
|
-
return this.container;
|
|
122
|
-
}
|
|
123
93
|
};
|
|
94
|
+
_a = CHECKED;
|
|
124
95
|
ExmChip.styles = [styles, styles$1, styles$2, styles$3, styles$4];
|
|
125
96
|
__decorate([
|
|
126
97
|
property({ type: Boolean })
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
Copyright (c) Microsoft Corporation.
|
|
3
|
+
|
|
4
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
5
|
+
purpose with or without fee is hereby granted.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
9
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
14
|
+
***************************************************************************** */
|
|
15
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
function __decorate(decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
26
|
+
var e = new Error(message);
|
|
27
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { __decorate };
|
|
31
|
+
//# sourceMappingURL=tslib.es6.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exmg/exm-chip-input",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "c27c4ffabda0c6a509df58537017af65719d1e57"
|
|
43
43
|
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { Validator } from '@material/web/labs/behaviors/validators/validator.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @license
|
|
5
|
-
* Copyright 2023 Google LLC
|
|
6
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* A validator that provides constraint validation that emulates
|
|
10
|
-
* `<input type="checkbox">` validation.
|
|
11
|
-
*/
|
|
12
|
-
class ChipValidator extends Validator {
|
|
13
|
-
computeValidity(states) {
|
|
14
|
-
if (!this.radioElement) {
|
|
15
|
-
// Lazily create the radio element
|
|
16
|
-
this.radioElement = document.createElement('input');
|
|
17
|
-
this.radioElement.type = 'radio';
|
|
18
|
-
// A name is required for validation to run
|
|
19
|
-
this.radioElement.name = 'group';
|
|
20
|
-
}
|
|
21
|
-
let isRequired = false;
|
|
22
|
-
let isChecked = false;
|
|
23
|
-
for (const { checked, required } of states) {
|
|
24
|
-
if (required) {
|
|
25
|
-
isRequired = true;
|
|
26
|
-
}
|
|
27
|
-
if (checked) {
|
|
28
|
-
isChecked = true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// Firefox v119 doesn't compute grouped radio validation correctly while
|
|
32
|
-
// they are detached from the DOM, which is why we don't render multiple
|
|
33
|
-
// virtual <input>s. Instead, we can check the required/checked states and
|
|
34
|
-
// grab the i18n'd validation message if the value is missing.
|
|
35
|
-
this.radioElement.checked = isChecked;
|
|
36
|
-
this.radioElement.required = isRequired;
|
|
37
|
-
return {
|
|
38
|
-
validity: {
|
|
39
|
-
valueMissing: isRequired && !isChecked,
|
|
40
|
-
},
|
|
41
|
-
validationMessage: this.radioElement.validationMessage,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
equals(prevGroup, nextGroup) {
|
|
45
|
-
if (prevGroup.length !== nextGroup.length) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
for (let i = 0; i < prevGroup.length; i++) {
|
|
49
|
-
const prev = prevGroup[i];
|
|
50
|
-
const next = nextGroup[i];
|
|
51
|
-
if (prev.checked !== next.checked || prev.required !== next.required) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
copy(states) {
|
|
58
|
-
// Cast as unknown since typescript does not have enough information to
|
|
59
|
-
// infer that the array always has at least one element.
|
|
60
|
-
return states.map(({ checked, required }) => ({
|
|
61
|
-
checked,
|
|
62
|
-
required,
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { ChipValidator };
|
|
68
|
-
//# sourceMappingURL=chip-validator.js.map
|