@shipfox/react-ui 0.7.0 → 0.9.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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check.log +3 -3
- package/.turbo/turbo-type.log +2 -2
- package/CHANGELOG.md +13 -0
- package/dist/components/alert/alert.d.ts +15 -5
- package/dist/components/alert/alert.d.ts.map +1 -1
- package/dist/components/alert/alert.js +122 -22
- package/dist/components/alert/alert.js.map +1 -1
- package/dist/components/alert/alert.stories.js +121 -6
- package/dist/components/alert/alert.stories.js.map +1 -1
- package/dist/components/button/button-link.js +1 -1
- package/dist/components/button/button-link.js.map +1 -1
- package/dist/components/button/button.d.ts +2 -1
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +21 -3
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/button/button.stories.js +25 -0
- package/dist/components/button/button.stories.js.map +1 -1
- package/dist/components/button/icon-button.d.ts +2 -1
- package/dist/components/button/icon-button.d.ts.map +1 -1
- package/dist/components/button/icon-button.js +21 -3
- package/dist/components/button/icon-button.js.map +1 -1
- package/dist/components/button/icon-button.stories.js +90 -0
- package/dist/components/button/icon-button.stories.js.map +1 -1
- package/dist/components/checkbox/checkbox-links.d.ts.map +1 -1
- package/dist/components/checkbox/checkbox-links.js +8 -2
- package/dist/components/checkbox/checkbox-links.js.map +1 -1
- package/dist/components/checkbox/checkbox.stories.js +4 -4
- package/dist/components/checkbox/checkbox.stories.js.map +1 -1
- package/dist/components/form/form.d.ts +11 -0
- package/dist/components/form/form.d.ts.map +1 -0
- package/dist/components/form/form.js +106 -0
- package/dist/components/form/form.js.map +1 -0
- package/dist/components/form/form.stories.js +582 -0
- package/dist/components/form/form.stories.js.map +1 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -0
- package/dist/components/form/index.js +3 -0
- package/dist/components/form/index.js.map +1 -0
- package/dist/components/icon/custom/spinner.d.ts +1 -1
- package/dist/components/icon/custom/spinner.d.ts.map +1 -1
- package/dist/components/icon/custom/spinner.js +84 -30
- package/dist/components/icon/custom/spinner.js.map +1 -1
- package/dist/components/icon/icon.d.ts +19 -18
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +17 -17
- package/dist/components/icon/icon.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -1
- package/src/components/alert/alert.stories.tsx +103 -2
- package/src/components/alert/alert.tsx +163 -16
- package/src/components/button/button-link.tsx +1 -1
- package/src/components/button/button.stories.tsx +18 -0
- package/src/components/button/button.tsx +29 -3
- package/src/components/button/icon-button.stories.tsx +46 -0
- package/src/components/button/icon-button.tsx +28 -2
- package/src/components/checkbox/checkbox-links.tsx +5 -3
- package/src/components/checkbox/checkbox.stories.tsx +20 -4
- package/src/components/form/form.stories.tsx +500 -0
- package/src/components/form/form.tsx +154 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/icon/custom/spinner.tsx +64 -18
- package/src/components/icon/icon.tsx +18 -18
- package/src/components/index.ts +1 -0
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import {cva, type VariantProps} from 'class-variance-authority';
|
|
2
2
|
import {Icon} from 'components/icon';
|
|
3
|
-
import type
|
|
3
|
+
import {AnimatePresence, motion, type Transition} from 'framer-motion';
|
|
4
|
+
import {
|
|
5
|
+
type ComponentProps,
|
|
6
|
+
createContext,
|
|
7
|
+
type MouseEvent,
|
|
8
|
+
useCallback,
|
|
9
|
+
useContext,
|
|
10
|
+
useEffect,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
} from 'react';
|
|
4
14
|
import {cn} from 'utils/cn';
|
|
5
15
|
|
|
6
16
|
const alertVariants = cva(
|
|
@@ -51,21 +61,137 @@ const closeIconVariants = cva('w-16 h-16', {
|
|
|
51
61
|
},
|
|
52
62
|
});
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
const alertDefaultTransition: Transition = {
|
|
65
|
+
duration: 0.2,
|
|
66
|
+
ease: 'easeInOut',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type AlertContextValue = {
|
|
70
|
+
isOpen: boolean;
|
|
71
|
+
onClose: () => void;
|
|
72
|
+
variant: VariantProps<typeof alertVariants>['variant'];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const AlertContext = createContext<AlertContextValue | null>(null);
|
|
76
|
+
|
|
77
|
+
function useAlertContext() {
|
|
78
|
+
const context = useContext(AlertContext);
|
|
79
|
+
if (!context) {
|
|
80
|
+
throw new Error('Alert components must be used within an Alert component');
|
|
81
|
+
}
|
|
82
|
+
return context;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type AlertProps = ComponentProps<'div'> &
|
|
86
|
+
VariantProps<typeof alertVariants> & {
|
|
87
|
+
open?: boolean;
|
|
88
|
+
defaultOpen?: boolean;
|
|
89
|
+
onOpenChange?: (open: boolean) => void;
|
|
90
|
+
animated?: boolean;
|
|
91
|
+
transition?: Transition;
|
|
92
|
+
autoClose?: number;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function Alert({
|
|
96
|
+
className,
|
|
97
|
+
variant = 'default',
|
|
98
|
+
children,
|
|
99
|
+
open: controlledOpen,
|
|
100
|
+
defaultOpen = true,
|
|
101
|
+
onOpenChange,
|
|
102
|
+
animated = true,
|
|
103
|
+
transition = alertDefaultTransition,
|
|
104
|
+
autoClose,
|
|
105
|
+
...props
|
|
106
|
+
}: AlertProps) {
|
|
107
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
108
|
+
const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
|
|
109
|
+
|
|
110
|
+
const handleClose = useCallback(() => {
|
|
111
|
+
if (controlledOpen === undefined) {
|
|
112
|
+
setInternalOpen(false);
|
|
113
|
+
}
|
|
114
|
+
onOpenChange?.(false);
|
|
115
|
+
}, [controlledOpen, onOpenChange]);
|
|
116
|
+
|
|
117
|
+
const handleCloseRef = useRef(handleClose);
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
handleCloseRef.current = handleClose;
|
|
120
|
+
}, [handleClose]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (autoClose && isOpen && autoClose > 0) {
|
|
124
|
+
const timeoutId = setTimeout(() => {
|
|
125
|
+
handleCloseRef.current();
|
|
126
|
+
}, autoClose);
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
clearTimeout(timeoutId);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}, [autoClose, isOpen]);
|
|
133
|
+
|
|
134
|
+
const contextValue: AlertContextValue = {
|
|
135
|
+
isOpen,
|
|
136
|
+
onClose: handleClose,
|
|
137
|
+
variant,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (!animated) {
|
|
141
|
+
if (!isOpen) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<AlertContext.Provider value={contextValue}>
|
|
147
|
+
<div className="w-full flex items-start gap-4">
|
|
148
|
+
<div
|
|
149
|
+
data-slot="alert-line"
|
|
150
|
+
className={cn(alertLineVariants({variant}))}
|
|
151
|
+
aria-hidden="true"
|
|
152
|
+
/>
|
|
153
|
+
<div
|
|
154
|
+
data-slot="alert"
|
|
155
|
+
role="alert"
|
|
156
|
+
className={cn(alertVariants({variant}), className)}
|
|
157
|
+
{...props}
|
|
158
|
+
>
|
|
159
|
+
{children}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</AlertContext.Provider>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
55
165
|
|
|
56
|
-
function Alert({className, variant, children, ...props}: AlertProps) {
|
|
57
166
|
return (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
167
|
+
<AnimatePresence>
|
|
168
|
+
{isOpen && (
|
|
169
|
+
<motion.div
|
|
170
|
+
key="alert"
|
|
171
|
+
className="w-full flex items-start gap-4"
|
|
172
|
+
initial={{opacity: 0}}
|
|
173
|
+
animate={{opacity: 1}}
|
|
174
|
+
exit={{opacity: 0}}
|
|
175
|
+
transition={transition}
|
|
176
|
+
>
|
|
177
|
+
<AlertContext.Provider value={contextValue}>
|
|
178
|
+
<div
|
|
179
|
+
data-slot="alert-line"
|
|
180
|
+
className={cn(alertLineVariants({variant}))}
|
|
181
|
+
aria-hidden="true"
|
|
182
|
+
/>
|
|
183
|
+
<div
|
|
184
|
+
data-slot="alert"
|
|
185
|
+
role="alert"
|
|
186
|
+
className={cn(alertVariants({variant}), className)}
|
|
187
|
+
{...props}
|
|
188
|
+
>
|
|
189
|
+
{children}
|
|
190
|
+
</div>
|
|
191
|
+
</AlertContext.Provider>
|
|
192
|
+
</motion.div>
|
|
193
|
+
)}
|
|
194
|
+
</AnimatePresence>
|
|
69
195
|
);
|
|
70
196
|
}
|
|
71
197
|
|
|
@@ -122,9 +248,18 @@ function AlertAction({className, ...props}: ComponentProps<'button'>) {
|
|
|
122
248
|
|
|
123
249
|
function AlertClose({
|
|
124
250
|
className,
|
|
125
|
-
variant
|
|
251
|
+
variant: variantProp,
|
|
252
|
+
onClick,
|
|
126
253
|
...props
|
|
127
254
|
}: ComponentProps<'button'> & VariantProps<typeof closeIconVariants>) {
|
|
255
|
+
const {onClose, variant: contextVariant} = useAlertContext();
|
|
256
|
+
const variant = variantProp ?? contextVariant ?? 'default';
|
|
257
|
+
|
|
258
|
+
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
|
259
|
+
onClose();
|
|
260
|
+
onClick?.(e);
|
|
261
|
+
};
|
|
262
|
+
|
|
128
263
|
return (
|
|
129
264
|
<button
|
|
130
265
|
data-slot="alert-close"
|
|
@@ -134,6 +269,7 @@ function AlertClose({
|
|
|
134
269
|
className,
|
|
135
270
|
)}
|
|
136
271
|
aria-label="Close"
|
|
272
|
+
onClick={handleClick}
|
|
137
273
|
{...props}
|
|
138
274
|
>
|
|
139
275
|
<Icon name="close" className={cn(closeIconVariants({variant}))} />
|
|
@@ -141,4 +277,15 @@ function AlertClose({
|
|
|
141
277
|
);
|
|
142
278
|
}
|
|
143
279
|
|
|
144
|
-
export {
|
|
280
|
+
export {
|
|
281
|
+
Alert,
|
|
282
|
+
AlertContent,
|
|
283
|
+
AlertTitle,
|
|
284
|
+
AlertDescription,
|
|
285
|
+
AlertActions,
|
|
286
|
+
AlertAction,
|
|
287
|
+
AlertClose,
|
|
288
|
+
alertDefaultTransition,
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
export type {AlertProps};
|
|
@@ -5,7 +5,7 @@ import type {ComponentProps} from 'react';
|
|
|
5
5
|
import {cn} from 'utils/cn';
|
|
6
6
|
|
|
7
7
|
export const buttonLinkVariants = cva(
|
|
8
|
-
'inline-flex items-center justify-center gap-4 whitespace-nowrap transition-colors disabled:pointer-events-none outline-none font-medium',
|
|
8
|
+
'inline-flex items-center justify-center gap-4 whitespace-nowrap transition-colors cursor-pointer disabled:pointer-events-none outline-none font-medium',
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -26,6 +26,7 @@ const meta = {
|
|
|
26
26
|
options: sizeOptions,
|
|
27
27
|
},
|
|
28
28
|
asChild: {control: 'boolean'},
|
|
29
|
+
isLoading: {control: 'boolean'},
|
|
29
30
|
},
|
|
30
31
|
args: {
|
|
31
32
|
children: 'Click me',
|
|
@@ -118,3 +119,20 @@ export const Icons: Story = {
|
|
|
118
119
|
</div>
|
|
119
120
|
),
|
|
120
121
|
};
|
|
122
|
+
|
|
123
|
+
export const Loading: Story = {
|
|
124
|
+
render: (args) => (
|
|
125
|
+
<div className="flex flex-col gap-16">
|
|
126
|
+
<div>
|
|
127
|
+
<Button {...args} isLoading>
|
|
128
|
+
Loading...
|
|
129
|
+
</Button>
|
|
130
|
+
</div>
|
|
131
|
+
<div>
|
|
132
|
+
<Button {...args} isLoading iconLeft="google">
|
|
133
|
+
Loading with left icon
|
|
134
|
+
</Button>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
),
|
|
138
|
+
};
|
|
@@ -5,7 +5,7 @@ import type {ComponentProps} from 'react';
|
|
|
5
5
|
import {cn} from 'utils/cn';
|
|
6
6
|
|
|
7
7
|
export const buttonVariants = cva(
|
|
8
|
-
'rounded-6 inline-flex items-center justify-center whitespace-nowrap transition-colors disabled:pointer-events-none shrink-0 outline-none',
|
|
8
|
+
'rounded-6 inline-flex items-center justify-center whitespace-nowrap transition-colors cursor-pointer disabled:pointer-events-none shrink-0 outline-none',
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -38,6 +38,15 @@ export const buttonVariants = cva(
|
|
|
38
38
|
},
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
+
const spinnerSizeMap: Record<NonNullable<VariantProps<typeof buttonVariants>['size']>, string> = {
|
|
42
|
+
'2xs': 'size-10',
|
|
43
|
+
xs: 'size-10',
|
|
44
|
+
sm: 'size-12',
|
|
45
|
+
md: 'size-14',
|
|
46
|
+
lg: 'size-16',
|
|
47
|
+
xl: 'size-18',
|
|
48
|
+
};
|
|
49
|
+
|
|
41
50
|
export function Button({
|
|
42
51
|
className,
|
|
43
52
|
variant,
|
|
@@ -46,18 +55,35 @@ export function Button({
|
|
|
46
55
|
children,
|
|
47
56
|
iconLeft,
|
|
48
57
|
iconRight,
|
|
58
|
+
isLoading = false,
|
|
59
|
+
disabled,
|
|
49
60
|
...props
|
|
50
61
|
}: ComponentProps<'button'> &
|
|
51
62
|
VariantProps<typeof buttonVariants> & {
|
|
52
63
|
asChild?: boolean;
|
|
53
64
|
iconLeft?: IconName;
|
|
54
65
|
iconRight?: IconName;
|
|
66
|
+
isLoading?: boolean;
|
|
55
67
|
}) {
|
|
56
68
|
const Comp = asChild ? Slot : 'button';
|
|
69
|
+
const spinnerSize =
|
|
70
|
+
spinnerSizeMap[(size ?? 'md') as NonNullable<VariantProps<typeof buttonVariants>['size']>];
|
|
57
71
|
|
|
58
72
|
return (
|
|
59
|
-
<Comp
|
|
60
|
-
|
|
73
|
+
<Comp
|
|
74
|
+
data-slot="button"
|
|
75
|
+
className={cn(buttonVariants({variant, size, className}))}
|
|
76
|
+
disabled={disabled || isLoading}
|
|
77
|
+
aria-busy={isLoading}
|
|
78
|
+
aria-live={isLoading ? 'polite' : undefined}
|
|
79
|
+
{...(asChild ? {'aria-disabled': disabled || isLoading} : {})}
|
|
80
|
+
{...props}
|
|
81
|
+
>
|
|
82
|
+
{isLoading ? (
|
|
83
|
+
<Icon name="spinner" className={spinnerSize} />
|
|
84
|
+
) : (
|
|
85
|
+
iconLeft && <Icon name={iconLeft} />
|
|
86
|
+
)}
|
|
61
87
|
{children}
|
|
62
88
|
{iconRight && <Icon name={iconRight} />}
|
|
63
89
|
</Comp>
|
|
@@ -180,3 +180,49 @@ export const Sizes: Story = {
|
|
|
180
180
|
</div>
|
|
181
181
|
),
|
|
182
182
|
};
|
|
183
|
+
|
|
184
|
+
export const Loading: Story = {
|
|
185
|
+
render: ({children: _children, ...args}) => (
|
|
186
|
+
<div className="flex flex-col gap-32">
|
|
187
|
+
<div className="flex flex-col gap-16">
|
|
188
|
+
<Code variant="label">Loading by Size:</Code>
|
|
189
|
+
<div className="flex gap-16 items-center">
|
|
190
|
+
{sizeOptions.map((size) => (
|
|
191
|
+
<div key={size} className="flex flex-col gap-8 items-center">
|
|
192
|
+
<Code variant="label" className="text-foreground-neutral-subtle text-xs">
|
|
193
|
+
{size}
|
|
194
|
+
</Code>
|
|
195
|
+
<IconButton {...args} icon="addLine" aria-label="Loading" size={size} isLoading />
|
|
196
|
+
</div>
|
|
197
|
+
))}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
<div className="flex flex-col gap-16">
|
|
201
|
+
<Code variant="label">Loading by Variant:</Code>
|
|
202
|
+
<div className="flex gap-16 items-center">
|
|
203
|
+
{variantOptions.map((variant) => (
|
|
204
|
+
<div key={variant} className="flex flex-col gap-8 items-center">
|
|
205
|
+
<Code variant="label" className="text-foreground-neutral-subtle text-xs">
|
|
206
|
+
{variant}
|
|
207
|
+
</Code>
|
|
208
|
+
<IconButton
|
|
209
|
+
{...args}
|
|
210
|
+
icon="addLine"
|
|
211
|
+
aria-label="Loading"
|
|
212
|
+
variant={variant}
|
|
213
|
+
isLoading
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
))}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="flex flex-col gap-16">
|
|
220
|
+
<Code variant="label">Normal vs Loading:</Code>
|
|
221
|
+
<div className="flex gap-16 items-center">
|
|
222
|
+
<IconButton {...args} icon="addLine" aria-label="Add" />
|
|
223
|
+
<IconButton {...args} icon="addLine" aria-label="Loading" isLoading />
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
),
|
|
228
|
+
};
|
|
@@ -5,7 +5,7 @@ import type {ComponentProps} from 'react';
|
|
|
5
5
|
import {cn} from 'utils/cn';
|
|
6
6
|
|
|
7
7
|
export const iconButtonVariants = cva(
|
|
8
|
-
'inline-flex items-center justify-center whitespace-nowrap transition-colors disabled:pointer-events-none shrink-0 outline-none',
|
|
8
|
+
'inline-flex items-center justify-center whitespace-nowrap transition-colors cursor-pointer disabled:pointer-events-none shrink-0 outline-none',
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -40,6 +40,18 @@ export const iconButtonVariants = cva(
|
|
|
40
40
|
},
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
+
const spinnerSizeMap: Record<
|
|
44
|
+
NonNullable<VariantProps<typeof iconButtonVariants>['size']>,
|
|
45
|
+
string
|
|
46
|
+
> = {
|
|
47
|
+
'2xs': 'size-8',
|
|
48
|
+
xs: 'size-10',
|
|
49
|
+
sm: 'size-12',
|
|
50
|
+
md: 'size-14',
|
|
51
|
+
lg: 'size-16',
|
|
52
|
+
xl: 'size-18',
|
|
53
|
+
};
|
|
54
|
+
|
|
43
55
|
export function IconButton({
|
|
44
56
|
className,
|
|
45
57
|
variant,
|
|
@@ -49,21 +61,35 @@ export function IconButton({
|
|
|
49
61
|
asChild = false,
|
|
50
62
|
children,
|
|
51
63
|
icon,
|
|
64
|
+
isLoading = false,
|
|
65
|
+
disabled,
|
|
52
66
|
...props
|
|
53
67
|
}: ComponentProps<'button'> &
|
|
54
68
|
VariantProps<typeof iconButtonVariants> & {
|
|
55
69
|
asChild?: boolean;
|
|
56
70
|
icon?: IconName;
|
|
71
|
+
isLoading?: boolean;
|
|
57
72
|
}) {
|
|
58
73
|
const Comp = asChild ? Slot : 'button';
|
|
74
|
+
const spinnerSize = spinnerSizeMap[size ?? 'md'];
|
|
59
75
|
|
|
60
76
|
return (
|
|
61
77
|
<Comp
|
|
62
78
|
data-slot="icon-button"
|
|
63
79
|
className={cn(iconButtonVariants({variant, size, radius, muted}), className)}
|
|
80
|
+
disabled={disabled || isLoading}
|
|
81
|
+
aria-busy={isLoading}
|
|
82
|
+
aria-live={isLoading ? 'polite' : undefined}
|
|
83
|
+
{...(asChild ? {'aria-disabled': disabled || isLoading} : {})}
|
|
64
84
|
{...props}
|
|
65
85
|
>
|
|
66
|
-
{
|
|
86
|
+
{isLoading ? (
|
|
87
|
+
<Icon name="spinner" className={spinnerSize} />
|
|
88
|
+
) : icon ? (
|
|
89
|
+
<Icon name={icon} />
|
|
90
|
+
) : (
|
|
91
|
+
children
|
|
92
|
+
)}
|
|
67
93
|
</Comp>
|
|
68
94
|
);
|
|
69
95
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {buttonLinkVariants} from 'components/button/button-link';
|
|
1
2
|
import {Label} from 'components/label';
|
|
2
3
|
import {type ReactNode, useId} from 'react';
|
|
3
4
|
import {cn} from 'utils/cn';
|
|
@@ -58,10 +59,12 @@ export function CheckboxLinks({
|
|
|
58
59
|
{link.href ? (
|
|
59
60
|
<a
|
|
60
61
|
href={link.href}
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
61
64
|
onClick={link.onClick}
|
|
62
65
|
className={cn(
|
|
63
66
|
'text-sm leading-20 font-medium text-foreground-highlight-interactive',
|
|
64
|
-
'hover:text-foreground-highlight-interactive-hover',
|
|
67
|
+
'hover:text-foreground-highlight-interactive-hover transition-colors',
|
|
65
68
|
linkClassName,
|
|
66
69
|
)}
|
|
67
70
|
>
|
|
@@ -72,8 +75,7 @@ export function CheckboxLinks({
|
|
|
72
75
|
type="button"
|
|
73
76
|
onClick={link.onClick}
|
|
74
77
|
className={cn(
|
|
75
|
-
'
|
|
76
|
-
'hover:text-foreground-highlight-interactive-hover',
|
|
78
|
+
buttonLinkVariants({variant: 'interactive', size: 'sm'}),
|
|
77
79
|
linkClassName,
|
|
78
80
|
)}
|
|
79
81
|
>
|
|
@@ -356,16 +356,32 @@ export const CheckboxLinksStory: StoryObj = {
|
|
|
356
356
|
id="checkbox-links-default"
|
|
357
357
|
label="Accept policies"
|
|
358
358
|
links={[
|
|
359
|
-
{label: 'Terms of use', href: '
|
|
360
|
-
{
|
|
359
|
+
{label: 'Terms of use', href: 'https://www.shipfox.io/legal/terms-of-service'},
|
|
360
|
+
{
|
|
361
|
+
label: 'Privacy Policy',
|
|
362
|
+
onClick: () =>
|
|
363
|
+
window.open(
|
|
364
|
+
'https://www.shipfox.io/legal/privacy-policy',
|
|
365
|
+
'_blank',
|
|
366
|
+
'noopener,noreferrer',
|
|
367
|
+
),
|
|
368
|
+
},
|
|
361
369
|
]}
|
|
362
370
|
/>
|
|
363
371
|
<CheckboxLinks
|
|
364
372
|
id="checkbox-links-checked"
|
|
365
373
|
label="Accept policies"
|
|
366
374
|
links={[
|
|
367
|
-
{label: 'Terms of use', href: '
|
|
368
|
-
{
|
|
375
|
+
{label: 'Terms of use', href: 'https://www.shipfox.io/legal/terms-of-service'},
|
|
376
|
+
{
|
|
377
|
+
label: 'Privacy Policy',
|
|
378
|
+
onClick: () =>
|
|
379
|
+
window.open(
|
|
380
|
+
'https://www.shipfox.io/legal/privacy-policy',
|
|
381
|
+
'_blank',
|
|
382
|
+
'noopener,noreferrer',
|
|
383
|
+
),
|
|
384
|
+
},
|
|
369
385
|
]}
|
|
370
386
|
checked
|
|
371
387
|
/>
|