@fibery/ui-kit 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +14 -8
- package/src/actions-menu/actions-menu-item.tsx +1 -0
- package/src/actions-menu/actions-menu.tsx +1 -3
- package/src/antd/ant-modal.tsx +156 -7
- package/src/antd/ant-upload.tsx +285 -27
- package/src/antd/auto-complete.tsx +145 -0
- package/src/antd/auto-width-transparent-textarea.tsx +9 -2
- package/src/antd/global-overrides.ts +352 -0
- package/src/antd/index.tsx +3 -2
- package/src/antd/input-number.tsx +97 -11
- package/src/antd/input.tsx +127 -8
- package/src/antd/styles.ts +90 -32
- package/src/antd/tabs.tsx +139 -27
- package/src/avatar.tsx +2 -56
- package/src/color-adjuster.ts +24 -1
- package/src/count-badge.tsx +10 -1
- package/src/date-picker/date-range-picker.tsx +6 -6
- package/src/date-picker/single-date-picker.tsx +2 -3
- package/src/date-picker/types.ts +1 -1
- package/src/date-picker/with-popup-control.tsx +3 -9
- package/src/design-system/colors-callout.warm-dark.test.ts +22 -22
- package/src/design-system/colors-callout.warm-light.test.ts +19 -19
- package/src/design-system/colors-css.ts +1 -1
- package/src/design-system/colors-enum.dark.warm.test.ts +63 -63
- package/src/design-system/colors-enum.light.warm.test.ts +54 -54
- package/src/design-system/colors-highlight.warm-dark.test.ts +21 -21
- package/src/design-system/colors-highlight.warm-light.test.ts +21 -21
- package/src/design-system/colors.ts +1 -1
- package/src/design-system/fns.badge.dark.warm.test.ts +34 -34
- package/src/design-system/fns.badge.light.warm.test.ts +31 -31
- package/src/design-system/fns.deneutralize.test.ts +44 -0
- package/src/design-system/fns.icon.dark.warm.test.ts +21 -21
- package/src/design-system/fns.icon.light.warm.test.ts +17 -17
- package/src/design-system/fns.icon.ts +21 -13
- package/src/design-system/fns.ts +27 -3
- package/src/design-system/typography.ts +1 -1
- package/src/design-system.ts +1 -1
- package/src/fibermoji-placeholder.tsx +2 -3
- package/src/icons/icon.tsx +11 -1
- package/src/icons/types.ts +1 -1
- package/src/layout-styles.ts +5 -1
- package/src/link-input/components/ant-text-area-with-custom-read-state.tsx +2 -1
- package/src/media-query-utils.ts +4 -13
- package/src/palettes/inspect.defs.colors.warm-arch.test.ts +137 -137
- package/src/palettes/inspect.defs.colors.warm-user.test.ts +137 -137
- package/src/palettes/warm.ts +2 -0
- package/src/root-theme-provider.test.tsx +1 -1
- package/src/select/index.tsx +3 -3
- package/src/select/select-in-popover.tsx +34 -51
- package/src/select/select.tsx +2 -2
- package/src/theme-provider.test.tsx +1 -1
- package/src/theme-provider.tsx +3 -9
- package/src/theme-snapshots.test.ts +9 -13
- package/src/{theming/build.ts → theming.build.ts} +6 -9
- package/src/{theming/theming-fibery.snapshot.css → theming.generated.css} +52 -52
- package/src/{theming/theming-fibery.snapshot.ts → theming.generated.ts} +52 -52
- package/src/{themed-ink.tsx → type-label.tsx} +2 -5
- package/src/antd/auto-complete.d.ts +0 -2
- package/src/antd/auto-complete.ts +0 -37
- package/src/antd/tabs.d.ts +0 -5
- package/src/theming/index.ts +0 -20
- package/src/theming/theming-vzdbery.snapshot.css +0 -2001
- package/src/theming/theming-vzdbery.snapshot.ts +0 -2519
package/src/antd/input.tsx
CHANGED
|
@@ -1,14 +1,91 @@
|
|
|
1
|
-
import {css} from "@linaria/core";
|
|
2
|
-
import
|
|
3
|
-
import "
|
|
1
|
+
import {css, cx} from "@linaria/core";
|
|
2
|
+
import RcInput from "rc-input";
|
|
3
|
+
import type {InputProps, InputRef} from "rc-input";
|
|
4
|
+
import RcTextArea from "rc-textarea";
|
|
5
|
+
import type {TextAreaProps} from "rc-textarea";
|
|
6
|
+
import type {ResizableTextAreaRef} from "rc-textarea/lib/ResizableTextArea";
|
|
7
|
+
import {forwardRef, useImperativeHandle, useMemo, useRef} from "react";
|
|
4
8
|
|
|
5
|
-
import {tabularNumsOff, textStyles, themeVars} from "../design-system";
|
|
9
|
+
import {space, tabularNumsOff, textStyles, themeVars} from "../design-system";
|
|
10
|
+
import CloseCircleFilledIcon from "../icons/react/cross-circle";
|
|
6
11
|
import {mobileRootSelector} from "../mobile-styles";
|
|
7
12
|
import {getWrapper} from "./get-wrapper";
|
|
8
13
|
import {inputStyles} from "./styles";
|
|
9
14
|
import {wrapWith} from "./utils";
|
|
10
15
|
|
|
11
|
-
const
|
|
16
|
+
const clearIconNode = <CloseCircleFilledIcon />;
|
|
17
|
+
|
|
18
|
+
export type {InputRef};
|
|
19
|
+
|
|
20
|
+
export interface InputFocusOptions extends FocusOptions {
|
|
21
|
+
cursor?: "start" | "end" | "all";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TextAreaRef {
|
|
25
|
+
focus: (options?: InputFocusOptions) => void;
|
|
26
|
+
blur: () => void;
|
|
27
|
+
resizableTextArea?: ResizableTextAreaRef;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type AntInputProps = Omit<InputProps, "status"> & {
|
|
31
|
+
status?: "error";
|
|
32
|
+
bordered?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const Input = forwardRef<InputRef, AntInputProps>(function Input(
|
|
36
|
+
{status, className, allowClear, bordered = true, ...props},
|
|
37
|
+
ref
|
|
38
|
+
) {
|
|
39
|
+
const statusClass = status === "error" ? "ant-input-status-error" : undefined;
|
|
40
|
+
const allowClearProp = useMemo(() => {
|
|
41
|
+
if (allowClear === true) {
|
|
42
|
+
return {clearIcon: clearIconNode};
|
|
43
|
+
}
|
|
44
|
+
return allowClear;
|
|
45
|
+
}, [allowClear]);
|
|
46
|
+
return (
|
|
47
|
+
<RcInput
|
|
48
|
+
prefixCls="ant-input"
|
|
49
|
+
ref={ref}
|
|
50
|
+
allowClear={allowClearProp}
|
|
51
|
+
className={cx(className, statusClass, !bordered && "ant-input-borderless")}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
type AntTextAreaProps = Omit<TextAreaProps, "status"> & {
|
|
58
|
+
status?: "error";
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const TextArea = forwardRef<TextAreaRef, AntTextAreaProps>(function TextArea({status, className, ...props}, ref) {
|
|
62
|
+
const innerRef = useRef<RcTextArea>(null);
|
|
63
|
+
useImperativeHandle(ref, () => ({
|
|
64
|
+
focus: (options) => {
|
|
65
|
+
const textarea = innerRef.current?.resizableTextArea?.textArea;
|
|
66
|
+
if (!textarea) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
textarea.focus(options);
|
|
70
|
+
const len = textarea.value.length;
|
|
71
|
+
if (options?.cursor === "start") {
|
|
72
|
+
textarea.setSelectionRange(0, 0);
|
|
73
|
+
} else if (options?.cursor === "end") {
|
|
74
|
+
textarea.setSelectionRange(len, len);
|
|
75
|
+
} else if (options?.cursor === "all") {
|
|
76
|
+
textarea.setSelectionRange(0, len);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
blur: () => innerRef.current?.blur(),
|
|
80
|
+
get resizableTextArea() {
|
|
81
|
+
return innerRef.current?.resizableTextArea;
|
|
82
|
+
},
|
|
83
|
+
}));
|
|
84
|
+
const statusClass = status === "error" ? "ant-input-status-error" : undefined;
|
|
85
|
+
return (
|
|
86
|
+
<RcTextArea prefixCls="ant-input" ref={innerRef} className={cx("ant-input", className, statusClass)} {...props} />
|
|
87
|
+
);
|
|
88
|
+
});
|
|
12
89
|
|
|
13
90
|
export const transparentInputStyle = css`
|
|
14
91
|
${{
|
|
@@ -18,6 +95,7 @@ export const transparentInputStyle = css`
|
|
|
18
95
|
"& input, & textarea, & .ant-input-affix-wrapper": {
|
|
19
96
|
padding: 0,
|
|
20
97
|
border: "none",
|
|
98
|
+
outline: "none",
|
|
21
99
|
width: "100%",
|
|
22
100
|
resize: "none",
|
|
23
101
|
borderRadius: 0,
|
|
@@ -29,6 +107,50 @@ export const transparentInputStyle = css`
|
|
|
29
107
|
...textStyles.big,
|
|
30
108
|
},
|
|
31
109
|
},
|
|
110
|
+
"& .ant-input-affix-wrapper": {
|
|
111
|
+
display: "inline-flex",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
position: "relative",
|
|
114
|
+
minWidth: 0,
|
|
115
|
+
},
|
|
116
|
+
"& .ant-input-affix-wrapper > .ant-input": {
|
|
117
|
+
flex: 1,
|
|
118
|
+
minWidth: 0,
|
|
119
|
+
fontSize: "inherit",
|
|
120
|
+
border: "none",
|
|
121
|
+
outline: "none",
|
|
122
|
+
},
|
|
123
|
+
"& .ant-input-affix-wrapper > .ant-input:focus": {
|
|
124
|
+
boxShadow: "none !important",
|
|
125
|
+
},
|
|
126
|
+
"& .ant-input-affix-wrapper::before": {
|
|
127
|
+
width: 0,
|
|
128
|
+
visibility: "hidden",
|
|
129
|
+
content: "'\\a0'",
|
|
130
|
+
},
|
|
131
|
+
"& .ant-input-prefix, & .ant-input-suffix": {
|
|
132
|
+
display: "flex",
|
|
133
|
+
flex: "none",
|
|
134
|
+
alignItems: "center",
|
|
135
|
+
},
|
|
136
|
+
"& .ant-input-prefix > *:not(:last-child), & .ant-input-suffix > *:not(:last-child)": {
|
|
137
|
+
marginRight: space.s8,
|
|
138
|
+
},
|
|
139
|
+
"& .ant-input-prefix": {
|
|
140
|
+
marginRight: space.s4,
|
|
141
|
+
},
|
|
142
|
+
"& .ant-input-suffix": {
|
|
143
|
+
marginLeft: space.s4,
|
|
144
|
+
},
|
|
145
|
+
"& .ant-input-clear-icon": {
|
|
146
|
+
display: "inline-flex",
|
|
147
|
+
alignItems: "center",
|
|
148
|
+
lineHeight: 1,
|
|
149
|
+
cursor: "pointer",
|
|
150
|
+
},
|
|
151
|
+
"& .ant-input-clear-icon-hidden": {
|
|
152
|
+
visibility: "hidden",
|
|
153
|
+
},
|
|
32
154
|
"& textarea.ant-input": {
|
|
33
155
|
fontVariant: "inherit",
|
|
34
156
|
},
|
|
@@ -54,8 +176,5 @@ export const StyledTextArea = wrapWith(InputWrapper, TextArea);
|
|
|
54
176
|
|
|
55
177
|
export const TransparentInputWrapper = getWrapper(transparentInputStyle);
|
|
56
178
|
|
|
57
|
-
export type TextAreaRef = React.ElementRef<typeof TextArea>;
|
|
58
179
|
export const AntTransparentTextArea = wrapWith(TransparentInputWrapper, TextArea);
|
|
59
|
-
|
|
60
|
-
export type InputRef = React.ElementRef<typeof Input>;
|
|
61
180
|
export const AntTransparentInput = wrapWith(TransparentInputWrapper, Input);
|
package/src/antd/styles.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
2
|
|
|
3
|
-
import {border, layout, shadows, space, textStyles, themeVars,
|
|
3
|
+
import {border, layout, shadows, space, textStyles, themeVars, transitions} from "../design-system";
|
|
4
4
|
|
|
5
5
|
const inputVariables = {
|
|
6
6
|
default: {
|
|
@@ -32,7 +32,7 @@ export const inputOverrides = {
|
|
|
32
32
|
paddingRight: space.s12,
|
|
33
33
|
border: 0,
|
|
34
34
|
borderColor: themeVars.transparent,
|
|
35
|
-
transition: `box-shadow ${
|
|
35
|
+
transition: `box-shadow ${transitions.faster}`,
|
|
36
36
|
boxShadow: `var(--input-border, ${themeVars.inputBorderColor})`,
|
|
37
37
|
letterSpacing: 0,
|
|
38
38
|
"&::placeholder": {
|
|
@@ -41,18 +41,18 @@ export const inputOverrides = {
|
|
|
41
41
|
},
|
|
42
42
|
hover: {
|
|
43
43
|
boxShadow: `var(--input-hover-border, 0 0 0 1px ${themeVars.colorAccentStrokeHover}) !important`,
|
|
44
|
-
transition: `box-shadow ${
|
|
44
|
+
transition: `box-shadow ${transitions.faster}`,
|
|
45
45
|
},
|
|
46
46
|
focus: {
|
|
47
47
|
boxShadow: `var(--input-focus-border, 0 0 0 1px ${themeVars.colorAccentStroke}), var(--input-focus-shadow) !important`,
|
|
48
|
-
transition: `box-shadow ${
|
|
48
|
+
transition: `box-shadow ${transitions.faster}`,
|
|
49
49
|
},
|
|
50
50
|
disabled: {
|
|
51
51
|
color: `${themeVars.textColor}`,
|
|
52
52
|
backgroundColor: `${themeVars.inputDisabledBgColor} !important`,
|
|
53
53
|
borderColor: themeVars.transparent,
|
|
54
54
|
boxShadow: `${themeVars.inputDisabledBorderColor} !important`,
|
|
55
|
-
transition: `box-shadow ${
|
|
55
|
+
transition: `box-shadow ${transitions.faster}`,
|
|
56
56
|
WebkitTextFillColor: "currentColor",
|
|
57
57
|
cursor: "default",
|
|
58
58
|
"&:hover": {
|
|
@@ -69,6 +69,58 @@ export const inputStyles = css`
|
|
|
69
69
|
}
|
|
70
70
|
${{
|
|
71
71
|
...textStyles.regular,
|
|
72
|
+
"& .ant-input, & .ant-input-affix-wrapper, & .ant-input-group-wrapper, & .ant-input-wrapper": {
|
|
73
|
+
boxSizing: "border-box",
|
|
74
|
+
},
|
|
75
|
+
"& .ant-input-affix-wrapper": {
|
|
76
|
+
display: "inline-flex",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
width: "100%",
|
|
79
|
+
position: "relative",
|
|
80
|
+
font: "inherit",
|
|
81
|
+
padding: 0,
|
|
82
|
+
border: "none",
|
|
83
|
+
boxShadow: "none",
|
|
84
|
+
backgroundColor: themeVars.transparent,
|
|
85
|
+
},
|
|
86
|
+
"& .ant-input-affix-wrapper > input.ant-input": {
|
|
87
|
+
minWidth: 0,
|
|
88
|
+
flex: 1,
|
|
89
|
+
padding: "0 12px",
|
|
90
|
+
border: "inherit",
|
|
91
|
+
outline: "inherit",
|
|
92
|
+
},
|
|
93
|
+
"& .ant-input-prefix, & .ant-input-suffix": {
|
|
94
|
+
display: "flex",
|
|
95
|
+
flex: "none",
|
|
96
|
+
alignItems: "center",
|
|
97
|
+
},
|
|
98
|
+
// antd source: spacing between adjacent icons when prefix/suffix holds multiple nodes.
|
|
99
|
+
"& .ant-input-prefix > *:not(:last-child), & .ant-input-suffix > *:not(:last-child)": {
|
|
100
|
+
marginRight: space.s8,
|
|
101
|
+
},
|
|
102
|
+
"& .ant-input-suffix": {
|
|
103
|
+
marginLeft: space.s6,
|
|
104
|
+
},
|
|
105
|
+
"& .ant-input-clear-icon": {
|
|
106
|
+
display: "inline-flex",
|
|
107
|
+
alignItems: "center",
|
|
108
|
+
color: themeVars.iconColor,
|
|
109
|
+
cursor: "pointer",
|
|
110
|
+
lineHeight: 1,
|
|
111
|
+
},
|
|
112
|
+
"& .ant-input-clear-icon-hidden": {
|
|
113
|
+
visibility: "hidden",
|
|
114
|
+
},
|
|
115
|
+
"& .ant-input-group-wrapper": {
|
|
116
|
+
display: "inline-block",
|
|
117
|
+
width: "100%",
|
|
118
|
+
},
|
|
119
|
+
"& .ant-input-wrapper.ant-input-group": {
|
|
120
|
+
display: "inline-flex",
|
|
121
|
+
width: "100%",
|
|
122
|
+
alignItems: "stretch",
|
|
123
|
+
},
|
|
72
124
|
"& .ant-input-group-addon": {
|
|
73
125
|
backgroundColor: themeVars.transparent,
|
|
74
126
|
border: "none",
|
|
@@ -79,23 +131,37 @@ export const inputStyles = css`
|
|
|
79
131
|
"&:focus": inputOverrides.focus,
|
|
80
132
|
},
|
|
81
133
|
"& .ant-input-group": {borderSpacing: 1, ...textStyles.regular},
|
|
82
|
-
"& .ant-input":
|
|
134
|
+
"& .ant-input": {
|
|
135
|
+
...inputOverrides.main,
|
|
136
|
+
// antd source: .ant-input { width: 100%; min-width: 0 } — without it, bare <AntInput>
|
|
137
|
+
// doesn't fill its container and can't shrink inside a flex parent. AutoComplete's
|
|
138
|
+
// .ant-select-selection-search .ant-input rule (two-class selector) beats this on
|
|
139
|
+
// specificity for combobox inputs, so the nested-AntAutoComplete case is unaffected.
|
|
140
|
+
width: "100%",
|
|
141
|
+
minWidth: 0,
|
|
142
|
+
},
|
|
143
|
+
// antd source: long placeholders get ellipsis instead of wrap-clip.
|
|
144
|
+
"& .ant-input:placeholder-shown": {
|
|
145
|
+
textOverflow: "ellipsis",
|
|
146
|
+
},
|
|
147
|
+
// bordered={false} → wrap with `ant-input-borderless` class; strip the box-shadow ring + bg.
|
|
148
|
+
// antd source covers base + hover/focus/disabled with the same declarations.
|
|
149
|
+
// !important needed to beat inputOverrides.main's !important on backgroundColor/boxShadow.
|
|
150
|
+
"& .ant-input.ant-input-borderless, & .ant-input.ant-input-borderless:hover, & .ant-input.ant-input-borderless:focus, & .ant-input.ant-input-borderless:disabled, & .ant-input.ant-input-borderless[disabled]":
|
|
151
|
+
{
|
|
152
|
+
boxShadow: "none !important",
|
|
153
|
+
backgroundColor: `${themeVars.transparent} !important`,
|
|
154
|
+
},
|
|
155
|
+
// antd source: inner input inside affix-wrapper must not paint its own focus ring —
|
|
156
|
+
// the wrapper owns the visual focus state. Without this prefix/suffix usage shows a
|
|
157
|
+
// double ring.
|
|
158
|
+
"& .ant-input-affix-wrapper > .ant-input:focus": {
|
|
159
|
+
boxShadow: "none !important",
|
|
160
|
+
},
|
|
83
161
|
"& .ant-input-status-error": inputVariables.error,
|
|
84
162
|
"& .ant-input[readonly].ant-input[readonly]": {...inputOverrides.disabled, cursor: "inherit"},
|
|
85
163
|
"& .ant-input:hover[readonly].ant-input:hover[readonly]": {...inputOverrides.disabled, cursor: "inherit"},
|
|
86
164
|
"& .ant-input-affix-wrapper:hover .ant-input[readonly]": {...inputOverrides.disabled, cursor: "inherit"},
|
|
87
|
-
"& .ant-input-affix-wrapper > input.ant-input": {
|
|
88
|
-
padding: "0 12px",
|
|
89
|
-
border: "inherit",
|
|
90
|
-
outline: "inherit",
|
|
91
|
-
},
|
|
92
|
-
"& .ant-input-affix-wrapper": {
|
|
93
|
-
font: "inherit",
|
|
94
|
-
padding: 0,
|
|
95
|
-
border: "none",
|
|
96
|
-
boxShadow: "none",
|
|
97
|
-
backgroundColor: themeVars.transparent,
|
|
98
|
-
},
|
|
99
165
|
"& .ant-input-affix-wrapper.ant-input-affix-wrapper-readonly": {
|
|
100
166
|
border: "none !important",
|
|
101
167
|
outline: "none !important",
|
|
@@ -104,22 +170,15 @@ export const inputStyles = css`
|
|
|
104
170
|
"& .ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled)": {
|
|
105
171
|
borderColor: "transparent",
|
|
106
172
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
...textStyles.regular,
|
|
111
|
-
"& .ant-select-selection--single": {height: layout.inputHeight},
|
|
112
|
-
"& .ant-input": inputOverrides.main,
|
|
113
|
-
"& .ant-input.ant-input:hover": inputOverrides.hover,
|
|
114
|
-
"& .ant-input.ant-input:focus": inputOverrides.focus,
|
|
115
|
-
"& .ant-input.ant-input-disabled": inputOverrides.disabled,
|
|
116
|
-
},
|
|
173
|
+
// antd source: textarea defaults — min-height anchors empty <AntTextArea> to a sane
|
|
174
|
+
// height, max-width prevents user-resize from blowing out the container, and
|
|
175
|
+
// vertical-align: bottom matches the baseline of a single-line <input> next to it.
|
|
117
176
|
"& textarea.ant-input": {
|
|
118
177
|
paddingTop: space.s8,
|
|
119
178
|
paddingBottom: space.s8,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
179
|
+
minHeight: layout.inputHeight,
|
|
180
|
+
maxWidth: "100%",
|
|
181
|
+
verticalAlign: "bottom",
|
|
123
182
|
},
|
|
124
183
|
"&& input:hover:not(:focus):not(:disabled), && textarea:hover:not(:focus):not(:disabled), && .ant-input:hover:not(:focus):not(:disabled)":
|
|
125
184
|
inputOverrides.hover,
|
|
@@ -129,7 +188,6 @@ export const inputStyles = css`
|
|
|
129
188
|
"&& input::placeholder, && textarea::placeholder": {
|
|
130
189
|
color: themeVars.inputPlaceholderTextColor,
|
|
131
190
|
},
|
|
132
|
-
"& .ant-input-affix-wrapper .ant-input-prefix": {left: space.s6},
|
|
133
191
|
"& .ant-input-affix-wrapper .ant-input:not(:first-child)": {paddingLeft: space.s6 + space.s24 + space.s8}, // Match TitlePreview spacing
|
|
134
192
|
}}
|
|
135
193
|
`;
|
package/src/antd/tabs.tsx
CHANGED
|
@@ -1,32 +1,108 @@
|
|
|
1
|
-
import {css
|
|
2
|
-
import Tabs from "
|
|
1
|
+
import {css} from "@linaria/core";
|
|
2
|
+
import Tabs from "rc-tabs";
|
|
3
|
+
import type {ComponentProps} from "react";
|
|
3
4
|
import {forwardRef} from "react";
|
|
4
|
-
import "antd/lib/tabs/style/css";
|
|
5
5
|
|
|
6
|
-
import {textStyles, space, themeVars} from "../design-system";
|
|
6
|
+
import {textStyles, space, themeVars, transitions} from "../design-system";
|
|
7
|
+
import {getWrapper} from "./get-wrapper";
|
|
7
8
|
import {wrapWith} from "./utils";
|
|
8
9
|
|
|
10
|
+
type TabsPropsAny = ComponentProps<typeof Tabs>;
|
|
11
|
+
|
|
12
|
+
const PrefixedTabs = forwardRef<HTMLDivElement, TabsPropsAny>(function PrefixedTabs(props, ref) {
|
|
13
|
+
return <Tabs prefixCls="ant-tabs" {...props} ref={ref} />;
|
|
14
|
+
});
|
|
15
|
+
|
|
9
16
|
export const tabsStyles = css`
|
|
10
17
|
${{
|
|
11
18
|
width: "100%",
|
|
12
|
-
"& .ant-tabs
|
|
19
|
+
"& .ant-tabs": {
|
|
20
|
+
boxSizing: "border-box",
|
|
21
|
+
display: "flex",
|
|
22
|
+
overflow: "visible",
|
|
23
|
+
width: "100%",
|
|
24
|
+
},
|
|
25
|
+
"& .ant-tabs-top, & .ant-tabs-bottom": {
|
|
26
|
+
flexDirection: "column",
|
|
27
|
+
},
|
|
28
|
+
"& .ant-tabs-nav": {
|
|
29
|
+
display: "flex",
|
|
30
|
+
flex: "none",
|
|
31
|
+
alignItems: "center",
|
|
13
32
|
margin: 0,
|
|
33
|
+
width: "100%",
|
|
34
|
+
position: "relative",
|
|
35
|
+
"&::before": {
|
|
36
|
+
position: "absolute",
|
|
37
|
+
right: 0,
|
|
38
|
+
bottom: 0,
|
|
39
|
+
left: 0,
|
|
40
|
+
borderBottom: themeVars.borderSolidFilterGroup,
|
|
41
|
+
content: "''",
|
|
42
|
+
},
|
|
14
43
|
},
|
|
15
44
|
"& .ant-tabs-nav-wrap": {
|
|
45
|
+
display: "flex",
|
|
46
|
+
flex: "auto",
|
|
47
|
+
overflow: "hidden",
|
|
48
|
+
whiteSpace: "nowrap",
|
|
49
|
+
position: "relative",
|
|
50
|
+
alignSelf: "stretch",
|
|
51
|
+
transform: "translate(0)",
|
|
16
52
|
marginBottom: 1,
|
|
17
53
|
marginLeft: space.s12,
|
|
18
54
|
marginRight: space.s12,
|
|
19
55
|
},
|
|
20
|
-
"& .ant-tabs": {
|
|
21
|
-
|
|
56
|
+
"& .ant-tabs-nav-wrap::before, & .ant-tabs-nav-wrap::after": {
|
|
57
|
+
position: "absolute",
|
|
58
|
+
zIndex: 1,
|
|
59
|
+
opacity: 0,
|
|
60
|
+
transition: `opacity ${transitions.slow}`,
|
|
61
|
+
content: "''",
|
|
62
|
+
pointerEvents: "none",
|
|
63
|
+
},
|
|
64
|
+
"& .ant-tabs-nav-list": {
|
|
65
|
+
position: "relative",
|
|
66
|
+
display: "flex",
|
|
67
|
+
transition: `transform ${transitions.normal}`,
|
|
68
|
+
},
|
|
69
|
+
"& .ant-tabs-nav-operations": {
|
|
70
|
+
display: "flex",
|
|
71
|
+
alignSelf: "stretch",
|
|
72
|
+
},
|
|
73
|
+
"& .ant-tabs-nav-operations-hidden": {
|
|
74
|
+
position: "absolute",
|
|
75
|
+
visibility: "hidden",
|
|
76
|
+
pointerEvents: "none",
|
|
77
|
+
},
|
|
78
|
+
"& .ant-tabs-nav-more": {
|
|
79
|
+
position: "relative",
|
|
80
|
+
padding: `${space.s8}px ${space.s16}px`,
|
|
81
|
+
backgroundColor: themeVars.transparent,
|
|
82
|
+
border: 0,
|
|
83
|
+
},
|
|
84
|
+
"& .ant-tabs-nav-more::after": {
|
|
85
|
+
position: "absolute",
|
|
86
|
+
right: 0,
|
|
87
|
+
bottom: 0,
|
|
88
|
+
left: 0,
|
|
89
|
+
height: 5,
|
|
90
|
+
transform: "translateY(100%)",
|
|
91
|
+
content: "''",
|
|
92
|
+
},
|
|
93
|
+
"& .ant-tabs-extra-content": {
|
|
94
|
+
flex: "none",
|
|
95
|
+
},
|
|
96
|
+
"& .ant-tabs-centered .ant-tabs-nav-wrap:not([class*='ant-tabs-nav-wrap-ping'])": {
|
|
97
|
+
justifyContent: "center",
|
|
22
98
|
},
|
|
23
99
|
"& .ant-tabs-ink-bar": {
|
|
100
|
+
position: "absolute",
|
|
101
|
+
bottom: 0,
|
|
24
102
|
backgroundColor: themeVars.colorAccent,
|
|
25
103
|
height: 2,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
margin: 0,
|
|
29
|
-
width: "100%",
|
|
104
|
+
pointerEvents: "none",
|
|
105
|
+
transition: `left ${transitions.faster}, right ${transitions.faster}, width ${transitions.faster}`,
|
|
30
106
|
},
|
|
31
107
|
"& .ant-tabs-tab, & .ant-tabs-tab:hover, & .ant-tabs-tab-btn:active, & .ant-tabs-tabpane, & .ant-tabs-tab-active .ant-tabs-tab-btn":
|
|
32
108
|
{
|
|
@@ -34,29 +110,65 @@ export const tabsStyles = css`
|
|
|
34
110
|
textShadow: "none",
|
|
35
111
|
},
|
|
36
112
|
"& .ant-tabs-tab": {
|
|
113
|
+
position: "relative",
|
|
114
|
+
display: "inline-flex",
|
|
115
|
+
alignItems: "center",
|
|
37
116
|
padding: "12px 12px",
|
|
38
117
|
margin: `0 ${space.s8}px 0 0`,
|
|
118
|
+
cursor: "pointer",
|
|
119
|
+
outline: "none",
|
|
39
120
|
},
|
|
40
121
|
"& .ant-tabs-tab + .ant-tabs-tab": {
|
|
41
122
|
margin: `0 ${space.s8}px 0 0`,
|
|
42
123
|
},
|
|
43
|
-
".ant-tabs-
|
|
44
|
-
|
|
124
|
+
"& .ant-tabs-tab-btn": {
|
|
125
|
+
outline: "none",
|
|
126
|
+
transition: `all ${transitions.slow}`,
|
|
127
|
+
},
|
|
128
|
+
"& .ant-tabs-tab-active .ant-tabs-tab-btn": {
|
|
129
|
+
color: themeVars.colorAccent,
|
|
130
|
+
},
|
|
131
|
+
"& .ant-tabs-content-holder": {
|
|
132
|
+
flex: "auto",
|
|
133
|
+
minWidth: 0,
|
|
134
|
+
minHeight: 0,
|
|
135
|
+
},
|
|
136
|
+
"& .ant-tabs-content": {
|
|
137
|
+
position: "relative",
|
|
138
|
+
display: "flex",
|
|
139
|
+
width: "100%",
|
|
140
|
+
},
|
|
141
|
+
"& .ant-tabs-tabpane": {
|
|
142
|
+
flex: "none",
|
|
143
|
+
width: "100%",
|
|
144
|
+
outline: "none",
|
|
145
|
+
},
|
|
146
|
+
"& .ant-tabs-tabpane-hidden": {
|
|
147
|
+
display: "none",
|
|
148
|
+
},
|
|
149
|
+
"& .ant-tabs-switch-appear, & .ant-tabs-switch-enter": {
|
|
150
|
+
transition: "none",
|
|
151
|
+
},
|
|
152
|
+
"& .ant-tabs-switch-appear-start, & .ant-tabs-switch-enter-start": {
|
|
153
|
+
opacity: 0,
|
|
154
|
+
},
|
|
155
|
+
"& .ant-tabs-switch-appear-active, & .ant-tabs-switch-enter-active": {
|
|
156
|
+
opacity: 1,
|
|
157
|
+
transition: `opacity ${transitions.slow}`,
|
|
158
|
+
},
|
|
159
|
+
"& .ant-tabs-switch-leave": {
|
|
160
|
+
position: "absolute",
|
|
161
|
+
inset: 0,
|
|
162
|
+
transition: "none",
|
|
163
|
+
},
|
|
164
|
+
"& .ant-tabs-switch-leave-start": {
|
|
165
|
+
opacity: 1,
|
|
166
|
+
},
|
|
167
|
+
"& .ant-tabs-switch-leave-active": {
|
|
168
|
+
opacity: 0,
|
|
169
|
+
transition: `opacity ${transitions.slow}`,
|
|
45
170
|
},
|
|
46
171
|
}}
|
|
47
172
|
`;
|
|
48
173
|
|
|
49
|
-
|
|
50
|
-
return forwardRef<HTMLDivElement, {children: React.ReactNode; className?: string}>(function TabWrapper(
|
|
51
|
-
{children, className},
|
|
52
|
-
ref
|
|
53
|
-
) {
|
|
54
|
-
return (
|
|
55
|
-
<div className={cx(className, tabsStyles)} ref={ref}>
|
|
56
|
-
{children}
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const AntTabs = wrapWith(getWrapper(), Tabs);
|
|
174
|
+
export const AntTabs = wrapWith(getWrapper(tabsStyles), PrefixedTabs);
|
package/src/avatar.tsx
CHANGED
|
@@ -3,12 +3,9 @@ import {css, cx} from "@linaria/core";
|
|
|
3
3
|
import type {CSSProperties, FC} from "react";
|
|
4
4
|
import {memo, useCallback, useMemo, useState} from "react";
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {useFeatures} from "./features";
|
|
9
|
-
import {FibermojiPlaceholder} from "./fibermoji-placeholder";
|
|
6
|
+
import {border, space, themeVars} from "./design-system";
|
|
7
|
+
import {FibermojiPlaceholder as AvatarPlaceholder} from "./fibermoji-placeholder";
|
|
10
8
|
import {getShiftStyle} from "./icons/get-shift-style";
|
|
11
|
-
import {useTheme} from "./theme-provider";
|
|
12
9
|
|
|
13
10
|
export const getContainerStyle = memoize((containerSize: number) => {
|
|
14
11
|
return {
|
|
@@ -167,57 +164,6 @@ const AvatarImage: FC<AvatarImageProps> = memo(function AvatarImage({
|
|
|
167
164
|
);
|
|
168
165
|
});
|
|
169
166
|
|
|
170
|
-
const avatarPlaceholderStaticClassName = css`
|
|
171
|
-
text-align: center;
|
|
172
|
-
text-transform: uppercase;
|
|
173
|
-
letter-spacing: -0.2px;
|
|
174
|
-
`;
|
|
175
|
-
|
|
176
|
-
const getAvatarPlaceholderStyle = ({
|
|
177
|
-
size,
|
|
178
|
-
backgroundColor,
|
|
179
|
-
radius,
|
|
180
|
-
}: {
|
|
181
|
-
size: number;
|
|
182
|
-
backgroundColor: string;
|
|
183
|
-
radius?: number;
|
|
184
|
-
}) => ({
|
|
185
|
-
backgroundColor: getDarkenColor(backgroundColor),
|
|
186
|
-
color: themeVars.whiteColor,
|
|
187
|
-
lineHeight: `${size}px`,
|
|
188
|
-
fontSize: size / 2.222222,
|
|
189
|
-
width: size,
|
|
190
|
-
height: size,
|
|
191
|
-
borderRadius: radius ?? "50%",
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
type AvatarPlaceholderProps = {
|
|
195
|
-
size: number;
|
|
196
|
-
title?: string;
|
|
197
|
-
radius?: number;
|
|
198
|
-
hasAccess?: boolean;
|
|
199
|
-
backgroundColor?: string;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const AvatarPlaceholder: FC<AvatarPlaceholderProps> = memo(function AvatarPlaceholder(props) {
|
|
203
|
-
const features = useFeatures<{enableFibermojiAvatars: boolean}>();
|
|
204
|
-
const theme = useTheme();
|
|
205
|
-
if (features.enableFibermojiAvatars) {
|
|
206
|
-
return <FibermojiPlaceholder {...props} />;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const {size, title, radius, backgroundColor = getObjectColorMemoized(title)} = props;
|
|
210
|
-
const displayBg = theme.fns.deneutralize(backgroundColor);
|
|
211
|
-
return (
|
|
212
|
-
<div
|
|
213
|
-
className={cx(textClasses.regular, avatarPlaceholderStaticClassName)}
|
|
214
|
-
style={getAvatarPlaceholderStyle({size, backgroundColor: displayBg, radius})}
|
|
215
|
-
>
|
|
216
|
-
{abbreviateName(title)}
|
|
217
|
-
</div>
|
|
218
|
-
);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
167
|
const getNullUserAvatarContainerStyle = ({containerSize}: {containerSize: number}) => ({
|
|
222
168
|
width: containerSize,
|
|
223
169
|
height: containerSize,
|
package/src/color-adjuster.ts
CHANGED
|
@@ -7,6 +7,11 @@ export type AdjustSpec = {
|
|
|
7
7
|
achromaticTint?: {h: number; c: number};
|
|
8
8
|
strengths?: {common?: number; l?: number; c?: number; h?: number};
|
|
9
9
|
lFade?: {lo?: number; hi?: number};
|
|
10
|
+
// Linear strength ramp from `min` at c=0 to 1 at c=hi, then 1 above.
|
|
11
|
+
// Softens pastels (Notion imports, vizydrop "Fibery" preset) without touching saturated swatches.
|
|
12
|
+
cFade?: {hi: number; min: number};
|
|
13
|
+
// Inputs with c ≤ greyC skip cFade so designer cool-greys keep full warmth.
|
|
14
|
+
greyC?: number;
|
|
10
15
|
};
|
|
11
16
|
|
|
12
17
|
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
|
@@ -60,6 +65,21 @@ function makeStrengthFromL(lo: number, hi: number) {
|
|
|
60
65
|
};
|
|
61
66
|
}
|
|
62
67
|
|
|
68
|
+
function makeStrengthFromC(spec: AdjustSpec["cFade"]) {
|
|
69
|
+
if (!spec || spec.min >= 1 || spec.hi <= 0) {
|
|
70
|
+
return function strengthFromC(_c: number) {
|
|
71
|
+
return 1;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const {hi, min} = spec;
|
|
75
|
+
return function strengthFromC(c: number) {
|
|
76
|
+
if (c >= hi) {
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
return lerp(min, 1, c / hi);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
export function makeAdjust(adjuster: AdjustSpec) {
|
|
64
84
|
const derive = makeDerive(adjuster);
|
|
65
85
|
const sCommon = adjuster.strengths?.common ?? 1;
|
|
@@ -70,14 +90,17 @@ export function makeAdjust(adjuster: AdjustSpec) {
|
|
|
70
90
|
adjuster.lFade?.lo ?? WARM_L_LO_DEFAULT,
|
|
71
91
|
adjuster.lFade?.hi ?? WARM_L_HI_DEFAULT
|
|
72
92
|
);
|
|
93
|
+
const strengthFromC = makeStrengthFromC(adjuster.cFade);
|
|
94
|
+
const greyC = adjuster.greyC ?? 0;
|
|
73
95
|
return function adjust(color: Oklch): Oklch {
|
|
74
96
|
const hue = color.h ?? 0;
|
|
97
|
+
const isGrey = color.h === null || color.c <= greyC;
|
|
75
98
|
|
|
76
99
|
const mulL = 1 + (derive(hue, (p) => p.mul.l) - 1) * sL;
|
|
77
100
|
const powC = 1 + (derive(hue, (p) => p.pow.c) - 1) * sC;
|
|
78
101
|
const addH = derive(hue, (p) => p.add.h) * sH;
|
|
79
102
|
|
|
80
|
-
const strength = strengthFromL(color.l);
|
|
103
|
+
const strength = strengthFromL(color.l) * (isGrey ? 1 : strengthFromC(color.c));
|
|
81
104
|
|
|
82
105
|
const l = Math.min(lerp(color.l, color.l * mulL, strength), 0.999);
|
|
83
106
|
let c: number;
|
package/src/count-badge.tsx
CHANGED
|
@@ -34,8 +34,17 @@ const warningStyle = css`
|
|
|
34
34
|
color: ${themeVars.textColor};
|
|
35
35
|
`;
|
|
36
36
|
|
|
37
|
+
const countBadgeLimitOverride = Number.parseInt(
|
|
38
|
+
new URLSearchParams(window.location.search).get("count-badge-limit") ?? ""
|
|
39
|
+
);
|
|
40
|
+
const COUNT_BADGE_LIMIT = Number.isInteger(countBadgeLimitOverride) ? countBadgeLimitOverride : 99;
|
|
41
|
+
|
|
37
42
|
export function CountBadge({className, count}: Props) {
|
|
38
|
-
return
|
|
43
|
+
return (
|
|
44
|
+
<span className={cx(countStyle, countBadgeStyle, className)}>
|
|
45
|
+
{count <= COUNT_BADGE_LIMIT ? count : `${COUNT_BADGE_LIMIT}+`}
|
|
46
|
+
</span>
|
|
47
|
+
);
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
export function CountBadgeWithNoLimits({count, warning}: {count: number | string; warning?: boolean}) {
|