mtrl 0.2.2 → 0.2.4
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/.typedocignore +11 -0
- package/DOCS.md +153 -0
- package/index.ts +18 -3
- package/package.json +7 -2
- package/src/components/badge/_styles.scss +174 -0
- package/src/components/badge/api.ts +292 -0
- package/src/components/badge/badge.ts +52 -0
- package/src/components/badge/config.ts +68 -0
- package/src/components/badge/constants.ts +30 -0
- package/src/components/badge/features.ts +185 -0
- package/src/components/badge/index.ts +4 -0
- package/src/components/badge/types.ts +105 -0
- package/src/components/button/types.ts +174 -29
- package/src/components/carousel/_styles.scss +645 -0
- package/src/components/carousel/api.ts +147 -0
- package/src/components/carousel/carousel.ts +178 -0
- package/src/components/carousel/config.ts +91 -0
- package/src/components/carousel/constants.ts +95 -0
- package/src/components/carousel/features/drag.ts +388 -0
- package/src/components/carousel/features/index.ts +8 -0
- package/src/components/carousel/features/slides.ts +682 -0
- package/src/components/carousel/index.ts +38 -0
- package/src/components/carousel/types.ts +327 -0
- package/src/components/dialog/_styles.scss +213 -0
- package/src/components/dialog/api.ts +283 -0
- package/src/components/dialog/config.ts +113 -0
- package/src/components/dialog/constants.ts +32 -0
- package/src/components/dialog/dialog.ts +56 -0
- package/src/components/dialog/features.ts +713 -0
- package/src/components/dialog/index.ts +15 -0
- package/src/components/dialog/types.ts +221 -0
- package/src/components/progress/_styles.scss +13 -1
- package/src/components/progress/api.ts +2 -2
- package/src/components/progress/progress.ts +2 -2
- package/src/components/progress/types.ts +3 -0
- package/src/components/radios/_styles.scss +232 -0
- package/src/components/radios/api.ts +100 -0
- package/src/components/radios/config.ts +60 -0
- package/src/components/radios/constants.ts +28 -0
- package/src/components/radios/index.ts +4 -0
- package/src/components/radios/radio.ts +269 -0
- package/src/components/radios/radios.ts +42 -0
- package/src/components/radios/types.ts +232 -0
- package/src/components/sheet/_styles.scss +236 -0
- package/src/components/sheet/api.ts +96 -0
- package/src/components/sheet/config.ts +66 -0
- package/src/components/sheet/constants.ts +20 -0
- package/src/components/sheet/features/content.ts +51 -0
- package/src/components/sheet/features/gestures.ts +177 -0
- package/src/components/sheet/features/index.ts +6 -0
- package/src/components/sheet/features/position.ts +42 -0
- package/src/components/sheet/features/state.ts +116 -0
- package/src/components/sheet/features/title.ts +86 -0
- package/src/components/sheet/index.ts +4 -0
- package/src/components/sheet/sheet.ts +57 -0
- package/src/components/sheet/types.ts +266 -0
- package/src/components/slider/_styles.scss +518 -0
- package/src/components/slider/api.ts +336 -0
- package/src/components/slider/config.ts +145 -0
- package/src/components/slider/constants.ts +28 -0
- package/src/components/slider/features/appearance.ts +140 -0
- package/src/components/slider/features/disabled.ts +43 -0
- package/src/components/slider/features/events.ts +164 -0
- package/src/components/slider/features/index.ts +5 -0
- package/src/components/slider/features/interactions.ts +256 -0
- package/src/components/slider/features/keyboard.ts +114 -0
- package/src/components/slider/features/slider.ts +336 -0
- package/src/components/slider/features/structure.ts +264 -0
- package/src/components/slider/features/ui.ts +518 -0
- package/src/components/slider/index.ts +9 -0
- package/src/components/slider/slider.ts +58 -0
- package/src/components/slider/types.ts +166 -0
- package/src/components/tabs/_styles.scss +224 -0
- package/src/components/tabs/api.ts +443 -0
- package/src/components/tabs/config.ts +80 -0
- package/src/components/tabs/constants.ts +12 -0
- package/src/components/tabs/index.ts +4 -0
- package/src/components/tabs/tabs.ts +52 -0
- package/src/components/tabs/types.ts +247 -0
- package/src/components/textfield/_styles.scss +97 -4
- package/src/components/tooltip/_styles.scss +241 -0
- package/src/components/tooltip/api.ts +411 -0
- package/src/components/tooltip/config.ts +78 -0
- package/src/components/tooltip/constants.ts +27 -0
- package/src/components/tooltip/index.ts +4 -0
- package/src/components/tooltip/tooltip.ts +60 -0
- package/src/components/tooltip/types.ts +178 -0
- package/src/core/build/_ripple.scss +79 -0
- package/src/core/build/constants.ts +48 -0
- package/src/core/build/icon.ts +137 -0
- package/src/core/build/ripple.ts +216 -0
- package/src/core/build/text.ts +91 -0
- package/src/index.ts +9 -1
- package/src/styles/abstract/_variables.scss +24 -12
- package/tsconfig.json +22 -0
- package/typedoc.json +28 -0
- package/typedoc.simple.json +14 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/components/dialog/index.ts
|
|
2
|
+
export { default } from './dialog';
|
|
3
|
+
export {
|
|
4
|
+
DIALOG_SIZES,
|
|
5
|
+
DIALOG_ANIMATIONS,
|
|
6
|
+
DIALOG_FOOTER_ALIGNMENTS,
|
|
7
|
+
DIALOG_EVENTS
|
|
8
|
+
} from './constants';
|
|
9
|
+
export {
|
|
10
|
+
DialogConfig,
|
|
11
|
+
DialogComponent,
|
|
12
|
+
DialogButton,
|
|
13
|
+
DialogEvent,
|
|
14
|
+
DialogConfirmOptions
|
|
15
|
+
} from './types';
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// src/components/dialog/types.ts
|
|
2
|
+
import { DIALOG_SIZES, DIALOG_ANIMATIONS, DIALOG_FOOTER_ALIGNMENTS, DIALOG_EVENTS } from './constants';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration interface for the Dialog component
|
|
6
|
+
*/
|
|
7
|
+
export interface DialogConfig {
|
|
8
|
+
/** Dialog title text */
|
|
9
|
+
title?: string;
|
|
10
|
+
|
|
11
|
+
/** Dialog subtitle text */
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
|
|
14
|
+
/** Dialog content (text or HTML) */
|
|
15
|
+
content?: string;
|
|
16
|
+
|
|
17
|
+
/** Whether to show close button in header */
|
|
18
|
+
closeButton?: boolean;
|
|
19
|
+
|
|
20
|
+
/** CSS class to add to dialog */
|
|
21
|
+
class?: string;
|
|
22
|
+
|
|
23
|
+
/** Dialog size variant */
|
|
24
|
+
size?: keyof typeof DIALOG_SIZES | DIALOG_SIZES;
|
|
25
|
+
|
|
26
|
+
/** Dialog animation variant */
|
|
27
|
+
animation?: keyof typeof DIALOG_ANIMATIONS | DIALOG_ANIMATIONS;
|
|
28
|
+
|
|
29
|
+
/** Footer buttons alignment */
|
|
30
|
+
footerAlignment?: keyof typeof DIALOG_FOOTER_ALIGNMENTS | DIALOG_FOOTER_ALIGNMENTS;
|
|
31
|
+
|
|
32
|
+
/** Whether dialog is initially open */
|
|
33
|
+
open?: boolean;
|
|
34
|
+
|
|
35
|
+
/** Whether to close when clicking overlay */
|
|
36
|
+
closeOnOverlayClick?: boolean;
|
|
37
|
+
|
|
38
|
+
/** Whether to close when Escape key is pressed */
|
|
39
|
+
closeOnEscape?: boolean;
|
|
40
|
+
|
|
41
|
+
/** Whether dialog should be modal (prevent interaction with background) */
|
|
42
|
+
modal?: boolean;
|
|
43
|
+
|
|
44
|
+
/** Whether to focus the first focusable element when opened */
|
|
45
|
+
autofocus?: boolean;
|
|
46
|
+
|
|
47
|
+
/** Whether to trap focus within dialog when opened */
|
|
48
|
+
trapFocus?: boolean;
|
|
49
|
+
|
|
50
|
+
/** Parent element to append dialog to (defaults to document.body) */
|
|
51
|
+
container?: HTMLElement;
|
|
52
|
+
|
|
53
|
+
/** Footer buttons configuration */
|
|
54
|
+
buttons?: DialogButton[];
|
|
55
|
+
|
|
56
|
+
/** Whether to show a divider between header and content */
|
|
57
|
+
headerDivider?: boolean;
|
|
58
|
+
|
|
59
|
+
/** Whether to show a divider between content and footer */
|
|
60
|
+
footerDivider?: boolean;
|
|
61
|
+
|
|
62
|
+
/** Dialog z-index (defaults to 1000) */
|
|
63
|
+
zIndex?: number;
|
|
64
|
+
|
|
65
|
+
/** Duration of open/close animations in ms */
|
|
66
|
+
animationDuration?: number;
|
|
67
|
+
|
|
68
|
+
/** Event handlers for dialog events */
|
|
69
|
+
on?: {
|
|
70
|
+
[key in keyof typeof DIALOG_EVENTS]?: (event: DialogEvent) => void;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Dialog button configuration
|
|
76
|
+
*/
|
|
77
|
+
export interface DialogButton {
|
|
78
|
+
/** Button text */
|
|
79
|
+
text: string;
|
|
80
|
+
|
|
81
|
+
/** Button variant (uses button component variants) */
|
|
82
|
+
variant?: string;
|
|
83
|
+
|
|
84
|
+
/** Button color */
|
|
85
|
+
color?: string;
|
|
86
|
+
|
|
87
|
+
/** Button size */
|
|
88
|
+
size?: string;
|
|
89
|
+
|
|
90
|
+
/** Button onclick handler */
|
|
91
|
+
onClick?: (event: MouseEvent, dialog: DialogComponent) => void | boolean;
|
|
92
|
+
|
|
93
|
+
/** Close dialog when button is clicked */
|
|
94
|
+
closeDialog?: boolean;
|
|
95
|
+
|
|
96
|
+
/** Autofocus this button when dialog opens */
|
|
97
|
+
autofocus?: boolean;
|
|
98
|
+
|
|
99
|
+
/** Additional button attributes */
|
|
100
|
+
attrs?: Record<string, any>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Dialog event object
|
|
105
|
+
*/
|
|
106
|
+
export interface DialogEvent {
|
|
107
|
+
/** Dialog component instance */
|
|
108
|
+
dialog: DialogComponent;
|
|
109
|
+
|
|
110
|
+
/** Original event if applicable */
|
|
111
|
+
originalEvent?: Event;
|
|
112
|
+
|
|
113
|
+
/** Whether to prevent the default action */
|
|
114
|
+
preventDefault: () => void;
|
|
115
|
+
|
|
116
|
+
/** Whether default action was prevented */
|
|
117
|
+
defaultPrevented: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Dialog component interface
|
|
122
|
+
*/
|
|
123
|
+
export interface DialogComponent {
|
|
124
|
+
/** Dialog element */
|
|
125
|
+
element: HTMLElement;
|
|
126
|
+
|
|
127
|
+
/** Dialog overlay element */
|
|
128
|
+
overlay: HTMLElement;
|
|
129
|
+
|
|
130
|
+
/** Opens the dialog */
|
|
131
|
+
open: () => DialogComponent;
|
|
132
|
+
|
|
133
|
+
/** Closes the dialog */
|
|
134
|
+
close: () => DialogComponent;
|
|
135
|
+
|
|
136
|
+
/** Toggles dialog open/closed state */
|
|
137
|
+
toggle: (open?: boolean) => DialogComponent;
|
|
138
|
+
|
|
139
|
+
/** Checks if dialog is open */
|
|
140
|
+
isOpen: () => boolean;
|
|
141
|
+
|
|
142
|
+
/** Sets dialog title */
|
|
143
|
+
setTitle: (title: string) => DialogComponent;
|
|
144
|
+
|
|
145
|
+
/** Gets dialog title */
|
|
146
|
+
getTitle: () => string;
|
|
147
|
+
|
|
148
|
+
/** Sets dialog subtitle */
|
|
149
|
+
setSubtitle: (subtitle: string) => DialogComponent;
|
|
150
|
+
|
|
151
|
+
/** Gets dialog subtitle */
|
|
152
|
+
getSubtitle: () => string;
|
|
153
|
+
|
|
154
|
+
/** Sets dialog content */
|
|
155
|
+
setContent: (content: string) => DialogComponent;
|
|
156
|
+
|
|
157
|
+
/** Gets dialog content */
|
|
158
|
+
getContent: () => string;
|
|
159
|
+
|
|
160
|
+
/** Adds a button to the dialog footer */
|
|
161
|
+
addButton: (button: DialogButton) => DialogComponent;
|
|
162
|
+
|
|
163
|
+
/** Removes a button by index or text */
|
|
164
|
+
removeButton: (indexOrText: number | string) => DialogComponent;
|
|
165
|
+
|
|
166
|
+
/** Gets all footer buttons */
|
|
167
|
+
getButtons: () => DialogButton[];
|
|
168
|
+
|
|
169
|
+
/** Sets footer alignment */
|
|
170
|
+
setFooterAlignment: (alignment: keyof typeof DIALOG_FOOTER_ALIGNMENTS | DIALOG_FOOTER_ALIGNMENTS) => DialogComponent;
|
|
171
|
+
|
|
172
|
+
/** Sets dialog size */
|
|
173
|
+
setSize: (size: keyof typeof DIALOG_SIZES | DIALOG_SIZES) => DialogComponent;
|
|
174
|
+
|
|
175
|
+
/** Adds event listener */
|
|
176
|
+
on: (event: keyof typeof DIALOG_EVENTS | DIALOG_EVENTS, handler: (event: DialogEvent) => void) => DialogComponent;
|
|
177
|
+
|
|
178
|
+
/** Removes event listener */
|
|
179
|
+
off: (event: keyof typeof DIALOG_EVENTS | DIALOG_EVENTS, handler: (event: DialogEvent) => void) => DialogComponent;
|
|
180
|
+
|
|
181
|
+
/** Gets dialog header element */
|
|
182
|
+
getHeaderElement: () => HTMLElement | null;
|
|
183
|
+
|
|
184
|
+
/** Gets dialog content element */
|
|
185
|
+
getContentElement: () => HTMLElement | null;
|
|
186
|
+
|
|
187
|
+
/** Gets dialog footer element */
|
|
188
|
+
getFooterElement: () => HTMLElement | null;
|
|
189
|
+
|
|
190
|
+
/** Creates a confirmation dialog with Yes/No buttons */
|
|
191
|
+
confirm: (options?: DialogConfirmOptions) => Promise<boolean>;
|
|
192
|
+
|
|
193
|
+
/** Destroys the dialog and removes it from DOM */
|
|
194
|
+
destroy: () => void;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Options for confirmation dialog
|
|
199
|
+
*/
|
|
200
|
+
export interface DialogConfirmOptions {
|
|
201
|
+
/** Confirmation title */
|
|
202
|
+
title?: string;
|
|
203
|
+
|
|
204
|
+
/** Confirmation message */
|
|
205
|
+
message: string;
|
|
206
|
+
|
|
207
|
+
/** Confirm button text */
|
|
208
|
+
confirmText?: string;
|
|
209
|
+
|
|
210
|
+
/** Cancel button text */
|
|
211
|
+
cancelText?: string;
|
|
212
|
+
|
|
213
|
+
/** Confirm button variant */
|
|
214
|
+
confirmVariant?: string;
|
|
215
|
+
|
|
216
|
+
/** Cancel button variant */
|
|
217
|
+
cancelVariant?: string;
|
|
218
|
+
|
|
219
|
+
/** Dialog size */
|
|
220
|
+
size?: keyof typeof DIALOG_SIZES | DIALOG_SIZES;
|
|
221
|
+
}
|
|
@@ -23,7 +23,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
23
23
|
&--linear {
|
|
24
24
|
width: 100%;
|
|
25
25
|
height: 4px; // Default height
|
|
26
|
-
|
|
26
|
+
border-radius: 2px;
|
|
27
27
|
.#{$component}-track {
|
|
28
28
|
position: absolute;
|
|
29
29
|
top: 0;
|
|
@@ -31,6 +31,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
31
31
|
width: 100%;
|
|
32
32
|
height: 100%;
|
|
33
33
|
background-color: t.color('surface-container-highest');
|
|
34
|
+
border-radius: 2px;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
.#{$component}-buffer {
|
|
@@ -41,6 +42,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
41
42
|
width: 0;
|
|
42
43
|
background-color: t.alpha('primary', 0.4);
|
|
43
44
|
transition: width 0.3s ease;
|
|
45
|
+
border-radius: 2px;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
.#{$component}-indicator {
|
|
@@ -51,6 +53,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
51
53
|
width: 0;
|
|
52
54
|
background-color: t.color('primary');
|
|
53
55
|
transition: width 0.3s ease;
|
|
56
|
+
border-radius: 2px;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
// Indeterminate animation for linear progress
|
|
@@ -62,6 +65,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
62
65
|
.#{$component}-indicator {
|
|
63
66
|
width: 30%;
|
|
64
67
|
animation: linear-indeterminate 2s infinite;
|
|
68
|
+
border-radius: 2px;
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -99,6 +103,7 @@ $component: '#{base.$prefix}-progress';
|
|
|
99
103
|
stroke-dasharray: 283; // 2*PI*45
|
|
100
104
|
stroke-dashoffset: 283;
|
|
101
105
|
transition: stroke-dashoffset 0.3s ease;
|
|
106
|
+
stroke-linecap: round;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
// Indeterminate animation for circular progress
|
|
@@ -128,6 +133,8 @@ $component: '#{base.$prefix}-progress';
|
|
|
128
133
|
.#{$component}-track,
|
|
129
134
|
.#{$component}-indicator {
|
|
130
135
|
stroke-width: 3;
|
|
136
|
+
border-radius: 1px;
|
|
137
|
+
linecap: round
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
}
|
|
@@ -152,6 +159,11 @@ $component: '#{base.$prefix}-progress';
|
|
|
152
159
|
&.#{$component}--linear {
|
|
153
160
|
height: 8px;
|
|
154
161
|
|
|
162
|
+
.#{$component}-track,
|
|
163
|
+
.#{$component}-indicator {
|
|
164
|
+
border-radius: 4px;
|
|
165
|
+
}
|
|
166
|
+
|
|
155
167
|
.#{$component}-label {
|
|
156
168
|
font-size: 14px;
|
|
157
169
|
top: 12px;
|
|
@@ -59,7 +59,7 @@ export const withAPI = (options: ApiOptions) =>
|
|
|
59
59
|
if (!options.state.isIndeterminate()) {
|
|
60
60
|
const percent = (value / options.value.getMax()) * 100;
|
|
61
61
|
|
|
62
|
-
if (component.getClass('progress')
|
|
62
|
+
if (component.element.classList.contains(`${component.getClass('progress')}--linear`)) {
|
|
63
63
|
component.indicatorElement.style.width = `${percent}%`;
|
|
64
64
|
} else {
|
|
65
65
|
// Circular progress calculation
|
|
@@ -91,7 +91,7 @@ export const withAPI = (options: ApiOptions) =>
|
|
|
91
91
|
setBuffer(value: number) {
|
|
92
92
|
options.buffer.setBuffer(value);
|
|
93
93
|
|
|
94
|
-
if (component.getClass('progress')
|
|
94
|
+
if (component.element.classList.contains(`${component.getClass('progress')}--linear`)) {
|
|
95
95
|
const bufferElement = component.element.querySelector(`.${component.getClass('progress')}-buffer`);
|
|
96
96
|
if (bufferElement) {
|
|
97
97
|
const percent = (value / options.value.getMax()) * 100;
|
|
@@ -37,7 +37,7 @@ const createCircularProgressDOM = (baseClass: string) => {
|
|
|
37
37
|
track.setAttribute('r', '45');
|
|
38
38
|
track.setAttribute('fill', 'none');
|
|
39
39
|
track.setAttribute('stroke-width', '6');
|
|
40
|
-
track.
|
|
40
|
+
track.setAttribute('class', `${baseClass}-track`); // FIXED: use setAttribute instead of className
|
|
41
41
|
|
|
42
42
|
const indicator = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
43
43
|
indicator.setAttribute('cx', '48');
|
|
@@ -45,7 +45,7 @@ const createCircularProgressDOM = (baseClass: string) => {
|
|
|
45
45
|
indicator.setAttribute('r', '45');
|
|
46
46
|
indicator.setAttribute('fill', 'none');
|
|
47
47
|
indicator.setAttribute('stroke-width', '6');
|
|
48
|
-
indicator.
|
|
48
|
+
indicator.setAttribute('class', `${baseClass}-indicator`); // FIXED: use setAttribute instead of className
|
|
49
49
|
|
|
50
50
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
51
51
|
svg.setAttribute('viewBox', `0 0 ${size} ${size}`);
|
|
@@ -49,6 +49,9 @@ export interface ProgressComponent {
|
|
|
49
49
|
/** The indicator element */
|
|
50
50
|
indicatorElement: HTMLElement;
|
|
51
51
|
|
|
52
|
+
/** The buffer element (for linear variant) */
|
|
53
|
+
bufferElement?: HTMLElement;
|
|
54
|
+
|
|
52
55
|
/** The label element (if enabled) */
|
|
53
56
|
labelElement?: HTMLElement;
|
|
54
57
|
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// src/components/radios/_styles.scss
|
|
2
|
+
@use '../../styles/abstract/base' as base;
|
|
3
|
+
@use '../../styles/abstract/variables' as v;
|
|
4
|
+
@use '../../styles/abstract/functions' as f;
|
|
5
|
+
@use '../../styles/abstract/mixins' as m;
|
|
6
|
+
@use '../../styles/abstract/theme' as t;
|
|
7
|
+
|
|
8
|
+
$component: '#{base.$prefix}-radios';
|
|
9
|
+
|
|
10
|
+
.#{$component} {
|
|
11
|
+
display: flex;
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 0;
|
|
14
|
+
|
|
15
|
+
// Vertical layout (default)
|
|
16
|
+
&--vertical {
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
|
|
19
|
+
.#{$component}-item {
|
|
20
|
+
margin-bottom: 12px;
|
|
21
|
+
|
|
22
|
+
&:last-child {
|
|
23
|
+
margin-bottom: 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Horizontal layout
|
|
29
|
+
&--horizontal {
|
|
30
|
+
flex-direction: row;
|
|
31
|
+
flex-wrap: wrap;
|
|
32
|
+
align-items: flex-start;
|
|
33
|
+
|
|
34
|
+
.#{$component}-item {
|
|
35
|
+
margin-right: 16px;
|
|
36
|
+
|
|
37
|
+
&:last-child {
|
|
38
|
+
margin-right: 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Disabled state
|
|
44
|
+
&--disabled {
|
|
45
|
+
opacity: 0.6;
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Radio item
|
|
50
|
+
&-item {
|
|
51
|
+
position: relative;
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
|
|
55
|
+
&--disabled {
|
|
56
|
+
cursor: not-allowed;
|
|
57
|
+
opacity: 0.6;
|
|
58
|
+
|
|
59
|
+
.#{$component}-label {
|
|
60
|
+
cursor: not-allowed;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Radio input
|
|
66
|
+
&-input {
|
|
67
|
+
position: absolute;
|
|
68
|
+
opacity: 0;
|
|
69
|
+
height: 0;
|
|
70
|
+
width: 0;
|
|
71
|
+
margin: 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
|
|
74
|
+
// Focus styles
|
|
75
|
+
&:focus ~ .#{$component}-label .#{$component}-ripple {
|
|
76
|
+
background-color: t.alpha('primary', 0.12);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Checked styles
|
|
80
|
+
&:checked ~ .#{$component}-label {
|
|
81
|
+
.#{$component}-circle {
|
|
82
|
+
border-color: t.color('primary');
|
|
83
|
+
border-width: 1.5px;
|
|
84
|
+
|
|
85
|
+
&:after {
|
|
86
|
+
transform: translate(-50%, -50%) scale(1);
|
|
87
|
+
opacity: 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Disabled styles
|
|
93
|
+
&:disabled ~ .#{$component}-label {
|
|
94
|
+
.#{$component}-circle {
|
|
95
|
+
border-color: t.alpha('on-surface', 0.38);
|
|
96
|
+
|
|
97
|
+
&:after {
|
|
98
|
+
background-color: t.alpha('on-surface', 0.38);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.#{$component}-text {
|
|
103
|
+
color: t.alpha('on-surface', 0.38);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Radio label
|
|
109
|
+
&-label {
|
|
110
|
+
position: relative;
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
padding: 8px 0;
|
|
115
|
+
height: 48px;
|
|
116
|
+
|
|
117
|
+
// Typography for label
|
|
118
|
+
@include m.typography('body-medium');
|
|
119
|
+
|
|
120
|
+
&--before {
|
|
121
|
+
flex-direction: row-reverse;
|
|
122
|
+
|
|
123
|
+
.#{$component}-text {
|
|
124
|
+
margin-right: 12px;
|
|
125
|
+
margin-left: 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Radio control
|
|
131
|
+
&-control {
|
|
132
|
+
position: relative;
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
width: 40px;
|
|
137
|
+
height: 40px;
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Radio circle
|
|
142
|
+
&-circle {
|
|
143
|
+
position: relative;
|
|
144
|
+
width: 20px;
|
|
145
|
+
height: 20px;
|
|
146
|
+
border: 1.5px solid t.color('outline');
|
|
147
|
+
border-radius: 50%;
|
|
148
|
+
box-sizing: border-box;
|
|
149
|
+
transition: border-color 0.2s ease;
|
|
150
|
+
|
|
151
|
+
&:after {
|
|
152
|
+
content: '';
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: 50%;
|
|
155
|
+
left: 50%;
|
|
156
|
+
transform: translate(-50%, -50%) scale(0);
|
|
157
|
+
width: 12px;
|
|
158
|
+
height: 12px;
|
|
159
|
+
border-radius: 50%;
|
|
160
|
+
background-color: t.color('primary');
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transition: transform 0.15s ease, opacity 0.15s ease;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Ripple effect
|
|
167
|
+
&-ripple {
|
|
168
|
+
position: absolute;
|
|
169
|
+
top: 0;
|
|
170
|
+
left: 0;
|
|
171
|
+
right: 0;
|
|
172
|
+
bottom: 0;
|
|
173
|
+
border-radius: 50%;
|
|
174
|
+
transition: background-color 0.15s ease;
|
|
175
|
+
|
|
176
|
+
// Apply ripple effect on hover
|
|
177
|
+
.#{$component}-input:not(:disabled) ~ .#{$component}-label:hover & {
|
|
178
|
+
background-color: t.alpha('primary', 0.08);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Radio text
|
|
183
|
+
&-text {
|
|
184
|
+
margin-left: 8px;
|
|
185
|
+
line-height: 1.2;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Size variants
|
|
189
|
+
&--small {
|
|
190
|
+
.#{$component}-control {
|
|
191
|
+
width: 36px;
|
|
192
|
+
height: 36px;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.#{$component}-circle {
|
|
196
|
+
width: 18px;
|
|
197
|
+
height: 18px;
|
|
198
|
+
|
|
199
|
+
&:after {
|
|
200
|
+
width: 10px;
|
|
201
|
+
height: 10px;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.#{$component}-label {
|
|
206
|
+
font-size: 13px;
|
|
207
|
+
height: 36px;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
&--large {
|
|
212
|
+
.#{$component}-control {
|
|
213
|
+
width: 48px;
|
|
214
|
+
height: 48px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.#{$component}-circle {
|
|
218
|
+
width: 24px;
|
|
219
|
+
height: 24px;
|
|
220
|
+
|
|
221
|
+
&:after {
|
|
222
|
+
width: 14px;
|
|
223
|
+
height: 14px;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.#{$component}-label {
|
|
228
|
+
font-size: 16px;
|
|
229
|
+
height: 48px;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/components/radios/api.ts
|
|
2
|
+
import { RadiosComponent } from './types';
|
|
3
|
+
|
|
4
|
+
interface ApiOptions {
|
|
5
|
+
disabled: {
|
|
6
|
+
enable: () => void;
|
|
7
|
+
disable: () => void;
|
|
8
|
+
};
|
|
9
|
+
lifecycle: {
|
|
10
|
+
destroy: () => void;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ComponentWithRadio {
|
|
15
|
+
element: HTMLElement;
|
|
16
|
+
radios: any[];
|
|
17
|
+
getValue: () => string;
|
|
18
|
+
setValue: (value: string) => any;
|
|
19
|
+
getSelected: () => any;
|
|
20
|
+
addOption: (option: any) => any;
|
|
21
|
+
removeOption: (value: string) => any;
|
|
22
|
+
enable: () => any;
|
|
23
|
+
disable: () => any;
|
|
24
|
+
enableOption: (value: string) => any;
|
|
25
|
+
disableOption: (value: string) => any;
|
|
26
|
+
getClass: (name: string) => string;
|
|
27
|
+
events: {
|
|
28
|
+
on: (event: string, handler: Function) => void;
|
|
29
|
+
off: (event: string, handler: Function) => void;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Enhances a radios component with API methods
|
|
35
|
+
* @param {ApiOptions} options - API configuration options
|
|
36
|
+
* @returns {Function} Higher-order function that adds API methods to component
|
|
37
|
+
* @internal This is an internal utility for the Radios component
|
|
38
|
+
*/
|
|
39
|
+
export const withAPI = ({ disabled, lifecycle }: ApiOptions) =>
|
|
40
|
+
(component: ComponentWithRadio): RadiosComponent => ({
|
|
41
|
+
...component as any,
|
|
42
|
+
element: component.element,
|
|
43
|
+
radios: component.radios,
|
|
44
|
+
|
|
45
|
+
getValue: () => component.getValue(),
|
|
46
|
+
|
|
47
|
+
setValue(value: string) {
|
|
48
|
+
component.setValue(value);
|
|
49
|
+
return this;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
getSelected: () => component.getSelected(),
|
|
53
|
+
|
|
54
|
+
addOption(option) {
|
|
55
|
+
component.addOption(option);
|
|
56
|
+
return this;
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
removeOption(value: string) {
|
|
60
|
+
component.removeOption(value);
|
|
61
|
+
return this;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
enable() {
|
|
65
|
+
disabled.enable();
|
|
66
|
+
return this;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
disable() {
|
|
70
|
+
disabled.disable();
|
|
71
|
+
return this;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
enableOption(value: string) {
|
|
75
|
+
component.enableOption(value);
|
|
76
|
+
return this;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
disableOption(value: string) {
|
|
80
|
+
component.disableOption(value);
|
|
81
|
+
return this;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
on(event: string, handler: Function) {
|
|
85
|
+
component.events.on(event, handler);
|
|
86
|
+
return this;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
off(event: string, handler: Function) {
|
|
90
|
+
component.events.off(event, handler);
|
|
91
|
+
return this;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
destroy() {
|
|
95
|
+
// First destroy all radio items
|
|
96
|
+
component.radios.forEach(radio => radio.destroy());
|
|
97
|
+
// Then destroy the component
|
|
98
|
+
lifecycle.destroy();
|
|
99
|
+
}
|
|
100
|
+
});
|