@fluid-topics/ftds-radio 2.1.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,19 @@
1
+ The design system radio component
2
+
3
+ ## Install
4
+
5
+ ```shell
6
+ npm install @fluid-topics/ftds-radio
7
+ yarn add @fluid-topics/ftds-radio
8
+ ```
9
+
10
+ ## Usage
11
+
12
+ ```typescript
13
+ import { html } from "lit"
14
+ import "@fluid-topics/ftds-radio"
15
+
16
+ function render() {
17
+ return html` <ftds-radio foo="bar"></ftds-radio> `
18
+ }
19
+ ```
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { customElements } from "@fluid-topics/ft-wc-utils";
2
+ import definitions from "./definitions";
3
+ customElements(definitions);
@@ -0,0 +1,7 @@
1
+ import { FtdsRadio } from "./ftds-radio";
2
+ import { FtdsRadioGroup } from "./ftds-radio-group";
3
+ declare const _default: {
4
+ "ftds-radio": typeof FtdsRadio;
5
+ "ftds-radio-group": typeof FtdsRadioGroup;
6
+ };
7
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { FtdsRadio } from "./ftds-radio";
2
+ import { FtdsRadioGroup } from "./ftds-radio-group";
3
+ export default {
4
+ "ftds-radio": FtdsRadio,
5
+ "ftds-radio-group": FtdsRadioGroup,
6
+ };
@@ -0,0 +1,29 @@
1
+ import { FtLitElement } from "@fluid-topics/ft-wc-utils";
2
+ import { DesignSystemFamily } from "@fluid-topics/design-system-variables";
3
+ import { PropertyValues } from "lit";
4
+ import { FtdsRadio } from "./ftds-radio";
5
+ import { FtdsRadioGroupProperties } from "./ftds-radio-group.properties";
6
+ declare const FtdsRadioGroup_base: import("@fluid-topics/ft-wc-utils").FtdsBaseType<typeof FtLitElement>;
7
+ export declare class FtdsRadioGroup extends FtdsRadioGroup_base implements FtdsRadioGroupProperties {
8
+ static elementDefinitions: {};
9
+ static styles: import("lit").CSSResult[];
10
+ family: DesignSystemFamily;
11
+ name: string;
12
+ role: string;
13
+ ariaLabelledBy: string;
14
+ assignedElements?: Array<Element>;
15
+ get radioButtons(): Array<FtdsRadio>;
16
+ currentSelectedIndex: number;
17
+ protected render(): import("lit-html").TemplateResult<1>;
18
+ connectedCallback(): void;
19
+ disconnectedCallback(): void;
20
+ private onSlotChange;
21
+ protected contentAvailableCallback(props: PropertyValues): void;
22
+ private onFocus;
23
+ private onFocusOut;
24
+ private onChange;
25
+ private onKeyDown;
26
+ private resolveCurrentSelectedIndex;
27
+ private focusCurrentChecked;
28
+ }
29
+ export {};
@@ -0,0 +1,126 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { FtLitElement, safariEllipsisFix, toFtdsBase, } from "@fluid-topics/ft-wc-utils";
8
+ import { groupStyles } from "./ftds-radio-group.styles";
9
+ import { DesignSystemFamily } from "@fluid-topics/design-system-variables";
10
+ import { property, queryAssignedElements, state, } from "lit/decorators.js";
11
+ import { html, } from "lit";
12
+ export class FtdsRadioGroup extends toFtdsBase(FtLitElement) {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.family = DesignSystemFamily.brand;
16
+ this.name = "";
17
+ this.role = "radiogroup";
18
+ this.ariaLabelledBy = "";
19
+ this.currentSelectedIndex = 0;
20
+ this.onFocus = () => {
21
+ setTimeout(() => this.focusCurrentChecked(), 100);
22
+ };
23
+ this.onFocusOut = (e) => {
24
+ };
25
+ }
26
+ get radioButtons() {
27
+ var _a, _b;
28
+ return (_b = (_a = this.assignedElements) === null || _a === void 0 ? void 0 : _a.flatMap((e) => e.matches("ft-radio,ftds-radio")
29
+ ? [e]
30
+ : [...e.querySelectorAll("ftds-radio")])) !== null && _b !== void 0 ? _b : [];
31
+ }
32
+ render() {
33
+ return html `
34
+ <slot @slotchange=${this.onSlotChange}
35
+ @change=${this.onChange}
36
+ @keydown=${this.onKeyDown}
37
+ ></slot>
38
+ `;
39
+ }
40
+ connectedCallback() {
41
+ super.connectedCallback();
42
+ this.addEventListener("focus", this.onFocus);
43
+ this.addEventListener("focusout", this.onFocusOut);
44
+ }
45
+ disconnectedCallback() {
46
+ super.disconnectedCallback();
47
+ this.removeEventListener("focus", this.onFocus);
48
+ this.removeEventListener("focusout", this.onFocusOut);
49
+ }
50
+ onSlotChange() {
51
+ this.radioButtons.forEach((rb) => rb.name = this.name);
52
+ }
53
+ contentAvailableCallback(props) {
54
+ super.contentAvailableCallback(props);
55
+ this.radioButtons.forEach((rb) => rb.setInputTabIndex(-1)); // RadioButton is no more selectable alone
56
+ this.resolveCurrentSelectedIndex();
57
+ }
58
+ onChange(event) {
59
+ event.stopPropagation();
60
+ this.radioButtons.forEach((rb) => rb.checked = event.detail.value === rb.value);
61
+ this.dispatchEvent(new CustomEvent("change", { detail: event.detail.value }));
62
+ this.resolveCurrentSelectedIndex();
63
+ }
64
+ onKeyDown(event) {
65
+ let blockUnwhishedReaction = false;
66
+ switch (event.key) {
67
+ case "ArrowUp":
68
+ case "ArrowLeft": {
69
+ blockUnwhishedReaction = true;
70
+ const indexToFocus = this.currentSelectedIndex - 1;
71
+ this.radioButtons[indexToFocus < 0 ? this.radioButtons.length - 1 : indexToFocus].select();
72
+ this.radioButtons[indexToFocus < 0 ? this.radioButtons.length - 1 : indexToFocus].focus();
73
+ break;
74
+ }
75
+ case "ArrowDown":
76
+ case "ArrowRight": {
77
+ blockUnwhishedReaction = true;
78
+ const indexToFocus = this.currentSelectedIndex + 1;
79
+ this.radioButtons[indexToFocus > this.radioButtons.length - 1 ? 0 : indexToFocus].select();
80
+ this.radioButtons[indexToFocus > this.radioButtons.length - 1 ? 0 : indexToFocus].focus();
81
+ break;
82
+ }
83
+ case "Enter": {
84
+ this.radioButtons[this.currentSelectedIndex].select();
85
+ }
86
+ }
87
+ if (blockUnwhishedReaction) {
88
+ event.stopPropagation();
89
+ event.preventDefault();
90
+ }
91
+ }
92
+ resolveCurrentSelectedIndex() {
93
+ const checkedButtonIndex = this.radioButtons.findIndex((rb) => rb.checked);
94
+ if (checkedButtonIndex == -1) {
95
+ this.currentSelectedIndex = 0;
96
+ this.radioButtons[0].select();
97
+ }
98
+ else {
99
+ this.currentSelectedIndex = checkedButtonIndex;
100
+ }
101
+ this.radioButtons[this.currentSelectedIndex].setInputTabIndex(0);
102
+ }
103
+ focusCurrentChecked() {
104
+ this.radioButtons[this.currentSelectedIndex].focus();
105
+ }
106
+ }
107
+ FtdsRadioGroup.elementDefinitions = {};
108
+ FtdsRadioGroup.styles = [
109
+ safariEllipsisFix,
110
+ groupStyles,
111
+ ];
112
+ __decorate([
113
+ property()
114
+ ], FtdsRadioGroup.prototype, "name", void 0);
115
+ __decorate([
116
+ property({ reflect: true, attribute: "role" })
117
+ ], FtdsRadioGroup.prototype, "role", void 0);
118
+ __decorate([
119
+ property({ reflect: true, attribute: "aria-labelledby" })
120
+ ], FtdsRadioGroup.prototype, "ariaLabelledBy", void 0);
121
+ __decorate([
122
+ queryAssignedElements()
123
+ ], FtdsRadioGroup.prototype, "assignedElements", void 0);
124
+ __decorate([
125
+ state()
126
+ ], FtdsRadioGroup.prototype, "currentSelectedIndex", void 0);
@@ -0,0 +1,5 @@
1
+ export interface FtdsRadioGroupProperties {
2
+ name?: string;
3
+ role?: string;
4
+ ariaLabelledBy?: string;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const groupStyles: import("lit").CSSResult;
@@ -0,0 +1,13 @@
1
+ import { css } from "lit";
2
+ import { radio } from "@fluid-topics/ft-wc-utils";
3
+ export const groupStyles = css `
4
+ :host {
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: ${radio.verticalGap}
8
+ }
9
+
10
+ :host(:focus-visible) {
11
+ outline: none;
12
+ }
13
+ `;
@@ -0,0 +1,35 @@
1
+ import { ElementDefinitionsMap, FtLitElement } from "@fluid-topics/ft-wc-utils";
2
+ import { DesignSystemFamily } from "@fluid-topics/design-system-variables";
3
+ import { FtdsRadioProperties } from "./ftds-radio.properties";
4
+ import { PropertyValues } from "lit";
5
+ import { ClassInfo } from "lit/directives/class-map.js";
6
+ export declare class FtdsRadioChangeEvent extends CustomEvent<{
7
+ value: string;
8
+ checked: boolean;
9
+ }> {
10
+ constructor(value: string, checked: boolean);
11
+ }
12
+ declare const FtdsRadio_base: import("@fluid-topics/ft-wc-utils").FtdsBaseType<typeof FtLitElement>;
13
+ export declare class FtdsRadio extends FtdsRadio_base implements FtdsRadioProperties {
14
+ static elementDefinitions: ElementDefinitionsMap;
15
+ family: DesignSystemFamily;
16
+ value: string;
17
+ name: string;
18
+ checked: boolean;
19
+ ariaChecked: string;
20
+ disabled: boolean;
21
+ private container?;
22
+ private ripple?;
23
+ private input?;
24
+ static styles: import("lit").CSSResult[];
25
+ get radioClasses(): ClassInfo;
26
+ protected render(): import("lit-html").TemplateResult<1>;
27
+ protected willUpdate(changedProperties: PropertyValues): void;
28
+ private onChange;
29
+ protected contentAvailableCallback(props: PropertyValues): void;
30
+ select(): void;
31
+ setInputTabIndex(index: number): void;
32
+ focus(): void;
33
+ onClick(e: Event): void;
34
+ }
35
+ export {};
@@ -0,0 +1,139 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { FtLitElement, safariEllipsisFix, toFtdsBase, } from "@fluid-topics/ft-wc-utils";
8
+ import { styles } from "./ftds-radio.styles";
9
+ import { DesignSystemFamily } from "@fluid-topics/design-system-variables";
10
+ import { FtdsTypography, FtdsTypographyVariants, } from "@fluid-topics/ftds-typography";
11
+ import { FtRipple } from "@fluid-topics/ft-ripple";
12
+ import { html, } from "lit";
13
+ import { classMap, } from "lit/directives/class-map.js";
14
+ import { property, query, } from "lit/decorators.js";
15
+ export class FtdsRadioChangeEvent extends CustomEvent {
16
+ constructor(value, checked) {
17
+ super("change", { detail: { value: value, checked: checked }, bubbles: true, composed: true });
18
+ }
19
+ }
20
+ export class FtdsRadio extends toFtdsBase(FtLitElement) {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.family = DesignSystemFamily.brand;
24
+ this.value = "";
25
+ this.name = "";
26
+ this.checked = false;
27
+ this.ariaChecked = "false";
28
+ this.disabled = false;
29
+ }
30
+ get radioClasses() {
31
+ return {
32
+ "ftds-radio": true,
33
+ "ftds-radio--checked": this.checked,
34
+ "ftds-radio--disabled": this.disabled,
35
+ ...this.getDesignSystemBaseClasses(),
36
+ };
37
+ }
38
+ render() {
39
+ // We use inert on the input to prevent the "radio" role to be present twice in the aria tree
40
+ return html `
41
+ <div class="${classMap(this.radioClasses)}"
42
+ @click=${this.onClick}>
43
+ <div class="ftds-radio--box-container">
44
+ <ft-ripple
45
+ ?disabled=${this.disabled}
46
+ ?primary=${this.checked}
47
+ unbounded>
48
+ </ft-ripple>
49
+ <div class="ftds-radio--box">
50
+ </div>
51
+ <input id="radio-button"
52
+ type="radio"
53
+ name="${this.name}"
54
+ value="${this.value}"
55
+ .checked=${this.checked}
56
+ .disabled=${this.disabled}
57
+ @change=${this.onChange}
58
+ >
59
+ </div>
60
+ <label for="radio-button" inert>
61
+ <ftds-typography variant="${FtdsTypographyVariants.body2medium}">
62
+ <slot></slot>
63
+ </ftds-typography>
64
+ </label>
65
+ </div>
66
+ `;
67
+ }
68
+ willUpdate(changedProperties) {
69
+ super.willUpdate(changedProperties);
70
+ if (changedProperties.has("checked")) {
71
+ this.ariaChecked = this.checked ? "true" : "false";
72
+ }
73
+ }
74
+ onChange(event) {
75
+ event.stopPropagation();
76
+ this.checked = event.target.checked;
77
+ this.dispatchEvent(new FtdsRadioChangeEvent(this.value, this.checked));
78
+ }
79
+ contentAvailableCallback(props) {
80
+ var _a;
81
+ super.contentAvailableCallback(props);
82
+ (_a = this.ripple) === null || _a === void 0 ? void 0 : _a.setupFor(this.container);
83
+ }
84
+ select() {
85
+ this.checked = true;
86
+ this.dispatchEvent(new FtdsRadioChangeEvent(this.value, this.checked));
87
+ }
88
+ setInputTabIndex(index) {
89
+ if (this.input) {
90
+ this.input.tabIndex = index;
91
+ }
92
+ }
93
+ focus() {
94
+ var _a;
95
+ (_a = this.input) === null || _a === void 0 ? void 0 : _a.focus();
96
+ setTimeout(() => { var _a; return (_a = this.ripple) === null || _a === void 0 ? void 0 : _a.forceFocusUpdate(); }, 0);
97
+ }
98
+ onClick(e) {
99
+ var _a;
100
+ if (e.target === this.input) {
101
+ return;
102
+ }
103
+ e.stopPropagation();
104
+ e.preventDefault();
105
+ (_a = this.input) === null || _a === void 0 ? void 0 : _a.click();
106
+ }
107
+ }
108
+ FtdsRadio.elementDefinitions = {
109
+ "ft-ripple": FtRipple,
110
+ "ftds-typography": FtdsTypography,
111
+ };
112
+ FtdsRadio.styles = [
113
+ safariEllipsisFix,
114
+ styles,
115
+ ];
116
+ __decorate([
117
+ property()
118
+ ], FtdsRadio.prototype, "value", void 0);
119
+ __decorate([
120
+ property()
121
+ ], FtdsRadio.prototype, "name", void 0);
122
+ __decorate([
123
+ property({ type: Boolean, reflect: true })
124
+ ], FtdsRadio.prototype, "checked", void 0);
125
+ __decorate([
126
+ property({ attribute: "aria-checked", reflect: true })
127
+ ], FtdsRadio.prototype, "ariaChecked", void 0);
128
+ __decorate([
129
+ property({ type: Boolean })
130
+ ], FtdsRadio.prototype, "disabled", void 0);
131
+ __decorate([
132
+ query(".ftds-radio")
133
+ ], FtdsRadio.prototype, "container", void 0);
134
+ __decorate([
135
+ query("ft-ripple")
136
+ ], FtdsRadio.prototype, "ripple", void 0);
137
+ __decorate([
138
+ query("input")
139
+ ], FtdsRadio.prototype, "input", void 0);