@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 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).
@@ -0,0 +1,9 @@
1
+ declare module '*.scss' {
2
+ const content: Record<string, string>;
3
+ export default content;
4
+ }
5
+
6
+ declare module '*.scss?inline' {
7
+ const content: Record<string, string>;
8
+ export default content;
9
+ }
@@ -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
+ };
@@ -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
+ }
@@ -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
+ }