@justin_evo/evo-ui 1.2.0 → 1.2.1
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/LICENSE +21 -21
- package/README.md +70 -70
- package/dist/declarations.d.ts +6 -6
- package/package.json +52 -52
- package/src/Alert/Alert.tsx +49 -49
- package/src/AutoComplete/AutoComplete.tsx +810 -810
- package/src/Badge/Badge.tsx +53 -53
- package/src/Breadcrumb/Breadcrumb.tsx +53 -53
- package/src/Button/Button.tsx +125 -125
- package/src/Card/Card.tsx +257 -257
- package/src/Checkbox/Checkbox.tsx +59 -59
- package/src/CommandPalette/CommandPalette.tsx +185 -185
- package/src/Container/Container.tsx +31 -31
- package/src/Divider/Divider.tsx +31 -31
- package/src/Form/Form.tsx +185 -185
- package/src/Grid/Grid.tsx +66 -66
- package/src/ImageCropper/ImageCropper.tsx +911 -911
- package/src/Input/Input.tsx +74 -74
- package/src/Modal/Modal.tsx +77 -77
- package/src/Nav/Nav.tsx +708 -708
- package/src/Notification/Notification.tsx +1503 -1503
- package/src/Pagination/Pagination.tsx +76 -76
- package/src/Radio/Radio.tsx +69 -69
- package/src/RichTextArea/RichTextArea.tsx +886 -886
- package/src/Select/Select.tsx +515 -515
- package/src/Skeleton/Skeleton.tsx +70 -70
- package/src/Stack/Stack.tsx +52 -52
- package/src/Table/Table.tsx +335 -335
- package/src/Tabs/Tabs.tsx +90 -90
- package/src/Theme/ThemeProvider.tsx +253 -253
- package/src/Theme/ThemeToggle.tsx +79 -79
- package/src/Toggle/Toggle.tsx +48 -48
- package/src/Tooltip/Tooltip.tsx +38 -38
- package/src/TopNav/TopNav.tsx +1163 -1163
- package/src/TreeSelect/TreeSelect.tsx +825 -825
- package/src/css/alert.module.scss +93 -93
- package/src/css/autocomplete.module.scss +416 -416
- package/src/css/badge.module.scss +82 -82
- package/src/css/base/_color.scss +159 -159
- package/src/css/base/_theme.scss +237 -237
- package/src/css/base/_variables.scss +161 -161
- package/src/css/breadcrumb.module.scss +50 -50
- package/src/css/button.module.scss +385 -385
- package/src/css/card.module.scss +217 -217
- package/src/css/checkbox.module.scss +123 -123
- package/src/css/commandpalette.module.scss +211 -211
- package/src/css/container.module.scss +18 -18
- package/src/css/divider.module.scss +41 -41
- package/src/css/form.module.scss +245 -245
- package/src/css/imagecropper.module.scss +397 -397
- package/src/css/input.module.scss +89 -89
- package/src/css/modal.module.scss +105 -105
- package/src/css/nav.module.scss +494 -494
- package/src/css/notification.module.scss +691 -691
- package/src/css/pagination.module.scss +63 -63
- package/src/css/radio.module.scss +89 -89
- package/src/css/richtextarea.module.scss +307 -307
- package/src/css/select.module.scss +525 -525
- package/src/css/skeleton.module.scss +30 -30
- package/src/css/table.module.scss +386 -386
- package/src/css/tabs.module.scss +63 -63
- package/src/css/theme-toggle.module.scss +83 -83
- package/src/css/toggle.module.scss +54 -54
- package/src/css/tooltip.module.scss +97 -97
- package/src/css/topnav.module.scss +568 -568
- package/src/css/treeselect.module.scss +558 -558
- package/src/css/utilities/_borders.scss +111 -111
- package/src/css/utilities/_colors.scss +66 -66
- package/src/css/utilities/_effects.scss +216 -216
- package/src/css/utilities/_layout.scss +181 -181
- package/src/css/utilities/_position.scss +75 -75
- package/src/css/utilities/_sizing.scss +138 -138
- package/src/css/utilities/_spacing.scss +99 -99
- package/src/css/utilities/_typography.scss +121 -121
- package/src/css/utilities/index.scss +24 -24
- package/src/declarations.d.ts +6 -6
- package/src/index.ts +60 -60
package/src/Card/Card.tsx
CHANGED
|
@@ -1,257 +1,257 @@
|
|
|
1
|
-
import {
|
|
2
|
-
forwardRef,
|
|
3
|
-
createElement,
|
|
4
|
-
type AnchorHTMLAttributes,
|
|
5
|
-
type ButtonHTMLAttributes,
|
|
6
|
-
type CSSProperties,
|
|
7
|
-
type HTMLAttributes,
|
|
8
|
-
type ReactNode,
|
|
9
|
-
type Ref,
|
|
10
|
-
} from 'react';
|
|
11
|
-
import styles from '../css/card.module.scss';
|
|
12
|
-
|
|
13
|
-
// ============================================================
|
|
14
|
-
// EvoCard
|
|
15
|
-
// ------------------------------------------------------------
|
|
16
|
-
// Compose-based card primitive. The API takes after Radix:
|
|
17
|
-
// <EvoCard.Root> is the documented surface; <EvoCard> is kept
|
|
18
|
-
// as an ergonomic alias that forwards to Root.
|
|
19
|
-
//
|
|
20
|
-
// Visual axes are deliberately minimal — three structural
|
|
21
|
-
// variants (elevated / outlined / ghost). All colour comes
|
|
22
|
-
// from semantic theme tokens, so dark mode is automatic.
|
|
23
|
-
//
|
|
24
|
-
// When `interactive` is set the root is rendered as a real
|
|
25
|
-
// <button type="button"> or, if `href` is provided, a real
|
|
26
|
-
// <a>. We never use <div onClick> — keyboard users and screen
|
|
27
|
-
// readers get first-class treatment.
|
|
28
|
-
// ============================================================
|
|
29
|
-
|
|
30
|
-
export type EvoCardVariant = 'elevated' | 'outlined' | 'ghost';
|
|
31
|
-
export type EvoCardOrientation = 'vertical' | 'horizontal' | 'responsive';
|
|
32
|
-
export type EvoCardHeadingLevel = 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
33
|
-
|
|
34
|
-
interface EvoCardRootBase {
|
|
35
|
-
variant?: EvoCardVariant;
|
|
36
|
-
orientation?: EvoCardOrientation;
|
|
37
|
-
children: ReactNode;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface EvoCardRootStaticProps
|
|
41
|
-
extends EvoCardRootBase,
|
|
42
|
-
Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
43
|
-
interactive?: false;
|
|
44
|
-
href?: never;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface EvoCardRootButtonProps
|
|
48
|
-
extends EvoCardRootBase,
|
|
49
|
-
Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type'> {
|
|
50
|
-
interactive: true;
|
|
51
|
-
href?: never;
|
|
52
|
-
/** @default 'button' (never auto-submits) */
|
|
53
|
-
type?: 'button' | 'submit' | 'reset';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
interface EvoCardRootAnchorProps
|
|
57
|
-
extends EvoCardRootBase,
|
|
58
|
-
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children' | 'href'> {
|
|
59
|
-
interactive: true;
|
|
60
|
-
href: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export type EvoCardRootProps =
|
|
64
|
-
| EvoCardRootStaticProps
|
|
65
|
-
| EvoCardRootButtonProps
|
|
66
|
-
| EvoCardRootAnchorProps;
|
|
67
|
-
|
|
68
|
-
function cx(...parts: Array<string | undefined | false | null>) {
|
|
69
|
-
return parts.filter(Boolean).join(' ');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function rootClasses(
|
|
73
|
-
variant: EvoCardVariant,
|
|
74
|
-
orientation: EvoCardOrientation,
|
|
75
|
-
interactive: boolean,
|
|
76
|
-
className?: string,
|
|
77
|
-
) {
|
|
78
|
-
return cx(
|
|
79
|
-
styles.root,
|
|
80
|
-
styles[variant],
|
|
81
|
-
styles[`orient-${orientation}`],
|
|
82
|
-
interactive && styles.interactive,
|
|
83
|
-
className,
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export const EvoCardRoot = forwardRef<HTMLElement, EvoCardRootProps>(
|
|
88
|
-
function EvoCardRoot(props, ref) {
|
|
89
|
-
const {
|
|
90
|
-
variant = 'elevated',
|
|
91
|
-
orientation = 'vertical',
|
|
92
|
-
interactive,
|
|
93
|
-
className,
|
|
94
|
-
children,
|
|
95
|
-
...rest
|
|
96
|
-
} = props as EvoCardRootStaticProps &
|
|
97
|
-
Partial<EvoCardRootAnchorProps> &
|
|
98
|
-
Partial<EvoCardRootButtonProps> & { interactive?: boolean };
|
|
99
|
-
|
|
100
|
-
const isInteractive = interactive === true;
|
|
101
|
-
const classes = rootClasses(variant, orientation, isInteractive, className);
|
|
102
|
-
|
|
103
|
-
if (isInteractive && typeof (rest as { href?: string }).href === 'string') {
|
|
104
|
-
const anchorRest = rest as AnchorHTMLAttributes<HTMLAnchorElement>;
|
|
105
|
-
return (
|
|
106
|
-
<a ref={ref as Ref<HTMLAnchorElement>} className={classes} {...anchorRest}>
|
|
107
|
-
{children}
|
|
108
|
-
</a>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (isInteractive) {
|
|
113
|
-
const { type = 'button', ...buttonRest } =
|
|
114
|
-
rest as ButtonHTMLAttributes<HTMLButtonElement>;
|
|
115
|
-
return (
|
|
116
|
-
<button
|
|
117
|
-
ref={ref as Ref<HTMLButtonElement>}
|
|
118
|
-
type={type}
|
|
119
|
-
className={classes}
|
|
120
|
-
{...buttonRest}
|
|
121
|
-
>
|
|
122
|
-
{children}
|
|
123
|
-
</button>
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<div
|
|
129
|
-
ref={ref as Ref<HTMLDivElement>}
|
|
130
|
-
className={classes}
|
|
131
|
-
{...(rest as HTMLAttributes<HTMLDivElement>)}
|
|
132
|
-
>
|
|
133
|
-
{children}
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
EvoCardRoot.displayName = 'EvoCardRoot';
|
|
139
|
-
|
|
140
|
-
// ----- Section slots -----
|
|
141
|
-
|
|
142
|
-
export type EvoCardHeaderProps = HTMLAttributes<HTMLDivElement>;
|
|
143
|
-
export type EvoCardBodyProps = HTMLAttributes<HTMLDivElement>;
|
|
144
|
-
export type EvoCardFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
145
|
-
|
|
146
|
-
export const EvoCardHeader = forwardRef<HTMLDivElement, EvoCardHeaderProps>(
|
|
147
|
-
function EvoCardHeader({ className, ...rest }, ref) {
|
|
148
|
-
return <div ref={ref} className={cx(styles.header, className)} {...rest} />;
|
|
149
|
-
},
|
|
150
|
-
);
|
|
151
|
-
EvoCardHeader.displayName = 'EvoCardHeader';
|
|
152
|
-
|
|
153
|
-
export const EvoCardBody = forwardRef<HTMLDivElement, EvoCardBodyProps>(
|
|
154
|
-
function EvoCardBody({ className, ...rest }, ref) {
|
|
155
|
-
return <div ref={ref} className={cx(styles.body, className)} {...rest} />;
|
|
156
|
-
},
|
|
157
|
-
);
|
|
158
|
-
EvoCardBody.displayName = 'EvoCardBody';
|
|
159
|
-
|
|
160
|
-
export const EvoCardFooter = forwardRef<HTMLDivElement, EvoCardFooterProps>(
|
|
161
|
-
function EvoCardFooter({ className, ...rest }, ref) {
|
|
162
|
-
return <div ref={ref} className={cx(styles.footer, className)} {...rest} />;
|
|
163
|
-
},
|
|
164
|
-
);
|
|
165
|
-
EvoCardFooter.displayName = 'EvoCardFooter';
|
|
166
|
-
|
|
167
|
-
// ----- Title (configurable heading level) -----
|
|
168
|
-
|
|
169
|
-
export interface EvoCardTitleProps extends HTMLAttributes<HTMLHeadingElement> {
|
|
170
|
-
/** Semantic level. Visual size is fixed by the stylesheet. @default 'h3' */
|
|
171
|
-
as?: EvoCardHeadingLevel;
|
|
172
|
-
children: ReactNode;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export const EvoCardTitle = forwardRef<HTMLHeadingElement, EvoCardTitleProps>(
|
|
176
|
-
function EvoCardTitle({ as = 'h3', className, children, ...rest }, ref) {
|
|
177
|
-
return createElement(
|
|
178
|
-
as,
|
|
179
|
-
{ ref, className: cx(styles.title, className), ...rest },
|
|
180
|
-
children,
|
|
181
|
-
);
|
|
182
|
-
},
|
|
183
|
-
);
|
|
184
|
-
EvoCardTitle.displayName = 'EvoCardTitle';
|
|
185
|
-
|
|
186
|
-
// ----- Description -----
|
|
187
|
-
|
|
188
|
-
export type EvoCardDescriptionProps = HTMLAttributes<HTMLParagraphElement>;
|
|
189
|
-
|
|
190
|
-
export const EvoCardDescription = forwardRef<
|
|
191
|
-
HTMLParagraphElement,
|
|
192
|
-
EvoCardDescriptionProps
|
|
193
|
-
>(function EvoCardDescription({ className, ...rest }, ref) {
|
|
194
|
-
return <p ref={ref} className={cx(styles.description, className)} {...rest} />;
|
|
195
|
-
});
|
|
196
|
-
EvoCardDescription.displayName = 'EvoCardDescription';
|
|
197
|
-
|
|
198
|
-
// ----- Media -----
|
|
199
|
-
|
|
200
|
-
export interface EvoCardMediaProps extends HTMLAttributes<HTMLDivElement> {
|
|
201
|
-
/** Convenience: renders an <img>. Omit and pass children for custom media. */
|
|
202
|
-
src?: string;
|
|
203
|
-
/** Required when `src` is set. Empty string marks the image as decorative. */
|
|
204
|
-
alt?: string;
|
|
205
|
-
/** e.g. 16/9 or '4/3'. Applied when no explicit dimensions are given. */
|
|
206
|
-
aspectRatio?: number | string;
|
|
207
|
-
children?: ReactNode;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export const EvoCardMedia = forwardRef<HTMLDivElement, EvoCardMediaProps>(
|
|
211
|
-
function EvoCardMedia(
|
|
212
|
-
{ src, alt, aspectRatio, className, style, children, ...rest },
|
|
213
|
-
ref,
|
|
214
|
-
) {
|
|
215
|
-
const mediaStyle: CSSProperties | undefined =
|
|
216
|
-
aspectRatio != null
|
|
217
|
-
? { aspectRatio: aspectRatio as CSSProperties['aspectRatio'], ...style }
|
|
218
|
-
: style;
|
|
219
|
-
return (
|
|
220
|
-
<div
|
|
221
|
-
ref={ref}
|
|
222
|
-
className={cx(styles.media, className)}
|
|
223
|
-
style={mediaStyle}
|
|
224
|
-
{...rest}
|
|
225
|
-
>
|
|
226
|
-
{src ? <img src={src} alt={alt ?? ''} className={styles.mediaImg} /> : children}
|
|
227
|
-
</div>
|
|
228
|
-
);
|
|
229
|
-
},
|
|
230
|
-
);
|
|
231
|
-
EvoCardMedia.displayName = 'EvoCardMedia';
|
|
232
|
-
|
|
233
|
-
// ----- Compound export -----
|
|
234
|
-
//
|
|
235
|
-
// `EvoCard` is callable (forwards to Root) AND carries the sub-components
|
|
236
|
-
// as static properties so consumers can write either:
|
|
237
|
-
// <EvoCard variant="outlined">…</EvoCard>
|
|
238
|
-
// <EvoCard.Root variant="outlined">…</EvoCard.Root>
|
|
239
|
-
|
|
240
|
-
type EvoCardComponent = typeof EvoCardRoot & {
|
|
241
|
-
Root: typeof EvoCardRoot;
|
|
242
|
-
Header: typeof EvoCardHeader;
|
|
243
|
-
Title: typeof EvoCardTitle;
|
|
244
|
-
Description: typeof EvoCardDescription;
|
|
245
|
-
Body: typeof EvoCardBody;
|
|
246
|
-
Footer: typeof EvoCardFooter;
|
|
247
|
-
Media: typeof EvoCardMedia;
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
export const EvoCard = EvoCardRoot as EvoCardComponent;
|
|
251
|
-
EvoCard.Root = EvoCardRoot;
|
|
252
|
-
EvoCard.Header = EvoCardHeader;
|
|
253
|
-
EvoCard.Title = EvoCardTitle;
|
|
254
|
-
EvoCard.Description = EvoCardDescription;
|
|
255
|
-
EvoCard.Body = EvoCardBody;
|
|
256
|
-
EvoCard.Footer = EvoCardFooter;
|
|
257
|
-
EvoCard.Media = EvoCardMedia;
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
createElement,
|
|
4
|
+
type AnchorHTMLAttributes,
|
|
5
|
+
type ButtonHTMLAttributes,
|
|
6
|
+
type CSSProperties,
|
|
7
|
+
type HTMLAttributes,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
type Ref,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import styles from '../css/card.module.scss';
|
|
12
|
+
|
|
13
|
+
// ============================================================
|
|
14
|
+
// EvoCard
|
|
15
|
+
// ------------------------------------------------------------
|
|
16
|
+
// Compose-based card primitive. The API takes after Radix:
|
|
17
|
+
// <EvoCard.Root> is the documented surface; <EvoCard> is kept
|
|
18
|
+
// as an ergonomic alias that forwards to Root.
|
|
19
|
+
//
|
|
20
|
+
// Visual axes are deliberately minimal — three structural
|
|
21
|
+
// variants (elevated / outlined / ghost). All colour comes
|
|
22
|
+
// from semantic theme tokens, so dark mode is automatic.
|
|
23
|
+
//
|
|
24
|
+
// When `interactive` is set the root is rendered as a real
|
|
25
|
+
// <button type="button"> or, if `href` is provided, a real
|
|
26
|
+
// <a>. We never use <div onClick> — keyboard users and screen
|
|
27
|
+
// readers get first-class treatment.
|
|
28
|
+
// ============================================================
|
|
29
|
+
|
|
30
|
+
export type EvoCardVariant = 'elevated' | 'outlined' | 'ghost';
|
|
31
|
+
export type EvoCardOrientation = 'vertical' | 'horizontal' | 'responsive';
|
|
32
|
+
export type EvoCardHeadingLevel = 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
33
|
+
|
|
34
|
+
interface EvoCardRootBase {
|
|
35
|
+
variant?: EvoCardVariant;
|
|
36
|
+
orientation?: EvoCardOrientation;
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface EvoCardRootStaticProps
|
|
41
|
+
extends EvoCardRootBase,
|
|
42
|
+
Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
43
|
+
interactive?: false;
|
|
44
|
+
href?: never;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface EvoCardRootButtonProps
|
|
48
|
+
extends EvoCardRootBase,
|
|
49
|
+
Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type'> {
|
|
50
|
+
interactive: true;
|
|
51
|
+
href?: never;
|
|
52
|
+
/** @default 'button' (never auto-submits) */
|
|
53
|
+
type?: 'button' | 'submit' | 'reset';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface EvoCardRootAnchorProps
|
|
57
|
+
extends EvoCardRootBase,
|
|
58
|
+
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children' | 'href'> {
|
|
59
|
+
interactive: true;
|
|
60
|
+
href: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type EvoCardRootProps =
|
|
64
|
+
| EvoCardRootStaticProps
|
|
65
|
+
| EvoCardRootButtonProps
|
|
66
|
+
| EvoCardRootAnchorProps;
|
|
67
|
+
|
|
68
|
+
function cx(...parts: Array<string | undefined | false | null>) {
|
|
69
|
+
return parts.filter(Boolean).join(' ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function rootClasses(
|
|
73
|
+
variant: EvoCardVariant,
|
|
74
|
+
orientation: EvoCardOrientation,
|
|
75
|
+
interactive: boolean,
|
|
76
|
+
className?: string,
|
|
77
|
+
) {
|
|
78
|
+
return cx(
|
|
79
|
+
styles.root,
|
|
80
|
+
styles[variant],
|
|
81
|
+
styles[`orient-${orientation}`],
|
|
82
|
+
interactive && styles.interactive,
|
|
83
|
+
className,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const EvoCardRoot = forwardRef<HTMLElement, EvoCardRootProps>(
|
|
88
|
+
function EvoCardRoot(props, ref) {
|
|
89
|
+
const {
|
|
90
|
+
variant = 'elevated',
|
|
91
|
+
orientation = 'vertical',
|
|
92
|
+
interactive,
|
|
93
|
+
className,
|
|
94
|
+
children,
|
|
95
|
+
...rest
|
|
96
|
+
} = props as EvoCardRootStaticProps &
|
|
97
|
+
Partial<EvoCardRootAnchorProps> &
|
|
98
|
+
Partial<EvoCardRootButtonProps> & { interactive?: boolean };
|
|
99
|
+
|
|
100
|
+
const isInteractive = interactive === true;
|
|
101
|
+
const classes = rootClasses(variant, orientation, isInteractive, className);
|
|
102
|
+
|
|
103
|
+
if (isInteractive && typeof (rest as { href?: string }).href === 'string') {
|
|
104
|
+
const anchorRest = rest as AnchorHTMLAttributes<HTMLAnchorElement>;
|
|
105
|
+
return (
|
|
106
|
+
<a ref={ref as Ref<HTMLAnchorElement>} className={classes} {...anchorRest}>
|
|
107
|
+
{children}
|
|
108
|
+
</a>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (isInteractive) {
|
|
113
|
+
const { type = 'button', ...buttonRest } =
|
|
114
|
+
rest as ButtonHTMLAttributes<HTMLButtonElement>;
|
|
115
|
+
return (
|
|
116
|
+
<button
|
|
117
|
+
ref={ref as Ref<HTMLButtonElement>}
|
|
118
|
+
type={type}
|
|
119
|
+
className={classes}
|
|
120
|
+
{...buttonRest}
|
|
121
|
+
>
|
|
122
|
+
{children}
|
|
123
|
+
</button>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
ref={ref as Ref<HTMLDivElement>}
|
|
130
|
+
className={classes}
|
|
131
|
+
{...(rest as HTMLAttributes<HTMLDivElement>)}
|
|
132
|
+
>
|
|
133
|
+
{children}
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
EvoCardRoot.displayName = 'EvoCardRoot';
|
|
139
|
+
|
|
140
|
+
// ----- Section slots -----
|
|
141
|
+
|
|
142
|
+
export type EvoCardHeaderProps = HTMLAttributes<HTMLDivElement>;
|
|
143
|
+
export type EvoCardBodyProps = HTMLAttributes<HTMLDivElement>;
|
|
144
|
+
export type EvoCardFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
145
|
+
|
|
146
|
+
export const EvoCardHeader = forwardRef<HTMLDivElement, EvoCardHeaderProps>(
|
|
147
|
+
function EvoCardHeader({ className, ...rest }, ref) {
|
|
148
|
+
return <div ref={ref} className={cx(styles.header, className)} {...rest} />;
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
EvoCardHeader.displayName = 'EvoCardHeader';
|
|
152
|
+
|
|
153
|
+
export const EvoCardBody = forwardRef<HTMLDivElement, EvoCardBodyProps>(
|
|
154
|
+
function EvoCardBody({ className, ...rest }, ref) {
|
|
155
|
+
return <div ref={ref} className={cx(styles.body, className)} {...rest} />;
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
EvoCardBody.displayName = 'EvoCardBody';
|
|
159
|
+
|
|
160
|
+
export const EvoCardFooter = forwardRef<HTMLDivElement, EvoCardFooterProps>(
|
|
161
|
+
function EvoCardFooter({ className, ...rest }, ref) {
|
|
162
|
+
return <div ref={ref} className={cx(styles.footer, className)} {...rest} />;
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
EvoCardFooter.displayName = 'EvoCardFooter';
|
|
166
|
+
|
|
167
|
+
// ----- Title (configurable heading level) -----
|
|
168
|
+
|
|
169
|
+
export interface EvoCardTitleProps extends HTMLAttributes<HTMLHeadingElement> {
|
|
170
|
+
/** Semantic level. Visual size is fixed by the stylesheet. @default 'h3' */
|
|
171
|
+
as?: EvoCardHeadingLevel;
|
|
172
|
+
children: ReactNode;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const EvoCardTitle = forwardRef<HTMLHeadingElement, EvoCardTitleProps>(
|
|
176
|
+
function EvoCardTitle({ as = 'h3', className, children, ...rest }, ref) {
|
|
177
|
+
return createElement(
|
|
178
|
+
as,
|
|
179
|
+
{ ref, className: cx(styles.title, className), ...rest },
|
|
180
|
+
children,
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
EvoCardTitle.displayName = 'EvoCardTitle';
|
|
185
|
+
|
|
186
|
+
// ----- Description -----
|
|
187
|
+
|
|
188
|
+
export type EvoCardDescriptionProps = HTMLAttributes<HTMLParagraphElement>;
|
|
189
|
+
|
|
190
|
+
export const EvoCardDescription = forwardRef<
|
|
191
|
+
HTMLParagraphElement,
|
|
192
|
+
EvoCardDescriptionProps
|
|
193
|
+
>(function EvoCardDescription({ className, ...rest }, ref) {
|
|
194
|
+
return <p ref={ref} className={cx(styles.description, className)} {...rest} />;
|
|
195
|
+
});
|
|
196
|
+
EvoCardDescription.displayName = 'EvoCardDescription';
|
|
197
|
+
|
|
198
|
+
// ----- Media -----
|
|
199
|
+
|
|
200
|
+
export interface EvoCardMediaProps extends HTMLAttributes<HTMLDivElement> {
|
|
201
|
+
/** Convenience: renders an <img>. Omit and pass children for custom media. */
|
|
202
|
+
src?: string;
|
|
203
|
+
/** Required when `src` is set. Empty string marks the image as decorative. */
|
|
204
|
+
alt?: string;
|
|
205
|
+
/** e.g. 16/9 or '4/3'. Applied when no explicit dimensions are given. */
|
|
206
|
+
aspectRatio?: number | string;
|
|
207
|
+
children?: ReactNode;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const EvoCardMedia = forwardRef<HTMLDivElement, EvoCardMediaProps>(
|
|
211
|
+
function EvoCardMedia(
|
|
212
|
+
{ src, alt, aspectRatio, className, style, children, ...rest },
|
|
213
|
+
ref,
|
|
214
|
+
) {
|
|
215
|
+
const mediaStyle: CSSProperties | undefined =
|
|
216
|
+
aspectRatio != null
|
|
217
|
+
? { aspectRatio: aspectRatio as CSSProperties['aspectRatio'], ...style }
|
|
218
|
+
: style;
|
|
219
|
+
return (
|
|
220
|
+
<div
|
|
221
|
+
ref={ref}
|
|
222
|
+
className={cx(styles.media, className)}
|
|
223
|
+
style={mediaStyle}
|
|
224
|
+
{...rest}
|
|
225
|
+
>
|
|
226
|
+
{src ? <img src={src} alt={alt ?? ''} className={styles.mediaImg} /> : children}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
EvoCardMedia.displayName = 'EvoCardMedia';
|
|
232
|
+
|
|
233
|
+
// ----- Compound export -----
|
|
234
|
+
//
|
|
235
|
+
// `EvoCard` is callable (forwards to Root) AND carries the sub-components
|
|
236
|
+
// as static properties so consumers can write either:
|
|
237
|
+
// <EvoCard variant="outlined">…</EvoCard>
|
|
238
|
+
// <EvoCard.Root variant="outlined">…</EvoCard.Root>
|
|
239
|
+
|
|
240
|
+
type EvoCardComponent = typeof EvoCardRoot & {
|
|
241
|
+
Root: typeof EvoCardRoot;
|
|
242
|
+
Header: typeof EvoCardHeader;
|
|
243
|
+
Title: typeof EvoCardTitle;
|
|
244
|
+
Description: typeof EvoCardDescription;
|
|
245
|
+
Body: typeof EvoCardBody;
|
|
246
|
+
Footer: typeof EvoCardFooter;
|
|
247
|
+
Media: typeof EvoCardMedia;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export const EvoCard = EvoCardRoot as EvoCardComponent;
|
|
251
|
+
EvoCard.Root = EvoCardRoot;
|
|
252
|
+
EvoCard.Header = EvoCardHeader;
|
|
253
|
+
EvoCard.Title = EvoCardTitle;
|
|
254
|
+
EvoCard.Description = EvoCardDescription;
|
|
255
|
+
EvoCard.Body = EvoCardBody;
|
|
256
|
+
EvoCard.Footer = EvoCardFooter;
|
|
257
|
+
EvoCard.Media = EvoCardMedia;
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import styles from '../css/checkbox.module.scss';
|
|
3
|
-
|
|
4
|
-
interface EvoCheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
5
|
-
label?: string;
|
|
6
|
-
helperText?: string;
|
|
7
|
-
indeterminate?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface EvoCheckboxGroupProps {
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
label?: string;
|
|
13
|
-
className?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const EvoCheckboxGroup = ({ children, label, className = '' }: EvoCheckboxGroupProps) => (
|
|
17
|
-
<fieldset className={`${styles.group} ${className}`}>
|
|
18
|
-
{label && <legend className={styles.groupLabel}>{label}</legend>}
|
|
19
|
-
{children}
|
|
20
|
-
</fieldset>
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
export const EvoCheckbox = ({
|
|
24
|
-
label,
|
|
25
|
-
helperText,
|
|
26
|
-
indeterminate = false,
|
|
27
|
-
className = '',
|
|
28
|
-
disabled,
|
|
29
|
-
id,
|
|
30
|
-
...rest
|
|
31
|
-
}: EvoCheckboxProps) => {
|
|
32
|
-
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
33
|
-
|
|
34
|
-
React.useEffect(() => {
|
|
35
|
-
if (inputRef.current) inputRef.current.indeterminate = indeterminate;
|
|
36
|
-
}, [indeterminate]);
|
|
37
|
-
|
|
38
|
-
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<div className={[styles.checkbox, disabled ? styles.disabled : '', className].filter(Boolean).join(' ')}>
|
|
42
|
-
<input
|
|
43
|
-
type="checkbox"
|
|
44
|
-
id={inputId}
|
|
45
|
-
ref={inputRef}
|
|
46
|
-
disabled={disabled}
|
|
47
|
-
className={styles.input}
|
|
48
|
-
{...rest}
|
|
49
|
-
/>
|
|
50
|
-
<label htmlFor={inputId} className={styles.label}>
|
|
51
|
-
<span className={styles.checkmark} />
|
|
52
|
-
<span className={styles.labelText}>{label}</span>
|
|
53
|
-
</label>
|
|
54
|
-
{helperText && <p className={styles.helperText}>{helperText}</p>}
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
EvoCheckbox.Group = EvoCheckboxGroup;
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from '../css/checkbox.module.scss';
|
|
3
|
+
|
|
4
|
+
interface EvoCheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
5
|
+
label?: string;
|
|
6
|
+
helperText?: string;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface EvoCheckboxGroupProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
label?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const EvoCheckboxGroup = ({ children, label, className = '' }: EvoCheckboxGroupProps) => (
|
|
17
|
+
<fieldset className={`${styles.group} ${className}`}>
|
|
18
|
+
{label && <legend className={styles.groupLabel}>{label}</legend>}
|
|
19
|
+
{children}
|
|
20
|
+
</fieldset>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const EvoCheckbox = ({
|
|
24
|
+
label,
|
|
25
|
+
helperText,
|
|
26
|
+
indeterminate = false,
|
|
27
|
+
className = '',
|
|
28
|
+
disabled,
|
|
29
|
+
id,
|
|
30
|
+
...rest
|
|
31
|
+
}: EvoCheckboxProps) => {
|
|
32
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
33
|
+
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
if (inputRef.current) inputRef.current.indeterminate = indeterminate;
|
|
36
|
+
}, [indeterminate]);
|
|
37
|
+
|
|
38
|
+
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, '-');
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className={[styles.checkbox, disabled ? styles.disabled : '', className].filter(Boolean).join(' ')}>
|
|
42
|
+
<input
|
|
43
|
+
type="checkbox"
|
|
44
|
+
id={inputId}
|
|
45
|
+
ref={inputRef}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
className={styles.input}
|
|
48
|
+
{...rest}
|
|
49
|
+
/>
|
|
50
|
+
<label htmlFor={inputId} className={styles.label}>
|
|
51
|
+
<span className={styles.checkmark} />
|
|
52
|
+
<span className={styles.labelText}>{label}</span>
|
|
53
|
+
</label>
|
|
54
|
+
{helperText && <p className={styles.helperText}>{helperText}</p>}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
EvoCheckbox.Group = EvoCheckboxGroup;
|