@justeattakeaway/pie-switch 0.16.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 ADDED
@@ -0,0 +1,79 @@
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. [Contributing](#contributing)
19
+
20
+ ## pie-switch
21
+
22
+ `pie-switch` is a Web Component built using the Lit library.
23
+
24
+ This component can be easily integrated into various frontend frameworks and customized through a set of properties.
25
+
26
+
27
+ ## Installation
28
+
29
+ To install `pie-switch` in your application, run the following on your command line:
30
+
31
+ ```bash
32
+ # npm
33
+ $ npm i @justeattakeaway/pie-switch
34
+
35
+ # yarn
36
+ $ yarn add @justeattakeaway/pie-switch
37
+ ```
38
+
39
+ 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).
40
+
41
+
42
+ ### Importing the component
43
+
44
+ ```js
45
+ // default
46
+ import { PieSwitch } from '@justeattakeaway/pie-switch';
47
+
48
+ // react
49
+ import { PieSwitch } from '@justeattakeaway/pie-switch/dist/react';
50
+ ```
51
+
52
+
53
+ ## Peer Dependencies
54
+
55
+ > [!IMPORTANT]
56
+ > 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.
57
+
58
+
59
+ ## Props
60
+
61
+ | Property | Type | Default | Description |
62
+ |---|---|---|---|
63
+ | isChecked | `Boolean` | false | Indicates whether the switch is on or off |
64
+ | isDisabled | `Boolean` | false | Indicates whether the switch is disabled or not |
65
+ | aria | `Object` | `undefined` | An object representing the aria labels `label` & `describedBy` that can be used on the switch;
66
+
67
+ In your markup or JSX, you can then use these to set the properties for the `pie-switch` component:
68
+
69
+ ```html
70
+ <!-- Native HTML -->
71
+ <pie-switch></pie-switch>
72
+
73
+ <!-- JSX -->
74
+ <PieSwitch></PieSwitch>
75
+ ```
76
+
77
+ ## Contributing
78
+
79
+ 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,226 @@
1
+ import { isServer as z, css as I, LitElement as f, html as d, unsafeCSS as P, nothing as l } from "lit";
2
+ import { property as n, query as E } from "lit/decorators.js";
3
+ const _ = (c) => {
4
+ class e extends c {
5
+ /**
6
+ * A getter to determine whether the text direction is right-to-left (RTL).
7
+ * If the `dir` property is present on the component, it will be used to determine the text direction.
8
+ * If running on the client-side (not SSR) and the `dir` property is not present, the text direction will be inferred
9
+ * from the document's root element. This inference is not available during SSR.
10
+ * In all other cases, it will return `false`, indicating a left-to-right (LTR) text direction.
11
+ *
12
+ * @returns {boolean} - Returns `true` if the text direction is RTL, otherwise `false`.
13
+ */
14
+ get isRTL() {
15
+ return this.dir ? this.dir === "rtl" : !z && !this.dir ? document.documentElement.getAttribute("dir") === "rtl" : !1;
16
+ }
17
+ }
18
+ return e;
19
+ }, L = (c, e, t) => function(i, s) {
20
+ const o = `#${s}`;
21
+ Object.defineProperty(i, s, {
22
+ get() {
23
+ return this[o];
24
+ },
25
+ set(h) {
26
+ const C = this[o];
27
+ e.includes(h) ? this[o] = h : (console.error(
28
+ `<${c}> Invalid value "${h}" provided for property "${s}".`,
29
+ `Must be one of: ${e.join(" | ")}.`,
30
+ `Falling back to default value: "${t}"`
31
+ ), this[o] = t), this.requestUpdate(s, C);
32
+ }
33
+ });
34
+ };
35
+ function S(c, e) {
36
+ customElements.get(c) ? console.warn(`PIE Web Component: "${c}" has already been defined. Please ensure the component is only being defined once in your application.`) : customElements.define(c, e);
37
+ }
38
+ const T = `*,*: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{background-color:var(--switch-bg-color);box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer)}.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;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}.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}}
39
+ `, O = ["leading", "trailing"], j = "pie-switch-changed";
40
+ function D(c, e) {
41
+ customElements.get(c) ? console.warn(`PIE Web Component: "${c}" has already been defined. Please ensure the component is only being defined once in your application.`) : customElements.define(c, e);
42
+ }
43
+ const b = {
44
+ xs: 16,
45
+ s: 20,
46
+ m: 24,
47
+ l: 28,
48
+ xl: 32,
49
+ xxl: 40
50
+ }, m = Object.keys(b), k = "xs", y = 8, w = 32;
51
+ function R(c, e, t) {
52
+ const r = parseInt(c, 10), i = r % t === 0;
53
+ return r >= e && i;
54
+ }
55
+ const x = {
56
+ large: (c) => R(c, w, y),
57
+ regular: (c) => m.includes(c)
58
+ };
59
+ function A(c) {
60
+ const e = x.large(c);
61
+ return { isValid: e, size: e ? c : w };
62
+ }
63
+ function B(c) {
64
+ const e = x.regular(c), t = e ? b[c] : b[k];
65
+ return { isValid: e, size: t };
66
+ }
67
+ const g = (c, e, t, r) => {
68
+ const i = c.endsWith("Large") || c.endsWith("-large");
69
+ let s, o;
70
+ if (t) {
71
+ if ({ isValid: s, size: o } = i ? A(t) : B(t), !s) {
72
+ const h = i ? `Invalid prop "size" value supplied to "${r}". The prop value should be a number equal or greater than ${w} and multiple of ${y}.` : `Invalid prop "size" value supplied to "${r}". The prop value should be one of the following values: ${m.join(", ")}.`;
73
+ console.error(h);
74
+ }
75
+ } else
76
+ o = i ? w : b[k];
77
+ return {
78
+ class: [c, e].filter(Boolean).join(" "),
79
+ width: o,
80
+ height: o
81
+ };
82
+ };
83
+ var V = Object.defineProperty, W = Object.getOwnPropertyDescriptor, v = (c, e, t, r) => {
84
+ for (var i = r > 1 ? void 0 : r ? W(e, t) : e, s = c.length - 1, o; s >= 0; s--)
85
+ (o = c[s]) && (i = (r ? o(e, t, i) : o(i)) || i);
86
+ return r && i && V(e, t, i), i;
87
+ };
88
+ const q = "icon-check";
89
+ class p extends f {
90
+ constructor() {
91
+ super(...arguments), this.size = "xs", this.class = "c-pieIcon c-pieIcon--check";
92
+ }
93
+ connectedCallback() {
94
+ var e, t, r;
95
+ if (super.connectedCallback(), ((e = this._svg) == null ? void 0 : e.getAttribute("width")) === null) {
96
+ const i = g("c-pieIcon c-pieIcon--check", "", null, "IconCheck");
97
+ (t = this._svg) == null || t.setAttribute("width", i.width), (r = this._svg) == null || r.setAttribute("height", i.height);
98
+ }
99
+ }
100
+ updated(e) {
101
+ var t, r;
102
+ let i;
103
+ e.has("size") && (i = g("c-pieIcon c-pieIcon--check", "", this.size, "IconCheck"), (t = this._svg) == null || t.setAttribute("width", i.width), (r = this._svg) == null || r.setAttribute("height", i.height));
104
+ }
105
+ render() {
106
+ return d`<svg xmlns="http://www.w3.org/2000/svg" role="presentation" focusable="false" fill="currentColor" viewBox="0 0 16 16" class="c-pieIcon c-pieIcon--check"><path d="M5.865 12.489a1.217 1.217 0 0 1-.875-.385L1.875 8.656l.98-.875 3.028 3.369 7.253-7.822.963.875-7.35 7.875a1.216 1.216 0 0 1-.875.385l-.009.026Z"></path></svg>`;
107
+ }
108
+ }
109
+ p.styles = I`
110
+ :host-context(pie-icon-button) svg,
111
+ :host-context(pie-button) svg {
112
+ display: block;
113
+ width: var(--btn-icon-size);
114
+ height: var(--btn-icon-size);
115
+ }
116
+ `;
117
+ v([
118
+ n({ type: String, reflect: !0 })
119
+ ], p.prototype, "size", 2);
120
+ v([
121
+ n({ type: String, reflect: !0 })
122
+ ], p.prototype, "class", 2);
123
+ v([
124
+ E("svg")
125
+ ], p.prototype, "_svg", 2);
126
+ D(q, p);
127
+ var M = Object.defineProperty, N = Object.getOwnPropertyDescriptor, u = (c, e, t, r) => {
128
+ for (var i = r > 1 ? void 0 : r ? N(e, t) : e, s = c.length - 1, o; s >= 0; s--)
129
+ (o = c[s]) && (i = (r ? o(e, t, i) : o(i)) || i);
130
+ return r && i && M(e, t, i), i;
131
+ };
132
+ const $ = "pie-switch";
133
+ class a extends _(f) {
134
+ constructor() {
135
+ super(...arguments), this.labelPlacement = "leading", this.isChecked = !1, this.isDisabled = !1;
136
+ }
137
+ onChange(e) {
138
+ const { checked: t } = e == null ? void 0 : e.currentTarget;
139
+ this.isChecked = t;
140
+ const r = new CustomEvent(
141
+ j,
142
+ {
143
+ bubbles: !0,
144
+ composed: !0,
145
+ detail: this.isChecked
146
+ }
147
+ );
148
+ this.dispatchEvent(r);
149
+ }
150
+ /**
151
+ * Renders the label for a switch if provided.
152
+ * if invalid value is passed, nothing gets rendered
153
+ *
154
+ * @private
155
+ */
156
+ renderSwitchLabel() {
157
+ const { label: e, labelPlacement: t } = this;
158
+ return e ? d`
159
+ <label
160
+ for="switch"
161
+ data-test-id="switch-label-${t}">
162
+ ${e}
163
+ </label>` : d``;
164
+ }
165
+ render() {
166
+ const {
167
+ labelPlacement: e,
168
+ aria: t,
169
+ isChecked: r,
170
+ isDisabled: i,
171
+ isRTL: s
172
+ } = this, o = "switch-description";
173
+ return d`
174
+ <div
175
+ class="c-switch-wrapper"
176
+ ?isRTL=${s}
177
+ ?isDisabled=${i}>
178
+ ${e === "leading" ? this.renderSwitchLabel() : l}
179
+ <label
180
+ data-test-id="switch-component"
181
+ class="c-switch"
182
+ ?isChecked=${r}>
183
+ <input
184
+ id="switch"
185
+ data-test-id="switch-input"
186
+ role="switch"
187
+ type="checkbox"
188
+ class="c-switch-input"
189
+ .checked="${r}"
190
+ .disabled="${i}"
191
+ @change="${this.onChange}"
192
+ aria-label="${(t == null ? void 0 : t.label) || l}"
193
+ aria-describedby="${t != null && t.describedBy ? o : l}">
194
+ <div class="c-switch-control">
195
+ ${r ? d`<icon-check></icon-check>` : l}
196
+ </div>
197
+ </label>
198
+ ${t != null && t.describedBy ? d`<div id="${o}" data-test-id="${o}" class="c-switch-description">${t == null ? void 0 : t.describedBy}</div>` : l}
199
+ ${e === "trailing" ? this.renderSwitchLabel() : l}
200
+ </div>
201
+ `;
202
+ }
203
+ }
204
+ a.styles = P(T);
205
+ u([
206
+ n({ type: String })
207
+ ], a.prototype, "label", 2);
208
+ u([
209
+ n({ type: String }),
210
+ L($, O, "leading")
211
+ ], a.prototype, "labelPlacement", 2);
212
+ u([
213
+ n({ type: Object })
214
+ ], a.prototype, "aria", 2);
215
+ u([
216
+ n({ type: Boolean, reflect: !0 })
217
+ ], a.prototype, "isChecked", 2);
218
+ u([
219
+ n({ type: Boolean, reflect: !0 })
220
+ ], a.prototype, "isDisabled", 2);
221
+ S($, a);
222
+ export {
223
+ j as ON_SWITCH_CHANGED_EVENT,
224
+ a as PieSwitch,
225
+ O as labelPlacements
226
+ };
@@ -0,0 +1,76 @@
1
+ import type { CSSResult } from 'lit';
2
+ import type { EventName } from '@lit-labs/react';
3
+ import type { LitElement } from 'lit';
4
+ import type { ReactWebComponent } from '@lit-labs/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,74 @@
1
+ import * as S from "react";
2
+ import { PieSwitch as g } from "./index.js";
3
+ import { ON_SWITCH_CHANGED_EVENT as L, labelPlacements as O } from "./index.js";
4
+ import "lit";
5
+ import "lit/decorators.js";
6
+ /**
7
+ * @license
8
+ * Copyright 2018 Google LLC
9
+ * SPDX-License-Identifier: BSD-3-Clause
10
+ */
11
+ const C = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), E = /* @__PURE__ */ new WeakMap(), P = (h, l, d, m, p) => {
12
+ const n = p == null ? void 0 : p[l];
13
+ n === void 0 || d === m ? d == null && l in HTMLElement.prototype ? h.removeAttribute(l) : h[l] = d : ((s, t, u) => {
14
+ let o = E.get(s);
15
+ o === void 0 && E.set(s, o = /* @__PURE__ */ new Map());
16
+ let a = o.get(t);
17
+ u !== void 0 ? a === void 0 ? (o.set(t, a = { handleEvent: u }), s.addEventListener(t, a)) : a.handleEvent = u : a !== void 0 && (o.delete(t), s.removeEventListener(t, a));
18
+ })(h, n, d);
19
+ };
20
+ function _(h = window.React, l, d, m, p) {
21
+ let n, s, t;
22
+ if (l === void 0) {
23
+ const r = h;
24
+ ({ tagName: s, elementClass: t, events: m, displayName: p } = r), n = r.react;
25
+ } else
26
+ n = h, t = d, s = l;
27
+ const u = n.Component, o = n.createElement, a = new Set(Object.keys(m ?? {}));
28
+ class f extends u {
29
+ constructor() {
30
+ super(...arguments), this.o = null;
31
+ }
32
+ t(e) {
33
+ if (this.o !== null)
34
+ for (const v in this.i)
35
+ P(this.o, v, this.props[v], e ? e[v] : void 0, m);
36
+ }
37
+ componentDidMount() {
38
+ this.t();
39
+ }
40
+ componentDidUpdate(e) {
41
+ this.t(e);
42
+ }
43
+ render() {
44
+ const { _$Gl: e, ...v } = this.props;
45
+ this.h !== e && (this.u = (i) => {
46
+ e !== null && ((c, y) => {
47
+ typeof c == "function" ? c(y) : c.current = y;
48
+ })(e, i), this.o = i, this.h = e;
49
+ }), this.i = {};
50
+ const w = { ref: this.u };
51
+ for (const [i, c] of Object.entries(v))
52
+ C.has(i) ? w[i === "className" ? "class" : i] = c : a.has(i) || i in t.prototype ? this.i[i] = c : w[i] = c;
53
+ return o(s, w);
54
+ }
55
+ }
56
+ f.displayName = p ?? t.name;
57
+ const N = n.forwardRef((r, e) => o(f, { ...r, _$Gl: e }, r == null ? void 0 : r.children));
58
+ return N.displayName = f.displayName, N;
59
+ }
60
+ const D = _({
61
+ displayName: "PieSwitch",
62
+ elementClass: g,
63
+ react: S,
64
+ tagName: "pie-switch",
65
+ events: {
66
+ onPieSwitchChanged: "pie-switch-changed"
67
+ // when the switch checked state is changed.
68
+ }
69
+ });
70
+ export {
71
+ L as ON_SWITCH_CHANGED_EVENT,
72
+ D as PieSwitch,
73
+ O as labelPlacements
74
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@justeattakeaway/pie-switch",
3
+ "description": "PIE Design System Switch built using Web Components",
4
+ "version": "0.16.0",
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.4.0",
32
+ "@justeattakeaway/pie-icons-webc": "0.11.1"
33
+ },
34
+ "volta": {
35
+ "extends": "../../../package.json"
36
+ },
37
+ "sideEffects": [
38
+ "dist/*.js"
39
+ ]
40
+ }
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,148 @@
1
+ @use '@justeat/pie-design-tokens/dist/jet.scss' as dt;
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
+ background-color: var(--switch-bg-color);
57
+ box-shadow: 0 0 0 2px var(--dt-color-focus-inner), 0 0 0 4px var(--dt-color-focus-outer);
58
+ }
59
+
60
+ &:active {
61
+ 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)));
62
+ }
63
+
64
+ &[isChecked] {
65
+ @include switch-transition(background-color);
66
+ background-color: var(--switch-bg-color--checked);
67
+
68
+ &:hover {
69
+ 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)));
70
+ }
71
+
72
+ &:focus,
73
+ &:focus-within {
74
+ background-color: var(--switch-bg-color--checked);
75
+ }
76
+
77
+ &:active {
78
+ 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)));
79
+ }
80
+ }
81
+
82
+ [isDisabled] & {
83
+ background-color: var(--switch-bg-color--disabled);
84
+ pointer-events: none;
85
+ }
86
+ }
87
+
88
+ .c-switch-input {
89
+ appearance: none;
90
+ margin: 0;
91
+
92
+ &:disabled {
93
+ background-color: transparent;
94
+ }
95
+ }
96
+
97
+ .c-switch-control {
98
+ position: absolute;
99
+ left: 2px;
100
+ @include switch-transition(transform);
101
+ width: var(--switch-control-size);
102
+ height: var(--switch-control-size);
103
+ border-radius: var(--switch-radius);
104
+ background-color: var(--dt-color-interactive-light);
105
+ padding: var(--switch-padding);
106
+
107
+ .c-switch-input:checked + & {
108
+ @include switch-transition(transform);
109
+ transform: translateX(var(--switch-translation));
110
+
111
+ .c-pieIcon--check {
112
+ @include switch-transition(color);
113
+ color: var(--switch-bg-color--checked);
114
+ }
115
+ }
116
+
117
+ .c-switch-input:disabled ~ & {
118
+ color: var(--switch-bg-color--disabled);
119
+
120
+ .c-pieIcon--check {
121
+ @include switch-transition(color);
122
+ color: var(--switch-bg-color--disabled);
123
+ }
124
+ }
125
+ }
126
+
127
+ // The description is only required for screen readers so we need to visually hide the description
128
+ .c-switch-description {
129
+ position: absolute;
130
+ left: -9999px;
131
+ top: auto;
132
+ width: 1px;
133
+ height: 1px;
134
+ overflow: hidden;
135
+ }
136
+
137
+ .c-switch-wrapper[isRTL] {
138
+ .c-switch-control {
139
+ position: absolute;
140
+ left: initial;
141
+ right: 2px;
142
+ }
143
+
144
+ .c-switch-input:checked + .c-switch-control {
145
+ @include switch-transition(transform);
146
+ transform: translateX(calc(-1 * var(--switch-translation)));
147
+ }
148
+ }