@m3e/chips 1.0.0-rc.1 → 1.0.0-rc.2

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 (43) hide show
  1. package/README.md +1 -2
  2. package/dist/custom-elements.json +4780 -481
  3. package/dist/html-custom-data.json +10 -10
  4. package/dist/index.js +10 -12
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.min.js +60 -60
  7. package/dist/index.min.js.map +1 -1
  8. package/package.json +5 -5
  9. package/cem.config.mjs +0 -16
  10. package/demo/index.html +0 -183
  11. package/dist/src/AssistChipElement.d.ts +0 -82
  12. package/dist/src/AssistChipElement.d.ts.map +0 -1
  13. package/dist/src/ChipElement.d.ts +0 -86
  14. package/dist/src/ChipElement.d.ts.map +0 -1
  15. package/dist/src/ChipSetElement.d.ts +0 -43
  16. package/dist/src/ChipSetElement.d.ts.map +0 -1
  17. package/dist/src/ChipVariant.d.ts +0 -3
  18. package/dist/src/ChipVariant.d.ts.map +0 -1
  19. package/dist/src/FilterChipElement.d.ts +0 -93
  20. package/dist/src/FilterChipElement.d.ts.map +0 -1
  21. package/dist/src/FilterChipSetElement.d.ts +0 -78
  22. package/dist/src/FilterChipSetElement.d.ts.map +0 -1
  23. package/dist/src/InputChipElement.d.ts +0 -104
  24. package/dist/src/InputChipElement.d.ts.map +0 -1
  25. package/dist/src/InputChipSetElement.d.ts +0 -75
  26. package/dist/src/InputChipSetElement.d.ts.map +0 -1
  27. package/dist/src/SuggestionChipElement.d.ts +0 -83
  28. package/dist/src/SuggestionChipElement.d.ts.map +0 -1
  29. package/dist/src/index.d.ts +0 -10
  30. package/dist/src/index.d.ts.map +0 -1
  31. package/eslint.config.mjs +0 -13
  32. package/rollup.config.js +0 -32
  33. package/src/AssistChipElement.ts +0 -103
  34. package/src/ChipElement.ts +0 -336
  35. package/src/ChipSetElement.ts +0 -60
  36. package/src/ChipVariant.ts +0 -2
  37. package/src/FilterChipElement.ts +0 -254
  38. package/src/FilterChipSetElement.ts +0 -161
  39. package/src/InputChipElement.ts +0 -287
  40. package/src/InputChipSetElement.ts +0 -360
  41. package/src/SuggestionChipElement.ts +0 -104
  42. package/src/index.ts +0 -9
  43. package/tsconfig.json +0 -9
