@justeattakeaway/pie-chip 0.13.0 → 0.14.0

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/README.md CHANGED
@@ -31,63 +31,114 @@ Ideally, you should install the component using the **`@justeattakeaway/pie-webc
31
31
 
32
32
  ### Properties
33
33
 
34
- | Prop | Options | Description | Default |
35
- |----------------|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|-------------|
36
- | `variant` | `"default"`, `"outline"`, `"ghost"` | Sets the variant of the chip. | `"default"` |
37
- | `disabled` | `true`, `false` | If true, disables the chip. | `false` |
38
- | `isSelected` | `true`, `false` | If true, the chip component will apply the selected styles. | `false` |
39
- | `isDismissible`| `true`, `false` | If true, displays a close icon. Can be only used if `isSelected` is set to true. | `false` |
40
- | `isLoading` | `true`, `false` | If true, displays a loading indicator inside the chip. | `false` |
41
- | `aria` | `{ label?: string, close?: string }` | Aria properties for the chip to help with making it accessible. | `undefined` |
34
+ | Prop | Options | Description | Default |
35
+ |-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
36
+ | `type` | `"button"`, `"checkbox"` | Sets the functional type of the chip. | `"button"` |
37
+ | `variant` | `"default"`, `"outline"`, `"ghost"` | Sets the variant of the chip. | `"default"` |
38
+ | `disabled` | `true`, `false` | If true, disables the chip. | `false` |
39
+ | `isSelected` | `true`, `false` | If true, the chip component will apply the selected styles. **This is a controlled property, meaning you are responsible for updating its value in response to user interaction events.** | `false` |
40
+ | `isDismissible` | `true`, `false` | If true, displays a close icon. Can be only used if `isSelected` is set to true. When true, the chip itself will not be interactive. Only the close icon will be. | `false` |
41
+ | `isLoading` | `true`, `false` | If true, displays a loading indicator inside the chip. It is advised to provide an appropriate `aria.label` value during and after loading. | `false` |
42
+ | `aria` | `{ label?: string, close?: string, haspopup?: "menu" \| "listbox" \| "tree" \| "grid" \| "dialog" \| "true" \| "false", expanded?: boolean }` | Accessibility properties for the chip. Use `haspopup` and `expanded` for chips that trigger a popup like a menu or dialog. | `undefined` |
42
43
 
43
44
  ### Slots
44
45
 
45
- | Slot | Description |
46
- |-----------|-----------------------------------------------------------|
46
+ | Slot | Description |
47
+ |-----------|----------------------------------------------------------------|
47
48
  | `default` | The default slot is used to pass text into the chip component. |
48
- | `icon` | Used to pass an icon into the chip component. |
49
+ | `icon` | Used to pass an icon into the chip component. |
49
50
 
50
51
  ### CSS Variables
51
52
  This component does not expose any CSS variables for style overrides.
52
53
 
53
54
  ### Events
54
55
 
55
- | Event | Type | Description |
56
- |-------------------|---------------|-----------------------------------------------------|
57
- | `pie-chip-close` | `CustomEvent` | Triggered when the user interacts with the close icon. |
56
+ | Event | Type | Description |
57
+ |----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
58
+ | `change` | `Event` | **Emitted when a `type="checkbox"` chip is interacted with.** The component will not change its own `isSelected` state. You should use this event to toggle the `isSelected` property in your application's state. |
59
+ | `close` | `Event` | Triggered when the user interacts with the close icon on a dismissible chip. |
58
60
 
59
61
  Visit [Chip | PIE Design System](https://pie.design/components/chip) to view more information on this component.
60
62
 
61
63
  ## Usage Examples
62
64
 
65
+ `pie-chip` is a controlled component. This means you are responsible for listening to events (`change` or `click`) and updating the `isSelected` property. This gives you full control over the component's state and behaviour.
66
+
63
67
  **For HTML:**
68
+ ### Basic Example (Checkbox)
64
69
 
65
- ```js
66
- // import as module into a js file e.g. main.js
67
- import '@justeattakeaway/pie-webc/components/chip.js'
68
- ```
70
+ Here is how you would manage the state of a single checkbox-type chip.
69
71
 
70
72
  ```html
71
- <pie-chip>String</pie-chip>
72
-
73
- <script type="module" src="/main.js"></script>
73
+ <pie-chip type="checkbox" id="my-checkbox-chip">Enable notifications</pie-chip>
74
+
75
+ <script>
76
+ const checkboxChip = document.getElementById('pie-chip');
77
+ checkboxChip.addEventListener('change', () => {
78
+ // As a controlled component, we update the `isSelected` property ourselves
79
+ checkboxChip.isSelected = !checkboxChip.isSelected;
80
+ console.log('Notification chip is now:', checkboxChip.isSelected ? 'selected' : 'deselected');
81
+ });
82
+ </script>
74
83
  ```
75
84
 
76
85
  **For Native JS Applications, Vue, Angular, Svelte etc.:**
86
+ ### Interactive Chip Groups (Button)
87
+
88
+ You can easily create interactive groups, such as a single-select group where only one chip can be active at a time. For accessibility, it is recommended to wrap functional groups in a `<fieldset>` or give them a `role="group"`.
89
+
90
+ ```html
91
+ <div id="single-select-group" role="group" aria-label="Choose a size">
92
+ <pie-chip type="button">Small</pie-chip>
93
+ <pie-chip type="button" isSelected>Medium</pie-chip>
94
+ <pie-chip type="button">Large</pie-chip>
95
+ </div>
77
96
 
78
- ```js
79
- // Vue templates (using Nuxt 3)
80
- import '@justeattakeaway/pie-webc/components/chip.js';
97
+ <script>
98
+ const chipGroup = document.getElementById('single-select-group');
81
99
 
82
- <pie-chip>String</pie-chip>
100
+ chipGroup.addEventListener('click', (event) => {
101
+ const clickedChip = event.target.closest('pie-chip');
102
+
103
+ // Ensure a chip was actually clicked
104
+ if (!clickedChip) return;
105
+
106
+ const wasSelected = clickedChip.isSelected;
107
+ const allChips = chipGroup.querySelectorAll('pie-chip');
108
+
109
+ // 1. Deselect all chips in the group
110
+ allChips.forEach(chip => chip.isSelected = false);
111
+
112
+ // 2. Toggle the clicked chip
113
+ // This allows a user to deselect a chip by clicking it again
114
+ clickedChip.isSelected = !wasSelected;
115
+ });
116
+ </script>
83
117
  ```
84
118
 
85
119
  **For React Applications:**
86
120
 
87
121
  ```jsx
88
122
  import { PieChip } from '@justeattakeaway/pie-webc/react/chip.js';
89
-
90
- <PieChip>String</PieChip>
123
+ import { useState } from 'react';
124
+
125
+ export default function ChipExample () {
126
+ const [isSelected, setIsSelected] = useState(false);
127
+
128
+ // For checkbox chips, use the `onChange` event
129
+ const handleOnChange = () => {
130
+ setIsSelected(!isSelected);
131
+ }
132
+
133
+ return (
134
+ <PieChip
135
+ type="checkbox"
136
+ isSelected={isSelected}
137
+ onChange={handleOnChange}>
138
+ Enable notifications
139
+ </PieChip>
140
+ );
141
+ }
91
142
  ```
92
143
 
93
144
  ### Icons
