@justeattakeaway/pie-radio 0.2.2 → 0.4.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.
@@ -41,6 +41,15 @@
41
41
  "description": "",
42
42
  "name": "PieRadio",
43
43
  "members": [
44
+ {
45
+ "kind": "field",
46
+ "name": "_disabledByParent",
47
+ "type": {
48
+ "text": "boolean"
49
+ },
50
+ "privacy": "private",
51
+ "default": "false"
52
+ },
44
53
  {
45
54
  "kind": "field",
46
55
  "name": "checked",
@@ -89,21 +98,29 @@
89
98
  },
90
99
  {
91
100
  "kind": "field",
92
- "name": "radio",
101
+ "name": "_radio",
93
102
  "type": {
94
103
  "text": "HTMLInputElement"
95
104
  },
96
105
  "privacy": "private"
97
106
  },
107
+ {
108
+ "kind": "field",
109
+ "name": "_abortController",
110
+ "type": {
111
+ "text": "AbortController"
112
+ },
113
+ "privacy": "private"
114
+ },
98
115
  {
99
116
  "kind": "method",
100
- "name": "handleFormAssociation",
117
+ "name": "_handleFormAssociation",
101
118
  "privacy": "private",
102
119
  "description": "Ensures that the form value is in sync with the component."
103
120
  },
104
121
  {
105
122
  "kind": "method",
106
- "name": "handleChange",
123
+ "name": "_handleChange",
107
124
  "privacy": "private",
108
125
  "parameters": [
109
126
  {
@@ -133,6 +150,15 @@
133
150
  "description": "Called when the form that contains this component is reset.\nIf the current checked state is different to the default checked state,\nthe checked state is reset to the default checked state and a `change` event is emitted."
134
151
  }
135
152
  ],
153
+ "events": [
154
+ {
155
+ "type": {
156
+ "text": "CustomEvent"
157
+ },
158
+ "description": "when the radio state is changed.",
159
+ "name": "change"
160
+ }
161
+ ],
136
162
  "attributes": [
137
163
  {
138
164
  "name": "checked",
package/dist/index.d.ts CHANGED
@@ -12,24 +12,29 @@ export declare const defaultProps: DefaultProps;
12
12
 
13
13
  /**
14
14
  * @tagname pie-radio
15
+ * @event {CustomEvent} change - when the radio state is changed.
15
16
  */
16
17
  export declare class PieRadio extends PieRadio_base implements RadioProps {
18
+ private _disabledByParent;
17
19
  checked: boolean;
18
20
  defaultChecked: boolean;
19
21
  disabled: boolean;
20
22
  name: RadioProps['name'];
21
23
  required: boolean;
22
24
  value: RadioProps['value'];
23
- private radio;
25
+ private _radio;
26
+ private _abortController;
27
+ connectedCallback(): void;
28
+ disconnectedCallback(): void;
24
29
  /**
25
30
  * Ensures that the form value is in sync with the component.
26
31
  */
27
- private handleFormAssociation;
32
+ private _handleFormAssociation;
28
33
  /**
29
34
  * Captures the native change event and wraps it in a custom event.
30
35
  * @param {Event} event - This should be the change event that was listened for on an input element with `type="radio"`.
31
36
  */
32
- private handleChange;
37
+ private _handleChange;
33
38
  /**
34
39
  * (Read-only) returns a ValidityState with the validity states that this element is in.
35
40
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
package/dist/index.js CHANGED
@@ -1,45 +1,56 @@
1
- import { LitElement as u, html as f, unsafeCSS as m } from "lit";
2
- import { property as s, query as y } from "lit/decorators.js";
3
- import { ifDefined as k } from "lit/directives/if-defined.js";
4
- import { live as b } from "lit/directives/live.js";
5
- import { FormControlMixin as v, RtlMixin as C, wrapNativeEvent as g, requiredProperty as q, defineCustomElement as E } from "@justeattakeaway/pie-webc-core";
6
- const n = {
1
+ import { LitElement as v, html as u, unsafeCSS as f } from "lit";
2
+ import { state as m, property as i, query as k } from "lit/decorators.js";
3
+ import { ifDefined as g } from "lit/directives/if-defined.js";
4
+ import { live as y } from "lit/directives/live.js";
5
+ import { classMap as C } from "lit/directives/class-map.js";
6
+ import { FormControlMixin as _, RtlMixin as z, wrapNativeEvent as x, requiredProperty as w, defineCustomElement as E } from "@justeattakeaway/pie-webc-core";
7
+ const l = {
7
8
  checked: !1,
8
9
  defaultChecked: !1,
9
10
  disabled: !1,
10
11
  required: !1
11
- }, F = "*,*:after,*:before{box-sizing:inherit}";
12
- var $ = Object.defineProperty, i = (c, e, o, d) => {
13
- for (var r = void 0, a = c.length - 1, h; a >= 0; a--)
14
- (h = c[a]) && (r = h(e, o, r) || r);
15
- return r && $(e, o, r), r;
12
+ }, q = '*,*:after,*:before{box-sizing:inherit}.c-radio{--radio-dot-bg-color: var(--dt-color-content-interactive-primary);--radio-bg-color: transparent;--radio-bg-color--checked: var(--dt-color-interactive-brand);--radio-border-color: var(--dt-color-interactive-form);--radio-font-size: p.font-size(--dt-font-body-l-size);--radio-font-weight: var(--dt-font-weight-regular);--radio-text-color: var(--dt-color-content-default);--radio-size: 24px;--radio-dot-size: 8px;--radio-cursor: pointer;--radio-motion-easing: var(--dt-motion-easing-persistent-functional);--radio-border-width: 1px;display:flex;align-items:center;gap:var(--dt-spacing-b);cursor:var(--radio-cursor);font-size:var(--radio-font-size);font-weight:var(--radio-font-weight);color:var(--radio-text-color)}.c-radio.c-radio--disabled{--radio-cursor: not-allowed}.c-radio input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:block;position:relative;inline-size:var(--radio-size);block-size:var(--radio-size);border:var(--radio-border-width) solid var(--radio-border-color);border-radius:50%;margin:0;cursor:var(--radio-cursor);background-color:transparent;transition:background-color var(--dt-motion-timing-100) var(--radio-motion-easing);flex-shrink:0}.c-radio input[type=radio]:focus{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-radio input[type=radio]:before{--circle-size: calc(var(--radio-border-width) * -1);content:"";display:block;inset:var(--circle-size);border-radius:inherit;background-color:var(--radio-bg-color--checked);position:absolute;transform:scale(0)}@media (prefers-reduced-motion: no-preference){.c-radio input[type=radio]:not(:disabled):before{transition:all var(--dt-motion-timing-100) var(--radio-motion-easing)}}.c-radio input[type=radio]:after{content:"";position:absolute;top:50%;left:50%;width:var(--radio-dot-size);height:var(--radio-dot-size);background-color:var(--radio-dot-bg-color);border-radius:50%;transform:translate(-50%,-50%) scale(0)}.c-radio input[type=radio]:checked:after{transform:translate(-50%,-50%) scale(1)}.c-radio input[type=radio]:checked:before{transform:scale(1)}@media (prefers-reduced-motion: no-preference){.c-radio input[type=radio]:not(:disabled):after{transition:all var(--dt-motion-timing-100) var(--radio-motion-easing)}}@media (prefers-reduced-motion: no-preference){.c-radio input[type=radio]:not(:disabled):checked:after{transition:all var(--dt-motion-timing-150) var(--radio-motion-easing)}}.c-radio input[type=radio]:disabled{background-color:var(--dt-color-disabled-01);border-color:var(--dt-color-border-default)}.c-radio input[type=radio]:checked:disabled{--radio-dot-bg-color: var(--dt-color-content-disabled);--radio-bg-color--checked: var(--dt-color-disabled-01)}.c-radio input[type=radio]:hover:not(:checked,:disabled){background-color:hsl(var(--dt-color-black-h),var(--dt-color-black-s),var(--dt-color-black-l),var(--dt-color-hover-01))}.c-radio input[type=radio]:active:not(:checked,:disabled){background-color:hsl(var(--dt-color-black-h),var(--dt-color-black-s),var(--dt-color-black-l),var(--dt-color-active-01))}.c-radio input[type=radio]:hover:checked:not(:disabled):before{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)));border-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-radio input[type=radio]:active:checked:not(:disabled):before{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)));border-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)))}';
13
+ var B = Object.defineProperty, a = (p, r, o, c) => {
14
+ for (var t = void 0, d = p.length - 1, n; d >= 0; d--)
15
+ (n = p[d]) && (t = n(r, o, t) || t);
16
+ return t && B(r, o, t), t;
16
17
  };
17
- const p = "pie-radio", l = class l extends v(C(u)) {
18
+ const h = "pie-radio", s = class s extends _(z(v)) {
18
19
  constructor() {
19
- super(...arguments), this.checked = n.checked, this.defaultChecked = n.defaultChecked, this.disabled = n.disabled, this.required = n.required;
20
+ super(...arguments), this._disabledByParent = !1, this.checked = l.checked, this.defaultChecked = l.defaultChecked, this.disabled = l.disabled, this.required = l.required;
21
+ }
22
+ connectedCallback() {
23
+ super.connectedCallback(), this._abortController = new AbortController();
24
+ const { signal: r } = this._abortController;
25
+ this.addEventListener("pie-radio-group-disabled", (o) => {
26
+ this._disabledByParent = o.detail.disabled;
27
+ }, { signal: r });
28
+ }
29
+ disconnectedCallback() {
30
+ super.disconnectedCallback(), this._abortController.abort();
20
31
  }
21
32
  /**
22
33
  * Ensures that the form value is in sync with the component.
23
34
  */
24
- handleFormAssociation() {
35
+ _handleFormAssociation() {
25
36
  this.form && this.name && this._internals.setFormValue(this.checked ? this.value : null);
26
37
  }
27
38
  /**
28
39
  * Captures the native change event and wraps it in a custom event.
29
40
  * @param {Event} event - This should be the change event that was listened for on an input element with `type="radio"`.
30
41
  */
31
- handleChange(e) {
32
- const { checked: o } = e == null ? void 0 : e.currentTarget;
42
+ _handleChange(r) {
43
+ const { checked: o } = r == null ? void 0 : r.currentTarget;
33
44
  this.checked = o;
34
- const d = g(e);
35
- this.dispatchEvent(d), this.handleFormAssociation();
45
+ const c = x(r);
46
+ this.dispatchEvent(c), this._handleFormAssociation();
36
47
  }
37
48
  /**
38
49
  * (Read-only) returns a ValidityState with the validity states that this element is in.
39
50
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
40
51
  */
41
52
  get validity() {
42
- return this.radio.validity;
53
+ return this._radio.validity;
43
54
  }
44
55
  /**
45
56
  * Called when the form that contains this component is reset.
@@ -50,62 +61,69 @@ const p = "pie-radio", l = class l extends v(C(u)) {
50
61
  if (this.checked === this.defaultChecked)
51
62
  return;
52
63
  this.checked = this.defaultChecked;
53
- const e = new Event("change", { bubbles: !0, composed: !0 });
54
- this.dispatchEvent(e), this.handleFormAssociation();
64
+ const r = new Event("change", { bubbles: !0, composed: !0 });
65
+ this.dispatchEvent(r), this._handleFormAssociation();
55
66
  }
56
67
  updated() {
57
- this.handleFormAssociation();
68
+ this._handleFormAssociation();
58
69
  }
59
70
  render() {
60
71
  const {
61
- checked: e,
72
+ checked: r,
62
73
  disabled: o,
63
- name: d,
64
- required: r,
65
- value: a
66
- } = this;
67
- return f`
68
- <input
69
- type="radio"
70
- id="radioId"
71
- data-test-id="pie-radio"
72
- .checked="${b(e)}"
73
- .value="${a}"
74
- name="${k(d)}"
75
- ?disabled="${o}"
76
- ?required="${r}"
77
- @change="${this.handleChange}">
78
- <label for="radioId">
74
+ _disabledByParent: c,
75
+ name: t,
76
+ required: d,
77
+ value: n
78
+ } = this, b = o || c;
79
+ return u`
80
+ <label class=${C({
81
+ "c-radio": !0,
82
+ "c-radio--disabled": b
83
+ })} for="radioId">
84
+ <input
85
+ type="radio"
86
+ id="radioId"
87
+ data-test-id="pie-radio"
88
+ .checked="${y(r)}"
89
+ .value="${n}"
90
+ name="${g(t)}"
91
+ ?disabled="${b}"
92
+ ?required="${d}"
93
+ @change="${this._handleChange}">
79
94
  <slot></slot>
80
95
  </label>`;
81
96
  }
82
97
  };
83
- l.styles = m(F);
84
- let t = l;
85
- i([
86
- s({ type: Boolean, reflect: !0 })
87
- ], t.prototype, "checked");
88
- i([
89
- s({ type: Boolean, reflect: !0 })
90
- ], t.prototype, "defaultChecked");
91
- i([
92
- s({ type: Boolean, reflect: !0 })
93
- ], t.prototype, "disabled");
94
- i([
95
- s({ type: String })
96
- ], t.prototype, "name");
97
- i([
98
- s({ type: Boolean, reflect: !0 })
99
- ], t.prototype, "required");
100
- i([
101
- s({ type: String }),
102
- q(p)
103
- ], t.prototype, "value");
104
- i([
105
- y('input[type="radio"]')
106
- ], t.prototype, "radio");
107
- E(p, t);
98
+ s.styles = f(q);
99
+ let e = s;
100
+ a([
101
+ m()
102
+ ], e.prototype, "_disabledByParent");
103
+ a([
104
+ i({ type: Boolean, reflect: !0 })
105
+ ], e.prototype, "checked");
106
+ a([
107
+ i({ type: Boolean, reflect: !0 })
108
+ ], e.prototype, "defaultChecked");
109
+ a([
110
+ i({ type: Boolean, reflect: !0 })
111
+ ], e.prototype, "disabled");
112
+ a([
113
+ i({ type: String })
114
+ ], e.prototype, "name");
115
+ a([
116
+ i({ type: Boolean, reflect: !0 })
117
+ ], e.prototype, "required");
118
+ a([
119
+ i({ type: String }),
120
+ w(h)
121
+ ], e.prototype, "value");
122
+ a([
123
+ k('input[type="radio"]')
124
+ ], e.prototype, "_radio");
125
+ E(h, e);
108
126
  export {
109
- t as PieRadio,
110
- n as defaultProps
127
+ e as PieRadio,
128
+ l as defaultProps
111
129
  };
package/dist/react.d.ts CHANGED
@@ -11,28 +11,33 @@ export declare type DefaultProps = ComponentDefaultProps<RadioProps, keyof Omit<
11
11
 
12
12
  export declare const defaultProps: DefaultProps;
13
13
 
14
- export declare const PieRadio: React_2.ForwardRefExoticComponent<RadioProps & React_2.RefAttributes<PieRadio_2> & ReactBaseType>;
14
+ export declare const PieRadio: React_2.ForwardRefExoticComponent<RadioProps & React_2.RefAttributes<PieRadio_2> & PieRadioEvents & ReactBaseType>;
15
15
 
16
16
  /**
17
17
  * @tagname pie-radio
18
+ * @event {CustomEvent} change - when the radio state is changed.
18
19
  */
19
20
  declare class PieRadio_2 extends PieRadio_base implements RadioProps {
21
+ private _disabledByParent;
20
22
  checked: boolean;
21
23
  defaultChecked: boolean;
22
24
  disabled: boolean;
23
25
  name: RadioProps['name'];
24
26
  required: boolean;
25
27
  value: RadioProps['value'];
26
- private radio;
28
+ private _radio;
29
+ private _abortController;
30
+ connectedCallback(): void;
31
+ disconnectedCallback(): void;
27
32
  /**
28
33
  * Ensures that the form value is in sync with the component.
29
34
  */
30
- private handleFormAssociation;
35
+ private _handleFormAssociation;
31
36
  /**
32
37
  * Captures the native change event and wraps it in a custom event.
33
38
  * @param {Event} event - This should be the change event that was listened for on an input element with `type="radio"`.
34
39
  */
35
- private handleChange;
40
+ private _handleChange;
36
41
  /**
37
42
  * (Read-only) returns a ValidityState with the validity states that this element is in.
38
43
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
@@ -51,6 +56,10 @@ declare class PieRadio_2 extends PieRadio_base implements RadioProps {
51
56
 
52
57
  declare const PieRadio_base: GenericConstructor<FormControlInterface> & GenericConstructor<RTLInterface> & typeof LitElement;
53
58
 
59
+ declare type PieRadioEvents = {
60
+ onChange?: (event: CustomEvent) => void;
61
+ };
62
+
54
63
  export declare interface RadioProps {
55
64
  /**
56
65
  * The checked state of the radio.
package/dist/react.js CHANGED
@@ -1,15 +1,18 @@
1
1
  import * as e from "react";
2
2
  import { createComponent as o } from "@lit/react";
3
3
  import { PieRadio as a } from "./index.js";
4
- import { defaultProps as d } from "./index.js";
4
+ import { defaultProps as s } from "./index.js";
5
5
  const i = o({
6
6
  displayName: "PieRadio",
7
7
  elementClass: a,
8
8
  react: e,
9
9
  tagName: "pie-radio",
10
- events: {}
10
+ events: {
11
+ onChange: "change"
12
+ // when the radio state is changed.
13
+ }
11
14
  }), m = i;
12
15
  export {
13
16
  m as PieRadio,
14
- d as defaultProps
17
+ s as defaultProps
15
18
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-radio",
3
3
  "description": "PIE Design System Radio built using Web Components",
4
- "version": "0.2.2",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
package/src/index.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  import { LitElement, html, unsafeCSS } from 'lit';
2
- import { property, query } from 'lit/decorators.js';
2
+ import { property, query, state } from 'lit/decorators.js';
3
3
  import { ifDefined } from 'lit/directives/if-defined.js';
4
4
  import { live } from 'lit/directives/live.js';
5
+ import { classMap } from 'lit/directives/class-map.js';
5
6
 
6
7
  import {
7
- defineCustomElement, FormControlMixin, requiredProperty, RtlMixin, wrapNativeEvent,
8
+ defineCustomElement,
9
+ FormControlMixin,
10
+ requiredProperty,
11
+ RtlMixin,
12
+ wrapNativeEvent,
8
13
  } from '@justeattakeaway/pie-webc-core';
9
14
 
10
15
  import { type RadioProps, defaultProps } from './defs';
@@ -17,8 +22,12 @@ const componentSelector = 'pie-radio';
17
22
 
18
23
  /**
19
24
  * @tagname pie-radio
25
+ * @event {CustomEvent} change - when the radio state is changed.
20
26
  */
21
27
  export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements RadioProps {
28
+ @state()
29
+ private _disabledByParent = false;
30
+
22
31
  @property({ type: Boolean, reflect: true })
23
32
  public checked = defaultProps.checked;
24
33
 
@@ -39,12 +48,27 @@ export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements
39
48
  public value!: RadioProps['value'];
40
49
 
41
50
  @query('input[type="radio"]')
42
- private radio!: HTMLInputElement;
51
+ private _radio!: HTMLInputElement;
52
+
53
+ private _abortController!: AbortController;
54
+
55
+ connectedCallback () : void {
56
+ super.connectedCallback();
57
+ this._abortController = new AbortController();
58
+ const { signal } = this._abortController;
59
+
60
+ this.addEventListener('pie-radio-group-disabled', (e: CustomEventInit) => { this._disabledByParent = e.detail.disabled; }, { signal });
61
+ }
62
+
63
+ disconnectedCallback () : void {
64
+ super.disconnectedCallback();
65
+ this._abortController.abort();
66
+ }
43
67
 
44
68
  /**
45
69
  * Ensures that the form value is in sync with the component.
46
70
  */
47
- private handleFormAssociation () {
71
+ private _handleFormAssociation () {
48
72
  if (this.form && this.name) {
49
73
  this._internals.setFormValue(this.checked ? this.value : null);
50
74
  }
@@ -54,7 +78,7 @@ export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements
54
78
  * Captures the native change event and wraps it in a custom event.
55
79
  * @param {Event} event - This should be the change event that was listened for on an input element with `type="radio"`.
56
80
  */
57
- private handleChange (event: Event) {
81
+ private _handleChange (event: Event) {
58
82
  const { checked } = event?.currentTarget as HTMLInputElement;
59
83
  this.checked = checked;
60
84
  // This is because some events set `composed` to `false`.
@@ -62,7 +86,7 @@ export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements
62
86
  const customChangeEvent = wrapNativeEvent(event);
63
87
  this.dispatchEvent(customChangeEvent);
64
88
 
65
- this.handleFormAssociation();
89
+ this._handleFormAssociation();
66
90
  }
67
91
 
68
92
  /**
@@ -70,7 +94,7 @@ export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements
70
94
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
71
95
  */
72
96
  public get validity () : ValidityState {
73
- return this.radio.validity;
97
+ return this._radio.validity;
74
98
  }
75
99
 
76
100
  /**
@@ -88,30 +112,42 @@ export class PieRadio extends FormControlMixin(RtlMixin(LitElement)) implements
88
112
  const changeEvent = new Event('change', { bubbles: true, composed: true });
89
113
  this.dispatchEvent(changeEvent);
90
114
 
91
- this.handleFormAssociation();
115
+ this._handleFormAssociation();
92
116
  }
93
117
 
94
118
  updated () {
95
- this.handleFormAssociation();
119
+ this._handleFormAssociation();
96
120
  }
97
121
 
98
122
  render () {
99
123
  const {
100
- checked, disabled, name, required, value,
124
+ checked,
125
+ disabled,
126
+ _disabledByParent,
127
+ name,
128
+ required,
129
+ value,
101
130
  } = this;
102
131
 
132
+ const componentDisabled = disabled || _disabledByParent;
133
+
134
+ const classes = {
135
+ 'c-radio': true,
136
+ 'c-radio--disabled': componentDisabled,
137
+ };
138
+
103
139
  return html`
104
- <input
105
- type="radio"
106
- id="radioId"
107
- data-test-id="pie-radio"
108
- .checked="${live(checked)}"
109
- .value="${value}"
110
- name="${ifDefined(name)}"
111
- ?disabled="${disabled}"
112
- ?required="${required}"
113
- @change="${this.handleChange}">
114
- <label for="radioId">
140
+ <label class=${classMap(classes)} for="radioId">
141
+ <input
142
+ type="radio"
143
+ id="radioId"
144
+ data-test-id="pie-radio"
145
+ .checked="${live(checked)}"
146
+ .value="${value}"
147
+ name="${ifDefined(name)}"
148
+ ?disabled="${componentDisabled}"
149
+ ?required="${required}"
150
+ @change="${this._handleChange}">
115
151
  <slot></slot>
116
152
  </label>`;
117
153
  }
package/src/radio.scss CHANGED
@@ -1 +1,145 @@
1
1
  @use '@justeattakeaway/pie-css/scss' as p;
2
+
3
+ .c-radio {
4
+ --radio-dot-bg-color: var(--dt-color-content-interactive-primary);
5
+ --radio-bg-color: transparent;
6
+ --radio-bg-color--checked: var(--dt-color-interactive-brand);
7
+ --radio-border-color: var(--dt-color-interactive-form);
8
+ --radio-font-size: p.font-size(--dt-font-body-l-size);
9
+ --radio-font-weight: var(--dt-font-weight-regular);
10
+ --radio-text-color: var(--dt-color-content-default);
11
+ --radio-size: 24px;
12
+ --radio-dot-size: 8px;
13
+ --radio-cursor: pointer;
14
+ --radio-motion-easing: var(--dt-motion-easing-persistent-functional);
15
+ --radio-border-width: 1px;
16
+
17
+ display: flex;
18
+ align-items: center;
19
+ gap: var(--dt-spacing-b);
20
+ cursor: var(--radio-cursor);
21
+ font-size: var(--radio-font-size);
22
+ font-weight: var(--radio-font-weight);
23
+ color: var(--radio-text-color);
24
+
25
+ &.c-radio--disabled {
26
+ --radio-cursor: not-allowed;
27
+ }
28
+
29
+ input[type="radio"] {
30
+ appearance: none;
31
+ display: block;
32
+ position: relative;
33
+ inline-size: var(--radio-size);
34
+ block-size: var(--radio-size);
35
+ border: var(--radio-border-width) solid var(--radio-border-color);
36
+ border-radius: 50%;
37
+ margin: 0;
38
+ cursor: var(--radio-cursor);
39
+ background-color: transparent;
40
+ transition: background-color var(--dt-motion-timing-100) var(--radio-motion-easing);
41
+ flex-shrink: 0;
42
+
43
+ &:focus {
44
+ @include p.focus;
45
+ }
46
+
47
+ // The filled circle before checking the radio
48
+ &:before {
49
+ --circle-size: calc(var(--radio-border-width) * -1);
50
+
51
+ content: '';
52
+ display: block;
53
+ inset: var(--circle-size);
54
+ border-radius: inherit;
55
+ background-color: var(--radio-bg-color--checked);
56
+ position: absolute;
57
+ transform: scale(0);
58
+ }
59
+
60
+ &:not(:disabled):before {
61
+ @media (prefers-reduced-motion: no-preference) {
62
+ transition: all var(--dt-motion-timing-100) var(--radio-motion-easing);
63
+ }
64
+ }
65
+
66
+ // The dot in the middle before checking the radio
67
+ &:after {
68
+ content: '';
69
+ position: absolute;
70
+ top: 50%;
71
+ left: 50%;
72
+ width: var(--radio-dot-size);
73
+ height: var(--radio-dot-size);
74
+ background-color: var(--radio-dot-bg-color);
75
+ border-radius: 50%;
76
+ transform: translate(-50%, -50%) scale(0);
77
+ }
78
+
79
+ &:checked {
80
+ // The dot in the middle after checking the radio
81
+ &:after {
82
+ transform: translate(-50%, -50%) scale(1);
83
+ }
84
+
85
+ // The filled circle after checking the radio
86
+ &:before {
87
+ transform: scale(1);
88
+ }
89
+ }
90
+
91
+ // Scales down at 100ms when unchecked
92
+ &:not(:disabled):after {
93
+ @media (prefers-reduced-motion: no-preference) {
94
+ transition: all var(--dt-motion-timing-100) var(--radio-motion-easing);
95
+ }
96
+ }
97
+
98
+ // Scales up at 150ms when checked
99
+ &:not(:disabled):checked:after {
100
+ @media (prefers-reduced-motion: no-preference) {
101
+ transition: all var(--dt-motion-timing-150) var(--radio-motion-easing);
102
+ }
103
+ }
104
+
105
+ &:disabled {
106
+ background-color: var(--dt-color-disabled-01);
107
+ border-color: var(--dt-color-border-default);
108
+ }
109
+
110
+ &:checked:disabled {
111
+ --radio-dot-bg-color: var(--dt-color-content-disabled);
112
+ --radio-bg-color--checked: var(--dt-color-disabled-01);
113
+ }
114
+
115
+ &:hover:not(:checked, :disabled) {
116
+ background-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-hover-01));
117
+ }
118
+
119
+ &:active:not(:checked, :disabled) {
120
+ background-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--dt-color-active-01));
121
+ }
122
+
123
+ &:hover:checked:not(:disabled) {
124
+ &:before {
125
+ background-color: hsl(var(--dt-color-interactive-brand-h),
126
+ var(--dt-color-interactive-brand-s),
127
+ calc(var(--dt-color-interactive-brand-l) - var(--dt-color-hover-01)));
128
+ border-color: hsl(var(--dt-color-interactive-brand-h),
129
+ var(--dt-color-interactive-brand-s),
130
+ calc(var(--dt-color-interactive-brand-l) - var(--dt-color-hover-01)));
131
+ }
132
+ }
133
+
134
+ &:active:checked:not(:disabled) {
135
+ &:before {
136
+ background-color: hsl(var(--dt-color-interactive-brand-h),
137
+ var(--dt-color-interactive-brand-s),
138
+ calc(var(--dt-color-interactive-brand-l) - var(--dt-color-active-01)));
139
+ border-color: hsl(var(--dt-color-interactive-brand-h),
140
+ var(--dt-color-interactive-brand-s),
141
+ calc(var(--dt-color-interactive-brand-l) - var(--dt-color-active-01)));
142
+ }
143
+ }
144
+ }
145
+ }
package/src/react.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { createComponent } from '@lit/react';
2
+ import { createComponent, type EventName } from '@lit/react';
3
3
  import { PieRadio as PieRadioLit } from './index';
4
4
  import { type RadioProps } from './defs';
5
5
 
@@ -10,10 +10,16 @@ const PieRadioReact = createComponent({
10
10
  elementClass: PieRadioLit,
11
11
  react: React,
12
12
  tagName: 'pie-radio',
13
- events: {},
13
+ events: {
14
+ onChange: 'change' as EventName<CustomEvent>, // when the radio state is changed.
15
+ },
14
16
  });
15
17
 
16
18
  type ReactBaseType = React.InputHTMLAttributes<HTMLInputElement>;
17
19
 
20
+ type PieRadioEvents = {
21
+ onChange?: (event: CustomEvent) => void;
22
+ };
23
+
18
24
  export const PieRadio = PieRadioReact as React.ForwardRefExoticComponent<React.PropsWithoutRef<RadioProps>
19
- & React.RefAttributes<PieRadioLit> & ReactBaseType>;
25
+ & React.RefAttributes<PieRadioLit> & PieRadioEvents & ReactBaseType>;