@@ -1,360 +0,0 @@
1
- import { css, CSSResultGroup, html, PropertyValues } from "lit";
2
- import { customElement } from "lit/decorators.js";
3
-
4
- import {
5
- AttachInternals,
6
- ConstraintValidation,
7
- DesignToken,
8
- Dirty,
9
- Disabled,
10
- FormAssociated,
11
- formValue,
12
- Required,
13
- RequiredConstraintValidation,
14
- Role,
15
- Touched,
16
- } from "@m3e/core";
17
-
18
- import { ListKeyManager, ListManager } from "@m3e/core/a11y";
19
- import { FormFieldControl } from "@m3e/form-field";
20
-
21
- import { M3eChipSetElement } from "./ChipSetElement";
22
- import { M3eInputChipElement } from "./InputChipElement";
23
-
24
- /**
25
- * @summary
26
- * A container that transforms user input into a cohesive set of interactive chips, supporting entry, editing, and removal of discrete values.
27
- *
28
- * @description
29
- * The `m3e-input-chip-set` component enables users to input, display, and manage a collection of discrete
30
- * values as input chips. Designed for expressive, accessible forms, it supports keyboard navigation, validation,
31
- * and seamless integration with form controls. This component is ideal for capturing user-generated tags,
32
- * keywords, or selections in a visually consistent and interactive manner.
33
- *
34
- * @example
35
- * The following example illustrates the use of the `m3e-input-chip-set` inside a `m3e-form-field`.
36
- * In this example, the `input` slot specifies the `input` element used to add input chips and the
37
- * field label's `for` attribute targets the `input` element to provide an accessible label.
38
- * ```html
39
- * <m3e-form-field>
40
- * <label slot="label" for="keywords">Keywords</label>
41
- * <m3e-input-chip-set aria-label="Enter keywords">
42
- * <input id="keywords" slot="input" placeholder="New keyword..." />
43
- * </m3e-input-chip-set>
44
- * </m3e-form-field>
45
- * ```
46
- *
47
- * @tag m3e-input-chip-set
48
- *
49
- * @slot - Renders the chips of the set.
50
- * @slot input - Renders the input element used to add new chips to the set.
51
- *
52
- * @attr disabled - Whether the element is disabled.
53
- * @attr name - The name that identifies the element when submitting the associated form.
54
- * @attr required - Whether a value is required for the element.
55
- * @attr vertical - Whether the element is oriented vertically.
56
- *
57
- * @fires change - Emitted when a chip is added to, or removed from, the set.
58
- *
59
- * @cssprop --m3e-chip-set-spacing - The spacing (gap) between chips in the set.
60
- */
61
- @customElement("m3e-input-chip-set")
62
- export class M3eInputChipSetElement
63
- extends RequiredConstraintValidation(
64
- Required(
65
- ConstraintValidation(Dirty(Touched(FormAssociated(Disabled(AttachInternals(Role(M3eChipSetElement, "grid")))))))
66
- )
67
- )
68
- implements FormFieldControl
69
- {
70
- /** The styles of the element. */
71
- static override styles: CSSResultGroup = [
72
- M3eChipSetElement.styles,
73
- css`
74
- ::slotted([slot="input"]) {
75
- outline: unset;
76
- border: unset;
77
- background-color: transparent;
78
- box-shadow: none;
79
- font-family: inherit;
80
- font-size: inherit;
81
- line-height: initial;
82
- letter-spacing: inherit;
83
- color: var(--_form-field-input-color, inherit);
84
- flex: 1 1 auto;
85
- min-width: 0;
86
- padding: unset;
87
- }
88
- ::slotted(m3e-input-chip) {
89
- min-width: 0;
90
- }
91
- ::slotted([slot="input"])::placeholder {
92
- user-select: none;
93
- color: currentColor;
94
- transition: opacity ${DesignToken.motion.duration.extraLong1};
95
- }
96
- :host(:not(:focus-within)) ::slotted([slot="input"])::placeholder {
97
- opacity: 0;
98
- transition: 0s;
99
- }
100
- :host(:hover) ::slotted([slot="input"])::placeholder {
101
- transition: 0s;
102
- }
103
- span[role="row"],
104
- span[role="gridcell"] {
105
- display: contents;
106
- }
107
- @media (prefers-reduced-motion) {
108
- ::slotted([slot="input"])::placeholder {
109
- transition: none !important;
110
- }
111
- }
112
- `,
113
- ];
114
-
115
- /** @private */ readonly #inputChangeHandler = () => this.#handleInputChange();
116
- /** @private */ readonly #inputKeyDownHandler = (e: KeyboardEvent) => this.#handleInputKeyDown(e);
117
- /** @private */ readonly #focusHandler = () => this.#handleFocus();
118
- /** @private */ readonly #focusInHandler = () => this.#handleFocusIn();
119
- /** @private */ readonly #focusOutHandler = () => this.#handleFocusOut();
120
- /** @private */ readonly #chipRemoveHandler = (e: Event) => this.#handleChipRemove(e);
121
- /** @private */ readonly #chipClickHandler = (e: Event) => this.#handleChipClick(e);
122
-
123
- /** @private */ readonly #listManager = new ListManager<M3eInputChipElement>();
124
- /** @private */ readonly #listKeyManager = new ListKeyManager<HTMLElement>()
125
- .onActiveItemChange(() => this.#listKeyManager.activeItem?.focus())
126
- .withHomeAndEnd()
127
- .withSkipPredicate((x) => !x.hasAttribute("tabindex"));
128
-
129
- /** @private */ #ignoreInputChange = false;
130
- /** @private */ #input: HTMLInputElement | null = null;
131
- /** @private */ #tabindex = 0;
132
-
133
- /** The chips of the set. */
134
- get chips(): readonly M3eInputChipElement[] {
135
- // NOTE: query is used instead of the internal list management due to
136
- // validating required state on change to support form-field integration.
137
- return [...this.querySelectorAll("m3e-input-chip")];
138
- }
139
-
140
- /** The selected values of the set. */
141
- get value(): readonly string[] | null {
142
- const values = this.chips.map((x) => x.value);
143
- return values.length == 0 ? null : values;
144
- }
145
-
146
- /** @inheritdoc @internal */
147
- override get [formValue]() {
148
- const values = this.value;
149
- if (!values) return null;
150
- const data = new FormData();
151
- for (const value of values) {
152
- data.append(this.name, value);
153
- }
154
- return data;
155
- }
156
-
157
- /** @inheritdoc */
158
- get shouldLabelFloat(): boolean {
159
- return this.chips.length > 0;
160
- }
161
-
162
- /** @inheritdoc */
163
- onContainerClick(): void {
164
- this.#input?.focus();
165
- }
166
-
167
- /** @inheritdoc */
168
- override connectedCallback(): void {
169
- super.connectedCallback();
170
-
171
- this.closest("m3e-form-field")?.notifyControlStateChange();
172
-
173
- this.#tabindex = Number.parseInt(this.getAttribute("tabindex") ?? "0");
174
- this.addEventListener("focus", this.#focusHandler);
175
- this.addEventListener("focusin", this.#focusInHandler);
176
- this.addEventListener("focusout", this.#focusOutHandler);
177
- }
178
-
179
- /** @inheritdoc */
180
- override disconnectedCallback(): void {
181
- super.disconnectedCallback();
182
-
183
- this.removeEventListener("focus", this.#focusHandler);
184
- this.removeEventListener("focusin", this.#focusInHandler);
185
- this.removeEventListener("focusout", this.#focusOutHandler);
186
- }
187
-
188
- /** @inheritdoc */
189
- protected override firstUpdated(_changedProperties: PropertyValues): void {
190
- super.firstUpdated(_changedProperties);
191
-
192
- if (!this.hasAttribute("tabindex")) {
193
- this.setAttribute("tabindex", `${this.#tabindex}`);
194
- }
195
- }
196
-
197
- /** @inheritdoc */
198
- protected override update(changedProperties: PropertyValues<this>): void {
199
- super.update(changedProperties);
200
-
201
- if (changedProperties.has("vertical")) {
202
- this.ariaOrientation = null;
203
- }
204
- if (changedProperties.has("disabled")) {
205
- this.#listManager.items.forEach((x) => (x.disabled = this.disabled));
206
- if (this.#input) {
207
- this.#input.disabled = this.disabled;
208
- }
209
- }
210
- }
211
-
212
- /** @inheritdoc */
213
- protected override render(): unknown {
214
- return html`<slot @keydown="${this.#handleKeyDown}" @slotchange="${this.#handleSlotChange}"></slot>
215
- <span role="row">
216
- <span role="gridcell"><slot name="input" @slotchange="${this.#handleInputSlotChange}"></slot></span>
217
- </span> `;
218
- }
219
-
220
- /** @private */
221
- #handleKeyDown(e: KeyboardEvent): void {
222
- this.#listKeyManager.onKeyDown(e);
223
- }
224
-
225
- /** @private */
226
- async #handleSlotChange(): Promise<void> {
227
- const { added, removed } = this.#listManager.setItems([...this.querySelectorAll("m3e-input-chip")]);
228
-
229
- for (const chip of added) {
230
- if (chip.isUpdatePending) {
231
- await chip.updateComplete;
232
- }
233
- if (this.disabled) {
234
- chip.disabled = true;
235
- }
236
- chip.addEventListener("remove", this.#chipRemoveHandler);
237
- chip.cell.addEventListener("click", this.#chipClickHandler);
238
- }
239
-
240
- removed.forEach((x) => {
241
- x.removeEventListener("remove", this.#chipRemoveHandler);
242
- x.cell.removeEventListener("click", this.#chipClickHandler);
243
- });
244
-
245
- this.#listKeyManager.setItems(
246
- this.#listManager.items.flatMap((x) => (x.removeButton ? [x.cell, x.removeButton] : [x.cell]))
247
- );
248
- if (!this.#listKeyManager.activeItem) {
249
- this.#listKeyManager.updateActiveItem(this.#listKeyManager.items.find((x) => x.hasAttribute("tabindex")));
250
- }
251
- }
252
-
253
- /** @private */
254
- #handleInputSlotChange(): void {
255
- const input = this.querySelector("input");
256
- if (this.#input) {
257
- this.#input.removeEventListener("change", this.#inputChangeHandler);
258
- this.#input.removeEventListener("keydown", this.#inputKeyDownHandler);
259
- }
260
-
261
- this.#input = input;
262
- if (this.#input) {
263
- this.#input.disabled = this.disabled;
264
- this.#input.addEventListener("change", this.#inputChangeHandler);
265
- this.#input.addEventListener("keydown", this.#inputKeyDownHandler);
266
-
267
- const property = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!;
268
- Object.defineProperty(input, "value", {
269
- get: () => property.get?.call(input),
270
- set: (value: string) => {
271
- property.set?.call(input, value);
272
- if (this.#input === input && !this.#ignoreInputChange) {
273
- this.#handleInputChange();
274
- }
275
- },
276
- });
277
- }
278
- }
279
-
280
- /** @private */
281
- #handleFocus(): void {
282
- setTimeout(() => (this.#listKeyManager.activeItem ?? this.#input)?.focus());
283
- }
284
-
285
- /** @private */
286
- #handleFocusIn(): void {
287
- this.setAttribute("tabindex", "-1");
288
- }
289
-
290
- /** @private */
291
- #handleFocusOut(): void {
292
- this.setAttribute("tabindex", `${this.#tabindex}`);
293
- }
294
-
295
- /** @private */
296
- #handleChipRemove(e: Event): void {
297
- const chip = <M3eInputChipElement>e.target;
298
- const index = this.#listManager.items.indexOf(chip);
299
- const nextChip = this.#listManager.items.find((x, y) => y > index && !x.disabled && x.removable);
300
-
301
- chip.remove();
302
-
303
- this.#listKeyManager.setActiveItem(this.#listKeyManager.items.find((x) => x === nextChip?.removeButton));
304
- if (!this.#listKeyManager.activeItem) {
305
- this.#input?.focus();
306
- }
307
-
308
- this.dispatchEvent(new Event("change", { bubbles: true }));
309
- }
310
-
311
- /** @private */
312
- #handleChipClick(e: Event): void {
313
- this.#listKeyManager.updateActiveItem(e.composedPath().find((x) => x instanceof M3eInputChipElement)?.cell);
314
- }
315
-
316
- /** @private */
317
- #handleInputChange(): void {
318
- const value = this.#input?.value;
319
- if (!value) return;
320
-
321
- setTimeout(() => {
322
- const value = this.#input?.value;
323
- if (!value) return;
324
-
325
- const chip = document.createElement("m3e-input-chip");
326
- chip.removable = true;
327
- chip.appendChild(document.createTextNode(value));
328
- this.appendChild(chip);
329
-
330
- if (this.#input) {
331
- try {
332
- this.#ignoreInputChange = true;
333
- this.#input.value = "";
334
- } finally {
335
- this.#ignoreInputChange = false;
336
- }
337
- }
338
-
339
- this.dispatchEvent(new Event("change", { bubbles: true }));
340
- });
341
- }
342
-
343
- /** @private */
344
- #handleInputKeyDown(e: KeyboardEvent): void {
345
- if (e.key === "Backspace" && !this.#input?.value) {
346
- const item = [...this.#listManager.items]
347
- .reverse()
348
- .find((x) => !x.disabled && !x.disabledInteractive && x.removable);
349
- if (item) {
350
- item.dispatchEvent(new Event("remove"));
351
- }
352
- }
353
- }
354
- }
355
-
356
- declare global {
357
- interface HTMLElementTagNameMap {
358
- "m3e-input-chip-set": M3eInputChipSetElement;
359
- }
360
- }
@@ -1,104 +0,0 @@
1
- import { nothing } from "lit";
2
- import { customElement } from "lit/decorators.js";
3
-
4
- import {
5
- AttachInternals,
6
- Disabled,
7
- DisabledInteractive,
8
- Focusable,
9
- FormSubmitter,
10
- KeyboardClick,
11
- LinkButton,
12
- Role,
13
- } from "@m3e/core";
14
-
15
- import { M3eChipElement } from "./ChipElement";
16
-
17
- /**
18
- * @summary
19
- * A chip used to help narrow a user's intent by presenting dynamically generated suggestions, such as
20
- * suggested responses or search filters.
21
- *
22
- * @description
23
- * The `m3e-suggestion-chip` component presents a suggestion chip, offering users contextually relevant
24
- * actions or responses. It is designed for dynamic suggestion scenarios, such as search filters or
25
- * smart replies, and supports accessibility, keyboard interaction, and expressive state styling in line
26
- * with Material 3 guidelines. Appearance variants include `elevated` and `outlined`, enabling visual
27
- * differentiation and contextual emphasis.
28
- *
29
- * @example
30
- * The following example illustrates use of the `m3e-suggestion-chip`. In this example, multiple chips are nested inside
31
- * a `m3e-chip-set` container to create a cohesive set of chips. The container is given the ARIA `role="group"` to convey
32
- * to assistive technologies that the chips are part of a related set of element.
33
- * ```html
34
- * <m3e-chip-set role="group" aria-label="Suggested replies">
35
- * <m3e-suggestion-chip>Sounds good!</m3e-suggestion-chip>
36
- * <m3e-suggestion-chip>Can you clarify?</m3e-suggestion-chip>
37
- * <m3e-suggestion-chip>Let's do it.</m3e-suggestion-chip>
38
- * <m3e-suggestion-chip>Maybe later.</m3e-suggestion-chip>
39
- * </m3e-chip-set>
40
- * ```
41
- *
42
- * @tag m3e-suggestion-chip
43
- *
44
- * @slot - Renders the label of the chip.
45
- * @slot icon - Renders an icon before the chip's label.
46
- *
47
- * @attr disabled - A value indicating whether the element is disabled.
48
- * @attr disabled-interactive - A value indicating whether the element is disabled and interactive.
49
- * @attr download - A value indicating whether the `target` of the link button will be downloaded, optionally specifying the new name of the file.
50
- * @attr href - The URL to which the link button points.
51
- * @attr name - The name of the element, submitted as a pair with the element's `value` as part of form data, when the element is used to submit a form.
52
- * @attr rel - The relationship between the `target` of the link button and the document.
53
- * @attr target - The target of the link button.
54
- * @attr type - The type of the element.
55
- * @attr value - A string representing the value of the chip.
56
- * @attr variant - The appearance variant of the chip.
57
- *
58
- * @cssprop --m3e-chip-container-shape - Border radius of the chip container.
59
- * @cssprop --m3e-chip-container-height - Base height of the chip container before density adjustment.
60
- * @cssprop --m3e-chip-label-text-font-size - Font size of the chip label text.
61
- * @cssprop --m3e-chip-label-text-font-weight - Font weight of the chip label text.
62
- * @cssprop --m3e-chip-label-text-line-height - Line height of the chip label text.
63
- * @cssprop --m3e-chip-label-text-tracking - Letter spacing of the chip label text.
64
- * @cssprop --m3e-chip-label-text-color - Label text color in default state.
65
- * @cssprop --m3e-chip-icon-color - Icon color in default state.
66
- * @cssprop --m3e-chip-icon-size - Font size of leading/trailing icons.
67
- * @cssprop --m3e-chip-spacing - Horizontal gap between chip content elements.
68
- * @cssprop --m3e-chip-padding-start - Default start padding when no icon is present.
69
- * @cssprop --m3e-chip-padding-end - Default end padding when no trailing icon is present.
70
- * @cssprop --m3e-chip-with-icon-padding-start - Start padding when leading icon is present.
71
- * @cssprop --m3e-chip-with-icon-padding-end - End padding when trailing icon is present.
72
- * @cssprop --m3e-chip-disabled-label-text-color - Base color for disabled label text.
73
- * @cssprop --m3e-chip-disabled-label-text-opacity - Opacity applied to disabled label text.
74
- * @cssprop --m3e-chip-disabled-icon-color - Base color for disabled icons.
75
- * @cssprop --m3e-chip-disabled-icon-opacity - Opacity applied to disabled icons.
76
- * @cssprop --m3e-elevated-chip-container-color - Background color for elevated variant.
77
- * @cssprop --m3e-elevated-chip-elevation - Elevation level for elevated variant.
78
- * @cssprop --m3e-elevated-chip-hover-elevation - Elevation level on hover.
79
- * @cssprop --m3e-elevated-chip-disabled-container-color - Background color for disabled elevated variant.
80
- * @cssprop --m3e-elevated-chip-disabled-container-opacity - Opacity applied to disabled elevated background.
81
- * @cssprop --m3e-elevated-chip-disabled-elevation - Elevation level for disabled elevated variant.
82
- * @cssprop --m3e-outlined-chip-outline-thickness - Outline thickness for outlined variant.
83
- * @cssprop --m3e-outlined-chip-outline-color - Outline color for outlined variant.
84
- * @cssprop --m3e-outlined-chip-disabled-outline-color - Outline color for disabled outlined variant.
85
- * @cssprop --m3e-outlined-chip-disabled-outline-opacity - Opacity applied to disabled outline.
86
- */
87
- @customElement("m3e-suggestion-chip")
88
- export class M3eSuggestionChipElement extends FormSubmitter(
89
- AttachInternals(
90
- LinkButton(KeyboardClick(Focusable(DisabledInteractive(Disabled(Role(M3eChipElement, "button")))))),
91
- true
92
- )
93
- ) {
94
- /** @internal @inheritdoc */
95
- protected override _renderTrailingIcon(): unknown {
96
- return nothing;
97
- }
98
- }
99
-
100
- declare global {
101
- interface HTMLElementTagNameMap {
102
- "m3e-suggestion-chip": M3eSuggestionChipElement;
103
- }
104
- }
package/src/index.ts DELETED
@@ -1,9 +0,0 @@
1
- export * from "./AssistChipElement";
2
- export * from "./ChipElement";
3
- export * from "./ChipSetElement";
4
- export * from "./ChipVariant";
5
- export * from "./FilterChipElement";
6
- export * from "./FilterChipSetElement";
7
- export * from "./InputChipElement";
8
- export * from "./InputChipSetElement";
9
- export * from "./SuggestionChipElement";
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": "./src",
5
- "outDir": "./dist/src"
6
- },
7
- "include": ["src/**/*.ts", "**/*.mjs", "**/*.js"],
8
- "exclude": []
9
- }