@@ -101,7 +152,7 @@ We recommend using [@justeattakeaway/pie-icons-webc](https://www.npmjs.com/packa
101
152
  -->
102
153
  <pie-chip>
103
154
  <icon-vegan slot="icon"></icon-vegan>
104
- String
155
+ Vegan
105
156
  </pie-chip>
106
157
  ```
107
158
 
@@ -22,12 +22,11 @@
22
22
  },
23
23
  {
24
24
  "kind": "variable",
25
- "name": "ON_CHIP_CLOSE_EVENT",
25
+ "name": "types",
26
26
  "type": {
27
- "text": "string"
27
+ "text": "['button', 'checkbox']"
28
28
  },
29
- "default": "'pie-chip-close'",
30
- "description": "Event name for when the chip is closed."
29
+ "default": "['button', 'checkbox']"
31
30
  },
32
31
  {
33
32
  "kind": "variable",
@@ -35,7 +34,7 @@
35
34
  "type": {
36
35
  "text": "DefaultProps"
37
36
  },
38
- "default": "{\n variant: 'default',\n disabled: false,\n isSelected: false,\n isLoading: false,\n isDismissible: false,\n}"
37
+ "default": "{\n variant: 'default',\n disabled: false,\n isSelected: false,\n isLoading: false,\n isDismissible: false,\n type: 'button',\n}"
39
38
  }
40
39
  ],
41
40
  "exports": [
@@ -49,9 +48,9 @@
49
48
  },
50
49
  {
51
50
  "kind": "js",
52
- "name": "ON_CHIP_CLOSE_EVENT",
51
+ "name": "types",
53
52
  "declaration": {
54
- "name": "ON_CHIP_CLOSE_EVENT",
53
+ "name": "types",
55
54
  "module": "src/defs.js"
56
55
  }
57
56
  },
@@ -89,6 +88,11 @@
89
88
  "name": "variant",
90
89
  "privacy": "public"
91
90
  },
91
+ {
92
+ "kind": "field",
93
+ "name": "type",
94
+ "privacy": "public"
95
+ },
92
96
  {
93
97
  "kind": "field",
94
98
  "name": "disabled",
@@ -119,59 +123,85 @@
119
123
  },
120
124
  {
121
125
  "kind": "method",
122
- "name": "onClickHandler",
126
+ "name": "_onCheckboxChange",
127
+ "privacy": "private",
128
+ "description": "Handles the change event for the native checkbox.\nThis component is controlled, so it does not set its own state.\nIt simply forwards the native change event."
129
+ },
130
+ {
131
+ "kind": "method",
132
+ "name": "_renderSpinner",
123
133
  "privacy": "private",
124
- "parameters": [
125
- {
126
- "name": "event",
127
- "type": {
128
- "text": "Event"
129
- }
134
+ "return": {
135
+ "type": {
136
+ "text": "TemplateResult"
130
137
  }
131
- ],
132
- "description": "Handler to prevent click events\nwhen the chip is disabled or dismissible"
138
+ },
139
+ "description": "Template for the loading state spinner."
133
140
  },
134
141
  {
135
142
  "kind": "method",
136
- "name": "renderSpinner",
143
+ "name": "_renderContent",
137
144
  "privacy": "private",
138
145
  "return": {
139
146
  "type": {
140
147
  "text": "TemplateResult"
141
148
  }
142
149
  },
143
- "description": "Template for the loading state"
150
+ "description": "Renders the core content of the chip (icon, text, spinner)."
144
151
  },
145
152
  {
146
153
  "kind": "method",
147
- "name": "_handleCloseButtonClick",
154
+ "name": "_renderCheckbox",
148
155
  "privacy": "private",
149
156
  "return": {
150
157
  "type": {
151
- "text": "void"
158
+ "text": "TemplateResult"
152
159
  }
153
160
  },
154
- "description": "Handles click on a close button by dispatching a custom event"
161
+ "description": "Template for the checkbox variant.\nThis uses a visually hidden native checkbox for accessibility and form integration."
162
+ },
163
+ {
164
+ "kind": "method",
165
+ "name": "_renderButton",
166
+ "privacy": "private",
167
+ "return": {
168
+ "type": {
169
+ "text": "TemplateResult"
170
+ }
171
+ }
155
172
  },
156
173
  {
157
174
  "kind": "method",
158
- "name": "renderCloseButton",
175
+ "name": "_renderDismissible",
159
176
  "privacy": "private",
160
177
  "return": {
161
178
  "type": {
162
179
  "text": "TemplateResult"
163
180
  }
164
181
  },
165
- "description": "Template for the dismissible state"
182
+ "description": "Template for the dismissible variant."
166
183
  }
167
184
  ],
168
185
  "events": [
169
186
  {
170
187
  "type": {
171
- "text": "CustomEvent"
188
+ "text": "Event"
172
189
  },
173
- "description": "when a user clicks close button.",
174
- "name": "pie-chip-close"
190
+ "description": "when a user clicks the close button.",
191
+ "name": "close"
192
+ },
193
+ {
194
+ "type": {
195
+ "text": "Event"
196
+ },
197
+ "description": "when a user interacts with the chip of type checkbox.",
198
+ "name": "change"
199
+ }
200
+ ],
201
+ "mixins": [
202
+ {
203
+ "name": "DelegatesFocusMixin",
204
+ "package": "@justeattakeaway/pie-webc-core"
175
205
  }
176
206
  ],
177
207
  "superclass": {
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ import { TemplateResult } from 'lit';
6
6
  declare type AriaProps = {
7
7
  close?: string;
8
8
  label?: string;
9
+ haspopup?: 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false';
10
+ expanded?: boolean;
9
11
  };
10
12
 
11
13
  export declare interface ChipProps {
@@ -34,61 +36,68 @@ export declare interface ChipProps {
34
36
  * Can be only used if `isSelected` is set to true
35
37
  */
36
38
  isDismissible?: boolean;
39
+ /**
40
+ * Sets the functional type of the chip. Can be `button` or `checkbox`. Defaults to `button`.
41
+ */
42
+ type?: typeof types[number];
37
43
  }
38
44
 
39
45
  export declare type DefaultProps = ComponentDefaultProps<ChipProps, keyof Omit<ChipProps, 'aria'>>;
40
46
 
41
47
  export declare const defaultProps: DefaultProps;
42
48
 
43
- /**
44
- * Event name for when the chip is closed.
45
- *
46
- * @constant
47
- */
48
- export declare const ON_CHIP_CLOSE_EVENT = "pie-chip-close";
49
-
50
49
  /**
51
50
  * @tagname pie-chip
52
51
  * @slot icon - The icon slot
53
52
  * @slot - Default slot
54
- * @event {CustomEvent} pie-chip-close - when a user clicks close button.
53
+ * @event {Event} close - when a user clicks the close button.
54
+ * @event {Event} change - when a user interacts with the chip of type checkbox.
55
55
  */
56
- export declare class PieChip extends PieElement implements ChipProps {
56
+ export declare class PieChip extends PieChip_base implements ChipProps {
57
57
  variant: "default" | "outline" | "ghost";
58
+ type: "button" | "checkbox";
58
59
  disabled: boolean;
59
60
  isSelected: boolean;
60
61
  isLoading: boolean;
61
62
  isDismissible: boolean;
62
63
  aria: ChipProps['aria'];
63
64
  /**
64
- * Handler to prevent click events
65
- * when the chip is disabled or dismissible
66
- *
65
+ * Handles the change event for the native checkbox.
66
+ * This component is controlled, so it does not set its own state.
67
+ * It simply forwards the native change event.
67
68
  * @private
68
69
  */
69
- private onClickHandler;
70
+ private _onCheckboxChange;
70
71
  /**
71
- * Template for the loading state
72
- *
72
+ * Template for the loading state spinner.
73
73
  * @private
74
74
  */
75
- private renderSpinner;
75
+ private _renderSpinner;
76
76
  /**
77
- * Handles click on a close button by dispatching a custom event
78
- *
77
+ * Renders the core content of the chip (icon, text, spinner).
79
78
  * @private
80
79
  */
81
- private _handleCloseButtonClick;
80
+ private _renderContent;
82
81
  /**
83
- * Template for the dismissible state
84
- *
82
+ * Template for the checkbox variant.
83
+ * This uses a visually hidden native checkbox for accessibility and form integration.
85
84
  * @private
86
85
  */
87
- private renderCloseButton;
88
- render(): TemplateResult<1>;
86
+ private _renderCheckbox;
87
+ private _renderButton;
88
+ /**
89
+ * Template for the dismissible variant.
90
+ * @private
91
+ */
92
+ private _renderDismissible;
93
+ render(): TemplateResult;
89
94
  static styles: CSSResult;
90
95
  }
91
96
 
97
+ declare const PieChip_base: typeof PieElement;
98
+
99
+ export declare const types: readonly ["button", "checkbox"];
100
+
92
101
  export declare const variants: readonly ["default", "outline", "ghost"];
93
102
 
94
103
  export { }
package/dist/index.js CHANGED
@@ -1,143 +1,212 @@
1
- import { LitElement as u, unsafeCSS as f, html as v, nothing as g } from "lit";
2
- import { property as d } from "lit/decorators.js";
3
- import { ifDefined as p } from "lit/directives/if-defined.js";
4
- import { classMap as x } from "lit/directives/class-map.js";
5
- import { validPropertyValues as y, safeCustomElement as k, dispatchCustomEvent as w } from "@justeattakeaway/pie-webc-core";
1
+ import { LitElement as _, html as p, nothing as x, unsafeCSS as S } from "lit";
2
+ import { property as n } from "lit/decorators.js";
3
+ import { ifDefined as d } from "lit/directives/if-defined.js";
4
+ import { classMap as g } from "lit/directives/class-map.js";
5
+ import { DelegatesFocusMixin as D, validPropertyValues as y, safeCustomElement as B } from "@justeattakeaway/pie-webc-core";
6
6
  import "@justeattakeaway/pie-icons-webc/dist/IconCloseCircleFilled.js";
7
7
  import "@justeattakeaway/pie-spinner";
8
- const h = class h extends u {
8
+ const v = class v extends _ {
9
9
  willUpdate() {
10
- this.getAttribute("v") || this.setAttribute("v", h.v);
10
+ this.getAttribute("v") || this.setAttribute("v", v.v);
11
11
  }
12
12
  };
13
- h.v = "0.13.0";
14
- let b = h;
15
- const C = "*,*:after,*:before{box-sizing:inherit}:host{display:inline-block}.c-chip{--int-states-mixin-bg-color: var(--dt-color-interactive-secondary);--chip-color: var(--dt-color-content-interactive-secondary);--chip-border-width: 1px;--chip-border-color: transparent;--chip-padding-block: calc(6px - var(--chip-border-width));--chip-padding-inline: calc(var(--dt-spacing-c) - var(--chip-border-width));--chip-padding-dismissible: calc(var(--dt-spacing-a) - var(--chip-border-width));--chip-min-width: calc(var(--dt-spacing-g) + var(--dt-spacing-b));--chip-gap: var(--dt-spacing-b);--chip-dismissible-offset: calc(var(--chip-gap) / -2);--chip-cursor: pointer;--chip-close-btn-cursor: pointer;--icon-display-override: block;position:relative;display:inline-flex;align-items:center;justify-content:center;gap:var(--chip-gap);padding-block-start:var(--chip-padding-block);padding-block-end:var(--chip-padding-block);padding-inline-start:var(--chip-padding-inline);padding-inline-end:var(--chip-padding-inline);background-color:var(--int-states-mixin-bg-color);color:var(--chip-color);border-radius:var(--dt-radius-rounded-e);border:1px solid;border-color:var(--chip-border-color);cursor:var(--chip-cursor);-webkit-user-select:none;user-select:none;min-width:var(--chip-min-width);font-size:calc(var(--dt-font-interactive-xs-size) * 1px);line-height:calc(var(--dt-font-interactive-xs-line-height) * 1px);font-weight:var(--dt-font-weight-bold)}.c-chip:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: calc(-1 * var(--dt-color-hover-01));--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-secondary-h), var(--dt-color-interactive-secondary-s), calc(var(--dt-color-interactive-secondary-l) + var(--hover-modifier)))}.c-chip:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.is-loading:not(:disabled,.is-disabled){--active-modifier: calc(-1 * var(--dt-color-active-01));--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-secondary-h), var(--dt-color-interactive-secondary-s), calc(var(--dt-color-interactive-secondary-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), var(--dt-color-interactive-secondary))}.c-chip:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), var(--dt-color-interactive-secondary))}}.c-chip.c-chip--outline:not(.is-disabled,.c-chip--selected){--chip-border-color: var(--dt-color-border-strong)}.c-chip.c-chip--outline,.c-chip.c-chip--ghost{--int-states-mixin-bg-color: transparent;--chip-color: var(--dt-color-content-interactive-secondary-solid)}.c-chip.c-chip--outline:hover:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: calc(-1 * var(--dt-color-hover-01));--hover-modifier: var(--dt-color-hover-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--hover-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--hover-modifier)))}.c-chip.c-chip--outline:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--outline.is-loading:not(:disabled,.is-disabled),.c-chip.c-chip--ghost:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost.is-loading:not(:disabled,.is-disabled){--active-modifier: calc(-1 * var(--dt-color-active-01));--active-modifier: var(--dt-color-active-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--active-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip.c-chip--outline:hover:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), var(--dt-color-transparent))}.c-chip.c-chip--outline:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--outline.is-loading:not(:disabled,.is-disabled),.c-chip.c-chip--ghost:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), var(--dt-color-transparent))}}.c-chip.c-chip--selected{--int-states-mixin-bg-color: var(--dt-color-interactive-primary);--chip-color: var(--dt-color-content-interactive-primary)}.c-chip.c-chip--selected:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: var(--dt-color-hover-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--hover-modifier)))}.c-chip.c-chip--selected:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--selected.is-loading:not(:disabled,.is-disabled){--active-modifier: var(--dt-color-active-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip.c-chip--selected:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-02-bg) var(--dt-color-hover-02), var(--dt-color-interactive-primary))}.c-chip.c-chip--selected:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--selected.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-02-bg) var(--dt-color-active-02), var(--dt-color-interactive-primary))}}.c-chip.is-loading>*:not(.c-chip-spinner){visibility:hidden}.c-chip.is-loading .c-chip-spinner{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}.c-chip.is-disabled{--int-states-mixin-bg-color: var(--dt-color-disabled-01);--chip-color: var(--dt-color-content-disabled);--chip-cursor: not-allowed;--chip-close-btn-cursor: not-allowed}.c-chip.is-disabled.c-chip--ghost{--int-states-mixin-bg-color: transparent;--chip-color: var(--dt-color-content-disabled-solid)}.c-chip.is-dismissible{--chip-cursor: text;padding-inline-end:var(--chip-padding-dismissible);padding-block-start:var(--chip-padding-dismissible);padding-block-end:var(--chip-padding-dismissible);-webkit-user-select:auto;user-select:auto}.c-chip.is-dismissible .c-chip-closeBtn{display:inline-flex;-webkit-user-select:none;user-select:none;outline:none;border:none;color:inherit;background-color:inherit;border-radius:inherit;cursor:var(--chip-close-btn-cursor);padding:0;margin-inline-start:var(--chip-dismissible-offset)}.c-chip.is-dismissible .c-chip-closeBtn:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-chip:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}::slotted(svg){display:block;height:var(--icon-size-override);width:var(--icon-size-override)}", $ = ["default", "outline", "ghost"], S = "pie-chip-close", l = {
13
+ v.v = "0.14.0";
14
+ let m = v;
15
+ const L = "*,*:after,*:before{box-sizing:inherit}:host{display:inline-block}:host([disabled]),:host([isLoading]){pointer-events:none}.c-chip{--int-states-mixin-bg-color: var(--dt-color-interactive-secondary);--chip-color: var(--dt-color-content-interactive-secondary);--chip-border-width: 1px;--chip-border-color: transparent;--chip-padding-block: calc(6px - var(--chip-border-width));--chip-padding-inline: calc(var(--dt-spacing-c) - var(--chip-border-width));--chip-padding-dismissible: calc(var(--dt-spacing-a) - var(--chip-border-width));--chip-min-width: calc(var(--dt-spacing-g) + var(--dt-spacing-b));--chip-gap: var(--dt-spacing-b);--chip-dismissible-offset: calc(var(--chip-gap) / -2);--chip-cursor: pointer;--chip-close-btn-cursor: pointer;--icon-display-override: block;position:relative;display:inline-flex;align-items:center;justify-content:center;gap:var(--chip-gap);padding-block-start:var(--chip-padding-block);padding-block-end:var(--chip-padding-block);padding-inline-start:var(--chip-padding-inline);padding-inline-end:var(--chip-padding-inline);background-color:var(--int-states-mixin-bg-color);color:var(--chip-color);border-radius:var(--dt-radius-rounded-e);border:1px solid;border-color:var(--chip-border-color);cursor:var(--chip-cursor);-webkit-user-select:none;user-select:none;min-width:var(--chip-min-width);font-family:inherit;font-size:calc(var(--dt-font-interactive-xs-size) * 1px);line-height:calc(var(--dt-font-interactive-xs-line-height) * 1px);font-weight:var(--dt-font-weight-bold)}.c-chip:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: calc(-1 * var(--dt-color-hover-01));--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-secondary-h), var(--dt-color-interactive-secondary-s), calc(var(--dt-color-interactive-secondary-l) + var(--hover-modifier)))}.c-chip:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.is-loading:not(:disabled,.is-disabled){--active-modifier: calc(-1 * var(--dt-color-active-01));--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-secondary-h), var(--dt-color-interactive-secondary-s), calc(var(--dt-color-interactive-secondary-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), var(--dt-color-interactive-secondary))}.c-chip:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), var(--dt-color-interactive-secondary))}}.c-chip.c-chip--outline:not(.is-disabled,.c-chip--selected){--chip-border-color: var(--dt-color-border-strong)}.c-chip.c-chip--outline,.c-chip.c-chip--ghost{--int-states-mixin-bg-color: transparent;--chip-color: var(--dt-color-content-interactive-secondary-solid)}.c-chip.c-chip--outline:hover:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: calc(-1 * var(--dt-color-hover-01));--hover-modifier: var(--dt-color-hover-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--hover-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--hover-modifier)))}.c-chip.c-chip--outline:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--outline.is-loading:not(:disabled,.is-disabled),.c-chip.c-chip--ghost:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost.is-loading:not(:disabled,.is-disabled){--active-modifier: calc(-1 * var(--dt-color-active-01));--active-modifier: var(--dt-color-active-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--active-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip.c-chip--outline:hover:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), var(--dt-color-transparent))}.c-chip.c-chip--outline:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--outline.is-loading:not(:disabled,.is-disabled),.c-chip.c-chip--ghost:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--ghost.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), var(--dt-color-transparent))}}.c-chip.c-chip--selected{--int-states-mixin-bg-color: var(--dt-color-interactive-primary);--chip-color: var(--dt-color-content-interactive-primary)}.c-chip.c-chip--selected:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: var(--dt-color-hover-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--hover-modifier)))}.c-chip.c-chip--selected:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--selected.is-loading:not(:disabled,.is-disabled){--active-modifier: var(--dt-color-active-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){.c-chip.c-chip--selected:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-02-bg) var(--dt-color-hover-02), var(--dt-color-interactive-primary))}.c-chip.c-chip--selected:active:not(:disabled,.is-disabled,.is-dismissible),.c-chip.c-chip--selected.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-02-bg) var(--dt-color-active-02), var(--dt-color-interactive-primary))}}.c-chip.is-loading>*:not(.c-chip-spinner){visibility:hidden}.c-chip.is-loading .c-chip-spinner{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}.c-chip.is-disabled{--int-states-mixin-bg-color: var(--dt-color-disabled-01);--chip-color: var(--dt-color-content-disabled);--chip-cursor: not-allowed;--chip-close-btn-cursor: not-allowed}.c-chip.is-disabled.c-chip--ghost{--int-states-mixin-bg-color: transparent;--chip-color: var(--dt-color-content-disabled-solid)}.c-chip.is-dismissible{--chip-cursor: text;padding-inline-end:var(--chip-padding-dismissible);padding-block-start:var(--chip-padding-dismissible);padding-block-end:var(--chip-padding-dismissible);-webkit-user-select:auto;user-select:auto}.c-chip.is-dismissible .c-chip-closeBtn{display:inline-flex;-webkit-user-select:none;user-select:none;outline:none;border:none;color:inherit;background-color:inherit;border-radius:inherit;cursor:var(--chip-close-btn-cursor);padding:0;margin-inline-start:var(--chip-dismissible-offset)}.c-chip.is-dismissible .c-chip-closeBtn:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-chip:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}[type=checkbox]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;border:0;overflow:hidden;white-space:nowrap;clip:rect(0,0,0,0);clip-path:inset(50%)}[type=checkbox]:focus-visible{outline:none}[type=checkbox]:focus-visible+.c-chip{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}::slotted(svg){display:block;height:var(--icon-size-override);width:var(--icon-size-override)}", z = ["default", "outline", "ghost"], P = ["button", "checkbox"], a = {
16
16
  variant: "default",
17
17
  disabled: !1,
18
18
  isSelected: !1,
19
19
  isLoading: !1,
20
- isDismissible: !1
20
+ isDismissible: !1,
21
+ type: "button"
21
22
  };
22
- var B = Object.defineProperty, D = Object.getOwnPropertyDescriptor, a = (i, t, c, r) => {
23
- for (var e = r > 1 ? void 0 : r ? D(t, c) : t, s = i.length - 1, n; s >= 0; s--)
24
- (n = i[s]) && (e = (r ? n(t, c, e) : n(e)) || e);
25
- return r && e && B(t, c, e), e;
23
+ var E = Object.defineProperty, O = Object.getOwnPropertyDescriptor, l = (i, o, t, s) => {
24
+ for (var e = s > 1 ? void 0 : s ? O(o, t) : o, c = i.length - 1, h; c >= 0; c--)
25
+ (h = i[c]) && (e = (s ? h(o, t, e) : h(e)) || e);
26
+ return s && e && E(o, t, e), e;
26
27
  };
27
- const _ = "pie-chip";
28
- let o = class extends b {
28
+ const k = "pie-chip";
29
+ let r = class extends D(m) {
29
30
  constructor() {
30
- super(...arguments), this.variant = l.variant, this.disabled = l.disabled, this.isSelected = l.isSelected, this.isLoading = l.isLoading, this.isDismissible = l.isDismissible;
31
+ super(...arguments), this.variant = a.variant, this.type = a.type, this.disabled = a.disabled, this.isSelected = a.isSelected, this.isLoading = a.isLoading, this.isDismissible = a.isDismissible;
31
32
  }
32
33
  /**
33
- * Handler to prevent click events
34
- * when the chip is disabled or dismissible
35
- *
34
+ * Handles the change event for the native checkbox.
35
+ * This component is controlled, so it does not set its own state.
36
+ * It simply forwards the native change event.
36
37
  * @private
37
38
  */
38
- onClickHandler(i) {
39
- (this.disabled || this.isDismissible) && (i.preventDefault(), i.stopPropagation());
39
+ _onCheckboxChange() {
40
+ const i = new Event("change", { bubbles: !0, composed: !0 });
41
+ this.dispatchEvent(i);
40
42
  }
41
43
  /**
42
- * Template for the loading state
43
- *
44
+ * Template for the loading state spinner.
44
45
  * @private
45
46
  */
46
- renderSpinner() {
47
- const { isSelected: i } = this;
48
- return v`
49
- <pie-spinner
50
- class="c-chip-spinner"
51
- size="small"
52
- variant="${i ? "inverse" : "secondary"}">
53
- </pie-spinner>`;
47
+ _renderSpinner() {
48
+ const i = this.isSelected ? "inverse" : "secondary";
49
+ return p`
50
+ <pie-spinner
51
+ class="c-chip-spinner"
52
+ size="small"
53
+ variant="${i}">
54
+ </pie-spinner>`;
54
55
  }
55
56
  /**
56
- * Handles click on a close button by dispatching a custom event
57
- *
57
+ * Renders the core content of the chip (icon, text, spinner).
58
58
  * @private
59
59
  */
60
- _handleCloseButtonClick() {
61
- w(this, S);
60
+ _renderContent() {
61
+ return p`
62
+ <slot name="icon"></slot>
63
+ ${this.isLoading ? this._renderSpinner() : x}
64
+ <slot></slot>
65
+ `;
62
66
  }
63
67
  /**
64
- * Template for the dismissible state
65
- *
68
+ * Template for the checkbox variant.
69
+ * This uses a visually hidden native checkbox for accessibility and form integration.
66
70
  * @private
67
71
  */
68
- renderCloseButton() {
69
- var i;
70
- return v`
71
- <button
72
- @click="${this._handleCloseButtonClick}"
73
- ?disabled=${this.disabled}
74
- aria-label="${p((i = this.aria) == null ? void 0 : i.close)}"
75
- class="c-chip-closeBtn"
76
- data-test-id="chip-close-button">
77
- <icon-close-circle-filled size="m"></icon-close-circle-filled>
78
- </button>`;
72
+ _renderCheckbox() {
73
+ const {
74
+ aria: i,
75
+ variant: o,
76
+ disabled: t,
77
+ isSelected: s,
78
+ isLoading: e
79
+ } = this, c = {
80
+ "c-chip": !0,
81
+ [`c-chip--${o}`]: !0,
82
+ "c-chip--selected": s,
83
+ "is-disabled": t,
84
+ "is-loading": e
85
+ };
86
+ return p`
87
+ <input
88
+ data-test-id="chip-checkbox-input"
89
+ type="checkbox"
90
+ id="pie-chip"
91
+ aria-label="${d(i == null ? void 0 : i.label)}"
92
+ ?checked=${s}
93
+ ?disabled=${t}
94
+ @change="${this._onCheckboxChange}"/>
95
+ <label
96
+ for="pie-chip"
97
+ class=${g(c)}
98
+ data-test-id="pie-chip">
99
+ ${this._renderContent()}
100
+ </label>`;
79
101
  }
80
- render() {
81
- var m;
102
+ _renderButton() {
82
103
  const {
83
- variant: i,
104
+ aria: i,
105
+ variant: o,
84
106
  disabled: t,
85
- isSelected: c,
86
- isLoading: r,
107
+ isSelected: s,
108
+ isLoading: e
109
+ } = this, c = {
110
+ "c-chip": !0,
111
+ [`c-chip--${o}`]: !0,
112
+ "c-chip--selected": s,
113
+ "is-disabled": t,
114
+ "is-loading": e
115
+ };
116
+ return p`
117
+ <button
118
+ id="pie-chip"
119
+ type="button"
120
+ class=${g(c)}
121
+ aria-busy="${d(e)}"
122
+ aria-haspopup="${d(i == null ? void 0 : i.haspopup)}"
123
+ aria-expanded="${d(i == null ? void 0 : i.expanded)}"
124
+ aria-pressed="${s}"
125
+ aria-label="${d(i == null ? void 0 : i.label)}"
126
+ ?disabled=${t}
127
+ data-test-id="pie-chip">
128
+ ${this._renderContent()}
129
+ </button>`;
130
+ }
131
+ /**
132
+ * Template for the dismissible variant.
133
+ * @private
134
+ */
135
+ _renderDismissible() {
136
+ var u, f;
137
+ const {
138
+ variant: i,
139
+ disabled: o,
140
+ isSelected: t,
141
+ isLoading: s,
87
142
  isDismissible: e
88
- } = this, s = e && c, n = {
143
+ } = this, c = e && t, h = {
89
144
  "c-chip": !0,
90
145
  [`c-chip--${i}`]: !0,
91
- "c-chip--selected": c,
92
- "is-dismissible": s,
93
- "is-disabled": t,
94
- "is-loading": r
146
+ "c-chip--selected": t,
147
+ "is-dismissible": c,
148
+ "is-disabled": o,
149
+ "is-loading": s
150
+ }, w = (b) => {
151
+ (o || e) && (b.preventDefault(), b.stopPropagation());
152
+ }, $ = (b) => {
153
+ b.stopPropagation();
154
+ const C = new Event("close", { bubbles: !0, composed: !0 });
155
+ this.dispatchEvent(C);
95
156
  };
96
- return v`
157
+ return p`
97
158
  <div
98
- role="${p(s ? void 0 : "button")}"
99
- tabindex="${p(s ? void 0 : "0")}"
100
- aria-atomic="true"
101
- aria-busy="${r}"
102
- aria-current="${c}"
103
- aria-label="${p((m = this.aria) == null ? void 0 : m.label)}"
104
- aria-live="polite"
105
- class=${x(n)}
159
+ aria-busy="${s}"
160
+ aria-current="${t}"
161
+ aria-label="${d((u = this.aria) == null ? void 0 : u.label)}"
162
+ class=${g(h)}
106
163
  data-test-id="pie-chip"
107
- @click="${this.onClickHandler}">
108
- <slot name="icon"></slot>
109
- ${r ? this.renderSpinner() : g}
110
- <slot></slot>
111
- ${s ? this.renderCloseButton() : g}
164
+ @click=${w}>
165
+ ${this._renderContent()}
166
+ ${c ? p`<button
167
+ @click="${$}"
168
+ ?disabled=${this.disabled}
169
+ aria-label="${d((f = this.aria) == null ? void 0 : f.close)}"
170
+ class="c-chip-closeBtn"
171
+ data-test-id="chip-close-button">
172
+ <icon-close-circle-filled size="m"></icon-close-circle-filled>
173
+ </button>` : x}
112
174
  </div>`;
113
175
  }
