@udixio/ui-react 2.9.13 → 2.9.14
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/CHANGELOG.md +26 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +2259 -1814
- package/dist/lib/components/Button.d.ts.map +1 -1
- package/dist/lib/components/Card.d.ts +2 -2
- package/dist/lib/components/Card.d.ts.map +1 -1
- package/dist/lib/components/Checkbox.d.ts +15 -0
- package/dist/lib/components/Checkbox.d.ts.map +1 -0
- package/dist/lib/components/DatePicker.d.ts +9 -0
- package/dist/lib/components/DatePicker.d.ts.map +1 -0
- package/dist/lib/components/FabMenu.d.ts.map +1 -1
- package/dist/lib/components/IconButton.d.ts.map +1 -1
- package/dist/lib/components/TabGroup.d.ts +1 -0
- package/dist/lib/components/TabGroup.d.ts.map +1 -1
- package/dist/lib/components/TabGroupContext.d.ts +1 -0
- package/dist/lib/components/TabGroupContext.d.ts.map +1 -1
- package/dist/lib/components/TabPanel.d.ts +1 -0
- package/dist/lib/components/TabPanel.d.ts.map +1 -1
- package/dist/lib/components/TabPanels.d.ts +1 -0
- package/dist/lib/components/TabPanels.d.ts.map +1 -1
- package/dist/lib/components/TextField.d.ts +4 -4
- package/dist/lib/components/TextField.d.ts.map +1 -1
- package/dist/lib/components/index.d.ts +2 -0
- package/dist/lib/components/index.d.ts.map +1 -1
- package/dist/lib/effects/State.d.ts +3 -1
- package/dist/lib/effects/State.d.ts.map +1 -1
- package/dist/lib/interfaces/card.interface.d.ts +1 -1
- package/dist/lib/interfaces/card.interface.d.ts.map +1 -1
- package/dist/lib/interfaces/checkbox.interface.d.ts +38 -0
- package/dist/lib/interfaces/checkbox.interface.d.ts.map +1 -0
- package/dist/lib/interfaces/date-picker.interface.d.ts +67 -0
- package/dist/lib/interfaces/date-picker.interface.d.ts.map +1 -0
- package/dist/lib/interfaces/icon-button.interface.d.ts +2 -1
- package/dist/lib/interfaces/icon-button.interface.d.ts.map +1 -1
- package/dist/lib/interfaces/index.d.ts +1 -0
- package/dist/lib/interfaces/index.d.ts.map +1 -1
- package/dist/lib/interfaces/text-field.interface.d.ts +7 -4
- package/dist/lib/interfaces/text-field.interface.d.ts.map +1 -1
- package/dist/lib/styles/card.style.d.ts +3 -3
- package/dist/lib/styles/checkbox.style.d.ts +45 -0
- package/dist/lib/styles/checkbox.style.d.ts.map +1 -0
- package/dist/lib/styles/date-picker.style.d.ts +45 -0
- package/dist/lib/styles/date-picker.style.d.ts.map +1 -0
- package/dist/lib/styles/icon-button.style.d.ts +10 -4
- package/dist/lib/styles/icon-button.style.d.ts.map +1 -1
- package/dist/lib/styles/index.d.ts +1 -0
- package/dist/lib/styles/index.d.ts.map +1 -1
- package/dist/lib/styles/text-field.style.d.ts +18 -9
- package/dist/lib/styles/text-field.style.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/lib/components/Button.tsx +1 -0
- package/src/lib/components/Card.tsx +9 -4
- package/src/lib/components/Checkbox.tsx +120 -0
- package/src/lib/components/DatePicker.tsx +432 -0
- package/src/lib/components/FabMenu.tsx +4 -5
- package/src/lib/components/IconButton.tsx +9 -7
- package/src/lib/components/TabGroup.tsx +8 -6
- package/src/lib/components/TabGroupContext.tsx +1 -1
- package/src/lib/components/TabPanel.tsx +1 -0
- package/src/lib/components/TabPanels.tsx +1 -0
- package/src/lib/components/TextField.tsx +95 -108
- package/src/lib/components/index.ts +2 -0
- package/src/lib/effects/State.tsx +4 -1
- package/src/lib/interfaces/card.interface.ts +1 -1
- package/src/lib/interfaces/checkbox.interface.ts +39 -0
- package/src/lib/interfaces/date-picker.interface.ts +79 -0
- package/src/lib/interfaces/icon-button.interface.ts +2 -1
- package/src/lib/interfaces/index.ts +1 -0
- package/src/lib/interfaces/text-field.interface.ts +7 -4
- package/src/lib/styles/checkbox.style.ts +64 -0
- package/src/lib/styles/date-picker.style.ts +43 -0
- package/src/lib/styles/index.ts +1 -0
- package/src/lib/styles/text-field.style.ts +2 -2
- package/src/stories/containment/card.stories.tsx +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useId, useState } from 'react';
|
|
2
2
|
import { Icon } from '../icon';
|
|
3
3
|
import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons';
|
|
4
4
|
import { motion } from 'motion/react';
|
|
5
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
6
5
|
|
|
7
6
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
8
7
|
import { useTextFieldStyle } from '../styles/text-field.style';
|
|
@@ -15,10 +14,10 @@ import { TextFieldInterface } from '../interfaces/text-field.interface';
|
|
|
15
14
|
* @status beta
|
|
16
15
|
* @category Input
|
|
17
16
|
* @devx
|
|
18
|
-
* - `
|
|
19
|
-
* - `
|
|
17
|
+
* - Supports controlled (`value`) and uncontrolled (`defaultValue`) usage.
|
|
18
|
+
* - `multiline` switches to textarea mode.
|
|
20
19
|
* @a11y
|
|
21
|
-
* -
|
|
20
|
+
* - `aria-describedby` links supporting text/error to input.
|
|
22
21
|
*/
|
|
23
22
|
export const TextField = ({
|
|
24
23
|
variant = 'filled',
|
|
@@ -33,51 +32,35 @@ export const TextField = ({
|
|
|
33
32
|
trailingIcon,
|
|
34
33
|
leadingIcon,
|
|
35
34
|
type = 'text',
|
|
36
|
-
|
|
35
|
+
multiline = false,
|
|
37
36
|
autoComplete = 'on',
|
|
38
37
|
onChange,
|
|
39
|
-
value:
|
|
40
|
-
|
|
38
|
+
value: valueProp,
|
|
39
|
+
defaultValue,
|
|
40
|
+
showSupportingText,
|
|
41
|
+
id: idProp,
|
|
42
|
+
style,
|
|
41
43
|
...restProps
|
|
42
44
|
}: ReactProps<TextFieldInterface>) => {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const [showSupportingText, setShowSupportingText] = useState(
|
|
47
|
-
defaultShowSupportingText,
|
|
48
|
-
);
|
|
45
|
+
const generatedId = useId();
|
|
46
|
+
const id = idProp || generatedId;
|
|
47
|
+
const helperTextId = `${id}-helper`;
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const isControlled = valueProp !== undefined;
|
|
50
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? '');
|
|
51
|
+
const value = isControlled ? valueProp : internalValue;
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
setShowErrorIcon(true);
|
|
57
|
-
} else {
|
|
58
|
-
setShowErrorIcon(false);
|
|
59
|
-
}
|
|
60
|
-
}, [errorText]);
|
|
53
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
54
|
+
const [showErrorIcon, setShowErrorIcon] = useState(!!errorText?.length);
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
setShowSupportingText(defaultShowSupportingText);
|
|
65
|
-
} else {
|
|
66
|
-
if (supportingText?.length) {
|
|
67
|
-
setShowSupportingText(true);
|
|
68
|
-
} else {
|
|
69
|
-
setShowSupportingText(false);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}, [showSupportingText, supportingText]);
|
|
56
|
+
const hasSupportingText =
|
|
57
|
+
showSupportingText ?? (!!errorText?.length || !!supportingText?.length);
|
|
73
58
|
|
|
74
59
|
useEffect(() => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
}, [isFocused]);
|
|
60
|
+
setShowErrorIcon(!!errorText?.length);
|
|
61
|
+
}, [errorText]);
|
|
79
62
|
|
|
80
|
-
const inputRef = React.useRef<HTMLInputElement
|
|
63
|
+
const inputRef = React.useRef<HTMLInputElement | HTMLTextAreaElement>(null);
|
|
81
64
|
|
|
82
65
|
const focusInput = () => {
|
|
83
66
|
if (inputRef.current && !isFocused) {
|
|
@@ -87,28 +70,34 @@ export const TextField = ({
|
|
|
87
70
|
|
|
88
71
|
const handleOnFocus = () => {
|
|
89
72
|
setIsFocused(true);
|
|
73
|
+
setShowErrorIcon(false);
|
|
90
74
|
};
|
|
91
75
|
|
|
92
76
|
const handleChange = (
|
|
93
|
-
event: React.ChangeEvent<HTMLInputElement
|
|
77
|
+
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
94
78
|
) => {
|
|
95
79
|
const newValue = event.target.value;
|
|
96
|
-
|
|
80
|
+
|
|
81
|
+
if (!isControlled) {
|
|
82
|
+
setInternalValue(newValue);
|
|
83
|
+
}
|
|
97
84
|
|
|
98
85
|
setShowErrorIcon(false);
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
onChange(newValue);
|
|
87
|
+
if (onChange) {
|
|
88
|
+
onChange(event);
|
|
103
89
|
}
|
|
104
90
|
};
|
|
105
91
|
|
|
106
92
|
const handleBlur = () => {
|
|
107
93
|
setIsFocused(false);
|
|
94
|
+
if (errorText?.length) {
|
|
95
|
+
setShowErrorIcon(true);
|
|
96
|
+
}
|
|
108
97
|
};
|
|
109
98
|
|
|
110
99
|
const styles = useTextFieldStyle({
|
|
111
|
-
showSupportingText,
|
|
100
|
+
showSupportingText: hasSupportingText,
|
|
112
101
|
isFocused,
|
|
113
102
|
showErrorIcon,
|
|
114
103
|
disabled,
|
|
@@ -124,34 +113,26 @@ export const TextField = ({
|
|
|
124
113
|
trailingIcon,
|
|
125
114
|
variant,
|
|
126
115
|
errorText,
|
|
127
|
-
value,
|
|
116
|
+
value: String(value),
|
|
128
117
|
suffix,
|
|
129
|
-
|
|
118
|
+
multiline,
|
|
130
119
|
});
|
|
131
120
|
|
|
132
|
-
const
|
|
121
|
+
const TextComponent = multiline ? TextareaAutosize : 'input';
|
|
122
|
+
const textComponentProps = multiline ? {} : { type };
|
|
133
123
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
TextComponent = TextareaAutosize;
|
|
139
|
-
textComponentProps = {};
|
|
140
|
-
break;
|
|
141
|
-
case 'textAreas':
|
|
142
|
-
TextComponent = 'textarea';
|
|
143
|
-
textComponentProps = {};
|
|
144
|
-
break;
|
|
145
|
-
case 'singleLine':
|
|
146
|
-
default:
|
|
147
|
-
TextComponent = 'input';
|
|
148
|
-
textComponentProps = { type: type };
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
124
|
+
const isFloating =
|
|
125
|
+
isFocused || (typeof value === 'string' && value.length > 0);
|
|
126
|
+
const showLegend = isFloating && variant === 'outlined';
|
|
127
|
+
const showLabel = !showLegend;
|
|
151
128
|
|
|
152
129
|
return (
|
|
153
|
-
<div className={styles.textField} {
|
|
154
|
-
<fieldset
|
|
130
|
+
<div className={styles.textField} style={style}>
|
|
131
|
+
<fieldset
|
|
132
|
+
onClick={focusInput}
|
|
133
|
+
className={styles.content}
|
|
134
|
+
role="presentation"
|
|
135
|
+
>
|
|
155
136
|
<div className={styles.stateLayer}></div>
|
|
156
137
|
{leadingIcon && (
|
|
157
138
|
<div className={styles.leadingIcon}>
|
|
@@ -163,59 +144,64 @@ export const TextField = ({
|
|
|
163
144
|
</div>
|
|
164
145
|
)}
|
|
165
146
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
</motion.span>
|
|
185
|
-
</span>
|
|
186
|
-
</motion.legend>
|
|
187
|
-
)}
|
|
147
|
+
<motion.legend
|
|
148
|
+
aria-hidden="true"
|
|
149
|
+
variants={{
|
|
150
|
+
hidden: { width: 0, padding: 0 },
|
|
151
|
+
visible: { width: 'auto', padding: '0 8px' },
|
|
152
|
+
}}
|
|
153
|
+
initial={showLegend ? 'visible' : 'hidden'}
|
|
154
|
+
animate={showLegend ? 'visible' : 'hidden'}
|
|
155
|
+
className={
|
|
156
|
+
'max-w-full ml-2 px-2 text-body-small h-0 overflow-hidden whitespace-nowrap'
|
|
157
|
+
}
|
|
158
|
+
transition={{ duration: 0.2 }}
|
|
159
|
+
>
|
|
160
|
+
<span className={'transform inline-flex -translate-y-1/2 opacity-0'}>
|
|
161
|
+
{label}
|
|
162
|
+
</span>
|
|
163
|
+
</motion.legend>
|
|
164
|
+
|
|
188
165
|
<div className={'flex-1 relative'}>
|
|
189
|
-
{
|
|
166
|
+
{showLabel && (
|
|
190
167
|
<motion.label
|
|
191
|
-
htmlFor={
|
|
168
|
+
htmlFor={id}
|
|
192
169
|
className={classNames(
|
|
193
|
-
'absolute left-4 transition-all duration-300',
|
|
170
|
+
'absolute left-4 transition-all duration-300 pointer-events-none',
|
|
194
171
|
{
|
|
195
|
-
'text-body-small top-2':
|
|
196
|
-
variant == 'filled' && !(!isFocused && !value.length),
|
|
172
|
+
'text-body-small top-2': variant == 'filled' && isFloating,
|
|
197
173
|
'text-body-large top-1/2 transform -translate-y-1/2': !(
|
|
198
|
-
variant == 'filled' &&
|
|
174
|
+
variant == 'filled' && isFloating
|
|
199
175
|
),
|
|
200
176
|
},
|
|
201
177
|
)}
|
|
202
178
|
transition={{ duration: 0.3 }}
|
|
179
|
+
layoutId={variant === 'outlined' ? `${id}-label` : undefined}
|
|
203
180
|
>
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
181
|
+
<span className={styles.label}>{label}</span>
|
|
182
|
+
</motion.label>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{showLegend && (
|
|
186
|
+
<motion.label
|
|
187
|
+
htmlFor={id}
|
|
188
|
+
className={classNames(
|
|
189
|
+
'absolute left-2 -top-3 px-1 text-body-small z-10',
|
|
190
|
+
styles.label,
|
|
191
|
+
)}
|
|
192
|
+
layoutId={`${id}-label`}
|
|
193
|
+
transition={{ duration: 0.3 }}
|
|
194
|
+
>
|
|
195
|
+
{label}
|
|
211
196
|
</motion.label>
|
|
212
197
|
)}
|
|
198
|
+
|
|
213
199
|
<TextComponent
|
|
214
|
-
ref={inputRef}
|
|
200
|
+
ref={inputRef as any}
|
|
215
201
|
value={value}
|
|
216
202
|
onChange={handleChange}
|
|
217
203
|
className={styles.input}
|
|
218
|
-
id={
|
|
204
|
+
id={id}
|
|
219
205
|
name={name}
|
|
220
206
|
placeholder={isFocused ? (placeholder ?? undefined) : ''}
|
|
221
207
|
onFocus={handleOnFocus}
|
|
@@ -223,8 +209,9 @@ export const TextField = ({
|
|
|
223
209
|
disabled={disabled}
|
|
224
210
|
autoComplete={autoComplete}
|
|
225
211
|
aria-invalid={!!errorText?.length}
|
|
226
|
-
aria-
|
|
212
|
+
aria-describedby={hasSupportingText ? helperTextId : undefined}
|
|
227
213
|
{...textComponentProps}
|
|
214
|
+
{...(restProps as any)}
|
|
228
215
|
/>
|
|
229
216
|
</div>
|
|
230
217
|
|
|
@@ -265,8 +252,8 @@ export const TextField = ({
|
|
|
265
252
|
</div>
|
|
266
253
|
)}
|
|
267
254
|
</fieldset>
|
|
268
|
-
{
|
|
269
|
-
<p className={styles.supportingText}>
|
|
255
|
+
{hasSupportingText && (
|
|
256
|
+
<p className={styles.supportingText} id={helperTextId}>
|
|
270
257
|
{errorText?.length
|
|
271
258
|
? errorText
|
|
272
259
|
: supportingText?.length
|
|
@@ -3,6 +3,7 @@ export * from './Card';
|
|
|
3
3
|
export * from './Carousel';
|
|
4
4
|
export * from './CarouselItem';
|
|
5
5
|
export * from './CarouselItem';
|
|
6
|
+
export * from './Checkbox';
|
|
6
7
|
export * from './Chip';
|
|
7
8
|
export * from './Chips';
|
|
8
9
|
export * from './Divider';
|
|
@@ -24,3 +25,4 @@ export * from './TextField';
|
|
|
24
25
|
export * from './NavigationRailItem';
|
|
25
26
|
export * from './NavigationRail';
|
|
26
27
|
export * from './Tooltip';
|
|
28
|
+
export * from './DatePicker';
|
|
@@ -18,6 +18,7 @@ export interface StateInterface {
|
|
|
18
18
|
| 'state-layer';
|
|
19
19
|
className?: string;
|
|
20
20
|
style?: React.CSSProperties;
|
|
21
|
+
children?: React.ReactNode;
|
|
21
22
|
};
|
|
22
23
|
states: { isClient: boolean };
|
|
23
24
|
elements: ['stateLayer'];
|
|
@@ -27,6 +28,7 @@ export const State = ({
|
|
|
27
28
|
style,
|
|
28
29
|
colorName,
|
|
29
30
|
stateClassName = 'state-ripple-group',
|
|
31
|
+
children,
|
|
30
32
|
className,
|
|
31
33
|
}: ReactProps<StateInterface>) => {
|
|
32
34
|
const ref = useRef<HTMLDivElement>(null);
|
|
@@ -67,6 +69,7 @@ export const State = ({
|
|
|
67
69
|
}}
|
|
68
70
|
>
|
|
69
71
|
{isClient && <RippleEffect triggerRef={groupStateRef} />}
|
|
72
|
+
{children}
|
|
70
73
|
</div>
|
|
71
74
|
);
|
|
72
75
|
};
|
|
@@ -76,8 +79,8 @@ const cardConfig: ClassNameComponent<StateInterface> = ({
|
|
|
76
79
|
stateClassName,
|
|
77
80
|
}) => ({
|
|
78
81
|
stateLayer: classNames([
|
|
79
|
-
stateClassName,
|
|
80
82
|
'w-full top-0 left-0 h-full absolute pointer-events-none overflow-hidden',
|
|
83
|
+
stateClassName,
|
|
81
84
|
]),
|
|
82
85
|
});
|
|
83
86
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
defaultChecked?: boolean;
|
|
6
|
+
indeterminate?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
error?: boolean;
|
|
9
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
10
|
+
name?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
className?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CheckboxStates = {
|
|
18
|
+
isChecked: boolean;
|
|
19
|
+
isIndeterminate: boolean;
|
|
20
|
+
isDisabled: boolean;
|
|
21
|
+
isError: boolean;
|
|
22
|
+
isFocused: boolean;
|
|
23
|
+
isHovered: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface CheckboxInterface {
|
|
27
|
+
type: 'div';
|
|
28
|
+
props: Props;
|
|
29
|
+
states: CheckboxStates;
|
|
30
|
+
elements: [
|
|
31
|
+
'checkbox',
|
|
32
|
+
'input',
|
|
33
|
+
'container',
|
|
34
|
+
'box',
|
|
35
|
+
'icon',
|
|
36
|
+
'stateLayer',
|
|
37
|
+
'ripple',
|
|
38
|
+
];
|
|
39
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type DateRange = [Date | null, Date | null];
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
/**
|
|
7
|
+
* Selection mode: 'single' for one date, 'range' for start/end period.
|
|
8
|
+
* @default 'single'
|
|
9
|
+
*/
|
|
10
|
+
mode?: 'single' | 'range';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The currently selected date(s).
|
|
14
|
+
* Date for single mode, [start, end] tuple for range mode.
|
|
15
|
+
*/
|
|
16
|
+
value?: Date | DateRange | null;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Default selected date(s) for uncontrolled usage.
|
|
20
|
+
*/
|
|
21
|
+
defaultValue?: Date | DateRange | null;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when selection changes.
|
|
25
|
+
* Returns Date in single mode, DateRange in range mode.
|
|
26
|
+
*/
|
|
27
|
+
onChange?: (value: any) => void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Minimum selectable date.
|
|
31
|
+
*/
|
|
32
|
+
minDate?: Date;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Maximum selectable date.
|
|
36
|
+
*/
|
|
37
|
+
maxDate?: Date;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Disables specific dates.
|
|
41
|
+
*/
|
|
42
|
+
shouldDisableDate?: (date: Date) => boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Locale for formatting dates.
|
|
46
|
+
*/
|
|
47
|
+
locale?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* First day of the week (0=Sunday, 1=Monday).
|
|
51
|
+
* @default 0
|
|
52
|
+
*/
|
|
53
|
+
weekStartDay?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
54
|
+
|
|
55
|
+
className?: string;
|
|
56
|
+
style?: React.CSSProperties;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type DatePickerStates = {
|
|
60
|
+
// Can be expanded if we need specific state-driven styles exposed to the config
|
|
61
|
+
hasSelected: boolean;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export interface DatePickerInterface {
|
|
65
|
+
type: 'div';
|
|
66
|
+
props: Props;
|
|
67
|
+
states: DatePickerStates;
|
|
68
|
+
elements: [
|
|
69
|
+
'datePicker',
|
|
70
|
+
'header',
|
|
71
|
+
'monthNav',
|
|
72
|
+
'monthLabel',
|
|
73
|
+
'weekDays',
|
|
74
|
+
'weekDay',
|
|
75
|
+
'daysGrid',
|
|
76
|
+
'dayCell',
|
|
77
|
+
'dayButton', // The interactive part of the day
|
|
78
|
+
];
|
|
79
|
+
}
|
|
@@ -7,7 +7,7 @@ import { Icon } from '../icon';
|
|
|
7
7
|
type Props = {
|
|
8
8
|
label?: string;
|
|
9
9
|
children?: string;
|
|
10
|
-
icon
|
|
10
|
+
icon?: Icon;
|
|
11
11
|
size?: 'xSmall' | 'small' | 'medium' | 'large' | 'xLarge';
|
|
12
12
|
width?: 'default' | 'narrow' | 'wide';
|
|
13
13
|
iconSelected?: IconDefinition;
|
|
@@ -15,6 +15,7 @@ type Props = {
|
|
|
15
15
|
variant?: IconButtonVariant;
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
activated?: boolean;
|
|
18
|
+
title?: string | null;
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* The shape of the button defines whether it is squared or rounded.
|
|
@@ -2,6 +2,7 @@ export * from './button.interface';
|
|
|
2
2
|
export * from './card.interface';
|
|
3
3
|
export * from './carousel-item.interface';
|
|
4
4
|
export * from './carousel.interface';
|
|
5
|
+
export * from './checkbox.interface';
|
|
5
6
|
export * from './chip.interface';
|
|
6
7
|
export * from './chips.interface';
|
|
7
8
|
export * from './divider.interface';
|
|
@@ -6,22 +6,25 @@ export type TextFieldVariant = 'filled' | 'outlined';
|
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
placeholder?: string;
|
|
9
|
-
name
|
|
9
|
+
name?: string;
|
|
10
10
|
label: string;
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
errorText?: string | null;
|
|
13
13
|
supportingText?: string;
|
|
14
14
|
trailingIcon?: React.ReactElement<typeof IconButton> | Icon;
|
|
15
15
|
leadingIcon?: React.ReactElement<typeof IconButton> | Icon;
|
|
16
|
-
onChange?:
|
|
16
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
|
17
17
|
showSupportingText?: boolean;
|
|
18
18
|
suffix?: string;
|
|
19
19
|
|
|
20
|
-
value
|
|
20
|
+
value?: string;
|
|
21
|
+
defaultValue?: string;
|
|
22
|
+
id?: string;
|
|
23
|
+
style?: React.CSSProperties;
|
|
21
24
|
variant?: TextFieldVariant;
|
|
22
25
|
type?: 'text' | 'password' | 'number';
|
|
23
26
|
autoComplete?: 'on' | 'off' | string;
|
|
24
|
-
|
|
27
|
+
multiline?: boolean;
|
|
25
28
|
};
|
|
26
29
|
export type TextFieldStates = {
|
|
27
30
|
isFocused: boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ClassNameComponent,
|
|
3
|
+
classNames,
|
|
4
|
+
createUseClassNames,
|
|
5
|
+
defaultClassNames,
|
|
6
|
+
} from '../utils';
|
|
7
|
+
import { CheckboxInterface } from '../interfaces/checkbox.interface';
|
|
8
|
+
|
|
9
|
+
const checkboxConfig: ClassNameComponent<CheckboxInterface> = ({
|
|
10
|
+
isChecked,
|
|
11
|
+
isIndeterminate,
|
|
12
|
+
isDisabled,
|
|
13
|
+
isError,
|
|
14
|
+
isFocused,
|
|
15
|
+
isHovered,
|
|
16
|
+
}) => ({
|
|
17
|
+
checkbox: classNames(
|
|
18
|
+
'inline-flex items-center justify-center relative size-4.5 ',
|
|
19
|
+
{
|
|
20
|
+
'pointer-events-none opacity-[0.38]': isDisabled,
|
|
21
|
+
},
|
|
22
|
+
),
|
|
23
|
+
input: classNames(
|
|
24
|
+
'absolute inset-0 w-full h-full opacity-0 z-10 cursor-pointer',
|
|
25
|
+
),
|
|
26
|
+
container: classNames(
|
|
27
|
+
'relative flex items-center justify-center w-[18px] h-[18px] ',
|
|
28
|
+
),
|
|
29
|
+
box: classNames(
|
|
30
|
+
'absolute left-1/2 top-1/2 -translate-1/2 to rounded-[2px] size-4 border-2 transition-colors duration-200',
|
|
31
|
+
// Unchecked state (Border only)
|
|
32
|
+
!isChecked &&
|
|
33
|
+
!isIndeterminate && {
|
|
34
|
+
'border-on-surface-variant': !isError && !isDisabled,
|
|
35
|
+
'border-error': isError && !isDisabled,
|
|
36
|
+
'border-on-surface': isDisabled,
|
|
37
|
+
},
|
|
38
|
+
// Checked or Indeterminate state (Filled)
|
|
39
|
+
(isChecked || isIndeterminate) && {
|
|
40
|
+
'bg-primary border-primary': !isError && !isDisabled,
|
|
41
|
+
'bg-error border-error': isError && !isDisabled,
|
|
42
|
+
'bg-on-surface border-on-surface': isDisabled,
|
|
43
|
+
},
|
|
44
|
+
),
|
|
45
|
+
icon: classNames(
|
|
46
|
+
'z-10 relative text-on-primary w-full h-full flex items-center justify-center pointer-events-none',
|
|
47
|
+
{
|
|
48
|
+
'text-on-error': isError && !isDisabled,
|
|
49
|
+
'text-surface': isDisabled, // Usually on-surface with opacity against on-surface bg? No, checked disabled is on-surface bg with surface icon usually.
|
|
50
|
+
},
|
|
51
|
+
),
|
|
52
|
+
stateLayer:
|
|
53
|
+
'size-10 state-ripple-group-[checkbox] rounded-full cursor-pointer pointer-events-auto absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const checkboxStyle = defaultClassNames<CheckboxInterface>(
|
|
57
|
+
'checkbox',
|
|
58
|
+
checkboxConfig,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export const useCheckboxStyle = createUseClassNames<CheckboxInterface>(
|
|
62
|
+
'checkbox',
|
|
63
|
+
checkboxConfig,
|
|
64
|
+
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ClassNameComponent,
|
|
3
|
+
classNames,
|
|
4
|
+
createUseClassNames,
|
|
5
|
+
defaultClassNames,
|
|
6
|
+
} from '../utils';
|
|
7
|
+
import { DatePickerInterface } from '../interfaces/date-picker.interface';
|
|
8
|
+
|
|
9
|
+
const datePickerConfig: ClassNameComponent<DatePickerInterface> = ({
|
|
10
|
+
hasSelected,
|
|
11
|
+
}) => ({
|
|
12
|
+
datePicker: classNames(
|
|
13
|
+
'inline-flex flex-col bg-surface-container-high rounded-[28px] p-3 shadow-sm select-none', // Using shadow-sm as placeholder for elevation
|
|
14
|
+
'min-w-[320px]',
|
|
15
|
+
),
|
|
16
|
+
header: classNames('flex items-center justify-between h-12 mb-2 px-2'),
|
|
17
|
+
monthNav: classNames(
|
|
18
|
+
'flex items-center justify-center w-10 h-10 rounded-full text-on-surface-variant hover:bg-on-surface-variant/8 transition-colors cursor-pointer',
|
|
19
|
+
),
|
|
20
|
+
monthLabel: classNames(
|
|
21
|
+
'text-label-large text-on-surface font-bold capitalize',
|
|
22
|
+
),
|
|
23
|
+
weekDays: classNames('grid grid-cols-7 mb-2'),
|
|
24
|
+
weekDay: classNames(
|
|
25
|
+
'h-10 flex items-center justify-center text-body-small text-on-surface-variant',
|
|
26
|
+
),
|
|
27
|
+
daysGrid: classNames('grid grid-cols-7 row-auto gap-y-2'),
|
|
28
|
+
dayCell: classNames('flex items-center justify-center h-10 p-0 relative'),
|
|
29
|
+
dayButton: classNames(
|
|
30
|
+
'w-10 h-10 rounded-full flex items-center justify-center text-body-large transition-all duration-200 relative overflow-hidden z-10 outline-none',
|
|
31
|
+
// Base style is implicit text-on-surface
|
|
32
|
+
),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const datePickerStyle = defaultClassNames<DatePickerInterface>(
|
|
36
|
+
'datePicker',
|
|
37
|
+
datePickerConfig,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const useDatePickerStyle = createUseClassNames<DatePickerInterface>(
|
|
41
|
+
'datePicker',
|
|
42
|
+
datePickerConfig,
|
|
43
|
+
);
|
package/src/lib/styles/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './button.style';
|
|
|
2
2
|
export * from './card.style';
|
|
3
3
|
export * from './carousel-item.style';
|
|
4
4
|
export * from './carousel.style';
|
|
5
|
+
export * from './checkbox.style';
|
|
5
6
|
export * from './chip.style';
|
|
6
7
|
export * from './chips.style';
|
|
7
8
|
export * from './divider.style';
|
|
@@ -16,14 +16,14 @@ const textFieldConfig: ClassNameComponent<TextFieldInterface> = ({
|
|
|
16
16
|
isFocused,
|
|
17
17
|
value,
|
|
18
18
|
suffix,
|
|
19
|
-
|
|
19
|
+
multiline,
|
|
20
20
|
}) => ({
|
|
21
21
|
textField: classNames({
|
|
22
22
|
'opacity-[.38]': disabled,
|
|
23
23
|
}),
|
|
24
24
|
content: classNames(
|
|
25
25
|
'group/text-field transition-border duration-200 relative flex items-center ',
|
|
26
|
-
{ 'h-14':
|
|
26
|
+
{ 'h-14': !multiline },
|
|
27
27
|
{
|
|
28
28
|
'border-on-surface-variant':
|
|
29
29
|
!errorText?.length && !isFocused && variant == 'filled',
|