@justeattakeaway/pie-switch 0.0.0-snapshot-release-20231129145325
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 +100 -0
- package/declaration.d.ts +9 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +106 -0
- package/dist/react.d.ts +76 -0
- package/dist/react.js +23 -0
- package/package.json +43 -0
- package/src/defs.ts +38 -0
- package/src/index.ts +125 -0
- package/src/switch.scss +142 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img align="center" src="../../../readme_image.png" height="200" alt="">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/@justeattakeaway/pie-switch">
|
|
7
|
+
<img alt="GitHub Workflow Status" src="https://img.shields.io/npm/v/@justeattakeaway/pie-switch.svg">
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
# Table of Contents
|
|
12
|
+
|
|
13
|
+
1. [Introduction](#pie-switch)
|
|
14
|
+
2. [Installation](#installation)
|
|
15
|
+
3. [Importing the component](#importing-the-component)
|
|
16
|
+
4. [Peer Dependencies](#peer-dependencies)
|
|
17
|
+
5. [Props](#props)
|
|
18
|
+
6. [Events](#Events)
|
|
19
|
+
7. [Contributing](#contributing)
|
|
20
|
+
|
|
21
|
+
## pie-switch
|
|
22
|
+
|
|
23
|
+
`pie-switch` is a Web Component built using the Lit library.
|
|
24
|
+
|
|
25
|
+
This component can be easily integrated into various frontend frameworks and customized through a set of properties.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
To install `pie-switch` in your application, run the following on your command line:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# npm
|
|
34
|
+
$ npm i @justeattakeaway/pie-switch
|
|
35
|
+
|
|
36
|
+
# yarn
|
|
37
|
+
$ yarn add @justeattakeaway/pie-switch
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For full information on using PIE components as part of an application, check out the [Getting Started Guide](https://github.com/justeattakeaway/pie/wiki/Getting-started-with-PIE-Web-Components).
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Importing the component
|
|
44
|
+
|
|
45
|
+
#### JavaScript
|
|
46
|
+
```js
|
|
47
|
+
// Default – for Native JS Applications, Vue, Angular, Svelte, etc.
|
|
48
|
+
import { PieSwitch } from '@justeattakeaway/pie-switch';
|
|
49
|
+
|
|
50
|
+
// If you don't need to reference the imported object, you can simply
|
|
51
|
+
// import the module which registers the component as a custom element.
|
|
52
|
+
import '@justeattakeaway/pie-switch';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### React
|
|
56
|
+
```js
|
|
57
|
+
// React
|
|
58
|
+
// For React, you will need to import our React-specific component build
|
|
59
|
+
// which wraps the web component using @lit/react
|
|
60
|
+
import { PieSwitch } from '@justeattakeaway/pie-switch/dist/react';
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> [!NOTE]
|
|
64
|
+
> When using the React version of the component, please make sure to also
|
|
65
|
+
> include React as a [peer dependency](#peer-dependencies) in your project.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Peer Dependencies
|
|
69
|
+
|
|
70
|
+
> [!IMPORTANT]
|
|
71
|
+
> When using `pie-switch`, you will also need to include a couple of dependencies to ensure the component renders as expected. See [the PIE Wiki](https://github.com/justeattakeaway/pie/wiki/Getting-started-with-PIE-Web-Components#expected-dependencies) for more information and how to include these in your application.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Props
|
|
75
|
+
|
|
76
|
+
| Property | Type | Default | Description |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| isChecked | `Boolean` | false | Indicates whether the switch is on or off |
|
|
79
|
+
| isDisabled | `Boolean` | false | Indicates whether the switch is disabled or not |
|
|
80
|
+
| aria | `Object` | `undefined` | An object representing the aria labels `label` & `describedBy` that can be used on the switch;
|
|
81
|
+
|
|
82
|
+
In your markup or JSX, you can then use these to set the properties for the `pie-switch` component:
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<!-- Native HTML -->
|
|
86
|
+
<pie-switch></pie-switch>
|
|
87
|
+
|
|
88
|
+
<!-- JSX -->
|
|
89
|
+
<PieSwitch></PieSwitch>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Events
|
|
93
|
+
|
|
94
|
+
| Event | Payload | Description |
|
|
95
|
+
| ----- |-----| ----- |
|
|
96
|
+
| `pie-switch-changed` | `this.isChecked` | Custom event to indicate a switch change. The event's detail contains the value of this.isChecked. |
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
Check out our [contributing guide](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide) for more information on [local development](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#local-development) and how to run specific [component tests](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#testing).
|
package/declaration.d.ts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CSSResult } from 'lit';
|
|
2
|
+
import type { LitElement } from 'lit';
|
|
3
|
+
import type { TemplateResult } from 'lit';
|
|
4
|
+
|
|
5
|
+
export declare type AriaProps = {
|
|
6
|
+
label?: string;
|
|
7
|
+
describedBy?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export declare type LabelPlacement = typeof labelPlacements[number];
|
|
11
|
+
|
|
12
|
+
export declare const labelPlacements: readonly ["leading", "trailing"];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Event name for when the switch checked state is changed.
|
|
16
|
+
*
|
|
17
|
+
* @constant
|
|
18
|
+
*/
|
|
19
|
+
export declare const ON_SWITCH_CHANGED_EVENT = "pie-switch-changed";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @tagname pie-switch
|
|
23
|
+
* @event {CustomEvent} pie-switch-changed - when the switch checked state is changed.
|
|
24
|
+
*/
|
|
25
|
+
export declare class PieSwitch extends PieSwitch_base implements SwitchProps {
|
|
26
|
+
label?: string;
|
|
27
|
+
labelPlacement: SwitchProps['labelPlacement'];
|
|
28
|
+
aria: AriaProps;
|
|
29
|
+
isChecked: boolean;
|
|
30
|
+
isDisabled: boolean;
|
|
31
|
+
static styles: CSSResult;
|
|
32
|
+
onChange(event: Event): void;
|
|
33
|
+
/**
|
|
34
|
+
* Renders the label for a switch if provided.
|
|
35
|
+
* if invalid value is passed, nothing gets rendered
|
|
36
|
+
*
|
|
37
|
+
* @private
|
|
38
|
+
*/
|
|
39
|
+
private renderSwitchLabel;
|
|
40
|
+
render(): TemplateResult<1>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare const PieSwitch_base: (new (...args: any[]) => {
|
|
44
|
+
isRTL: boolean;
|
|
45
|
+
}) & typeof LitElement;
|
|
46
|
+
|
|
47
|
+
export declare interface SwitchProps {
|
|
48
|
+
/**
|
|
49
|
+
* The ARIA labels used for the switch.
|
|
50
|
+
*/
|
|
51
|
+
aria?: AriaProps;
|
|
52
|
+
/**
|
|
53
|
+
* Same as the HTML checked attribute - indicates whether the switch is on or off
|
|
54
|
+
*/
|
|
55
|
+
isChecked?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Same as the HTML checked attribute - indicates whether the switch disabled or not
|
|
58
|
+
*/
|
|
59
|
+
isDisabled?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The label value of the component
|
|
62
|
+
*/
|
|
63
|
+
label?: string;
|
|
64
|
+
/**
|
|
65
|
+
* The placement of the label such as leading or trailing
|
|
66
|
+
*/
|
|
67
|
+
labelPlacement?: LabelPlacement;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { LitElement as w, html as n, nothing as s, unsafeCSS as b } from "lit";
|
|
2
|
+
import { property as d } from "lit/decorators.js";
|
|
3
|
+
import { RtlMixin as v, validPropertyValues as u, defineCustomElement as f } from "@justeattakeaway/pie-webc-core";
|
|
4
|
+
import "@justeattakeaway/pie-icons-webc/IconCheck";
|
|
5
|
+
const g = `*,*:before,*:after{box-sizing:border-box;cursor:inherit}.c-switch-wrapper{display:inline-flex;align-items:center;gap:var(--dt-spacing-b);font-family:var(--dt-font-body-l-family);cursor:pointer}.c-switch-wrapper[isDisabled]{cursor:not-allowed}.c-switch{--switch-bg-color: var(--dt-color-interactive-form);--switch-bg-color--checked: var(--dt-color-interactive-brand);--switch-bg-color--disabled: var(--dt-color-disabled-01);--switch-width: 48px;--switch-height: 24px;--switch-control-size: 20px;--switch-padding: 2px;--switch-radius: var(--dt-radius-rounded-e);--switch-translation: calc(var(--switch-width) - var(--switch-control-size) - 2 * var(--switch-padding));position:relative;display:flex;width:var(--switch-width);height:var(--switch-height);flex-shrink:0;padding:var(--switch-padding);border-radius:var(--switch-radius);background-color:var(--switch-bg-color)}@media (prefers-reduced-motion: no-preference){.c-switch{transition:background-color .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch:hover{background-color:hsl(var(--dt-color-interactive-form-h),var(--dt-color-interactive-form-s),calc(var(--dt-color-interactive-form-l) - var(--dt-color-hover-01)))}.c-switch:focus,.c-switch:focus-within{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-switch:active{background-color:hsl(var(--dt-color-interactive-form-h),var(--dt-color-interactive-form-s),calc(var(--dt-color-interactive-form-l) - var(--dt-color-active-01)))}.c-switch[isChecked]{background-color:var(--switch-bg-color--checked)}@media (prefers-reduced-motion: no-preference){.c-switch[isChecked]{transition:background-color .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch[isChecked]:hover{background-color:hsl(var(--dt-color-interactive-brand-h),var(--dt-color-interactive-brand-s),calc(var(--dt-color-interactive-brand-l) - var(--dt-color-hover-01)))}.c-switch[isChecked]:focus,.c-switch[isChecked]:focus-within{background-color:var(--switch-bg-color--checked)}.c-switch[isChecked]:active{background-color:hsl(var(--dt-color-interactive-brand-h),var(--dt-color-interactive-brand-s),calc(var(--dt-color-interactive-brand-l) - var(--dt-color-active-01)))}[isDisabled] .c-switch{background-color:var(--switch-bg-color--disabled);pointer-events:none}.c-switch-input{appearance:none;margin:0}.c-switch-input:disabled{background-color:transparent}.c-switch-control{position:absolute;left:2px;width:var(--switch-control-size);height:var(--switch-control-size);border-radius:var(--switch-radius);background-color:var(--dt-color-interactive-light);padding:var(--switch-padding)}@media (prefers-reduced-motion: no-preference){.c-switch-control{transition:transform .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch-input:checked+.c-switch-control{transform:translate(var(--switch-translation))}@media (prefers-reduced-motion: no-preference){.c-switch-input:checked+.c-switch-control{transition:transform .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch-input:checked+.c-switch-control .c-pieIcon--check{color:var(--switch-bg-color--checked)}@media (prefers-reduced-motion: no-preference){.c-switch-input:checked+.c-switch-control .c-pieIcon--check{transition:color .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch-input:disabled~.c-switch-control{color:var(--switch-bg-color--disabled)}.c-switch-input:disabled~.c-switch-control .c-pieIcon--check{color:var(--switch-bg-color--disabled)}@media (prefers-reduced-motion: no-preference){.c-switch-input:disabled~.c-switch-control .c-pieIcon--check{transition:color .15s cubic-bezier(.4,0,.9,1) 0s}}.c-switch-description{position:absolute;display:block;height:1px;width:1px;overflow:hidden;padding:1px;white-space:nowrap}.c-switch-wrapper[isRTL] .c-switch-control{position:absolute;left:initial;right:2px}.c-switch-wrapper[isRTL] .c-switch-input:checked+.c-switch-control{transform:translate(calc(-1 * var(--switch-translation)))}@media (prefers-reduced-motion: no-preference){.c-switch-wrapper[isRTL] .c-switch-input:checked+.c-switch-control{transition:transform .15s cubic-bezier(.4,0,.9,1) 0s}}
|
|
6
|
+
`, m = ["leading", "trailing"], k = "pie-switch-changed";
|
|
7
|
+
var y = Object.defineProperty, x = Object.getOwnPropertyDescriptor, l = (h, e, c, t) => {
|
|
8
|
+
for (var i = t > 1 ? void 0 : t ? x(e, c) : e, a = h.length - 1, r; a >= 0; a--)
|
|
9
|
+
(r = h[a]) && (i = (t ? r(e, c, i) : r(i)) || i);
|
|
10
|
+
return t && i && y(e, c, i), i;
|
|
11
|
+
};
|
|
12
|
+
const p = "pie-switch";
|
|
13
|
+
class o extends v(w) {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments), this.labelPlacement = "leading", this.isChecked = !1, this.isDisabled = !1;
|
|
16
|
+
}
|
|
17
|
+
onChange(e) {
|
|
18
|
+
const { checked: c } = e == null ? void 0 : e.currentTarget;
|
|
19
|
+
this.isChecked = c;
|
|
20
|
+
const t = new CustomEvent(
|
|
21
|
+
k,
|
|
22
|
+
{
|
|
23
|
+
bubbles: !0,
|
|
24
|
+
composed: !0,
|
|
25
|
+
detail: this.isChecked
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
this.dispatchEvent(t);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Renders the label for a switch if provided.
|
|
32
|
+
* if invalid value is passed, nothing gets rendered
|
|
33
|
+
*
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
renderSwitchLabel() {
|
|
37
|
+
const { label: e, labelPlacement: c } = this;
|
|
38
|
+
return e ? n`
|
|
39
|
+
<label
|
|
40
|
+
for="switch"
|
|
41
|
+
data-test-id="switch-label-${c}">
|
|
42
|
+
${e}
|
|
43
|
+
</label>` : n``;
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
const {
|
|
47
|
+
labelPlacement: e,
|
|
48
|
+
aria: c,
|
|
49
|
+
isChecked: t,
|
|
50
|
+
isDisabled: i,
|
|
51
|
+
isRTL: a
|
|
52
|
+
} = this, r = "switch-description";
|
|
53
|
+
return n`
|
|
54
|
+
<div
|
|
55
|
+
class="c-switch-wrapper"
|
|
56
|
+
?isRTL=${a}
|
|
57
|
+
?isDisabled=${i}>
|
|
58
|
+
${e === "leading" ? this.renderSwitchLabel() : s}
|
|
59
|
+
<label
|
|
60
|
+
data-test-id="switch-component"
|
|
61
|
+
class="c-switch"
|
|
62
|
+
?isChecked=${t}>
|
|
63
|
+
<input
|
|
64
|
+
id="switch"
|
|
65
|
+
data-test-id="switch-input"
|
|
66
|
+
role="switch"
|
|
67
|
+
type="checkbox"
|
|
68
|
+
class="c-switch-input"
|
|
69
|
+
.checked="${t}"
|
|
70
|
+
.disabled="${i}"
|
|
71
|
+
@change="${this.onChange}"
|
|
72
|
+
aria-label="${(c == null ? void 0 : c.label) || s}"
|
|
73
|
+
aria-describedby="${c != null && c.describedBy ? r : s}">
|
|
74
|
+
<div class="c-switch-control">
|
|
75
|
+
${t ? n`<icon-check></icon-check>` : s}
|
|
76
|
+
</div>
|
|
77
|
+
</label>
|
|
78
|
+
${c != null && c.describedBy ? n`<div id="${r}" data-test-id="${r}" class="c-switch-description">${c == null ? void 0 : c.describedBy}</div>` : s}
|
|
79
|
+
${e === "trailing" ? this.renderSwitchLabel() : s}
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
o.styles = b(g);
|
|
85
|
+
l([
|
|
86
|
+
d({ type: String })
|
|
87
|
+
], o.prototype, "label", 2);
|
|
88
|
+
l([
|
|
89
|
+
d({ type: String }),
|
|
90
|
+
u(p, m, "leading")
|
|
91
|
+
], o.prototype, "labelPlacement", 2);
|
|
92
|
+
l([
|
|
93
|
+
d({ type: Object })
|
|
94
|
+
], o.prototype, "aria", 2);
|
|
95
|
+
l([
|
|
96
|
+
d({ type: Boolean, reflect: !0 })
|
|
97
|
+
], o.prototype, "isChecked", 2);
|
|
98
|
+
l([
|
|
99
|
+
d({ type: Boolean, reflect: !0 })
|
|
100
|
+
], o.prototype, "isDisabled", 2);
|
|
101
|
+
f(p, o);
|
|
102
|
+
export {
|
|
103
|
+
k as ON_SWITCH_CHANGED_EVENT,
|
|
104
|
+
o as PieSwitch,
|
|
105
|
+
m as labelPlacements
|
|
106
|
+
};
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CSSResult } from 'lit';
|
|
2
|
+
import type { EventName } from '@lit/react';
|
|
3
|
+
import type { LitElement } from 'lit';
|
|
4
|
+
import type { ReactWebComponent } from '@lit/react';
|
|
5
|
+
import type { TemplateResult } from 'lit';
|
|
6
|
+
|
|
7
|
+
export declare type AriaProps = {
|
|
8
|
+
label?: string;
|
|
9
|
+
describedBy?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export declare type LabelPlacement = typeof labelPlacements[number];
|
|
13
|
+
|
|
14
|
+
export declare const labelPlacements: readonly ["leading", "trailing"];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Event name for when the switch checked state is changed.
|
|
18
|
+
*
|
|
19
|
+
* @constant
|
|
20
|
+
*/
|
|
21
|
+
export declare const ON_SWITCH_CHANGED_EVENT = "pie-switch-changed";
|
|
22
|
+
|
|
23
|
+
export declare const PieSwitch: ReactWebComponent<PieSwitch_2, {
|
|
24
|
+
onPieSwitchChanged: EventName<CustomEvent<any>>;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @tagname pie-switch
|
|
29
|
+
* @event {CustomEvent} pie-switch-changed - when the switch checked state is changed.
|
|
30
|
+
*/
|
|
31
|
+
declare class PieSwitch_2 extends PieSwitch_base implements SwitchProps {
|
|
32
|
+
label?: string;
|
|
33
|
+
labelPlacement: SwitchProps['labelPlacement'];
|
|
34
|
+
aria: AriaProps;
|
|
35
|
+
isChecked: boolean;
|
|
36
|
+
isDisabled: boolean;
|
|
37
|
+
static styles: CSSResult;
|
|
38
|
+
onChange(event: Event): void;
|
|
39
|
+
/**
|
|
40
|
+
* Renders the label for a switch if provided.
|
|
41
|
+
* if invalid value is passed, nothing gets rendered
|
|
42
|
+
*
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
private renderSwitchLabel;
|
|
46
|
+
render(): TemplateResult<1>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare const PieSwitch_base: (new (...args: any[]) => {
|
|
50
|
+
isRTL: boolean;
|
|
51
|
+
}) & typeof LitElement;
|
|
52
|
+
|
|
53
|
+
export declare interface SwitchProps {
|
|
54
|
+
/**
|
|
55
|
+
* The ARIA labels used for the switch.
|
|
56
|
+
*/
|
|
57
|
+
aria?: AriaProps;
|
|
58
|
+
/**
|
|
59
|
+
* Same as the HTML checked attribute - indicates whether the switch is on or off
|
|
60
|
+
*/
|
|
61
|
+
isChecked?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Same as the HTML checked attribute - indicates whether the switch disabled or not
|
|
64
|
+
*/
|
|
65
|
+
isDisabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The label value of the component
|
|
68
|
+
*/
|
|
69
|
+
label?: string;
|
|
70
|
+
/**
|
|
71
|
+
* The placement of the label such as leading or trailing
|
|
72
|
+
*/
|
|
73
|
+
labelPlacement?: LabelPlacement;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { }
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as e from "react";
|
|
2
|
+
import { createComponent as t } from "@lit/react";
|
|
3
|
+
import { PieSwitch as i } from "./index.js";
|
|
4
|
+
import { ON_SWITCH_CHANGED_EVENT as w, labelPlacements as l } from "./index.js";
|
|
5
|
+
import "lit";
|
|
6
|
+
import "lit/decorators.js";
|
|
7
|
+
import "@justeattakeaway/pie-webc-core";
|
|
8
|
+
import "@justeattakeaway/pie-icons-webc/IconCheck";
|
|
9
|
+
const s = t({
|
|
10
|
+
displayName: "PieSwitch",
|
|
11
|
+
elementClass: i,
|
|
12
|
+
react: e,
|
|
13
|
+
tagName: "pie-switch",
|
|
14
|
+
events: {
|
|
15
|
+
onPieSwitchChanged: "pie-switch-changed"
|
|
16
|
+
// when the switch checked state is changed.
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
export {
|
|
20
|
+
w as ON_SWITCH_CHANGED_EVENT,
|
|
21
|
+
s as PieSwitch,
|
|
22
|
+
l as labelPlacements
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@justeattakeaway/pie-switch",
|
|
3
|
+
"description": "PIE Design System Switch built using Web Components",
|
|
4
|
+
"version": "0.0.0-snapshot-release-20231129145325",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"dist",
|
|
12
|
+
"**/*.d.ts"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "yarn build:wrapper pie-switch && run -T vite build",
|
|
16
|
+
"lint:scripts": "run -T eslint .",
|
|
17
|
+
"lint:scripts:fix": "yarn lint:scripts --fix",
|
|
18
|
+
"lint:style": "run -T stylelint ./src/**/*.{css,scss}",
|
|
19
|
+
"lint:style:fix": "yarn lint:style --fix",
|
|
20
|
+
"watch": "run -T vite build --watch",
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 0",
|
|
22
|
+
"test:ci": "yarn test",
|
|
23
|
+
"test:browsers": "npx playwright test -c ./playwright-lit.config.ts",
|
|
24
|
+
"test:browsers:ci": "yarn test:browsers",
|
|
25
|
+
"test:visual": "run -T cross-env-shell PERCY_TOKEN=${PERCY_TOKEN_PIE_SWITCH} percy exec --allowed-hostname cloudfront.net -- npx playwright test -c ./playwright-lit-visual.config.ts",
|
|
26
|
+
"test:visual:ci": "yarn test:visual"
|
|
27
|
+
},
|
|
28
|
+
"author": "Just Eat Takeaway.com - Design System Team",
|
|
29
|
+
"license": "Apache-2.0",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@justeattakeaway/pie-components-config": "0.6.1"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@justeattakeaway/pie-icons-webc": "0.0.0-snapshot-release-20231129145325",
|
|
35
|
+
"@justeattakeaway/pie-webc-core": "0.13.0"
|
|
36
|
+
},
|
|
37
|
+
"volta": {
|
|
38
|
+
"extends": "../../../package.json"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": [
|
|
41
|
+
"dist/*.js"
|
|
42
|
+
]
|
|
43
|
+
}
|
package/src/defs.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const labelPlacements = ['leading', 'trailing'] as const;
|
|
2
|
+
|
|
3
|
+
export type LabelPlacement = typeof labelPlacements[number];
|
|
4
|
+
|
|
5
|
+
export type AriaProps = {
|
|
6
|
+
label?: string,
|
|
7
|
+
describedBy?: string
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface SwitchProps {
|
|
11
|
+
/**
|
|
12
|
+
* The ARIA labels used for the switch.
|
|
13
|
+
*/
|
|
14
|
+
aria?: AriaProps;
|
|
15
|
+
/**
|
|
16
|
+
* Same as the HTML checked attribute - indicates whether the switch is on or off
|
|
17
|
+
*/
|
|
18
|
+
isChecked?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Same as the HTML checked attribute - indicates whether the switch disabled or not
|
|
21
|
+
*/
|
|
22
|
+
isDisabled?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The label value of the component
|
|
25
|
+
*/
|
|
26
|
+
label?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The placement of the label such as leading or trailing
|
|
29
|
+
*/
|
|
30
|
+
labelPlacement?: LabelPlacement;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Event name for when the switch checked state is changed.
|
|
35
|
+
*
|
|
36
|
+
* @constant
|
|
37
|
+
*/
|
|
38
|
+
export const ON_SWITCH_CHANGED_EVENT = 'pie-switch-changed';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LitElement, html, unsafeCSS, nothing, TemplateResult,
|
|
3
|
+
} from 'lit';
|
|
4
|
+
import { property } from 'lit/decorators.js';
|
|
5
|
+
import { RtlMixin, validPropertyValues, defineCustomElement } from '@justeattakeaway/pie-webc-core';
|
|
6
|
+
import styles from './switch.scss?inline';
|
|
7
|
+
import {
|
|
8
|
+
SwitchProps, ON_SWITCH_CHANGED_EVENT, AriaProps, labelPlacements,
|
|
9
|
+
} from './defs';
|
|
10
|
+
import '@justeattakeaway/pie-icons-webc/IconCheck';
|
|
11
|
+
|
|
12
|
+
// Valid values available to consumers
|
|
13
|
+
export * from './defs';
|
|
14
|
+
|
|
15
|
+
const componentSelector = 'pie-switch';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @tagname pie-switch
|
|
19
|
+
* @event {CustomEvent} pie-switch-changed - when the switch checked state is changed.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export class PieSwitch extends RtlMixin(LitElement) implements SwitchProps {
|
|
23
|
+
@property({ type: String })
|
|
24
|
+
public label?: string;
|
|
25
|
+
|
|
26
|
+
@property({ type: String })
|
|
27
|
+
@validPropertyValues(componentSelector, labelPlacements, 'leading')
|
|
28
|
+
public labelPlacement: SwitchProps['labelPlacement'] = 'leading';
|
|
29
|
+
|
|
30
|
+
@property({ type: Object })
|
|
31
|
+
public aria!: AriaProps;
|
|
32
|
+
|
|
33
|
+
@property({ type: Boolean, reflect: true })
|
|
34
|
+
public isChecked = false;
|
|
35
|
+
|
|
36
|
+
@property({ type: Boolean, reflect: true })
|
|
37
|
+
public isDisabled = false;
|
|
38
|
+
|
|
39
|
+
static styles = unsafeCSS(styles);
|
|
40
|
+
|
|
41
|
+
onChange (event: Event) {
|
|
42
|
+
const { checked } = event?.currentTarget as HTMLInputElement;
|
|
43
|
+
this.isChecked = checked;
|
|
44
|
+
const changedEvent = new CustomEvent(
|
|
45
|
+
ON_SWITCH_CHANGED_EVENT,
|
|
46
|
+
{
|
|
47
|
+
bubbles: true,
|
|
48
|
+
composed: true,
|
|
49
|
+
detail: this.isChecked,
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
this.dispatchEvent(changedEvent);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Renders the label for a switch if provided.
|
|
57
|
+
* if invalid value is passed, nothing gets rendered
|
|
58
|
+
*
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private renderSwitchLabel (): TemplateResult {
|
|
62
|
+
const { label, labelPlacement } = this;
|
|
63
|
+
|
|
64
|
+
if (label) {
|
|
65
|
+
return html`
|
|
66
|
+
<label
|
|
67
|
+
for="switch"
|
|
68
|
+
data-test-id="switch-label-${labelPlacement}">
|
|
69
|
+
${label}
|
|
70
|
+
</label>`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return html``;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
render () {
|
|
77
|
+
const {
|
|
78
|
+
labelPlacement,
|
|
79
|
+
aria,
|
|
80
|
+
isChecked,
|
|
81
|
+
isDisabled,
|
|
82
|
+
isRTL,
|
|
83
|
+
} = this;
|
|
84
|
+
|
|
85
|
+
const switchId = 'switch-description';
|
|
86
|
+
|
|
87
|
+
return html`
|
|
88
|
+
<div
|
|
89
|
+
class="c-switch-wrapper"
|
|
90
|
+
?isRTL=${isRTL}
|
|
91
|
+
?isDisabled=${isDisabled}>
|
|
92
|
+
${labelPlacement === 'leading' ? this.renderSwitchLabel() : nothing}
|
|
93
|
+
<label
|
|
94
|
+
data-test-id="switch-component"
|
|
95
|
+
class="c-switch"
|
|
96
|
+
?isChecked=${isChecked}>
|
|
97
|
+
<input
|
|
98
|
+
id="switch"
|
|
99
|
+
data-test-id="switch-input"
|
|
100
|
+
role="switch"
|
|
101
|
+
type="checkbox"
|
|
102
|
+
class="c-switch-input"
|
|
103
|
+
.checked="${isChecked}"
|
|
104
|
+
.disabled="${isDisabled}"
|
|
105
|
+
@change="${this.onChange}"
|
|
106
|
+
aria-label="${aria?.label || nothing}"
|
|
107
|
+
aria-describedby="${aria?.describedBy ? switchId : nothing}">
|
|
108
|
+
<div class="c-switch-control">
|
|
109
|
+
${isChecked ? html`<icon-check></icon-check>` : nothing}
|
|
110
|
+
</div>
|
|
111
|
+
</label>
|
|
112
|
+
${aria?.describedBy ? html`<div id="${switchId}" data-test-id="${switchId}" class="c-switch-description">${aria?.describedBy}</div>` : nothing}
|
|
113
|
+
${labelPlacement === 'trailing' ? this.renderSwitchLabel() : nothing}
|
|
114
|
+
</div>
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
defineCustomElement(componentSelector, PieSwitch);
|
|
120
|
+
|
|
121
|
+
declare global {
|
|
122
|
+
interface HTMLElementTagNameMap {
|
|
123
|
+
[componentSelector]: PieSwitch;
|
|
124
|
+
}
|
|
125
|
+
}
|
package/src/switch.scss
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
@use '@justeattakeaway/pie-css/scss' as p;
|
|
2
|
+
|
|
3
|
+
@mixin switch-transition($property) {
|
|
4
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5
|
+
transition: $property 0.15s cubic-bezier(0.4, 0, 0.9, 1) 0s;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
*,
|
|
10
|
+
*:before,
|
|
11
|
+
*:after {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
cursor: inherit;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.c-switch-wrapper {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: var(--dt-spacing-b);
|
|
20
|
+
font-family: var(--dt-font-body-l-family);
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
|
|
23
|
+
&[isDisabled] {
|
|
24
|
+
cursor: not-allowed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.c-switch {
|
|
29
|
+
--switch-bg-color: var(--dt-color-interactive-form);
|
|
30
|
+
--switch-bg-color--checked: var(--dt-color-interactive-brand);
|
|
31
|
+
--switch-bg-color--disabled: var(--dt-color-disabled-01);
|
|
32
|
+
--switch-width: 48px;
|
|
33
|
+
--switch-height: 24px;
|
|
34
|
+
--switch-control-size: 20px;
|
|
35
|
+
--switch-padding: 2px;
|
|
36
|
+
--switch-radius: var(--dt-radius-rounded-e);
|
|
37
|
+
--switch-translation: calc(var(--switch-width) - var(--switch-control-size) - 2 * var(--switch-padding));
|
|
38
|
+
|
|
39
|
+
position: relative;
|
|
40
|
+
display: flex;
|
|
41
|
+
width: var(--switch-width);
|
|
42
|
+
height: var(--switch-height);
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
padding: var(--switch-padding);
|
|
45
|
+
border-radius: var(--switch-radius);
|
|
46
|
+
background-color: var(--switch-bg-color);
|
|
47
|
+
|
|
48
|
+
@include switch-transition(background-color);
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
background-color: hsl(var(--dt-color-interactive-form-h), var(--dt-color-interactive-form-s), calc(var(--dt-color-interactive-form-l) - var(--dt-color-hover-01)));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&:focus,
|
|
55
|
+
&:focus-within {
|
|
56
|
+
@include p.focus;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&:active {
|
|
60
|
+
background-color: hsl(var(--dt-color-interactive-form-h), var(--dt-color-interactive-form-s), calc(var(--dt-color-interactive-form-l) - var(--dt-color-active-01)));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&[isChecked] {
|
|
64
|
+
@include switch-transition(background-color);
|
|
65
|
+
background-color: var(--switch-bg-color--checked);
|
|
66
|
+
|
|
67
|
+
&:hover {
|
|
68
|
+
background-color: hsl(var(--dt-color-interactive-brand-h), var(--dt-color-interactive-brand-s), calc(var(--dt-color-interactive-brand-l) - var(--dt-color-hover-01)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&:focus,
|
|
72
|
+
&:focus-within {
|
|
73
|
+
background-color: var(--switch-bg-color--checked);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&:active {
|
|
77
|
+
background-color: hsl(var(--dt-color-interactive-brand-h), var(--dt-color-interactive-brand-s), calc(var(--dt-color-interactive-brand-l) - var(--dt-color-active-01)));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
[isDisabled] & {
|
|
82
|
+
background-color: var(--switch-bg-color--disabled);
|
|
83
|
+
pointer-events: none;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.c-switch-input {
|
|
88
|
+
appearance: none;
|
|
89
|
+
margin: 0;
|
|
90
|
+
|
|
91
|
+
&:disabled {
|
|
92
|
+
background-color: transparent;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.c-switch-control {
|
|
97
|
+
position: absolute;
|
|
98
|
+
left: 2px;
|
|
99
|
+
@include switch-transition(transform);
|
|
100
|
+
width: var(--switch-control-size);
|
|
101
|
+
height: var(--switch-control-size);
|
|
102
|
+
border-radius: var(--switch-radius);
|
|
103
|
+
background-color: var(--dt-color-interactive-light);
|
|
104
|
+
padding: var(--switch-padding);
|
|
105
|
+
|
|
106
|
+
.c-switch-input:checked + & {
|
|
107
|
+
@include switch-transition(transform);
|
|
108
|
+
transform: translateX(var(--switch-translation));
|
|
109
|
+
|
|
110
|
+
.c-pieIcon--check {
|
|
111
|
+
@include switch-transition(color);
|
|
112
|
+
color: var(--switch-bg-color--checked);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.c-switch-input:disabled ~ & {
|
|
117
|
+
color: var(--switch-bg-color--disabled);
|
|
118
|
+
|
|
119
|
+
.c-pieIcon--check {
|
|
120
|
+
@include switch-transition(color);
|
|
121
|
+
color: var(--switch-bg-color--disabled);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// The description is only required for screen readers so we need to visually hide the description
|
|
127
|
+
.c-switch-description {
|
|
128
|
+
@include p.visually-hidden;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.c-switch-wrapper[isRTL] {
|
|
132
|
+
.c-switch-control {
|
|
133
|
+
position: absolute;
|
|
134
|
+
left: initial;
|
|
135
|
+
right: 2px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.c-switch-input:checked + .c-switch-control {
|
|
139
|
+
@include switch-transition(transform);
|
|
140
|
+
transform: translateX(calc(-1 * var(--switch-translation)));
|
|
141
|
+
}
|
|
142
|
+
}
|