176
+ render() {
177
+ return this.isDismissible ? this._renderDismissible() : this.type === "checkbox" ? this._renderCheckbox() : this._renderButton();
178
+ }
114
179
  };
115
- o.styles = f(C);
116
- a([
117
- d({ type: String }),
118
- y(_, $, l.variant)
119
- ], o.prototype, "variant", 2);
120
- a([
121
- d({ type: Boolean })
122
- ], o.prototype, "disabled", 2);
123
- a([
124
- d({ type: Boolean })
125
- ], o.prototype, "isSelected", 2);
126
- a([
127
- d({ type: Boolean })
128
- ], o.prototype, "isLoading", 2);
129
- a([
130
- d({ type: Boolean })
131
- ], o.prototype, "isDismissible", 2);
132
- a([
133
- d({ type: Object })
134
- ], o.prototype, "aria", 2);
135
- o = a([
136
- k("pie-chip")
137
- ], o);
180
+ r.styles = S(L);
181
+ l([
182
+ n({ type: String }),
183
+ y(k, z, a.variant)
184
+ ], r.prototype, "variant", 2);
185
+ l([
186
+ n({ type: String }),
187
+ y(k, P, a.type)
188
+ ], r.prototype, "type", 2);
189
+ l([
190
+ n({ type: Boolean, reflect: !0 })
191
+ ], r.prototype, "disabled", 2);
192
+ l([
193
+ n({ type: Boolean, reflect: !0 })
194
+ ], r.prototype, "isSelected", 2);
195
+ l([
196
+ n({ type: Boolean, reflect: !0 })
197
+ ], r.prototype, "isLoading", 2);
198
+ l([
199
+ n({ type: Boolean })
200
+ ], r.prototype, "isDismissible", 2);
201
+ l([
202
+ n({ type: Object })
203
+ ], r.prototype, "aria", 2);
204
+ r = l([
205
+ B("pie-chip")
206
+ ], r);
138
207
  export {
139
- S as ON_CHIP_CLOSE_EVENT,
140
- o as PieChip,
141
- l as defaultProps,
142
- $ as variants
208
+ r as PieChip,
209
+ a as defaultProps,
210
+ P as types,
211
+ z as variants
143
212
  };
