@fibery/ui-kit 1.7.3 → 1.7.5
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/package.json +6 -5
- package/src/BackButton.tsx +3 -0
- package/src/Button/AddButton.tsx +13 -0
- package/src/Button/BackButton.tsx +26 -0
- package/src/Button/button-base.tsx +190 -0
- package/src/Button/button-group.tsx +76 -0
- package/src/Button/button.tsx +242 -0
- package/src/Button/icon-button-with-tooltip.tsx +15 -0
- package/src/Button/icon-button.tsx +42 -0
- /package/src/{Button.ts → Button.tsx} +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fibery/ui-kit",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.5",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
7
7
|
"src/antd/styles.ts",
|
|
8
|
-
"src/Button.
|
|
8
|
+
"src/Button.tsx",
|
|
9
9
|
"src/designSystem.ts",
|
|
10
10
|
"src/theme-styles.ts",
|
|
11
11
|
"src/theme-settings.ts",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"src/error-alert.tsx",
|
|
22
22
|
"src/Pallete.ts",
|
|
23
23
|
"src/tooltip.tsx",
|
|
24
|
-
"src/BackButton.tsx"
|
|
24
|
+
"src/BackButton.tsx",
|
|
25
|
+
"src/Button"
|
|
25
26
|
],
|
|
26
27
|
"license": "UNLICENSED",
|
|
27
28
|
"dependencies": {
|
|
@@ -57,8 +58,8 @@
|
|
|
57
58
|
"react-windowed-select": "5.0.0",
|
|
58
59
|
"screenfull": "6.0.1",
|
|
59
60
|
"ua-parser-js": "0.7.24",
|
|
60
|
-
"@fibery/
|
|
61
|
-
"@fibery/
|
|
61
|
+
"@fibery/helpers": "1.0.2",
|
|
62
|
+
"@fibery/react": "1.0.0"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
65
|
"react": "^18.2.0",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {forwardRef} from "react";
|
|
2
|
+
import AddIcon from "../icons/react/Add";
|
|
3
|
+
import {Button, ButtonProps} from "./button";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
disableReason?: string;
|
|
7
|
+
} & ButtonProps;
|
|
8
|
+
|
|
9
|
+
export const AddButton = forwardRef<HTMLButtonElement, Props>(
|
|
10
|
+
({disableReason = "", Icon = AddIcon, ...rest}, forwardedRef) => {
|
|
11
|
+
return <Button ref={forwardedRef} borderless Icon={Icon} title={disableReason} {...rest} />;
|
|
12
|
+
}
|
|
13
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {ButtonSize, Button} from "./button";
|
|
2
|
+
import BackIcon from "../icons/react/Back";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
pending?: boolean;
|
|
7
|
+
onClick: () => unknown;
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
color?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function BackButton({disabled, onClick, pending, size, color}: Props) {
|
|
13
|
+
return (
|
|
14
|
+
<Button
|
|
15
|
+
borderless
|
|
16
|
+
Icon={BackIcon}
|
|
17
|
+
disabled={disabled}
|
|
18
|
+
onClick={onClick}
|
|
19
|
+
pending={pending}
|
|
20
|
+
size={size}
|
|
21
|
+
color={color}
|
|
22
|
+
>
|
|
23
|
+
Back
|
|
24
|
+
</Button>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {styled} from "@linaria/react";
|
|
2
|
+
import {ComponentPropsWithoutRef, forwardRef} from "react";
|
|
3
|
+
import {border, colors, fontWeight, getDarkenColor, getOpacities, opacity, ThemeColors} from "../designSystem";
|
|
4
|
+
import {useTheme} from "../ThemeProvider";
|
|
5
|
+
|
|
6
|
+
export const getMainColor = ({color, dangerous, theme}: {color?: string; dangerous?: boolean; theme: ThemeColors}) => {
|
|
7
|
+
if (color) {
|
|
8
|
+
return color;
|
|
9
|
+
}
|
|
10
|
+
if (dangerous) {
|
|
11
|
+
return theme.danger;
|
|
12
|
+
}
|
|
13
|
+
if (!theme) {
|
|
14
|
+
return colors.primary;
|
|
15
|
+
}
|
|
16
|
+
return theme.buttonColor;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getBorderColor = ({
|
|
20
|
+
mainColor,
|
|
21
|
+
borderless,
|
|
22
|
+
primary,
|
|
23
|
+
}: {
|
|
24
|
+
mainColor: string;
|
|
25
|
+
borderless?: boolean;
|
|
26
|
+
primary?: boolean;
|
|
27
|
+
}) => {
|
|
28
|
+
if (borderless) {
|
|
29
|
+
return colors.transparent;
|
|
30
|
+
}
|
|
31
|
+
if (primary) {
|
|
32
|
+
return mainColor;
|
|
33
|
+
}
|
|
34
|
+
return getOpacities(mainColor).opacity15;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getHoverBorderColor = ({
|
|
38
|
+
mainColor,
|
|
39
|
+
borderless,
|
|
40
|
+
primary,
|
|
41
|
+
hoverBgColor,
|
|
42
|
+
}: {
|
|
43
|
+
mainColor: string;
|
|
44
|
+
borderless?: boolean;
|
|
45
|
+
primary?: boolean;
|
|
46
|
+
hoverBgColor: string;
|
|
47
|
+
}) => {
|
|
48
|
+
if (borderless) {
|
|
49
|
+
return colors.transparent;
|
|
50
|
+
}
|
|
51
|
+
if (primary) {
|
|
52
|
+
return hoverBgColor;
|
|
53
|
+
}
|
|
54
|
+
return mainColor;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const getTextColor = ({
|
|
58
|
+
mainColor,
|
|
59
|
+
primary,
|
|
60
|
+
theme,
|
|
61
|
+
}: {
|
|
62
|
+
mainColor: string;
|
|
63
|
+
primary?: boolean;
|
|
64
|
+
theme: ThemeColors;
|
|
65
|
+
}) => {
|
|
66
|
+
if (primary) {
|
|
67
|
+
return theme.buttonPrimaryTextColor;
|
|
68
|
+
}
|
|
69
|
+
return mainColor;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getBgColor = ({mainColor, primary}: {mainColor: string; primary?: boolean}) => {
|
|
73
|
+
if (primary) {
|
|
74
|
+
return mainColor;
|
|
75
|
+
}
|
|
76
|
+
return colors.transparent;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const getHoverBgColor = ({
|
|
80
|
+
mainColor,
|
|
81
|
+
borderless,
|
|
82
|
+
primary,
|
|
83
|
+
}: {
|
|
84
|
+
mainColor: string;
|
|
85
|
+
primary?: boolean;
|
|
86
|
+
borderless?: boolean;
|
|
87
|
+
}) => {
|
|
88
|
+
if (primary) {
|
|
89
|
+
return getDarkenColor(mainColor);
|
|
90
|
+
}
|
|
91
|
+
if (borderless) {
|
|
92
|
+
return getOpacities(mainColor).opacity15;
|
|
93
|
+
}
|
|
94
|
+
return colors.transparent;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const buttonBorderWidth = 1;
|
|
98
|
+
|
|
99
|
+
type StyledProps = {
|
|
100
|
+
textColor: string;
|
|
101
|
+
bgColor: string;
|
|
102
|
+
borderColor: string;
|
|
103
|
+
hoverBgColor: string;
|
|
104
|
+
hoverBorderColor: string;
|
|
105
|
+
borderWidth: number;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const StyledButtonBase = styled.button<StyledProps>`
|
|
109
|
+
display: block;
|
|
110
|
+
box-sizing: border-box;
|
|
111
|
+
font-weight: ${fontWeight.bold};
|
|
112
|
+
margin: 0;
|
|
113
|
+
border-radius: ${border.radius6}px;
|
|
114
|
+
border: 0;
|
|
115
|
+
border-width: ${({borderWidth}) => `${borderWidth}px`};
|
|
116
|
+
border-style: solid;
|
|
117
|
+
padding: 0;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
transition: all 150ms ease-out;
|
|
120
|
+
color: ${({textColor}) => textColor};
|
|
121
|
+
background-color: ${({bgColor}) => bgColor};
|
|
122
|
+
border-color: ${({borderColor}) => borderColor};
|
|
123
|
+
|
|
124
|
+
&:disabled {
|
|
125
|
+
cursor: default;
|
|
126
|
+
opacity: ${opacity.opacity40};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&:focus {
|
|
130
|
+
outline: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&:focus-visible {
|
|
134
|
+
box-shadow: ${({borderColor}) => `0 0 0 3px ${getOpacities(borderColor).opacity25}`};
|
|
135
|
+
background-color: ${({hoverBgColor}) => hoverBgColor};
|
|
136
|
+
border-color: ${({hoverBorderColor}) => hoverBorderColor};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&:hover:not(:disabled) {
|
|
140
|
+
background-color: ${({hoverBgColor}) => hoverBgColor};
|
|
141
|
+
border-color: ${({hoverBorderColor}) => hoverBorderColor};
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
export type ButtonBaseProps = {
|
|
146
|
+
dataId?: string;
|
|
147
|
+
color?: string;
|
|
148
|
+
primary?: boolean;
|
|
149
|
+
dangerous?: boolean;
|
|
150
|
+
borderless?: boolean;
|
|
151
|
+
} & ComponentPropsWithoutRef<"button">;
|
|
152
|
+
export const ButtonBase = forwardRef<HTMLButtonElement, ButtonBaseProps>(function ButtonBase(
|
|
153
|
+
{primary, color, borderless, dangerous, className, type = "button", dataId, ...buttonProps},
|
|
154
|
+
ref
|
|
155
|
+
) {
|
|
156
|
+
const theme = useTheme();
|
|
157
|
+
|
|
158
|
+
const mainColor = getMainColor({color, dangerous, theme});
|
|
159
|
+
const textColor = getTextColor({mainColor, primary, theme});
|
|
160
|
+
const bgColor = getBgColor({mainColor, primary});
|
|
161
|
+
const hoverBgColor = getHoverBgColor({mainColor, borderless, primary});
|
|
162
|
+
const borderColor = getBorderColor({mainColor, borderless, primary});
|
|
163
|
+
const hoverBorderColor = getHoverBorderColor({
|
|
164
|
+
mainColor,
|
|
165
|
+
borderless,
|
|
166
|
+
primary,
|
|
167
|
+
hoverBgColor,
|
|
168
|
+
});
|
|
169
|
+
const borderWidth = borderless ? 0 : buttonBorderWidth;
|
|
170
|
+
|
|
171
|
+
const styledProps: StyledProps = {
|
|
172
|
+
bgColor,
|
|
173
|
+
borderColor,
|
|
174
|
+
hoverBgColor,
|
|
175
|
+
hoverBorderColor,
|
|
176
|
+
textColor,
|
|
177
|
+
borderWidth,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<StyledButtonBase
|
|
182
|
+
ref={ref}
|
|
183
|
+
type={type}
|
|
184
|
+
className={className}
|
|
185
|
+
data-testid={dataId}
|
|
186
|
+
{...styledProps}
|
|
187
|
+
{...buttonProps}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {Children, cloneElement, CSSProperties, ReactElement} from "react";
|
|
2
|
+
import {css, cx} from "@linaria/core";
|
|
3
|
+
import {border} from "../designSystem";
|
|
4
|
+
|
|
5
|
+
const ordersInRowGroup = {
|
|
6
|
+
first: css`
|
|
7
|
+
${{
|
|
8
|
+
display: "inline-flex",
|
|
9
|
+
borderRadius: `${border.radius6}px 0 0 ${border.radius6}px`,
|
|
10
|
+
marginRight: -1,
|
|
11
|
+
}}
|
|
12
|
+
`,
|
|
13
|
+
middle: css`
|
|
14
|
+
${{
|
|
15
|
+
display: "inline-flex",
|
|
16
|
+
borderRadius: border.radius0,
|
|
17
|
+
marginRight: -1,
|
|
18
|
+
}}
|
|
19
|
+
`,
|
|
20
|
+
last: css`
|
|
21
|
+
${{
|
|
22
|
+
display: "inline-flex",
|
|
23
|
+
borderRadius: `0 ${border.radius6}px ${border.radius6}px 0`,
|
|
24
|
+
}}
|
|
25
|
+
`,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ordersInColumnGroup = {
|
|
29
|
+
first: css`
|
|
30
|
+
${{
|
|
31
|
+
display: "inline-flex",
|
|
32
|
+
borderRadius: `${border.radius6}px ${border.radius6}px 0 0 `,
|
|
33
|
+
marginBottom: -1,
|
|
34
|
+
}}
|
|
35
|
+
`,
|
|
36
|
+
middle: css`
|
|
37
|
+
${{
|
|
38
|
+
display: "inline-flex",
|
|
39
|
+
borderRadius: border.radius0,
|
|
40
|
+
marginBottom: -1,
|
|
41
|
+
}}
|
|
42
|
+
`,
|
|
43
|
+
last: css`
|
|
44
|
+
${{
|
|
45
|
+
display: "inline-flex",
|
|
46
|
+
borderRadius: `0 0 ${border.radius6}px ${border.radius6}px`,
|
|
47
|
+
}}
|
|
48
|
+
`,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export interface ButtonGroupProps {
|
|
52
|
+
direction: CSSProperties["flexDirection"];
|
|
53
|
+
children: ReactElement[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function ButtonGroup({direction, children}: ButtonGroupProps) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
style={{
|
|
60
|
+
display: "flex",
|
|
61
|
+
alignItems: "flex-start",
|
|
62
|
+
flexDirection: direction,
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{Children.map(children, (child, index) => {
|
|
66
|
+
const inGroup = index === 0 ? "first" : index === children.length - 1 ? "last" : "middle";
|
|
67
|
+
return cloneElement(child, {
|
|
68
|
+
className: cx(
|
|
69
|
+
child.props.className,
|
|
70
|
+
direction === "row" ? ordersInRowGroup[inGroup] : ordersInColumnGroup[inGroup]
|
|
71
|
+
),
|
|
72
|
+
});
|
|
73
|
+
})}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {hasNonEmptyChild} from "@fibery/react/src/has-non-empty-child";
|
|
2
|
+
import {css} from "@linaria/core";
|
|
3
|
+
import {styled} from "@linaria/react";
|
|
4
|
+
import cx from "classnames";
|
|
5
|
+
import _ from "lodash";
|
|
6
|
+
import React, {forwardRef} from "react";
|
|
7
|
+
import {ButtonBase, ButtonBaseProps, buttonBorderWidth, getMainColor, getTextColor} from "./button-base";
|
|
8
|
+
import {border, colors, getOpacities, opacity, space, textStyles} from "../designSystem";
|
|
9
|
+
import MoreIcon from "../icons/react/More";
|
|
10
|
+
import MoreCompactIcon from "../icons/react/MoreCompact";
|
|
11
|
+
import {IconBaseProps} from "../icons/types";
|
|
12
|
+
import {Item} from "../Item";
|
|
13
|
+
import {Spinner} from "../loaders";
|
|
14
|
+
import {useTheme} from "../ThemeProvider";
|
|
15
|
+
|
|
16
|
+
type PositionInGroup = "first" | "middle" | "last";
|
|
17
|
+
|
|
18
|
+
export const ButtonSize = {
|
|
19
|
+
superSmall: ":button-size/super-small",
|
|
20
|
+
small: ":button-size/small",
|
|
21
|
+
normal: ":button-size/normal",
|
|
22
|
+
big: ":button-size/big",
|
|
23
|
+
} as const;
|
|
24
|
+
export type ButtonSize = (typeof ButtonSize)[keyof typeof ButtonSize];
|
|
25
|
+
|
|
26
|
+
const FontSizes = {
|
|
27
|
+
":button-size/super-small": 10,
|
|
28
|
+
":button-size/small": 12,
|
|
29
|
+
":button-size/normal": 14,
|
|
30
|
+
":button-size/big": 16,
|
|
31
|
+
} as const;
|
|
32
|
+
|
|
33
|
+
const HorizontalPaddings = {
|
|
34
|
+
":button-size/super-small": 4,
|
|
35
|
+
":button-size/small": 8,
|
|
36
|
+
":button-size/normal": 12,
|
|
37
|
+
":button-size/big": 16,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
const IconSizes = {
|
|
41
|
+
":button-size/super-small": 16,
|
|
42
|
+
":button-size/small": 18,
|
|
43
|
+
":button-size/normal": 20,
|
|
44
|
+
":button-size/big": 22,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type StyledProps = {
|
|
48
|
+
buttonSize: ButtonSize;
|
|
49
|
+
borderless?: boolean;
|
|
50
|
+
buttonWidth?: number | string;
|
|
51
|
+
buttonHeight?: number | string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getFontSize = ({buttonSize}: StyledProps) => FontSizes[buttonSize];
|
|
55
|
+
const getButtonMinSize = ({buttonSize}: StyledProps) => FontSizes[buttonSize] * 2;
|
|
56
|
+
const getHorizontalPadding = ({buttonSize, borderless}: StyledProps) =>
|
|
57
|
+
borderless ? HorizontalPaddings[buttonSize] : HorizontalPaddings[buttonSize] - buttonBorderWidth;
|
|
58
|
+
|
|
59
|
+
const StyledButton = styled(ButtonBase)<StyledProps>`
|
|
60
|
+
display: flex;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
align-items: center;
|
|
63
|
+
vertical-align: middle;
|
|
64
|
+
position: relative;
|
|
65
|
+
|
|
66
|
+
width: ${({buttonWidth = "auto"}) => (typeof buttonWidth === "string" ? buttonWidth : `${buttonWidth}px`)};
|
|
67
|
+
height: ${({buttonHeight = "auto"}) => (typeof buttonHeight === "string" ? buttonHeight : `${buttonHeight}px`)};
|
|
68
|
+
|
|
69
|
+
font-size: ${getFontSize}px;
|
|
70
|
+
min-height: ${getButtonMinSize}px;
|
|
71
|
+
min-width: ${getButtonMinSize}px;
|
|
72
|
+
padding-left: ${getHorizontalPadding}px;
|
|
73
|
+
padding-right: ${getHorizontalPadding}px;
|
|
74
|
+
`;
|
|
75
|
+
export const buttonClassName = StyledButton;
|
|
76
|
+
|
|
77
|
+
// TODO: should be a different component, e.g. ButtonGroup
|
|
78
|
+
const ordersInGroup = {
|
|
79
|
+
first: css`
|
|
80
|
+
${{
|
|
81
|
+
display: "inline-flex",
|
|
82
|
+
borderRadius: `${border.radius6}px 0 0 ${border.radius6}px`,
|
|
83
|
+
marginRight: -1,
|
|
84
|
+
}}
|
|
85
|
+
`,
|
|
86
|
+
middle: css`
|
|
87
|
+
${{
|
|
88
|
+
display: "inline-flex",
|
|
89
|
+
borderRadius: border.radius0,
|
|
90
|
+
marginRight: -1,
|
|
91
|
+
}}
|
|
92
|
+
`,
|
|
93
|
+
last: css`
|
|
94
|
+
${{
|
|
95
|
+
display: "inline-flex",
|
|
96
|
+
borderRadius: `0 ${border.radius6}px ${border.radius6}px 0`,
|
|
97
|
+
}}
|
|
98
|
+
`,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const getSpinnerColor = ({mainColor, primary}: {mainColor: string; primary?: boolean}) => {
|
|
102
|
+
if (primary) {
|
|
103
|
+
return colors.inversedTextColor;
|
|
104
|
+
}
|
|
105
|
+
return mainColor;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const getIconMagicConstants = _.memoize((size: ButtonSize) => {
|
|
109
|
+
const iconSize = IconSizes[size];
|
|
110
|
+
|
|
111
|
+
const leftContainerSize = 8;
|
|
112
|
+
const leftContainerMarginRight = iconSize - leftContainerSize;
|
|
113
|
+
|
|
114
|
+
return {iconSize, leftContainerSize, leftContainerMarginRight};
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const shortcutStyle = css`
|
|
118
|
+
${{
|
|
119
|
+
...textStyles.small,
|
|
120
|
+
color: "currentColor",
|
|
121
|
+
borderRadius: border.radius4,
|
|
122
|
+
paddingLeft: space.xs,
|
|
123
|
+
paddingRight: space.xs,
|
|
124
|
+
paddingBottom: space.xxs,
|
|
125
|
+
marginRight: -space.xs,
|
|
126
|
+
marginLeft: space.xxs,
|
|
127
|
+
opacity: opacity.opacity80,
|
|
128
|
+
display: "inline-block",
|
|
129
|
+
}}
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
const leftContainerMarginRightVar = "--fibery-button-left-container-margin-right";
|
|
133
|
+
|
|
134
|
+
const leftContainerClassName = css`
|
|
135
|
+
margin-right: var(${leftContainerMarginRightVar});
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
export type ButtonProps = ButtonBaseProps & {
|
|
139
|
+
size?: ButtonSize;
|
|
140
|
+
pending?: boolean;
|
|
141
|
+
Icon?: ((props: IconBaseProps) => JSX.Element) | null;
|
|
142
|
+
inGroup?: PositionInGroup;
|
|
143
|
+
shortcut?: string;
|
|
144
|
+
noOverflow?: boolean;
|
|
145
|
+
width?: string | number;
|
|
146
|
+
height?: string | number;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
150
|
+
{
|
|
151
|
+
disabled,
|
|
152
|
+
pending,
|
|
153
|
+
Icon,
|
|
154
|
+
inGroup,
|
|
155
|
+
shortcut,
|
|
156
|
+
noOverflow = true,
|
|
157
|
+
width,
|
|
158
|
+
height,
|
|
159
|
+
size = ":button-size/normal",
|
|
160
|
+
className,
|
|
161
|
+
children,
|
|
162
|
+
...baseProps
|
|
163
|
+
},
|
|
164
|
+
forwardedRef
|
|
165
|
+
) {
|
|
166
|
+
const theme = useTheme();
|
|
167
|
+
const mainColor = getMainColor({color: baseProps.color, dangerous: baseProps.dangerous, theme});
|
|
168
|
+
const textColor = getTextColor({mainColor, primary: baseProps.primary, theme});
|
|
169
|
+
const spinnerBgColor = getOpacities(textColor).opacity25;
|
|
170
|
+
const spinnerColor = getSpinnerColor({mainColor, primary: baseProps.primary});
|
|
171
|
+
|
|
172
|
+
const {iconSize, leftContainerSize, leftContainerMarginRight} = getIconMagicConstants(size);
|
|
173
|
+
|
|
174
|
+
const leftContainer = pending ? (
|
|
175
|
+
<Spinner backgroundColor={spinnerBgColor} color={spinnerColor} size={iconSize} containerSize={leftContainerSize} />
|
|
176
|
+
) : Icon ? (
|
|
177
|
+
<Icon color={textColor} iconSize={iconSize} containerSize={leftContainerSize} />
|
|
178
|
+
) : null;
|
|
179
|
+
|
|
180
|
+
const styleVariable: StyledProps = {
|
|
181
|
+
buttonHeight: height,
|
|
182
|
+
buttonWidth: width,
|
|
183
|
+
buttonSize: size,
|
|
184
|
+
borderless: baseProps.borderless,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const isDisabled = disabled || pending;
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<StyledButton
|
|
191
|
+
{...styleVariable}
|
|
192
|
+
ref={forwardedRef}
|
|
193
|
+
onClick={!isDisabled && baseProps.onClick ? baseProps.onClick : _.noop}
|
|
194
|
+
disabled={isDisabled}
|
|
195
|
+
className={cx(className, inGroup && ordersInGroup[inGroup])}
|
|
196
|
+
{...baseProps}
|
|
197
|
+
>
|
|
198
|
+
<Item
|
|
199
|
+
leftContainer={leftContainer}
|
|
200
|
+
style={
|
|
201
|
+
{
|
|
202
|
+
[leftContainerMarginRightVar]: `${leftContainerMarginRight}px`,
|
|
203
|
+
} as React.CSSProperties
|
|
204
|
+
}
|
|
205
|
+
containerClassName={cx({[leftContainerClassName]: leftContainer && hasNonEmptyChild(children)})}
|
|
206
|
+
rightContainer={shortcut && <span className={cx(shortcutStyle)}>{shortcut}</span>}
|
|
207
|
+
noOverflow={noOverflow}
|
|
208
|
+
>
|
|
209
|
+
{children}
|
|
210
|
+
</Item>
|
|
211
|
+
</StyledButton>
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// TODO can be inlined
|
|
216
|
+
export function ActionsButton({
|
|
217
|
+
onClick,
|
|
218
|
+
label = ``,
|
|
219
|
+
disabled,
|
|
220
|
+
}: {
|
|
221
|
+
onClick: ButtonProps["onClick"];
|
|
222
|
+
label?: string;
|
|
223
|
+
disabled?: boolean;
|
|
224
|
+
}) {
|
|
225
|
+
return <Button Icon={MoreIcon} onClick={onClick} borderless aria-label={label} disabled={disabled} />;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const ActionsButtonCompact = forwardRef<HTMLButtonElement, ButtonProps & {inverted?: boolean}>(
|
|
229
|
+
({inverted, ...rest}, ref) => {
|
|
230
|
+
const theme = useTheme();
|
|
231
|
+
return (
|
|
232
|
+
<Button
|
|
233
|
+
ref={ref}
|
|
234
|
+
size={":button-size/small"}
|
|
235
|
+
Icon={MoreCompactIcon}
|
|
236
|
+
color={inverted ? colors.inversedTextColor : theme.buttonColor}
|
|
237
|
+
borderless
|
|
238
|
+
{...rest}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {forwardRef} from "react";
|
|
2
|
+
import {Tooltip, TooltipProps} from "../tooltip";
|
|
3
|
+
import {IconButton, IconButtonProps} from "./icon-button";
|
|
4
|
+
|
|
5
|
+
type IconButtonTooltipProps = Pick<TooltipProps, "title" | "description" | "content" | "side">;
|
|
6
|
+
|
|
7
|
+
export const IconButtonWithTooltip = forwardRef<HTMLButtonElement, IconButtonTooltipProps & IconButtonProps>(
|
|
8
|
+
function IconButtonWithTooltip({title, description, content, side, ...props}, ref) {
|
|
9
|
+
return (
|
|
10
|
+
<Tooltip title={title} description={description} content={content} side={side}>
|
|
11
|
+
<IconButton ref={ref} {...props} />
|
|
12
|
+
</Tooltip>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {styled} from "@linaria/react";
|
|
2
|
+
import {Children, cloneElement, forwardRef} from "react";
|
|
3
|
+
import {ButtonBase, ButtonBaseProps} from "./button-base";
|
|
4
|
+
import {IconProps} from "../icons/types";
|
|
5
|
+
|
|
6
|
+
export type IconButtonSize = "super-small" | "small" | "normal" | "big";
|
|
7
|
+
|
|
8
|
+
export type IconButtonProps = Omit<ButtonBaseProps, "borderless"> & {
|
|
9
|
+
size?: IconButtonSize;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Paddings = {
|
|
13
|
+
"super-small": 0,
|
|
14
|
+
small: 2,
|
|
15
|
+
normal: 4,
|
|
16
|
+
big: 6,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type StyledProps = {
|
|
20
|
+
/** The size of the component. `super-small` is equivalent to the dense button styling */
|
|
21
|
+
size: IconButtonSize;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getPadding = ({size}: StyledProps) => Paddings[size];
|
|
25
|
+
|
|
26
|
+
const StyledIconButton = styled(ButtonBase)<StyledProps>`
|
|
27
|
+
padding: ${getPadding}px;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(
|
|
31
|
+
{size = "normal", children, className, ...rest},
|
|
32
|
+
ref
|
|
33
|
+
) {
|
|
34
|
+
const iconNode = Children.only(children) as React.ReactElement<IconProps>;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<StyledIconButton ref={ref} size={size} className={className} borderless {...rest}>
|
|
38
|
+
{/* maybe better to use css variables? */}
|
|
39
|
+
{cloneElement(iconNode, {color: iconNode.props.color || "inherit"})}
|
|
40
|
+
</StyledIconButton>
|
|
41
|
+
);
|
|
42
|
+
});
|
|
File without changes
|