@fibery/ui-kit 1.5.0 → 1.7.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/index.ts +0 -1
- package/package.json +38 -24
- package/src/Button/AddButton.tsx +13 -0
- package/src/{BackButton.tsx → Button/BackButton.tsx} +3 -4
- 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/Item.tsx +5 -2
- package/src/Pallete.ts +191 -191
- package/src/Select/components.tsx +22 -21
- package/src/Select/index.tsx +60 -25
- package/src/Select/select-control-settings-context.tsx +20 -0
- package/src/Select/select-in-popover.tsx +333 -0
- package/src/Select/styles.ts +27 -1
- package/src/ThemeProvider.tsx +1 -1
- package/src/antd/styles.ts +3 -3
- package/src/designSystem.ts +80 -63
- package/src/error-alert.tsx +2 -2
- package/src/icons/Icon.tsx +17 -12
- package/src/icons/ast/AddGroup.ts +8 -0
- package/src/icons/ast/AiAssistant.ts +8 -0
- package/src/icons/ast/AiAvatar.ts +8 -0
- package/src/icons/ast/ArrowBottom.ts +3 -27
- package/src/icons/ast/ArrowCollapse.ts +3 -27
- package/src/icons/ast/ArrowCollapseVertical.ts +3 -27
- package/src/icons/ast/ArrowLeft.ts +3 -27
- package/src/icons/ast/ArrowRight.ts +3 -27
- package/src/icons/ast/ArrowTop.ts +3 -27
- package/src/icons/ast/Atom.ts +8 -0
- package/src/icons/ast/Copy.ts +8 -0
- package/src/icons/ast/DynamicFilterValue.ts +8 -0
- package/src/icons/ast/ExtensionAssignments.ts +3 -35
- package/src/icons/ast/FileUpload.ts +8 -0
- package/src/icons/ast/FormWithCover.ts +8 -0
- package/src/icons/ast/FormWithoutCover.ts +8 -0
- package/src/icons/ast/Github.ts +3 -27
- package/src/icons/ast/Gitlab.ts +3 -25
- package/src/icons/ast/Hint.ts +1 -1
- package/src/icons/ast/Jira.ts +8 -0
- package/src/icons/ast/Markdown.ts +8 -0
- package/src/icons/ast/Minus.ts +3 -23
- package/src/icons/ast/MoreCompact.ts +1 -1
- package/src/icons/ast/RicheditorBlockFile.ts +8 -0
- package/src/icons/ast/RicheditorBlockH4.ts +1 -1
- package/src/icons/ast/SimpleCompass.ts +8 -0
- package/src/icons/ast/Templates.ts +8 -0
- package/src/icons/ast/TypeLocation.ts +8 -0
- package/src/icons/ast/ViewForm.ts +3 -25
- package/src/icons/ast/ViewMap.ts +8 -0
- package/src/icons/ast/index.tsx +199 -182
- package/src/icons/react/AddGroup.tsx +12 -0
- package/src/icons/react/AiAssistant.tsx +12 -0
- package/src/icons/react/AiAvatar.tsx +12 -0
- package/src/icons/react/Atom.tsx +12 -0
- package/src/icons/react/Copy.tsx +12 -0
- package/src/icons/react/DynamicFilterValue.tsx +12 -0
- package/src/icons/react/FileUpload.tsx +12 -0
- package/src/icons/react/FormWithCover.tsx +12 -0
- package/src/icons/react/FormWithoutCover.tsx +12 -0
- package/src/icons/react/Github.tsx +8 -6
- package/src/icons/react/Gitlab.tsx +8 -6
- package/src/icons/react/Jira.tsx +12 -0
- package/src/icons/react/Markdown.tsx +12 -0
- package/src/icons/react/Minus.tsx +8 -6
- package/src/icons/react/RicheditorBlockFile.tsx +12 -0
- package/src/icons/react/SimpleCompass.tsx +12 -0
- package/src/icons/react/Templates.tsx +12 -0
- package/src/icons/react/TypeLocation.tsx +12 -0
- package/src/icons/react/ViewForm.tsx +8 -6
- package/src/icons/react/ViewMap.tsx +12 -0
- package/src/icons/react/index.tsx +199 -182
- package/src/tooltip.tsx +132 -152
- package/src/Button.d.ts +0 -28
- package/src/Button.js +0 -360
- package/src/antd/ant-tooltip.tsx +0 -5
- package/src/icons/IconAsBackground.tsx +0 -15
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fibery/ui-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
@@ -22,25 +22,28 @@
|
|
|
22
22
|
"src/error-alert.tsx",
|
|
23
23
|
"src/Pallete.ts",
|
|
24
24
|
"src/tooltip.tsx",
|
|
25
|
-
"src/
|
|
26
|
-
"src/BackButton.tsx"
|
|
25
|
+
"src/Button"
|
|
27
26
|
],
|
|
28
27
|
"license": "UNLICENSED",
|
|
29
28
|
"scripts": {
|
|
30
29
|
"test": "TZ=Europe/Minsk jest",
|
|
31
|
-
"test:ci": "yarn test --
|
|
30
|
+
"test:ci": "yarn test --reporters=default --reporters=jest-junit",
|
|
31
|
+
"test:coverage": "yarn test --coverage --coverageDirectory=${JEST_COVERAGE_RESULT_DIR:-$(pwd)}/coverage/ui-kit --reporters=default --reporters=jest-junit",
|
|
32
32
|
"lint": "eslint .",
|
|
33
33
|
"generate-icons": "node scripts/generate-icons.mjs"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@fibery/helpers": "
|
|
37
|
-
"@fibery/react": "
|
|
36
|
+
"@fibery/helpers": "workspace:*",
|
|
37
|
+
"@fibery/react": "workspace:*",
|
|
38
38
|
"@linaria/core": "3.0.0-beta.15",
|
|
39
39
|
"@linaria/react": "3.0.0-beta.15",
|
|
40
|
-
"@popperjs/core": "2.
|
|
40
|
+
"@popperjs/core": "2.11.6",
|
|
41
41
|
"@radix-ui/react-collapsible": "1.0.1",
|
|
42
|
-
"@radix-ui/react-
|
|
43
|
-
"
|
|
42
|
+
"@radix-ui/react-context-menu": "2.1.1",
|
|
43
|
+
"@radix-ui/react-dropdown-menu": "2.0.3-rc.7",
|
|
44
|
+
"@radix-ui/react-tooltip": "1.0.3",
|
|
45
|
+
"@types/ua-parser-js": "0.7.36",
|
|
46
|
+
"antd": "4.24.7",
|
|
44
47
|
"chroma-js": "2.1.2",
|
|
45
48
|
"chrono-node": "^2.4.1",
|
|
46
49
|
"classnames": "2.3.1",
|
|
@@ -51,41 +54,52 @@
|
|
|
51
54
|
"invariant": "2.2.4",
|
|
52
55
|
"lodash": "4.17.21",
|
|
53
56
|
"md5": "2.2.1",
|
|
54
|
-
"moment": "2.
|
|
57
|
+
"moment": "2.29.4",
|
|
55
58
|
"prop-types": "15.7.2",
|
|
56
|
-
"rc-input-number": "
|
|
57
|
-
"rc-menu": "9.
|
|
58
|
-
"rc-progress": "3.
|
|
59
|
+
"rc-input-number": "7.3.11",
|
|
60
|
+
"rc-menu": "9.8.1",
|
|
61
|
+
"rc-progress": "3.4.1",
|
|
59
62
|
"react-color": "2.13.8",
|
|
60
|
-
"react-day-picker": "8.1.
|
|
61
|
-
"react-popper": "2.
|
|
63
|
+
"react-day-picker": "8.1.4",
|
|
64
|
+
"react-popper": "2.3.0",
|
|
62
65
|
"react-select": "5.3.2",
|
|
63
66
|
"react-select-country-list": "2.2.1",
|
|
64
67
|
"react-windowed-select": "5.0.0",
|
|
65
|
-
"screenfull": "6.0.1"
|
|
68
|
+
"screenfull": "6.0.1",
|
|
69
|
+
"ua-parser-js": "0.7.24"
|
|
66
70
|
},
|
|
67
71
|
"peerDependencies": {
|
|
68
|
-
"react": "^
|
|
69
|
-
"react-dom": "^
|
|
72
|
+
"react": "^18.2.0",
|
|
73
|
+
"react-dom": "^18.2.0"
|
|
70
74
|
},
|
|
71
75
|
"devDependencies": {
|
|
72
|
-
"@
|
|
73
|
-
"@
|
|
74
|
-
"@fibery/
|
|
76
|
+
"@babel/core": "7.20.5",
|
|
77
|
+
"@babel/runtime": "7.20.6",
|
|
78
|
+
"@fibery/babel-preset": "workspace:*",
|
|
79
|
+
"@fibery/eslint-config": "workspace:*",
|
|
75
80
|
"@linaria/babel-preset": "3.0.0-beta.15",
|
|
81
|
+
"@testing-library/dom": "8.19.1",
|
|
82
|
+
"@testing-library/jest-dom": "5.16.5",
|
|
83
|
+
"@testing-library/react": "13.4.0",
|
|
84
|
+
"@testing-library/user-event": "13.5.0",
|
|
76
85
|
"@types/chroma-js": "2.1.3",
|
|
77
86
|
"@types/color-hash": "1.0.2",
|
|
78
87
|
"@types/emoji-mart": "3.0.8",
|
|
88
|
+
"@types/invariant": "2.2.34",
|
|
89
|
+
"@types/lodash": "4.14.172",
|
|
90
|
+
"@types/prop-types": "15.7.5",
|
|
91
|
+
"@types/react": "18.0.26",
|
|
92
|
+
"@types/react-dom": "18.0.10",
|
|
79
93
|
"csstype": "3.0.8",
|
|
80
|
-
"enzyme": "3.11.0",
|
|
81
|
-
"enzyme-adapter-react-16": "1.15.6",
|
|
82
94
|
"fs-extra": "10.0.0",
|
|
83
95
|
"glob": "7.1.7",
|
|
84
96
|
"jest": "27.5.1",
|
|
85
97
|
"jest-junit": "13.0.0",
|
|
98
|
+
"react": "18.2.0",
|
|
99
|
+
"react-dom": "18.2.0",
|
|
86
100
|
"svg-parser": "2.0.4",
|
|
87
101
|
"svgo": "2.8.0",
|
|
88
|
-
"typescript": "
|
|
102
|
+
"typescript": "5.0.3",
|
|
89
103
|
"unist-util-reduce": "0.2.2"
|
|
90
104
|
},
|
|
91
105
|
"jest": {
|
|
@@ -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
|
+
);
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {Button} from "
|
|
2
|
-
import BackIcon from "
|
|
3
|
-
import {Size} from "@fibery/ui-kit/src/Button";
|
|
1
|
+
import {ButtonSize, Button} from "./button";
|
|
2
|
+
import BackIcon from "../icons/react/Back";
|
|
4
3
|
|
|
5
4
|
type Props = {
|
|
6
5
|
disabled?: boolean;
|
|
7
6
|
pending?: boolean;
|
|
8
7
|
onClick: () => unknown;
|
|
9
|
-
size?:
|
|
8
|
+
size?: ButtonSize;
|
|
10
9
|
color?: string;
|
|
11
10
|
};
|
|
12
11
|
|
|
@@ -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
|
+
);
|