package/dist/react.d.ts CHANGED
@@ -7,6 +7,8 @@ import { TemplateResult } from 'lit';
7
7
  declare type AriaProps = {
8
8
  close?: string;
9
9
  label?: string;
10
+ haspopup?: 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false';
11
+ expanded?: boolean;
10
12
  };
11
13
 
12
14
  export declare interface ChipProps {
@@ -35,69 +37,77 @@ export declare interface ChipProps {
35
37
  * Can be only used if `isSelected` is set to true
36
38
  */
37
39
  isDismissible?: boolean;
40
+ /**
41
+ * Sets the functional type of the chip. Can be `button` or `checkbox`. Defaults to `button`.
42
+ */
43
+ type?: typeof types[number];
38
44
  }
39
45
 
40
46
  export declare type DefaultProps = ComponentDefaultProps<ChipProps, keyof Omit<ChipProps, 'aria'>>;
41
47
 
42
48
  export declare const defaultProps: DefaultProps;
43
49
 
44
- /**
45
- * Event name for when the chip is closed.
46
- *
47
- * @constant
48
- */
49
- export declare const ON_CHIP_CLOSE_EVENT = "pie-chip-close";
50
-
51
50
  export declare const PieChip: React_2.ForwardRefExoticComponent<React_2.PropsWithoutRef<ChipProps> & React_2.RefAttributes<PieChip_2> & PieChipEvents & ReactBaseType>;
52
51
 
53
52
  /**
54
53
  * @tagname pie-chip
55
54
  * @slot icon - The icon slot
56
55
  * @slot - Default slot
57
- * @event {CustomEvent} pie-chip-close - when a user clicks close button.
56
+ * @event {Event} close - when a user clicks the close button.
57
+ * @event {Event} change - when a user interacts with the chip of type checkbox.
58
58
  */
59
- declare class PieChip_2 extends PieElement implements ChipProps {
59
+ declare class PieChip_2 extends PieChip_base implements ChipProps {
60
60
  variant: "default" | "outline" | "ghost";
61
+ type: "button" | "checkbox";
61
62
  disabled: boolean;
62
63
  isSelected: boolean;
63
64
  isLoading: boolean;
64
65
  isDismissible: boolean;
65
66
  aria: ChipProps['aria'];
66
67
  /**
67
- * Handler to prevent click events
68
- * when the chip is disabled or dismissible
69
- *
68
+ * Handles the change event for the native checkbox.
69
+ * This component is controlled, so it does not set its own state.
70
+ * It simply forwards the native change event.
70
71
  * @private
71
72
  */
72
- private onClickHandler;
73
+ private _onCheckboxChange;
73
74
  /**
74
- * Template for the loading state
75
- *
75
+ * Template for the loading state spinner.
76
76
  * @private
77
77
  */
78
- private renderSpinner;
78
+ private _renderSpinner;
79
79
  /**
80
- * Handles click on a close button by dispatching a custom event
81
- *
80
+ * Renders the core content of the chip (icon, text, spinner).
82
81
  * @private
83
82
  */
84
- private _handleCloseButtonClick;
83
+ private _renderContent;
85
84
  /**
86
- * Template for the dismissible state
87
- *
85
+ * Template for the checkbox variant.
86
+ * This uses a visually hidden native checkbox for accessibility and form integration.
88
87
  * @private
89
88
  */
90
- private renderCloseButton;
91
- render(): TemplateResult<1>;
89
+ private _renderCheckbox;
90
+ private _renderButton;
91
+ /**
92
+ * Template for the dismissible variant.
93
+ * @private
94
+ */
95
+ private _renderDismissible;
96
+ render(): TemplateResult;
92
97
  static styles: CSSResult;
93
98
  }
94
99
 
100
+ declare const PieChip_base: typeof PieElement;
101
+
95
102
  declare type PieChipEvents = {
96
- onPieChipClose?: (event: CustomEvent) => void;
103
+ onClose?: (event: Event) => void;
104
+ onChange?: (event: Event) => void;
97
105
  };
98
106
 
99
107
  declare type ReactBaseType = React_2.HTMLAttributes<HTMLElement>;
100
108
 
109
+ export declare const types: readonly ["button", "checkbox"];
110
+
101
111
  export declare const variants: readonly ["default", "outline", "ghost"];
102
112
 
103
113
  export { }
package/dist/react.js CHANGED
@@ -1,20 +1,22 @@
1
1
  import * as e from "react";
2
- import { createComponent as i } from "@lit/react";
3
- import { PieChip as p } from "./index.js";
4
- import { ON_CHIP_CLOSE_EVENT as C, defaultProps as c, variants as h } from "./index.js";
5
- const o = i({
2
+ import { createComponent as o } from "@lit/react";
3
+ import { PieChip as t } from "./index.js";
4
+ import { defaultProps as n, types as c, variants as C } from "./index.js";
5
+ const i = o({
6
6
  displayName: "PieChip",
7
- elementClass: p,
7
+ elementClass: t,
8
8
  react: e,
9
9
  tagName: "pie-chip",
10
10
  events: {
11
- onPieChipClose: "pie-chip-close"
12
- // when a user clicks close button.
11
+ onClose: "close",
12
+ // when a user clicks the close button.
13
+ onChange: "change"
14
+ // when a user interacts with the chip of type checkbox.
13
15
  }
14
- }), r = o;
16
+ }), r = i;
15
17
  export {
16
- C as ON_CHIP_CLOSE_EVENT,
17
18
  r as PieChip,
18
- c as defaultProps,
19
- h as variants
19
+ n as defaultProps,
20
+ c as types,
21
+ C as variants
20
22
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-chip",
3
3
  "description": "PIE Design System Chip built using Web Components",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/justeattakeaway/pie",
package/src/chip.scss CHANGED
@@ -4,6 +4,11 @@
4
4
  display: inline-block;
5
5
  }
6
6
 
7
+ :host([disabled]),
8
+ :host([isLoading]) {
9
+ pointer-events: none;
10
+ }
11
+
7
12
  .c-chip {
8
13
  --int-states-mixin-bg-color: var(--dt-color-interactive-secondary);
9
14
  --chip-color: var(--dt-color-content-interactive-secondary);
@@ -39,6 +44,7 @@
39
44
  user-select: none;
40
45
  min-width: var(--chip-min-width);
41
46
 
47
+ font-family: inherit;
42
48
  @include p.font-size(--dt-font-interactive-xs-size);
43
49
  @include p.line-height(--dt-font-interactive-xs-line-height);
44
50
  font-weight: var(--dt-font-weight-bold);
@@ -123,6 +129,30 @@
123
129
  }
124
130
  }
125
131
 
132
+ [type="checkbox"] {
133
+ // the p.visually-hidden mixin doesn't properly work on checkboxes
134
+ position: absolute;
135
+ width: 1px;
136
+ height: 1px;
137
+ padding: 0;
138
+ margin: -1px;
139
+ border: 0;
140
+
141
+ overflow: hidden;
142
+ white-space: nowrap;
143
+
144
+ clip: rect(0, 0, 0, 0);
145
+ clip-path: inset(50%);
146
+
147
+ &:focus-visible {
148
+ outline: none;
149
+ }
150
+ }
151
+
152
+ [type="checkbox"]:focus-visible + .c-chip {
153
+ @include p.focus;
154
+ }
155
+
126
156
  ::slotted(svg) {
127
157
  display: block;
128
158
  height: var(--icon-size-override);
package/src/defs.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
2
 
3
3
  export const variants = ['default', 'outline', 'ghost'] as const;
4
+ export const types = ['button', 'checkbox'] as const;
4
5
 
5
6
  type AriaProps = {
6
7
  close?: string;
7
8
  label?: string;
9
+ haspopup?: 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false';
10
+ expanded?: boolean;
8
11
  };
9
12
 
10
13
  export interface ChipProps {
@@ -33,15 +36,12 @@ export interface ChipProps {
33
36
  * Can be only used if `isSelected` is set to true
34
37
  */
35
38
  isDismissible?: boolean;
36
- }
37
-
38
- /**
39
- * Event name for when the chip is closed.
40
- *
41
- * @constant
42
- */
43
39
 
44
- export const ON_CHIP_CLOSE_EVENT = 'pie-chip-close';
40
+ /**
41
+ * Sets the functional type of the chip. Can be `button` or `checkbox`. Defaults to `button`.
42
+ */
43
+ type?: typeof types[number];
44
+ }
45
45
 
46
46
  export type DefaultProps = ComponentDefaultProps<ChipProps, keyof Omit<ChipProps, 'aria'>>;
47
47
 
@@ -51,4 +51,5 @@ export const defaultProps: DefaultProps = {
51
51
  isSelected: false,
52
52
  isLoading: false,
53
53
  isDismissible: false,
54
+ type: 'button',
54
55
  };
package/src/index.ts CHANGED
@@ -7,12 +7,16 @@ import { ifDefined } from 'lit/directives/if-defined.js';
7
7
  import { classMap } from 'lit/directives/class-map.js';
8
8
 
9
9
  import {
10
- validPropertyValues, dispatchCustomEvent,
10
+ validPropertyValues,
11
11
  safeCustomElement,
12
+ DelegatesFocusMixin,
12
13
  } from '@justeattakeaway/pie-webc-core';
13
14
  import styles from './chip.scss?inline';
14
15
  import {
15
- type ChipProps, variants, ON_CHIP_CLOSE_EVENT, defaultProps,
16
+ type ChipProps,
17
+ variants,
18
+ types,
19
+ defaultProps,
16
20
  } from './defs';
17
21
  import '@justeattakeaway/pie-icons-webc/dist/IconCloseCircleFilled.js';
18
22
  import '@justeattakeaway/pie-spinner';
@@ -26,21 +30,26 @@ const componentSelector = 'pie-chip';
26
30
  * @tagname pie-chip
27
31
  * @slot icon - The icon slot
28
32
  * @slot - Default slot
29
- * @event {CustomEvent} pie-chip-close - when a user clicks close button.
33
+ * @event {Event} close - when a user clicks the close button.
34
+ * @event {Event} change - when a user interacts with the chip of type checkbox.
30
35
  */
31
36
  @safeCustomElement('pie-chip')
32
- export class PieChip extends PieElement implements ChipProps {
37
+ export class PieChip extends DelegatesFocusMixin(PieElement) implements ChipProps {
33
38
  @property({ type: String })
34
39
  @validPropertyValues(componentSelector, variants, defaultProps.variant)
35
40
  public variant = defaultProps.variant;
36
41
 
37
- @property({ type: Boolean })
42
+ @property({ type: String })
43
+ @validPropertyValues(componentSelector, types, defaultProps.type)
44
+ public type = defaultProps.type;
45
+
46
+ @property({ type: Boolean, reflect: true })
38
47
  public disabled = defaultProps.disabled;
39
48
 
40
- @property({ type: Boolean })
49
+ @property({ type: Boolean, reflect: true })
41
50
  public isSelected = defaultProps.isSelected;
42
51
 
43
- @property({ type: Boolean })
52
+ @property({ type: Boolean, reflect: true })
44
53
  public isLoading = defaultProps.isLoading;
45
54
 
46
55
  @property({ type: Boolean })
@@ -50,62 +59,123 @@ export class PieChip extends PieElement implements ChipProps {
50
59
  public aria: ChipProps['aria'];
51
60
 
52
61
  /**
53
- * Handler to prevent click events
54
- * when the chip is disabled or dismissible
55
- *
62
+ * Handles the change event for the native checkbox.
63
+ * This component is controlled, so it does not set its own state.
64
+ * It simply forwards the native change event.
56
65
  * @private
57
66
  */
58
- private onClickHandler (event: Event) {
59
- if (this.disabled || this.isDismissible) {
60
- event.preventDefault();
61
- event.stopPropagation();
62
- }
67
+ private _onCheckboxChange () {
68
+ // The original event from the input does not bubble past the shadow DOM boundary.
69
+ // We create and dispatch a new 'change' event to ensure it bubbles and is composed,
70
+ // allowing consumers to respond to the interaction.
71
+ const changeEvent = new Event('change', { bubbles: true, composed: true });
72
+ this.dispatchEvent(changeEvent);
63
73
  }
64
74
 
65
75
  /**
66
- * Template for the loading state
67
- *
76
+ * Template for the loading state spinner.
68
77
  * @private
69
78
  */
70
- private renderSpinner (): TemplateResult {
71
- const { isSelected } = this;
72
- const spinnerVariant = isSelected ? 'inverse' : 'secondary';
79
+ private _renderSpinner (): TemplateResult {
80
+ const spinnerVariant = this.isSelected ? 'inverse' : 'secondary';
73
81
 
74
82
  return html`
75
- <pie-spinner
76
- class="c-chip-spinner"
77
- size="small"
78
- variant="${spinnerVariant}">
79
- </pie-spinner>`;
83
+ <pie-spinner
84
+ class="c-chip-spinner"
85
+ size="small"
86
+ variant="${spinnerVariant}">
87
+ </pie-spinner>`;
80
88
  }
81
89
 
82
90
  /**
83
- * Handles click on a close button by dispatching a custom event
84
- *
91
+ * Renders the core content of the chip (icon, text, spinner).
85
92
  * @private
86
93
  */
87
- private _handleCloseButtonClick () : void {
88
- dispatchCustomEvent(this, ON_CHIP_CLOSE_EVENT);
94
+ private _renderContent (): TemplateResult {
95
+ return html`
96
+ <slot name="icon"></slot>
97
+ ${this.isLoading ? this._renderSpinner() : nothing}
98
+ <slot></slot>
99
+ `;
89
100
  }
90
101
 
91
102
  /**
92
- * Template for the dismissible state
93
- *
103
+ * Template for the checkbox variant.
104
+ * This uses a visually hidden native checkbox for accessibility and form integration.
94
105
  * @private
95
106
  */
96
- private renderCloseButton (): TemplateResult {
107
+ private _renderCheckbox (): TemplateResult {
108
+ const {
109
+ aria,
110
+ variant,
111
+ disabled,
112
+ isSelected,
113
+ isLoading,
114
+ } = this;
115
+
116
+ const classes = {
117
+ 'c-chip': true,
118
+ [`c-chip--${variant}`]: true,
119
+ 'c-chip--selected': isSelected,
120
+ 'is-disabled': disabled,
121
+ 'is-loading': isLoading,
122
+ };
123
+
97
124
  return html`
98
- <button
99
- @click="${this._handleCloseButtonClick}"
100
- ?disabled=${this.disabled}
101
- aria-label="${ifDefined(this.aria?.close)}"
102
- class="c-chip-closeBtn"
103
- data-test-id="chip-close-button">
104
- <icon-close-circle-filled size="m"></icon-close-circle-filled>
105
- </button>`;
125
+ <input
126
+ data-test-id="chip-checkbox-input"
127
+ type="checkbox"
128
+ id="pie-chip"
129
+ aria-label="${ifDefined(aria?.label)}"
130
+ ?checked=${isSelected}
131
+ ?disabled=${disabled}
132
+ @change="${this._onCheckboxChange}"/>
133
+ <label
134
+ for="pie-chip"
135
+ class=${classMap(classes)}
136
+ data-test-id="pie-chip">
137
+ ${this._renderContent()}
138
+ </label>`;
106
139
  }
107
140
 
108
- render () {
141
+ private _renderButton (): TemplateResult {
142
+ const {
143
+ aria,
144
+ variant,
145
+ disabled,
146
+ isSelected,
147
+ isLoading,
148
+ } = this;
149
+
150
+ const classes = {
151
+ 'c-chip': true,
152
+ [`c-chip--${variant}`]: true,
153
+ 'c-chip--selected': isSelected,
154
+ 'is-disabled': disabled,
155
+ 'is-loading': isLoading,
156
+ };
157
+
158
+ return html`
159
+ <button
160
+ id="pie-chip"
161
+ type="button"
162
+ class=${classMap(classes)}
163
+ aria-busy="${ifDefined(isLoading)}"
164
+ aria-haspopup="${ifDefined(aria?.haspopup)}"
165
+ aria-expanded="${ifDefined(aria?.expanded)}"
166
+ aria-pressed="${isSelected}"
167
+ aria-label="${ifDefined(aria?.label)}"
168
+ ?disabled=${disabled}
169
+ data-test-id="pie-chip">
170
+ ${this._renderContent()}
171
+ </button>`;
172
+ }
173
+
174
+ /**
175
+ * Template for the dismissible variant.
176
+ * @private
177
+ */
178
+ private _renderDismissible (): TemplateResult {
109
179
  const {
110
180
  variant,
111
181
  disabled,
@@ -113,6 +183,7 @@ export class PieChip extends PieElement implements ChipProps {
113
183
  isLoading,
114
184
  isDismissible,
115
185
  } = this;
186
+
116
187
  const showCloseButton = isDismissible && isSelected;
117
188
 
118
189
  const classes = {
@@ -124,26 +195,51 @@ export class PieChip extends PieElement implements ChipProps {
124
195
  'is-loading': isLoading,
125
196
  };
126
197
 
198
+ const handleClick = (event: Event) => {
199
+ if (disabled || isDismissible) {
200
+ event.preventDefault();
201
+ event.stopPropagation();
202
+ }
203
+ };
204
+
205
+ const handleCloseButtonClick = (event: Event) : void => {
206
+ event.stopPropagation();
207
+ const closeEvent = new Event('close', { bubbles: true, composed: true });
208
+ this.dispatchEvent(closeEvent);
209
+ };
210
+
127
211
  return html`
128
212
  <div
129
- role="${ifDefined(showCloseButton ? undefined : 'button')}"
130
- tabindex="${ifDefined(showCloseButton ? undefined : '0')}"
131
- aria-atomic="true"
132
213
  aria-busy="${isLoading}"
133
214
  aria-current="${isSelected}"
134
215
  aria-label="${ifDefined(this.aria?.label)}"
135
- aria-live="polite"
136
216
  class=${classMap(classes)}
137
217
  data-test-id="pie-chip"
138
- @click="${this.onClickHandler}">
139
- <slot name="icon"></slot>
140
- ${isLoading ? this.renderSpinner() : nothing}
141
- <slot></slot>
142
- ${showCloseButton ? this.renderCloseButton() : nothing}
218
+ @click=${handleClick}>
219
+ ${this._renderContent()}
220
+ ${showCloseButton ? html`<button
221
+ @click="${handleCloseButtonClick}"
222
+ ?disabled=${this.disabled}
223
+ aria-label="${ifDefined(this.aria?.close)}"
224
+ class="c-chip-closeBtn"
225
+ data-test-id="chip-close-button">
226
+ <icon-close-circle-filled size="m"></icon-close-circle-filled>
227
+ </button>` : nothing}
143
228
  </div>`;
144
229
  }
145
230
 
146
- // Renders a `CSSResult` generated from SCSS by Vite
231
+ render () {
232
+ if (this.isDismissible) {
233
+ return this._renderDismissible();
234
+ }
235
+
236
+ if (this.type === 'checkbox') {
237
+ return this._renderCheckbox();
238
+ }
239
+
240
+ return this._renderButton();
241
+ }
242
+
147
243
  static styles = unsafeCSS(styles);
148
244
  }
149
245
 
package/src/react.ts CHANGED
@@ -11,14 +11,16 @@ const PieChipReact = createComponent({
11
11
  react: React,
12
12
  tagName: 'pie-chip',
13
13
  events: {
14
- onPieChipClose: 'pie-chip-close' as EventName<CustomEvent>, // when a user clicks close button.
14
+ onClose: 'close' as EventName<Event>, // when a user clicks the close button.
15
+ onChange: 'change' as EventName<Event>, // when a user interacts with the chip of type checkbox.
15
16
  },
16
17
  });
17
18
 
18
19
  type ReactBaseType = React.HTMLAttributes<HTMLElement>
19
20
 
20
21
  type PieChipEvents = {
21
- onPieChipClose?: (event: CustomEvent) => void;
22
+ onClose?: (event: Event) => void;
23
+ onChange?: (event: Event) => void;
22
24
  };
23
25
 
24
26
  export const PieChip = PieChipReact as React.ForwardRefExoticComponent<React.PropsWithoutRef<ChipProps>