@grantbii/design-system 2.0.0 → 3.0.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/core/molecules/Badge.js +6 -9
- package/core/molecules/Badge.js.map +1 -1
- package/core/molecules/Toast.d.ts +20 -0
- package/core/molecules/Toast.js +239 -0
- package/core/molecules/Toast.js.map +1 -0
- package/core/molecules/ToggleButton.d.ts +21 -0
- package/core/molecules/ToggleButton.js +161 -0
- package/core/molecules/ToggleButton.js.map +1 -0
- package/core/molecules/Tooltip.d.ts +20 -0
- package/core/molecules/Tooltip.js +182 -0
- package/core/molecules/Tooltip.js.map +1 -0
- package/core/molecules/index.d.ts +3 -0
- package/core/molecules/index.js +3 -0
- package/core/molecules/index.js.map +1 -1
- package/core/organisms/Dropdown.d.ts +13 -7
- package/core/organisms/Dropdown.js +111 -12
- package/core/organisms/Dropdown.js.map +1 -1
- package/package.json +12 -12
- package/stories/molecules/Toast.stories.d.ts +9 -0
- package/stories/molecules/Toast.stories.js +106 -0
- package/stories/molecules/Toast.stories.js.map +1 -0
- package/stories/molecules/ToggleButton.stories.d.ts +11 -0
- package/stories/molecules/ToggleButton.stories.js +79 -0
- package/stories/molecules/ToggleButton.stories.js.map +1 -0
- package/stories/molecules/Tooltip.stories.d.ts +11 -0
- package/stories/molecules/Tooltip.stories.js +33 -0
- package/stories/molecules/Tooltip.stories.js.map +1 -0
- package/stories/organisms/Dropdown.stories.d.ts +4 -2
- package/stories/organisms/Dropdown.stories.js +75 -18
- package/stories/organisms/Dropdown.stories.js.map +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/core/molecules/Badge.js
CHANGED
|
@@ -44,17 +44,14 @@ const BaseBadge = styled.div `
|
|
|
44
44
|
display: flex;
|
|
45
45
|
align-items: center;
|
|
46
46
|
justify-content: space-between;
|
|
47
|
-
gap:
|
|
47
|
+
gap: ${Spacing.px12};
|
|
48
48
|
|
|
49
49
|
height: 26px;
|
|
50
50
|
padding: 5px ${Spacing.px12};
|
|
51
|
-
border-radius: 100px;
|
|
52
|
-
|
|
53
|
-
font-size: 12px;
|
|
54
|
-
font-weight: ${Typography.weight.medium};
|
|
55
51
|
|
|
56
52
|
color: ${(props) => props.$color};
|
|
57
53
|
background-color: ${(props) => props.$defaultBackgroundColor};
|
|
54
|
+
border-radius: ${Spacing.px100};
|
|
58
55
|
|
|
59
56
|
cursor: ${(props) => (props.$isClickable ? "pointer" : "auto")};
|
|
60
57
|
|
|
@@ -67,10 +64,10 @@ const BaseBadge = styled.div `
|
|
|
67
64
|
const BadgeContent = styled.div `
|
|
68
65
|
display: flex;
|
|
69
66
|
align-items: center;
|
|
70
|
-
gap:
|
|
67
|
+
gap: ${Spacing.px4};
|
|
71
68
|
|
|
72
69
|
width: ${({ $widthPixels }) => ($widthPixels ? `${$widthPixels}px` : "auto")};
|
|
73
|
-
max-width: ${({ $hasX: $closeable }) => $closeable ?
|
|
70
|
+
max-width: ${({ $hasX: $closeable }) => $closeable ? `calc(100% - ${Spacing.px20})` : "auto"};
|
|
74
71
|
`;
|
|
75
72
|
const IconContainer = styled.div `
|
|
76
73
|
display: flex;
|
|
@@ -85,13 +82,13 @@ const BadgeLabel = styled.div `
|
|
|
85
82
|
white-space: nowrap;
|
|
86
83
|
text-overflow: ellipsis;
|
|
87
84
|
|
|
88
|
-
${applyTypography(Typography.
|
|
85
|
+
${applyTypography(Typography.captionMedium)}
|
|
89
86
|
`;
|
|
90
87
|
const XButton = ({ onClick }) => (_jsx(BaseCloseButton, { type: "button", onClick: onClick, children: _jsx(SystemIcon.XIcon, { color: Color.typography.blackMedium, size: 12 }) }));
|
|
91
88
|
const BaseCloseButton = styled.button `
|
|
92
89
|
display: flex;
|
|
93
90
|
flex-direction: column;
|
|
94
91
|
|
|
95
|
-
min-width:
|
|
92
|
+
min-width: ${Spacing.px12};
|
|
96
93
|
`;
|
|
97
94
|
//# sourceMappingURL=Badge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../../core/molecules/Badge.tsx"],"names":[],"mappings":";AACA,OAAO,MAAM,MAAM,mBAAmB,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAgBlD,MAAM,KAAK,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,WAAW,EAAc,EAAE,EAAE;IACpE,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAChD,OAAO,KAAC,QAAQ,OAAK,YAAY,KAAM,WAAW,GAAI,CAAC;AACzD,CAAC,CAAC;AAEF,eAAe,KAAK,CAAC;AAQrB,MAAM,iBAAiB,GAAqD;IAC1E,OAAO,EAAE;QACP,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS;QACjC,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;QAC1C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;KACzC;IACD,OAAO,EAAE;QACP,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS;QACjC,sBAAsB,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;QAC3C,oBAAoB,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;KAC1C;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;QACzB,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;QAC1C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK;KACzC;IACD,KAAK,EAAE;QACL,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;QAC1B,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;QAC3C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;KAC1C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;QAC3B,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;QAC5C,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;KAC3C;IACD,GAAG,EAAE;QACH,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI;QACxB,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI;QACzC,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI;KACxC;CACF,CAAC;AAIF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,GAAG,sBAAsB,EAC7C,KAAK,GACS,EAAE,EAAE,CAAC,CACnB,MAAC,SAAS,IACR,OAAO,EAAE,OAAO,kBACF,CAAC,CAAC,OAAO,6BACE,sBAAsB,2BACxB,oBAAoB,YACnC,KAAK,aAEb,MAAC,YAAY,aAAQ,CAAC,CAAC,QAAQ,kBAAgB,gBAAgB,aAC5D,IAAI,CAAC,CAAC,CAAC,CACN,KAAC,aAAa,cACZ,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,GAAI,GAClB,CACjB,CAAC,CAAC,CAAC,CACF,mBAAK,CACN,EAED,KAAC,UAAU,cAAE,KAAK,GAAc,IACnB,EAEd,QAAQ,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,OAAO,EAAE,QAAQ,GAAI,CAAC,CAAC,CAAC,mBAAK,IACxC,CACb,CAAC;AAEF,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAK1B
|
|
1
|
+
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../../core/molecules/Badge.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType, MouseEventHandler, ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { Color, Spacing, SystemIcon, Typography } from \"../atoms\";\nimport { applyTypography } from \"../integrations\";\n\ntype BadgeVariant = \"default\" | \"neutral\" | \"blue\" | \"green\" | \"yellow\" | \"red\";\n\ntype CustomBadgeProps = {\n label: ReactNode;\n Icon?: ComponentType<SystemIcon.IconProps>;\n onClick?: MouseEventHandler<HTMLDivElement>;\n onClickX?: MouseEventHandler<HTMLButtonElement>;\n labelWidthPixels?: number;\n};\n\ntype BadgeProps = {\n variant?: BadgeVariant;\n} & CustomBadgeProps;\n\nconst Badge = ({ variant = \"default\", ...restOfProps }: BadgeProps) => {\n const variantProps = VARIANT_PROPS_MAP[variant];\n return <RawBadge {...variantProps} {...restOfProps} />;\n};\n\nexport default Badge;\n\ntype VariantStyleProps = {\n color: Color.DesignColor;\n defaultBackgroundColor: Color.DesignColor;\n hoverBackgroundColor?: Color.DesignColor;\n};\n\nconst VARIANT_PROPS_MAP: { [variant in BadgeVariant]: VariantStyleProps } = {\n default: {\n color: Color.typography.blackHigh,\n defaultBackgroundColor: Color.accent.blue3,\n hoverBackgroundColor: Color.accent.blue4,\n },\n neutral: {\n color: Color.typography.blackHigh,\n defaultBackgroundColor: Color.neutral.grey4,\n hoverBackgroundColor: Color.neutral.grey3,\n },\n blue: {\n color: Color.accent.blue1,\n defaultBackgroundColor: Color.accent.blue3,\n hoverBackgroundColor: Color.accent.blue4,\n },\n green: {\n color: Color.accent.green1,\n defaultBackgroundColor: Color.accent.green3,\n hoverBackgroundColor: Color.accent.green4,\n },\n yellow: {\n color: Color.accent.yellow1,\n defaultBackgroundColor: Color.accent.yellow3,\n hoverBackgroundColor: Color.accent.yellow4,\n },\n red: {\n color: Color.accent.red1,\n defaultBackgroundColor: Color.accent.red3,\n hoverBackgroundColor: Color.accent.red4,\n },\n};\n\ntype RawBadgeProps = CustomBadgeProps & VariantStyleProps;\n\nexport const RawBadge = ({\n label,\n Icon,\n onClick,\n onClickX,\n labelWidthPixels,\n defaultBackgroundColor,\n hoverBackgroundColor = defaultBackgroundColor,\n color,\n}: RawBadgeProps) => (\n <BaseBadge\n onClick={onClick}\n $isClickable={!!onClick}\n $defaultBackgroundColor={defaultBackgroundColor}\n $hoverBackgroundColor={hoverBackgroundColor}\n $color={color}\n >\n <BadgeContent $hasX={!!onClickX} $widthPixels={labelWidthPixels}>\n {Icon ? (\n <IconContainer>\n <Icon color={color} size={12} />\n </IconContainer>\n ) : (\n <></>\n )}\n\n <BadgeLabel>{label}</BadgeLabel>\n </BadgeContent>\n\n {onClickX ? <XButton onClick={onClickX} /> : <></>}\n </BaseBadge>\n);\n\nconst BaseBadge = styled.div<{\n $isClickable: boolean;\n $defaultBackgroundColor: Color.DesignColor;\n $hoverBackgroundColor: Color.DesignColor;\n $color: Color.DesignColor;\n}>`\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: ${Spacing.px12};\n\n height: 26px;\n padding: 5px ${Spacing.px12};\n\n color: ${(props) => props.$color};\n background-color: ${(props) => props.$defaultBackgroundColor};\n border-radius: ${Spacing.px100};\n\n cursor: ${(props) => (props.$isClickable ? \"pointer\" : \"auto\")};\n\n &:hover {\n background-color: ${(props) =>\n props.$isClickable\n ? props.$hoverBackgroundColor\n : props.$defaultBackgroundColor};\n }\n`;\n\nconst BadgeContent = styled.div<{\n $hasX: boolean;\n $widthPixels?: number;\n}>`\n display: flex;\n align-items: center;\n gap: ${Spacing.px4};\n\n width: ${({ $widthPixels }) => ($widthPixels ? `${$widthPixels}px` : \"auto\")};\n max-width: ${({ $hasX: $closeable }) =>\n $closeable ? `calc(100% - ${Spacing.px20})` : \"auto\"};\n`;\n\nconst IconContainer = styled.div<{ $iconSize?: string | number }>`\n display: flex;\n flex-direction: column;\n\n width: ${({ $iconSize = \"auto\" }) => $iconSize};\n min-width: ${({ $iconSize = \"auto\" }) => $iconSize};\n max-width: ${({ $iconSize = \"auto\" }) => $iconSize};\n`;\n\nconst BadgeLabel = styled.div`\n overflow-x: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n\n ${applyTypography(Typography.captionMedium)}\n`;\n\ntype XButtonProps = {\n onClick: MouseEventHandler<HTMLButtonElement>;\n};\n\nconst XButton = ({ onClick }: XButtonProps) => (\n <BaseCloseButton type=\"button\" onClick={onClick}>\n <SystemIcon.XIcon color={Color.typography.blackMedium} size={12} />\n </BaseCloseButton>\n);\n\nconst BaseCloseButton = styled.button`\n display: flex;\n flex-direction: column;\n\n min-width: ${Spacing.px12};\n`;\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type HTMLAttributes, type PropsWithChildren, type ReactNode } from "react";
|
|
2
|
+
export type ToastVariant = "neutral" | "success" | "danger";
|
|
3
|
+
export type ToastProps = Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> & {
|
|
4
|
+
title?: ReactNode;
|
|
5
|
+
message?: ReactNode;
|
|
6
|
+
variant?: ToastVariant;
|
|
7
|
+
action?: {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
};
|
|
11
|
+
onClose?: () => void;
|
|
12
|
+
};
|
|
13
|
+
declare const Toast: ({ title, message, variant, action, onClose, ...props }: ToastProps) => import("react").JSX.Element;
|
|
14
|
+
export default Toast;
|
|
15
|
+
export type ToastOptions = ToastProps & {
|
|
16
|
+
dedupeKey?: string;
|
|
17
|
+
duration?: number;
|
|
18
|
+
};
|
|
19
|
+
export declare const ToastProvider: ({ children }: PropsWithChildren) => import("react").JSX.Element;
|
|
20
|
+
export declare const useToast: () => (options: ToastOptions) => void;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useState, } from "react";
|
|
3
|
+
import styled, { keyframes } from "styled-components";
|
|
4
|
+
import { Color, Responsive, Spacing, SystemIcon, Typography } from "../atoms";
|
|
5
|
+
import { applyTypography } from "../integrations";
|
|
6
|
+
const toastVariants = {
|
|
7
|
+
neutral: {
|
|
8
|
+
iconBackground: Color.neutral.grey1,
|
|
9
|
+
border: Color.neutral.grey2,
|
|
10
|
+
background: Color.neutral.grey3,
|
|
11
|
+
Icon: null,
|
|
12
|
+
},
|
|
13
|
+
success: {
|
|
14
|
+
iconBackground: Color.accent.green1,
|
|
15
|
+
border: Color.accent.green2,
|
|
16
|
+
background: Color.accent.green4,
|
|
17
|
+
Icon: SystemIcon.CheckIcon,
|
|
18
|
+
},
|
|
19
|
+
danger: {
|
|
20
|
+
iconBackground: Color.accent.red1,
|
|
21
|
+
border: Color.accent.red2,
|
|
22
|
+
background: Color.accent.red4,
|
|
23
|
+
Icon: SystemIcon.XIcon,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const Toast = ({ title, message, variant = "neutral", action, onClose, ...props }) => {
|
|
27
|
+
const { iconBackground, border, background, Icon } = toastVariants[variant];
|
|
28
|
+
const isCompact = title == null && !action;
|
|
29
|
+
return (_jsxs(ToastContainer, { ...props, role: variant === "danger" ? "alert" : "status", "$border": border, "$background": background, "$compact": isCompact, children: [_jsxs(ToastLayout, { children: [_jsxs(ToastDetails, { children: [_jsx(ToastIconContainer, { "$background": iconBackground, children: Icon ? (_jsx(Icon, { color: Color.neutral.white, size: 14, weight: "bold" })) : (_jsx(SystemIcon.InfoIcon, { color: Color.neutral.white, size: 14, weight: "bold" })) }), _jsxs(ToastText, { children: [title != null && _jsx(Title, { children: title }), message != null && _jsx(Message, { children: message })] })] }), action && (_jsx(ActionButton, { type: "button", onClick: action.onClick, children: action.label }))] }), onClose && (_jsx(CloseButton, { type: "button", onClick: onClose, children: _jsx(SystemIcon.XIcon, { color: Color.typography.blackMedium, size: 18 }) }))] }));
|
|
30
|
+
};
|
|
31
|
+
export default Toast;
|
|
32
|
+
const ToastContext = createContext(null);
|
|
33
|
+
export const ToastProvider = ({ children }) => {
|
|
34
|
+
const [toasts, setToasts] = useState([]);
|
|
35
|
+
const dismissToast = useCallback((id) => setToasts((currentToasts) => currentToasts.filter((toast) => toast.id !== id)), []);
|
|
36
|
+
const showToast = useCallback((options) => setToasts((currentToasts) => {
|
|
37
|
+
const existingToastIndex = options.dedupeKey
|
|
38
|
+
? currentToasts.findIndex((toast) => toast.dedupeKey === options.dedupeKey)
|
|
39
|
+
: -1;
|
|
40
|
+
const nextToast = existingToastIndex < 0
|
|
41
|
+
? { ...options, id: crypto.randomUUID(), revision: 0 }
|
|
42
|
+
: {
|
|
43
|
+
...currentToasts[existingToastIndex],
|
|
44
|
+
...options,
|
|
45
|
+
revision: currentToasts[existingToastIndex].revision + 1,
|
|
46
|
+
};
|
|
47
|
+
return [
|
|
48
|
+
nextToast,
|
|
49
|
+
...currentToasts.filter((_, toastIndex) => toastIndex !== existingToastIndex),
|
|
50
|
+
].slice(0, 3);
|
|
51
|
+
}), []);
|
|
52
|
+
return (_jsxs(ToastContext.Provider, { value: showToast, children: [children, _jsx(Viewport, { "data-slot": "toast-viewport", "$stackDepth": Math.max(0, toasts.length - 1), children: toasts.map(({ id, revision, dedupeKey, duration = 8000, ...props }, index) => (_jsx(StackItem, { "data-pulse": revision > 0, "$index": index, children: _jsx(TimedToast, { ...props, id: id, duration: duration, dismiss: dismissToast }) }, `${dedupeKey ?? id}-${revision}`))) })] }));
|
|
53
|
+
};
|
|
54
|
+
export const useToast = () => {
|
|
55
|
+
const showToast = useContext(ToastContext);
|
|
56
|
+
if (!showToast)
|
|
57
|
+
throw new Error("useToast must be used within ToastProvider");
|
|
58
|
+
return showToast;
|
|
59
|
+
};
|
|
60
|
+
const TimedToast = ({ id, duration, dismiss, action, onClose, onMouseEnter, onMouseLeave, onFocus, onBlur, ...props }) => {
|
|
61
|
+
const [isTimerPaused, setIsTimerPaused] = useState(false);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (isTimerPaused)
|
|
64
|
+
return;
|
|
65
|
+
const timeoutId = window.setTimeout(() => dismiss(id), duration);
|
|
66
|
+
return () => window.clearTimeout(timeoutId);
|
|
67
|
+
}, [dismiss, duration, id, isTimerPaused]);
|
|
68
|
+
return (_jsx(Toast, { ...props, onMouseEnter: (event) => {
|
|
69
|
+
setIsTimerPaused(true);
|
|
70
|
+
onMouseEnter?.(event);
|
|
71
|
+
}, onMouseLeave: (event) => {
|
|
72
|
+
if (!event.currentTarget.contains(document.activeElement))
|
|
73
|
+
setIsTimerPaused(false);
|
|
74
|
+
onMouseLeave?.(event);
|
|
75
|
+
}, onFocus: (event) => {
|
|
76
|
+
setIsTimerPaused(true);
|
|
77
|
+
onFocus?.(event);
|
|
78
|
+
}, onBlur: (event) => {
|
|
79
|
+
if (!event.currentTarget.contains(event.relatedTarget) &&
|
|
80
|
+
!event.currentTarget.matches(":hover"))
|
|
81
|
+
setIsTimerPaused(false);
|
|
82
|
+
onBlur?.(event);
|
|
83
|
+
}, action: action && {
|
|
84
|
+
label: action.label,
|
|
85
|
+
onClick: () => {
|
|
86
|
+
dismiss(id);
|
|
87
|
+
action.onClick();
|
|
88
|
+
},
|
|
89
|
+
}, onClose: () => {
|
|
90
|
+
onClose?.();
|
|
91
|
+
dismiss(id);
|
|
92
|
+
} }));
|
|
93
|
+
};
|
|
94
|
+
const ToastContainer = styled.div `
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: ${Spacing.px16};
|
|
98
|
+
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
width: ${({ $compact }) => ($compact ? "300px" : "400px")};
|
|
101
|
+
max-width: calc(100vw - ${Spacing.px40});
|
|
102
|
+
min-height: ${({ $compact }) => ($compact ? "44px" : "64px")};
|
|
103
|
+
padding: ${Spacing.px12};
|
|
104
|
+
|
|
105
|
+
background: ${({ $background }) => $background};
|
|
106
|
+
border: 1px solid ${({ $border }) => $border};
|
|
107
|
+
border-radius: ${Spacing.px8};
|
|
108
|
+
`;
|
|
109
|
+
const ToastLayout = styled.div `
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: flex-start;
|
|
112
|
+
gap: ${Spacing.px8};
|
|
113
|
+
flex: 1;
|
|
114
|
+
|
|
115
|
+
min-width: 0;
|
|
116
|
+
`;
|
|
117
|
+
const ToastDetails = styled.div `
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: flex-start;
|
|
120
|
+
gap: ${Spacing.px12};
|
|
121
|
+
flex: 1;
|
|
122
|
+
|
|
123
|
+
min-width: 0;
|
|
124
|
+
`;
|
|
125
|
+
const ToastIconContainer = styled.span `
|
|
126
|
+
display: grid;
|
|
127
|
+
place-items: center;
|
|
128
|
+
flex: 0 0 ${Spacing.px20};
|
|
129
|
+
|
|
130
|
+
height: ${Spacing.px20};
|
|
131
|
+
|
|
132
|
+
background: ${({ $background }) => $background};
|
|
133
|
+
border-radius: ${Spacing.px4};
|
|
134
|
+
`;
|
|
135
|
+
const ToastText = styled.div `
|
|
136
|
+
display: flex;
|
|
137
|
+
flex: 1;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
|
|
141
|
+
min-width: 0;
|
|
142
|
+
`;
|
|
143
|
+
const Title = styled.div `
|
|
144
|
+
color: ${Color.typography.blackHigh};
|
|
145
|
+
|
|
146
|
+
${applyTypography(Typography.bodySecondaryBold)}
|
|
147
|
+
`;
|
|
148
|
+
const Message = styled.div `
|
|
149
|
+
color: ${Color.typography.blackMedium};
|
|
150
|
+
|
|
151
|
+
${applyTypography(Typography.bodySecondaryRegular)}
|
|
152
|
+
`;
|
|
153
|
+
const ActionButton = styled.button `
|
|
154
|
+
align-self: center;
|
|
155
|
+
flex: none;
|
|
156
|
+
|
|
157
|
+
box-sizing: border-box;
|
|
158
|
+
height: ${Spacing.px40};
|
|
159
|
+
padding: 10px 0;
|
|
160
|
+
|
|
161
|
+
color: ${Color.typography.blackHigh};
|
|
162
|
+
background: transparent;
|
|
163
|
+
|
|
164
|
+
text-decoration: underline;
|
|
165
|
+
white-space: nowrap;
|
|
166
|
+
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
|
|
169
|
+
${applyTypography(Typography.bodySecondaryMedium)}
|
|
170
|
+
`;
|
|
171
|
+
const CloseButton = styled.button `
|
|
172
|
+
display: grid;
|
|
173
|
+
place-items: center;
|
|
174
|
+
align-self: center;
|
|
175
|
+
flex: 0 0 ${Spacing.px24};
|
|
176
|
+
|
|
177
|
+
width: ${Spacing.px24};
|
|
178
|
+
height: ${Spacing.px24};
|
|
179
|
+
|
|
180
|
+
background: transparent;
|
|
181
|
+
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
`;
|
|
184
|
+
const toastPulse = keyframes `
|
|
185
|
+
0%, 100% { transform: scale(1); }
|
|
186
|
+
50% { transform: scale(1.03); }
|
|
187
|
+
`;
|
|
188
|
+
const StackItem = styled.div `
|
|
189
|
+
grid-area: 1 / 1;
|
|
190
|
+
z-index: ${({ $index }) => 3 - $index};
|
|
191
|
+
|
|
192
|
+
transform: translate(
|
|
193
|
+
${({ $index }) => `${-$index * 8}px`},
|
|
194
|
+
${({ $index }) => `${-$index * 8}px`}
|
|
195
|
+
);
|
|
196
|
+
transition: transform 250ms ease-in-out;
|
|
197
|
+
pointer-events: ${({ $index }) => ($index === 0 ? "auto" : "none")};
|
|
198
|
+
|
|
199
|
+
${({ $index }) => $index > 0 &&
|
|
200
|
+
`
|
|
201
|
+
> [role] > * {
|
|
202
|
+
visibility: hidden;
|
|
203
|
+
}
|
|
204
|
+
`}
|
|
205
|
+
|
|
206
|
+
&[data-pulse="true"] > [role] {
|
|
207
|
+
animation: ${toastPulse} 250ms ease-in-out;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@media (prefers-reduced-motion: reduce) {
|
|
211
|
+
transition: none;
|
|
212
|
+
|
|
213
|
+
&[data-pulse="true"] > [role] {
|
|
214
|
+
animation: none;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
`;
|
|
218
|
+
const Viewport = styled.div `
|
|
219
|
+
display: grid;
|
|
220
|
+
padding: ${({ $stackDepth }) => `${$stackDepth * 8}px 0 0 ${$stackDepth * 8}px`};
|
|
221
|
+
|
|
222
|
+
z-index: 100;
|
|
223
|
+
position: fixed;
|
|
224
|
+
top: 25px;
|
|
225
|
+
right: ${Spacing.px20};
|
|
226
|
+
transition: padding 250ms ease-in-out;
|
|
227
|
+
|
|
228
|
+
@media (prefers-reduced-motion: reduce) {
|
|
229
|
+
transition: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@media (width < ${Responsive.widthBreakpoint.laptop}) {
|
|
233
|
+
top: auto;
|
|
234
|
+
right: 50%;
|
|
235
|
+
bottom: var(--toast-mobile-bottom-offset, ${Spacing.px88});
|
|
236
|
+
transform: translateX(50%);
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
//# sourceMappingURL=Toast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toast.js","sourceRoot":"","sources":["../../../core/molecules/Toast.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n type HTMLAttributes,\n type PropsWithChildren,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport styled, { keyframes } from \"styled-components\";\nimport { Color, Responsive, Spacing, SystemIcon, Typography } from \"../atoms\";\nimport { applyTypography } from \"../integrations\";\n\nexport type ToastVariant = \"neutral\" | \"success\" | \"danger\";\n\nexport type ToastProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n \"children\" | \"title\"\n> & {\n title?: ReactNode;\n message?: ReactNode;\n variant?: ToastVariant;\n action?: {\n label: ReactNode;\n onClick: () => void;\n };\n onClose?: () => void;\n};\n\nconst toastVariants = {\n neutral: {\n iconBackground: Color.neutral.grey1,\n border: Color.neutral.grey2,\n background: Color.neutral.grey3,\n Icon: null,\n },\n success: {\n iconBackground: Color.accent.green1,\n border: Color.accent.green2,\n background: Color.accent.green4,\n Icon: SystemIcon.CheckIcon,\n },\n danger: {\n iconBackground: Color.accent.red1,\n border: Color.accent.red2,\n background: Color.accent.red4,\n Icon: SystemIcon.XIcon,\n },\n} as const;\n\nconst Toast = ({\n title,\n message,\n variant = \"neutral\",\n action,\n onClose,\n ...props\n}: ToastProps) => {\n const { iconBackground, border, background, Icon } = toastVariants[variant];\n const isCompact = title == null && !action;\n\n return (\n <ToastContainer\n {...props}\n role={variant === \"danger\" ? \"alert\" : \"status\"}\n $border={border}\n $background={background}\n $compact={isCompact}\n >\n <ToastLayout>\n <ToastDetails>\n <ToastIconContainer $background={iconBackground}>\n {Icon ? (\n <Icon color={Color.neutral.white} size={14} weight=\"bold\" />\n ) : (\n <SystemIcon.InfoIcon\n color={Color.neutral.white}\n size={14}\n weight=\"bold\"\n />\n )}\n </ToastIconContainer>\n <ToastText>\n {title != null && <Title>{title}</Title>}\n {message != null && <Message>{message}</Message>}\n </ToastText>\n </ToastDetails>\n {action && (\n <ActionButton type=\"button\" onClick={action.onClick}>\n {action.label}\n </ActionButton>\n )}\n </ToastLayout>\n {onClose && (\n <CloseButton type=\"button\" onClick={onClose}>\n <SystemIcon.XIcon color={Color.typography.blackMedium} size={18} />\n </CloseButton>\n )}\n </ToastContainer>\n );\n};\n\nexport default Toast;\n\nexport type ToastOptions = ToastProps & {\n dedupeKey?: string;\n duration?: number;\n};\n\ntype ToastItem = ToastOptions & { id: string; revision: number };\n\nconst ToastContext = createContext<((options: ToastOptions) => void) | null>(\n null,\n);\n\nexport const ToastProvider = ({ children }: PropsWithChildren) => {\n const [toasts, setToasts] = useState<ToastItem[]>([]);\n const dismissToast = useCallback(\n (id: string) =>\n setToasts((currentToasts) =>\n currentToasts.filter((toast) => toast.id !== id),\n ),\n [],\n );\n const showToast = useCallback(\n (options: ToastOptions) =>\n setToasts((currentToasts) => {\n const existingToastIndex = options.dedupeKey\n ? currentToasts.findIndex(\n (toast) => toast.dedupeKey === options.dedupeKey,\n )\n : -1;\n const nextToast =\n existingToastIndex < 0\n ? { ...options, id: crypto.randomUUID(), revision: 0 }\n : {\n ...currentToasts[existingToastIndex],\n ...options,\n revision: currentToasts[existingToastIndex].revision + 1,\n };\n\n return [\n nextToast,\n ...currentToasts.filter(\n (_, toastIndex) => toastIndex !== existingToastIndex,\n ),\n ].slice(0, 3);\n }),\n [],\n );\n\n return (\n <ToastContext.Provider value={showToast}>\n {children}\n <Viewport\n data-slot=\"toast-viewport\"\n $stackDepth={Math.max(0, toasts.length - 1)}\n >\n {toasts.map(\n ({ id, revision, dedupeKey, duration = 8000, ...props }, index) => (\n <StackItem\n key={`${dedupeKey ?? id}-${revision}`}\n data-pulse={revision > 0}\n $index={index}\n >\n <TimedToast\n {...props}\n id={id}\n duration={duration}\n dismiss={dismissToast}\n />\n </StackItem>\n ),\n )}\n </Viewport>\n </ToastContext.Provider>\n );\n};\n\nexport const useToast = () => {\n const showToast = useContext(ToastContext);\n if (!showToast) throw new Error(\"useToast must be used within ToastProvider\");\n return showToast;\n};\n\nconst TimedToast = ({\n id,\n duration,\n dismiss,\n action,\n onClose,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}: ToastOptions & { id: string; dismiss: (id: string) => void }) => {\n const [isTimerPaused, setIsTimerPaused] = useState(false);\n\n useEffect(() => {\n if (isTimerPaused) return;\n const timeoutId = window.setTimeout(() => dismiss(id), duration);\n return () => window.clearTimeout(timeoutId);\n }, [dismiss, duration, id, isTimerPaused]);\n\n return (\n <Toast\n {...props}\n onMouseEnter={(event) => {\n setIsTimerPaused(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n if (!event.currentTarget.contains(document.activeElement))\n setIsTimerPaused(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsTimerPaused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (\n !event.currentTarget.contains(event.relatedTarget) &&\n !event.currentTarget.matches(\":hover\")\n )\n setIsTimerPaused(false);\n onBlur?.(event);\n }}\n action={\n action && {\n label: action.label,\n onClick: () => {\n dismiss(id);\n action.onClick();\n },\n }\n }\n onClose={() => {\n onClose?.();\n dismiss(id);\n }}\n />\n );\n};\n\nconst ToastContainer = styled.div<{\n $border: Color.DesignColor;\n $background: Color.DesignColor;\n $compact: boolean;\n}>`\n display: flex;\n align-items: center;\n gap: ${Spacing.px16};\n\n box-sizing: border-box;\n width: ${({ $compact }) => ($compact ? \"300px\" : \"400px\")};\n max-width: calc(100vw - ${Spacing.px40});\n min-height: ${({ $compact }) => ($compact ? \"44px\" : \"64px\")};\n padding: ${Spacing.px12};\n\n background: ${({ $background }) => $background};\n border: 1px solid ${({ $border }) => $border};\n border-radius: ${Spacing.px8};\n`;\n\nconst ToastLayout = styled.div`\n display: flex;\n align-items: flex-start;\n gap: ${Spacing.px8};\n flex: 1;\n\n min-width: 0;\n`;\n\nconst ToastDetails = styled.div`\n display: flex;\n align-items: flex-start;\n gap: ${Spacing.px12};\n flex: 1;\n\n min-width: 0;\n`;\n\nconst ToastIconContainer = styled.span<{\n $background: Color.DesignColor;\n}>`\n display: grid;\n place-items: center;\n flex: 0 0 ${Spacing.px20};\n\n height: ${Spacing.px20};\n\n background: ${({ $background }) => $background};\n border-radius: ${Spacing.px4};\n`;\n\nconst ToastText = styled.div`\n display: flex;\n flex: 1;\n flex-direction: column;\n justify-content: center;\n\n min-width: 0;\n`;\n\nconst Title = styled.div`\n color: ${Color.typography.blackHigh};\n\n ${applyTypography(Typography.bodySecondaryBold)}\n`;\n\nconst Message = styled.div`\n color: ${Color.typography.blackMedium};\n\n ${applyTypography(Typography.bodySecondaryRegular)}\n`;\n\nconst ActionButton = styled.button`\n align-self: center;\n flex: none;\n\n box-sizing: border-box;\n height: ${Spacing.px40};\n padding: 10px 0;\n\n color: ${Color.typography.blackHigh};\n background: transparent;\n\n text-decoration: underline;\n white-space: nowrap;\n\n cursor: pointer;\n\n ${applyTypography(Typography.bodySecondaryMedium)}\n`;\n\nconst CloseButton = styled.button`\n display: grid;\n place-items: center;\n align-self: center;\n flex: 0 0 ${Spacing.px24};\n\n width: ${Spacing.px24};\n height: ${Spacing.px24};\n\n background: transparent;\n\n cursor: pointer;\n`;\n\nconst toastPulse = keyframes`\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.03); }\n`;\n\nconst StackItem = styled.div<{ $index: number }>`\n grid-area: 1 / 1;\n z-index: ${({ $index }) => 3 - $index};\n\n transform: translate(\n ${({ $index }) => `${-$index * 8}px`},\n ${({ $index }) => `${-$index * 8}px`}\n );\n transition: transform 250ms ease-in-out;\n pointer-events: ${({ $index }) => ($index === 0 ? \"auto\" : \"none\")};\n\n ${({ $index }) =>\n $index > 0 &&\n `\n > [role] > * {\n visibility: hidden;\n }\n `}\n\n &[data-pulse=\"true\"] > [role] {\n animation: ${toastPulse} 250ms ease-in-out;\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n\n &[data-pulse=\"true\"] > [role] {\n animation: none;\n }\n }\n`;\n\nconst Viewport = styled.div<{ $stackDepth: number }>`\n display: grid;\n padding: ${({ $stackDepth }) =>\n `${$stackDepth * 8}px 0 0 ${$stackDepth * 8}px`};\n\n z-index: 100;\n position: fixed;\n top: 25px;\n right: ${Spacing.px20};\n transition: padding 250ms ease-in-out;\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n\n @media (width < ${Responsive.widthBreakpoint.laptop}) {\n top: auto;\n right: 50%;\n bottom: var(--toast-mobile-bottom-offset, ${Spacing.px88});\n transform: translateX(50%);\n }\n`;\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type HTMLAttributes } from "react";
|
|
2
|
+
export type ToggleButtonSize = "small" | "medium" | "large";
|
|
3
|
+
export type ToggleButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> & {
|
|
4
|
+
pressed?: boolean;
|
|
5
|
+
onPressedChange?: (isPressed: boolean) => void;
|
|
6
|
+
size?: ToggleButtonSize;
|
|
7
|
+
};
|
|
8
|
+
declare const ToggleButton: ({ pressed, onPressedChange, onClick, size, type, ...props }: ToggleButtonProps) => import("react").JSX.Element;
|
|
9
|
+
export default ToggleButton;
|
|
10
|
+
export type ToggleButtonGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> & {
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
size?: ToggleButtonSize;
|
|
13
|
+
value?: string;
|
|
14
|
+
defaultValue?: string;
|
|
15
|
+
onValueChange?: (value: string) => void;
|
|
16
|
+
};
|
|
17
|
+
export declare const ToggleButtonGroup: ({ children, value, defaultValue, onValueChange, disabled, size, ...props }: ToggleButtonGroupProps) => import("react").JSX.Element;
|
|
18
|
+
export type ToggleButtonGroupItemProps = Omit<ToggleButtonProps, "onPressedChange" | "pressed" | "size"> & {
|
|
19
|
+
value: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const ToggleButtonGroupItem: ({ value, disabled, ...props }: ToggleButtonGroupItemProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useMemo, useState, } from "react";
|
|
4
|
+
import styled from "styled-components";
|
|
5
|
+
import { Color, Typography } from "../atoms";
|
|
6
|
+
import { applyTypography } from "../integrations";
|
|
7
|
+
const toggleButtonSizeStyles = {
|
|
8
|
+
small: {
|
|
9
|
+
iconButtonSize: "26px",
|
|
10
|
+
textButtonHeight: "26px",
|
|
11
|
+
iconSize: "14px",
|
|
12
|
+
padding: "6px",
|
|
13
|
+
},
|
|
14
|
+
medium: {
|
|
15
|
+
iconButtonSize: "46px",
|
|
16
|
+
textButtonHeight: "48px",
|
|
17
|
+
iconSize: "20px",
|
|
18
|
+
padding: "13px",
|
|
19
|
+
},
|
|
20
|
+
large: {
|
|
21
|
+
iconButtonSize: "52px",
|
|
22
|
+
textButtonHeight: "56px",
|
|
23
|
+
iconSize: "20px",
|
|
24
|
+
padding: "16px",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const toggleButtonTypography = {
|
|
28
|
+
small: {
|
|
29
|
+
...Typography.captionMedium,
|
|
30
|
+
fontSize: { small: "10px", large: "10px" },
|
|
31
|
+
},
|
|
32
|
+
medium: Typography.bodyPrimaryMedium,
|
|
33
|
+
large: Typography.subheading2Medium,
|
|
34
|
+
};
|
|
35
|
+
const ToggleButton = ({ pressed, onPressedChange, onClick, size = "medium", type = "button", ...props }) => {
|
|
36
|
+
const [isUncontrolledPressed, setIsUncontrolledPressed] = useState(false);
|
|
37
|
+
const isPressed = pressed ?? isUncontrolledPressed;
|
|
38
|
+
return (_jsx(ToggleButtonContainer, { ...props, type: type, "data-state": isPressed ? "on" : "off", "$size": size, onClick: (event) => {
|
|
39
|
+
onClick?.(event);
|
|
40
|
+
if (event.defaultPrevented)
|
|
41
|
+
return;
|
|
42
|
+
const shouldPress = !isPressed;
|
|
43
|
+
if (pressed === undefined)
|
|
44
|
+
setIsUncontrolledPressed(shouldPress);
|
|
45
|
+
onPressedChange?.(shouldPress);
|
|
46
|
+
} }));
|
|
47
|
+
};
|
|
48
|
+
export default ToggleButton;
|
|
49
|
+
const ToggleButtonGroupContext = createContext(null);
|
|
50
|
+
export const ToggleButtonGroup = ({ children, value, defaultValue, onValueChange, disabled = false, size = "medium", ...props }) => {
|
|
51
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? "");
|
|
52
|
+
const selectedValue = value ?? uncontrolledValue;
|
|
53
|
+
const contextValue = useMemo(() => ({
|
|
54
|
+
isGroupDisabled: disabled,
|
|
55
|
+
selectedValue,
|
|
56
|
+
groupSize: size,
|
|
57
|
+
toggleItem: (itemValue) => {
|
|
58
|
+
const nextSelectedValue = selectedValue === itemValue ? "" : itemValue;
|
|
59
|
+
if (value === undefined)
|
|
60
|
+
setUncontrolledValue(nextSelectedValue);
|
|
61
|
+
onValueChange?.(nextSelectedValue);
|
|
62
|
+
},
|
|
63
|
+
}), [disabled, onValueChange, selectedValue, size, value]);
|
|
64
|
+
return (_jsx(ToggleButtonGroupContext.Provider, { value: contextValue, children: _jsx(ToggleButtonGroupContainer, { ...props, role: "group", children: children }) }));
|
|
65
|
+
};
|
|
66
|
+
export const ToggleButtonGroupItem = ({ value, disabled, ...props }) => {
|
|
67
|
+
const groupContext = useContext(ToggleButtonGroupContext);
|
|
68
|
+
if (!groupContext)
|
|
69
|
+
throw new Error("ToggleButtonGroupItem must be used within ToggleButtonGroup");
|
|
70
|
+
return (_jsx(ToggleButton, { ...props, pressed: groupContext.selectedValue === value, disabled: groupContext.isGroupDisabled || disabled, size: groupContext.groupSize, onPressedChange: () => groupContext.toggleItem(value) }));
|
|
71
|
+
};
|
|
72
|
+
const ToggleButtonContainer = styled.button `
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
box-sizing: border-box;
|
|
77
|
+
min-width: ${({ $size }) => toggleButtonSizeStyles[$size].iconButtonSize};
|
|
78
|
+
height: ${({ $size }) => toggleButtonSizeStyles[$size].iconButtonSize};
|
|
79
|
+
padding: ${({ $size }) => toggleButtonSizeStyles[$size].padding};
|
|
80
|
+
|
|
81
|
+
${({ $size }) => applyTypography(toggleButtonTypography[$size])}
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
|
|
84
|
+
color: ${Color.typography.blackHigh};
|
|
85
|
+
background: ${Color.neutral.white};
|
|
86
|
+
border: 1px solid ${Color.neutral.grey2};
|
|
87
|
+
border-radius: 6px;
|
|
88
|
+
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
|
|
91
|
+
&:hover:not(:disabled):not([data-state="on"]) {
|
|
92
|
+
background: ${Color.accent.blue3};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&[data-state="on"] {
|
|
96
|
+
position: relative;
|
|
97
|
+
z-index: 1;
|
|
98
|
+
|
|
99
|
+
color: ${Color.typography.whiteHigh};
|
|
100
|
+
background: ${Color.brand.grantbiiBlue};
|
|
101
|
+
border-color: ${Color.brand.grantbiiBlue};
|
|
102
|
+
|
|
103
|
+
&:hover:not(:disabled) {
|
|
104
|
+
background: ${Color.accent.blue2};
|
|
105
|
+
border-color: ${Color.accent.blue2};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
&:focus-visible {
|
|
110
|
+
position: relative;
|
|
111
|
+
z-index: 2;
|
|
112
|
+
outline: 2px solid ${Color.accent.blue2};
|
|
113
|
+
outline-offset: 2px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
&:disabled {
|
|
117
|
+
color: ${Color.typography.blackMedium};
|
|
118
|
+
background: ${Color.neutral.grey2};
|
|
119
|
+
cursor: not-allowed;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
svg {
|
|
123
|
+
flex: none;
|
|
124
|
+
width: ${({ $size }) => toggleButtonSizeStyles[$size].iconSize};
|
|
125
|
+
height: ${({ $size }) => toggleButtonSizeStyles[$size].iconSize};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&:not(:has(svg)) {
|
|
129
|
+
height: ${({ $size }) => toggleButtonSizeStyles[$size].textButtonHeight};
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
const ToggleButtonGroupContainer = styled.div `
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
box-sizing: border-box;
|
|
135
|
+
|
|
136
|
+
border-radius: 6px;
|
|
137
|
+
outline: 1px solid ${Color.neutral.grey2};
|
|
138
|
+
outline-offset: -1px;
|
|
139
|
+
|
|
140
|
+
> button {
|
|
141
|
+
border: 0;
|
|
142
|
+
border-radius: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
> button:not(:last-child) {
|
|
146
|
+
border-right: 1px solid ${Color.neutral.grey2};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
> button:first-child {
|
|
150
|
+
border-radius: 6px 0 0 6px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
> button:last-child {
|
|
154
|
+
border-radius: 0 6px 6px 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
> button:only-child {
|
|
158
|
+
border-radius: 6px;
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
//# sourceMappingURL=ToggleButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToggleButton.js","sourceRoot":"","sources":["../../../core/molecules/ToggleButton.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useMemo,\n useState,\n type ButtonHTMLAttributes,\n type HTMLAttributes,\n} from \"react\";\nimport styled from \"styled-components\";\nimport { Color, Typography } from \"../atoms\";\nimport { applyTypography } from \"../integrations\";\n\nexport type ToggleButtonSize = \"small\" | \"medium\" | \"large\";\n\nconst toggleButtonSizeStyles: Record<\n ToggleButtonSize,\n {\n iconButtonSize: string;\n textButtonHeight: string;\n iconSize: string;\n padding: string;\n }\n> = {\n small: {\n iconButtonSize: \"26px\",\n textButtonHeight: \"26px\",\n iconSize: \"14px\",\n padding: \"6px\",\n },\n medium: {\n iconButtonSize: \"46px\",\n textButtonHeight: \"48px\",\n iconSize: \"20px\",\n padding: \"13px\",\n },\n large: {\n iconButtonSize: \"52px\",\n textButtonHeight: \"56px\",\n iconSize: \"20px\",\n padding: \"16px\",\n },\n};\n\nconst toggleButtonTypography: Record<ToggleButtonSize, Typography.TextStyle> = {\n small: {\n ...Typography.captionMedium,\n fontSize: { small: \"10px\", large: \"10px\" },\n },\n medium: Typography.bodyPrimaryMedium,\n large: Typography.subheading2Medium,\n};\n\nexport type ToggleButtonProps = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"onChange\"\n> & {\n pressed?: boolean;\n onPressedChange?: (isPressed: boolean) => void;\n size?: ToggleButtonSize;\n};\n\nconst ToggleButton = ({\n pressed,\n onPressedChange,\n onClick,\n size = \"medium\",\n type = \"button\",\n ...props\n}: ToggleButtonProps) => {\n const [isUncontrolledPressed, setIsUncontrolledPressed] = useState(false);\n const isPressed = pressed ?? isUncontrolledPressed;\n\n return (\n <ToggleButtonContainer\n {...props}\n type={type}\n data-state={isPressed ? \"on\" : \"off\"}\n $size={size}\n onClick={(event) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n\n const shouldPress = !isPressed;\n if (pressed === undefined) setIsUncontrolledPressed(shouldPress);\n onPressedChange?.(shouldPress);\n }}\n />\n );\n};\n\nexport default ToggleButton;\n\nexport type ToggleButtonGroupProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n \"defaultValue\" | \"onChange\"\n> & {\n disabled?: boolean;\n size?: ToggleButtonSize;\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n};\n\ntype ToggleButtonGroupContextValue = {\n isGroupDisabled: boolean;\n selectedValue: string;\n groupSize: ToggleButtonSize;\n toggleItem: (itemValue: string) => void;\n};\n\nconst ToggleButtonGroupContext =\n createContext<ToggleButtonGroupContextValue | null>(null);\n\nexport const ToggleButtonGroup = ({\n children,\n value,\n defaultValue,\n onValueChange,\n disabled = false,\n size = \"medium\",\n ...props\n}: ToggleButtonGroupProps) => {\n const [uncontrolledValue, setUncontrolledValue] = useState(\n defaultValue ?? \"\",\n );\n const selectedValue = value ?? uncontrolledValue;\n const contextValue = useMemo<ToggleButtonGroupContextValue>(\n () => ({\n isGroupDisabled: disabled,\n selectedValue,\n groupSize: size,\n toggleItem: (itemValue) => {\n const nextSelectedValue = selectedValue === itemValue ? \"\" : itemValue;\n if (value === undefined) setUncontrolledValue(nextSelectedValue);\n onValueChange?.(nextSelectedValue);\n },\n }),\n [disabled, onValueChange, selectedValue, size, value],\n );\n\n return (\n <ToggleButtonGroupContext.Provider value={contextValue}>\n <ToggleButtonGroupContainer {...props} role=\"group\">\n {children}\n </ToggleButtonGroupContainer>\n </ToggleButtonGroupContext.Provider>\n );\n};\n\nexport type ToggleButtonGroupItemProps = Omit<\n ToggleButtonProps,\n \"onPressedChange\" | \"pressed\" | \"size\"\n> & {\n value: string;\n};\n\nexport const ToggleButtonGroupItem = ({\n value,\n disabled,\n ...props\n}: ToggleButtonGroupItemProps) => {\n const groupContext = useContext(ToggleButtonGroupContext);\n if (!groupContext)\n throw new Error(\n \"ToggleButtonGroupItem must be used within ToggleButtonGroup\",\n );\n\n return (\n <ToggleButton\n {...props}\n pressed={groupContext.selectedValue === value}\n disabled={groupContext.isGroupDisabled || disabled}\n size={groupContext.groupSize}\n onPressedChange={() => groupContext.toggleItem(value)}\n />\n );\n};\n\nconst ToggleButtonContainer = styled.button<{ $size: ToggleButtonSize }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n min-width: ${({ $size }) => toggleButtonSizeStyles[$size].iconButtonSize};\n height: ${({ $size }) => toggleButtonSizeStyles[$size].iconButtonSize};\n padding: ${({ $size }) => toggleButtonSizeStyles[$size].padding};\n\n ${({ $size }) => applyTypography(toggleButtonTypography[$size])}\n white-space: nowrap;\n\n color: ${Color.typography.blackHigh};\n background: ${Color.neutral.white};\n border: 1px solid ${Color.neutral.grey2};\n border-radius: 6px;\n\n cursor: pointer;\n\n &:hover:not(:disabled):not([data-state=\"on\"]) {\n background: ${Color.accent.blue3};\n }\n\n &[data-state=\"on\"] {\n position: relative;\n z-index: 1;\n\n color: ${Color.typography.whiteHigh};\n background: ${Color.brand.grantbiiBlue};\n border-color: ${Color.brand.grantbiiBlue};\n\n &:hover:not(:disabled) {\n background: ${Color.accent.blue2};\n border-color: ${Color.accent.blue2};\n }\n }\n\n &:focus-visible {\n position: relative;\n z-index: 2;\n outline: 2px solid ${Color.accent.blue2};\n outline-offset: 2px;\n }\n\n &:disabled {\n color: ${Color.typography.blackMedium};\n background: ${Color.neutral.grey2};\n cursor: not-allowed;\n }\n\n svg {\n flex: none;\n width: ${({ $size }) => toggleButtonSizeStyles[$size].iconSize};\n height: ${({ $size }) => toggleButtonSizeStyles[$size].iconSize};\n }\n\n &:not(:has(svg)) {\n height: ${({ $size }) => toggleButtonSizeStyles[$size].textButtonHeight};\n }\n`;\n\nconst ToggleButtonGroupContainer = styled.div`\n display: inline-flex;\n box-sizing: border-box;\n\n border-radius: 6px;\n outline: 1px solid ${Color.neutral.grey2};\n outline-offset: -1px;\n\n > button {\n border: 0;\n border-radius: 0;\n }\n\n > button:not(:last-child) {\n border-right: 1px solid ${Color.neutral.grey2};\n }\n\n > button:first-child {\n border-radius: 6px 0 0 6px;\n }\n\n > button:last-child {\n border-radius: 0 6px 6px 0;\n }\n\n > button:only-child {\n border-radius: 6px;\n }\n`;\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type HTMLAttributes, type PropsWithChildren, type ReactElement } from "react";
|
|
2
|
+
export type TooltipSide = "top" | "right" | "bottom" | "left";
|
|
3
|
+
export type TooltipProps = PropsWithChildren<{
|
|
4
|
+
open?: boolean;
|
|
5
|
+
defaultOpen?: boolean;
|
|
6
|
+
onOpenChange?: (open: boolean) => void;
|
|
7
|
+
}>;
|
|
8
|
+
declare const Tooltip: ({ children, open: isControlledOpen, defaultOpen, onOpenChange, }: TooltipProps) => import("react").JSX.Element;
|
|
9
|
+
export default Tooltip;
|
|
10
|
+
export type TooltipTriggerProps = {
|
|
11
|
+
children: ReactElement<{
|
|
12
|
+
"aria-describedby"?: string;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
export declare const TooltipTrigger: ({ children }: TooltipTriggerProps) => import("react").JSX.Element;
|
|
16
|
+
export type TooltipContentProps = PropsWithChildren<Omit<HTMLAttributes<HTMLDivElement>, "content"> & {
|
|
17
|
+
side?: TooltipSide;
|
|
18
|
+
sideOffset?: number;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const TooltipContent: ({ children, side, sideOffset, ...props }: TooltipContentProps) => import("react").ReactPortal | null;
|