@fibery/ui-kit 1.36.0 → 1.36.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/CHANGELOG.md +8 -0
- package/package.json +3 -3
- package/src/avatar.tsx +26 -12
- package/src/command-menu/index.tsx +1 -12
- package/src/command-menu/nested-command-menu.tsx +349 -0
- package/src/date-picker/relative-date-picker.tsx +20 -9
- package/src/icons/ast/DateRange.ts +8 -0
- package/src/icons/ast/ReadOnly.ts +8 -0
- package/src/icons/ast/index.tsx +2 -0
- package/src/icons/react/DateRange.tsx +13 -0
- package/src/icons/react/ReadOnly.tsx +13 -0
- package/src/icons/react/index.tsx +2 -0
- package/src/icons/svg/date-range.svg +4 -0
- package/src/icons/svg/read-only.svg +6 -0
- package/src/loaders.tsx +28 -23
- package/src/number-input/number-input.tsx +37 -56
- package/src/select/custom-select-partials/group-heading.tsx +24 -1
- package/src/select/custom-select-partials/option.tsx +17 -11
- package/src/select/select-in-popover.tsx +1 -1
- package/src/select/util.ts +2 -1
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fibery/ui-kit",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"dependencies": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"tabbable": "5.2.1",
|
|
45
45
|
"ua-parser-js": "1.0.39",
|
|
46
46
|
"@fibery/emoji-data": "2.6.1",
|
|
47
|
-
"@fibery/helpers": "1.3.
|
|
48
|
-
"@fibery/react": "1.4.
|
|
47
|
+
"@fibery/helpers": "1.3.2",
|
|
48
|
+
"@fibery/react": "1.4.3"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"react": "^18.2.0",
|
package/src/avatar.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
2
|
import _ from "lodash";
|
|
3
|
-
import {CSSProperties, FC, memo, useCallback, useState} from "react";
|
|
3
|
+
import {CSSProperties, FC, memo, useCallback, useMemo, useState} from "react";
|
|
4
4
|
import {getDarkenColor, getObjectColorMemoized, textStyles, themeVars} from "./design-system";
|
|
5
5
|
import {FibermojiPlaceholder} from "./fibermoji-placeholder";
|
|
6
6
|
import {getShiftStyle} from "./icons/get-shift-style";
|
|
@@ -82,6 +82,7 @@ const EMOJI_BY_BADGE = {
|
|
|
82
82
|
vacation: "🌴",
|
|
83
83
|
dayon: "💻",
|
|
84
84
|
};
|
|
85
|
+
|
|
85
86
|
function getAvatarUrl(url: string, size: number) {
|
|
86
87
|
if (!url) {
|
|
87
88
|
return url;
|
|
@@ -92,7 +93,17 @@ function getAvatarUrl(url: string, size: number) {
|
|
|
92
93
|
return urlObject.toString();
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
/**
|
|
97
|
+
* This triggers a network request if the image is not cached.
|
|
98
|
+
* Avoid using in render or performance-critical code.
|
|
99
|
+
*/
|
|
100
|
+
const isImageCached = (src: string): boolean => {
|
|
101
|
+
const image = new Image();
|
|
102
|
+
image.src = src;
|
|
103
|
+
return image.complete && image.naturalWidth !== 0;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
type AvatarImageProps = {
|
|
96
107
|
size: number;
|
|
97
108
|
containerSize?: number;
|
|
98
109
|
title?: string;
|
|
@@ -102,11 +113,8 @@ interface AvatarImageProps {
|
|
|
102
113
|
backgroundColor?: string;
|
|
103
114
|
disableNativeTitle?: boolean;
|
|
104
115
|
placeholder?: React.ReactNode;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
*
|
|
108
|
-
* @type {React.NamedExoticComponent<{readonly badges?: *, readonly backgroundColor?: *, readonly size?: *, readonly avatarUrl?: *, readonly disableNativeTitle?: *, readonly title?: *, readonly radius?: *, readonly containerSize?: *}>}
|
|
109
|
-
*/
|
|
116
|
+
};
|
|
117
|
+
|
|
110
118
|
export const AvatarImage: FC<AvatarImageProps> = memo(function AvatarImage({
|
|
111
119
|
size,
|
|
112
120
|
containerSize,
|
|
@@ -118,7 +126,13 @@ export const AvatarImage: FC<AvatarImageProps> = memo(function AvatarImage({
|
|
|
118
126
|
disableNativeTitle = false,
|
|
119
127
|
placeholder,
|
|
120
128
|
}) {
|
|
121
|
-
const
|
|
129
|
+
const initialFetchState = useMemo(() => {
|
|
130
|
+
if (avatarUrl && isImageCached(avatarUrl)) {
|
|
131
|
+
return ":fetch-state/success";
|
|
132
|
+
}
|
|
133
|
+
return ":fetch-state/start";
|
|
134
|
+
}, [avatarUrl]);
|
|
135
|
+
const [fetchState, setFetchState] = useState(initialFetchState);
|
|
122
136
|
const onError = useCallback(() => {
|
|
123
137
|
setFetchState(":fetch-state/error");
|
|
124
138
|
}, []);
|
|
@@ -193,12 +207,12 @@ const getAvatarPlaceholderStyle = ({
|
|
|
193
207
|
borderRadius: radius ?? "50%",
|
|
194
208
|
});
|
|
195
209
|
|
|
196
|
-
|
|
210
|
+
type AvatarPlaceholderProps = {
|
|
197
211
|
size: number;
|
|
198
212
|
title?: string;
|
|
199
213
|
radius?: number;
|
|
200
214
|
backgroundColor?: string;
|
|
201
|
-
}
|
|
215
|
+
};
|
|
202
216
|
|
|
203
217
|
export const AvatarPlaceholder: FC<AvatarPlaceholderProps> = memo(function AvatarPlaceholder(props) {
|
|
204
218
|
const features = useFeatures<{enableFibermojiAvatars: boolean}>();
|
|
@@ -237,10 +251,10 @@ const getNullUserAvatarStyle = ({size, containerSize}: {size: number; containerS
|
|
|
237
251
|
|
|
238
252
|
const magicCoefficient = 4.5;
|
|
239
253
|
|
|
240
|
-
|
|
254
|
+
type NullUserAvatarProps = {
|
|
241
255
|
size: number;
|
|
242
256
|
containerSize: number;
|
|
243
|
-
}
|
|
257
|
+
};
|
|
244
258
|
|
|
245
259
|
export const NullUserAvatar: FC<NullUserAvatarProps> = memo(function NullUserAvatar({size, containerSize}) {
|
|
246
260
|
const dashSize = (Math.PI * size) / magicCoefficient / 12;
|
|
@@ -113,7 +113,7 @@ export const CommandMenuSeparator = styled(CommandSeparator)`
|
|
|
113
113
|
margin: ${space.s4}px 0;
|
|
114
114
|
`;
|
|
115
115
|
|
|
116
|
-
const commandItemCss = css`
|
|
116
|
+
export const commandItemCss = css`
|
|
117
117
|
display: flex;
|
|
118
118
|
align-items: center;
|
|
119
119
|
gap: ${space.s6}px;
|
|
@@ -135,17 +135,6 @@ const commandItemTitleCss = css`
|
|
|
135
135
|
overflow: hidden;
|
|
136
136
|
`;
|
|
137
137
|
|
|
138
|
-
export const CommandExpressionSelectItem = forwardRef<
|
|
139
|
-
React.ElementRef<typeof CommandItem>,
|
|
140
|
-
React.ComponentPropsWithoutRef<typeof CommandItem>
|
|
141
|
-
>(function CommandMenuItem({className, children, ...props}, ref) {
|
|
142
|
-
return (
|
|
143
|
-
<CommandItem ref={ref} className={cx(commandItemCss, className)} {...props}>
|
|
144
|
-
{children}
|
|
145
|
-
</CommandItem>
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
138
|
export const CommandMenuItem = forwardRef<
|
|
150
139
|
React.ElementRef<typeof CommandItem>,
|
|
151
140
|
React.ComponentPropsWithoutRef<typeof CommandItem> & {
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import {CommandItem} from "cmdk";
|
|
2
|
+
import React, {forwardRef, ReactNode, useCallback, useEffect, useMemo, useRef, useState} from "react";
|
|
3
|
+
import {css, cx} from "@linaria/core";
|
|
4
|
+
import {border, space, themeVars} from "../design-system";
|
|
5
|
+
import Back from "../icons/react/Back";
|
|
6
|
+
import {
|
|
7
|
+
commandItemCss,
|
|
8
|
+
CommandMenuEmpty,
|
|
9
|
+
CommandMenuGroup,
|
|
10
|
+
CommandMenuInput,
|
|
11
|
+
CommandMenuItem,
|
|
12
|
+
CommandMenuList,
|
|
13
|
+
CommandMenuRoot,
|
|
14
|
+
CommandMenuSeparator,
|
|
15
|
+
} from "./index";
|
|
16
|
+
import cn from "classnames";
|
|
17
|
+
import {Tooltip} from "../tooltip";
|
|
18
|
+
import {IconButton} from "../button/icon-button";
|
|
19
|
+
import ArrowRight from "../icons/react/ArrowRight";
|
|
20
|
+
|
|
21
|
+
const selectedItemClassName = css`
|
|
22
|
+
background-color: ${themeVars.colorBgListItemGeneralSelected};
|
|
23
|
+
|
|
24
|
+
&[data-selected="true"] {
|
|
25
|
+
background-color: ${themeVars.colorBgListItemGeneralSelectedHover};
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const arrowWrapperClassName = css`
|
|
30
|
+
flex-grow: 0;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
export type NestedMenuItem<T> = {
|
|
34
|
+
key: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
icon?: ReactNode;
|
|
37
|
+
value: T;
|
|
38
|
+
groupItems?: Array<NestedMenuItem<T>>;
|
|
39
|
+
filterPlaceholder?: string;
|
|
40
|
+
emptyMessage?: string;
|
|
41
|
+
calculateNestedItems?: () => Array<NestedMenuItem<T>>;
|
|
42
|
+
kind?: "separator" | "group";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type NestedMenuStack<T> = Array<{
|
|
46
|
+
item: NestedMenuItem<T>;
|
|
47
|
+
nestedItems: NestedMenuItem<T>[];
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
type Props<T> = {
|
|
51
|
+
defaultStack?: NestedMenuStack<T>;
|
|
52
|
+
defaultValue?: string;
|
|
53
|
+
rootItems: Array<NestedMenuItem<T>>;
|
|
54
|
+
onChange: (item: NestedMenuItem<T>) => void;
|
|
55
|
+
filterPlaceholder: string;
|
|
56
|
+
emptyMessage: string;
|
|
57
|
+
getArrowTooltipMessage: (item: NestedMenuItem<T>) => string;
|
|
58
|
+
maxDepthLevel?: number;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const NestedCommandMenuItem = forwardRef<
|
|
62
|
+
React.ElementRef<typeof CommandItem>,
|
|
63
|
+
React.ComponentPropsWithoutRef<typeof CommandItem>
|
|
64
|
+
>(function CommandMenuItem({className, children, ...props}, ref) {
|
|
65
|
+
return (
|
|
66
|
+
<CommandItem ref={ref} className={cx(commandItemCss, className)} {...props}>
|
|
67
|
+
{children}
|
|
68
|
+
</CommandItem>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function isItemMatchFilter<T>(item: NestedMenuItem<T>, lowerCaseFilterValue: string) {
|
|
73
|
+
return item.title?.toLowerCase()?.includes(lowerCaseFilterValue);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function NestedCommandMenu<T>({
|
|
77
|
+
defaultStack,
|
|
78
|
+
defaultValue,
|
|
79
|
+
rootItems,
|
|
80
|
+
onChange,
|
|
81
|
+
filterPlaceholder,
|
|
82
|
+
emptyMessage,
|
|
83
|
+
getArrowTooltipMessage,
|
|
84
|
+
maxDepthLevel = Number.MAX_VALUE,
|
|
85
|
+
}: Props<T>) {
|
|
86
|
+
const [stack, setStack] = React.useState<NestedMenuStack<T>>(defaultStack || []);
|
|
87
|
+
const currentStackItem = stack[stack.length - 1];
|
|
88
|
+
|
|
89
|
+
const currentItems = currentStackItem?.nestedItems || rootItems;
|
|
90
|
+
|
|
91
|
+
const onGoToRelation = useCallback(
|
|
92
|
+
(key: string) => {
|
|
93
|
+
const item = currentItems.find((item) => item.key === key)!;
|
|
94
|
+
setStack((items) => [...items, {item, nestedItems: item.calculateNestedItems?.() || []}]);
|
|
95
|
+
setFilter("");
|
|
96
|
+
},
|
|
97
|
+
[currentItems]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const onCmdkItemSelect = useCallback(
|
|
101
|
+
(item: NestedMenuItem<T>) => {
|
|
102
|
+
onChange(item);
|
|
103
|
+
},
|
|
104
|
+
[onChange]
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const [filter, setFilter] = useState("");
|
|
108
|
+
|
|
109
|
+
const filteredOptions = useMemo(() => {
|
|
110
|
+
const lowerCaseFilterValue = filter.toLowerCase();
|
|
111
|
+
if (!filter) {
|
|
112
|
+
return currentItems;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return currentItems
|
|
116
|
+
.map((item) => {
|
|
117
|
+
if (item.kind === "group") {
|
|
118
|
+
const groupItems = (item.groupItems || []).filter((groupItem) =>
|
|
119
|
+
isItemMatchFilter(groupItem, lowerCaseFilterValue)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return groupItems.length > 0 ? {...item, groupItems} : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return isItemMatchFilter(item, lowerCaseFilterValue) ? item : null;
|
|
126
|
+
})
|
|
127
|
+
.filter(Boolean) as Array<NestedMenuItem<T>>;
|
|
128
|
+
}, [filter, currentItems]);
|
|
129
|
+
|
|
130
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
inputRef.current?.focus();
|
|
135
|
+
}, 0);
|
|
136
|
+
}, [stack]);
|
|
137
|
+
|
|
138
|
+
const menuListRef = useRef<HTMLDivElement>(null);
|
|
139
|
+
const scrollToTop = useCallback(() => {
|
|
140
|
+
menuListRef.current?.scrollTo({top: 0, behavior: "instant"});
|
|
141
|
+
}, []);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
scrollToTop();
|
|
144
|
+
}, [stack, scrollToTop]);
|
|
145
|
+
|
|
146
|
+
const [value, setValue] = React.useState(defaultValue);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<>
|
|
150
|
+
{stack.length > 0 && (
|
|
151
|
+
<>
|
|
152
|
+
<div
|
|
153
|
+
className={css`
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
|
|
156
|
+
&:hover {
|
|
157
|
+
background-color: ${themeVars.colorBgActionsMenuItemHover};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
display: flex;
|
|
161
|
+
padding: ${space.s6}px ${space.s8}px;
|
|
162
|
+
border-radius: ${border.radius6}px;
|
|
163
|
+
margin: 0 ${space.s4}px;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: ${space.s8}px;
|
|
166
|
+
`}
|
|
167
|
+
onClick={() => {
|
|
168
|
+
setStack((relations) => relations.slice(0, -1));
|
|
169
|
+
setFilter("");
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
<div>
|
|
173
|
+
<Back iconSize={16} aria-hidden />
|
|
174
|
+
</div>
|
|
175
|
+
<div>
|
|
176
|
+
Back
|
|
177
|
+
{stack.length > 1 ? ` to ${stack[stack.length - 2].item.title}` : ""}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div
|
|
181
|
+
className={css`
|
|
182
|
+
background-color: ${themeVars.separatorColor};
|
|
183
|
+
height: 1px;
|
|
184
|
+
margin-top: ${space.s4}px;
|
|
185
|
+
`}
|
|
186
|
+
/>
|
|
187
|
+
</>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
<CommandMenuRoot
|
|
191
|
+
shouldFilter={false}
|
|
192
|
+
value={value}
|
|
193
|
+
onValueChange={setValue}
|
|
194
|
+
onKeyDown={(e) => {
|
|
195
|
+
if (e.key === "Escape" || (e.key === "Backspace" && !filter && stack.length > 0)) {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
setStack((relations) => relations.slice(0, -1));
|
|
198
|
+
} else if (e.key === "ArrowRight") {
|
|
199
|
+
const currentItem = currentItems.find((item) => item.key === value);
|
|
200
|
+
if (currentItem && currentItem.calculateNestedItems) {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
onGoToRelation(currentItem.key);
|
|
203
|
+
}
|
|
204
|
+
} else if (e.key === "ArrowLeft" && stack.length > 0) {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
setStack((relations) => relations.slice(0, -1));
|
|
207
|
+
}
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
<CommandMenuInput
|
|
211
|
+
ref={inputRef}
|
|
212
|
+
value={filter}
|
|
213
|
+
onValueChange={setFilter}
|
|
214
|
+
placeholder={currentStackItem?.item?.filterPlaceholder || filterPlaceholder}
|
|
215
|
+
/>
|
|
216
|
+
<CommandMenuList
|
|
217
|
+
ref={menuListRef}
|
|
218
|
+
className={css`
|
|
219
|
+
&& {
|
|
220
|
+
transition: none;
|
|
221
|
+
max-height: 300px;
|
|
222
|
+
}
|
|
223
|
+
`}
|
|
224
|
+
>
|
|
225
|
+
<CommandMenuEmpty>{currentStackItem?.item?.emptyMessage || emptyMessage}</CommandMenuEmpty>
|
|
226
|
+
{renderLevel({
|
|
227
|
+
options: filteredOptions,
|
|
228
|
+
stack,
|
|
229
|
+
maxDepthLevel,
|
|
230
|
+
onCmdkItemSelect,
|
|
231
|
+
onGoToRelation,
|
|
232
|
+
getArrowTooltipMessage,
|
|
233
|
+
defaultValue,
|
|
234
|
+
})}
|
|
235
|
+
</CommandMenuList>
|
|
236
|
+
</CommandMenuRoot>
|
|
237
|
+
</>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function renderLevel<T>({
|
|
242
|
+
options,
|
|
243
|
+
defaultValue,
|
|
244
|
+
stack,
|
|
245
|
+
maxDepthLevel,
|
|
246
|
+
onCmdkItemSelect,
|
|
247
|
+
onGoToRelation,
|
|
248
|
+
getArrowTooltipMessage,
|
|
249
|
+
}: {
|
|
250
|
+
options: Array<NestedMenuItem<T>>;
|
|
251
|
+
defaultValue?: string;
|
|
252
|
+
stack: NestedMenuStack<T>;
|
|
253
|
+
maxDepthLevel: number;
|
|
254
|
+
onCmdkItemSelect: (item: NestedMenuItem<T>) => void;
|
|
255
|
+
onGoToRelation: (key: string) => void;
|
|
256
|
+
getArrowTooltipMessage: (item: NestedMenuItem<T>) => string;
|
|
257
|
+
}) {
|
|
258
|
+
return options.map((item) => {
|
|
259
|
+
if (item.kind === "separator") {
|
|
260
|
+
return <CommandMenuSeparator key={item.key} />;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (item.kind === "group") {
|
|
264
|
+
const nestedItems = item.groupItems || [];
|
|
265
|
+
if (nestedItems.length > 0) {
|
|
266
|
+
return (
|
|
267
|
+
<CommandMenuGroup heading={item.title} key={item.key}>
|
|
268
|
+
{renderLevel({
|
|
269
|
+
options: nestedItems,
|
|
270
|
+
defaultValue,
|
|
271
|
+
stack,
|
|
272
|
+
maxDepthLevel,
|
|
273
|
+
onCmdkItemSelect,
|
|
274
|
+
onGoToRelation,
|
|
275
|
+
getArrowTooltipMessage,
|
|
276
|
+
})}
|
|
277
|
+
</CommandMenuGroup>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const isSelected = item.key === defaultValue;
|
|
285
|
+
if (item.calculateNestedItems && stack.length < maxDepthLevel) {
|
|
286
|
+
return (
|
|
287
|
+
<NestedCommandMenuItem
|
|
288
|
+
key={item.key}
|
|
289
|
+
value={item.key}
|
|
290
|
+
onSelect={() => onCmdkItemSelect(item)}
|
|
291
|
+
asChild
|
|
292
|
+
className={cn(
|
|
293
|
+
css`
|
|
294
|
+
padding-right: 0;
|
|
295
|
+
`,
|
|
296
|
+
isSelected ? selectedItemClassName : undefined
|
|
297
|
+
)}
|
|
298
|
+
>
|
|
299
|
+
<div
|
|
300
|
+
className={css`
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
justify-content: space-between;
|
|
304
|
+
gap: ${space.s6}px;
|
|
305
|
+
|
|
306
|
+
&[data-selected="true"]:has(.${arrowWrapperClassName}:hover) {
|
|
307
|
+
background-color: transparent;
|
|
308
|
+
}
|
|
309
|
+
`}
|
|
310
|
+
>
|
|
311
|
+
{item.icon}
|
|
312
|
+
<div
|
|
313
|
+
className={css`
|
|
314
|
+
flex: 1;
|
|
315
|
+
`}
|
|
316
|
+
>
|
|
317
|
+
{item.title}
|
|
318
|
+
</div>
|
|
319
|
+
<div className={arrowWrapperClassName}>
|
|
320
|
+
<Tooltip title={getArrowTooltipMessage(item)}>
|
|
321
|
+
<IconButton
|
|
322
|
+
size={"medium"}
|
|
323
|
+
onClick={(e) => {
|
|
324
|
+
e.stopPropagation();
|
|
325
|
+
onGoToRelation(item.key);
|
|
326
|
+
}}
|
|
327
|
+
>
|
|
328
|
+
<ArrowRight color={themeVars.iconColor} containerSize={10} aria-hidden />
|
|
329
|
+
</IconButton>
|
|
330
|
+
</Tooltip>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</NestedCommandMenuItem>
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
return (
|
|
337
|
+
<CommandMenuItem
|
|
338
|
+
key={item.key}
|
|
339
|
+
value={item.key}
|
|
340
|
+
onSelect={() => onCmdkItemSelect(item)}
|
|
341
|
+
icon={item.icon}
|
|
342
|
+
className={isSelected ? selectedItemClassName : undefined}
|
|
343
|
+
>
|
|
344
|
+
{item.title}
|
|
345
|
+
</CommandMenuItem>
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
@@ -17,6 +17,7 @@ const unitOptions = [
|
|
|
17
17
|
{value: "day", label: "days"},
|
|
18
18
|
{value: "week", label: "weeks"},
|
|
19
19
|
{value: "month", label: "months"},
|
|
20
|
+
{value: "quarter", label: "quarters"},
|
|
20
21
|
{value: "year", label: "years"},
|
|
21
22
|
];
|
|
22
23
|
|
|
@@ -25,12 +26,6 @@ const isBeforeNowOptions = [
|
|
|
25
26
|
{value: "from now", label: "from now"},
|
|
26
27
|
];
|
|
27
28
|
|
|
28
|
-
const blurOnEnter = (e: $TSFixMe) => {
|
|
29
|
-
if (e.key === "Enter") {
|
|
30
|
-
e.target.blur();
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
29
|
export const RelativeDatePicker = (props: RelativeDatePickerProps) => {
|
|
35
30
|
const {value, onChange, relativeDateToExactDate} = props;
|
|
36
31
|
|
|
@@ -45,14 +40,30 @@ export const RelativeDatePicker = (props: RelativeDatePickerProps) => {
|
|
|
45
40
|
|
|
46
41
|
const [amount, setAmount] = useState(valueWithDefault.amount);
|
|
47
42
|
const onAmountChange = useCallback((newAmount: $TSFixMe) => {
|
|
48
|
-
if (newAmount === null
|
|
43
|
+
if (newAmount === null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (newAmount < 0) {
|
|
49
47
|
setAmount(0);
|
|
50
48
|
} else {
|
|
51
49
|
setAmount(Math.floor(newAmount));
|
|
52
50
|
}
|
|
53
51
|
}, []);
|
|
52
|
+
|
|
53
|
+
const onKeyDown = useCallback(
|
|
54
|
+
(e: $TSFixMe) => {
|
|
55
|
+
if (e.key === "Enter") {
|
|
56
|
+
onChange({...valueWithDefault, amount});
|
|
57
|
+
e.target.blur();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[amount, onChange, valueWithDefault]
|
|
61
|
+
);
|
|
62
|
+
|
|
54
63
|
const handleBlur = useCallback(() => {
|
|
55
|
-
|
|
64
|
+
if (amount !== valueWithDefault.amount) {
|
|
65
|
+
onChange({...valueWithDefault, amount});
|
|
66
|
+
}
|
|
56
67
|
}, [onChange, amount, valueWithDefault]);
|
|
57
68
|
|
|
58
69
|
const onIsBeforeNowChange = useCallback(
|
|
@@ -85,7 +96,7 @@ export const RelativeDatePicker = (props: RelativeDatePickerProps) => {
|
|
|
85
96
|
style={{width: "56px"}}
|
|
86
97
|
onBlur={handleBlur}
|
|
87
98
|
onChange={onAmountChange}
|
|
88
|
-
onKeyDown={
|
|
99
|
+
onKeyDown={onKeyDown}
|
|
89
100
|
/>
|
|
90
101
|
</div>
|
|
91
102
|
<div
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
// This icon file is generated automatically.
|
|
3
|
+
|
|
4
|
+
import { IconDefinition } from '../types';
|
|
5
|
+
|
|
6
|
+
const DateRange: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M14 3.6h1.6A2.4 2.4 0 0 1 18 6v9.6a2.4 2.4 0 0 1-2.4 2.4H4.4A2.4 2.4 0 0 1 2 15.6V6a2.4 2.4 0 0 1 2.4-2.4H6v-.8a.8.8 0 0 1 1.6 0v.8h4.8v-.8a.8.8 0 0 1 1.6 0v.8Zm2.166 12.566a.8.8 0 0 0 .234-.566V6a.8.8 0 0 0-.8-.8H14V6a.8.8 0 0 1-1.6 0v-.8H7.6V6A.8.8 0 0 1 6 6v-.8H4.4a.8.8 0 0 0-.8.8v9.6a.8.8 0 0 0 .8.8h11.2a.8.8 0 0 0 .566-.234ZM8.78 8.22a.75.75 0 0 1 0 1.06L7.56 10.5h4.88l-1.22-1.22a.75.75 0 0 1 1.06-1.06l2.5 2.5a.75.75 0 0 1 0 1.06l-2.5 2.5a.75.75 0 1 1-1.06-1.06L12.44 12H7.56l1.22 1.22a.75.75 0 1 1-1.06 1.06l-2.5-2.5a.75.75 0 0 1 0-1.06l2.5-2.5a.75.75 0 0 1 1.06 0Z"},"children":[]}],"metadata":""}]},"name":"date-range"};
|
|
7
|
+
|
|
8
|
+
export default DateRange;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
// This icon file is generated automatically.
|
|
3
|
+
|
|
4
|
+
import { IconDefinition } from '../types';
|
|
5
|
+
|
|
6
|
+
const ReadOnly: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M2.22 2.22a.75.75 0 0 1 1.06 0l4.845 4.844 3.47-3.47a3.402 3.402 0 1 1 4.81 4.811l-3.47 3.47 4.845 4.845a.75.75 0 1 1-1.06 1.06L2.22 3.28a.75.75 0 0 1 0-1.06Zm11.683 6.567-2.028 2.027-2.69-2.689 2.028-2.028 2.69 2.69Zm1.442-4.132a1.902 1.902 0 0 0-2.69 0l-.558.558 2.69 2.69.558-.558a1.902 1.902 0 0 0 0-2.69ZM6.534 10.777a.75.75 0 0 0-1.06-1.061L2.72 12.47a.75.75 0 0 0-.22.53v3.5a1 1 0 0 0 1 1H7a.75.75 0 0 0 .53-.22l2.754-2.753a.75.75 0 0 0-1.06-1.061L6.688 16H4v-2.69l2.534-2.533Z"},"children":[]}],"metadata":""}]},"name":"read-only"};
|
|
7
|
+
|
|
8
|
+
export default ReadOnly;
|
package/src/icons/ast/index.tsx
CHANGED
|
@@ -84,6 +84,7 @@ export { default as CrossCircle } from './CrossCircle';
|
|
|
84
84
|
export { default as DatabaseOff } from './DatabaseOff';
|
|
85
85
|
export { default as DatabaseStroke } from './DatabaseStroke';
|
|
86
86
|
export { default as Database } from './Database';
|
|
87
|
+
export { default as DateRange } from './DateRange';
|
|
87
88
|
export { default as Delete } from './Delete';
|
|
88
89
|
export { default as Demo } from './Demo';
|
|
89
90
|
export { default as Dependency } from './Dependency';
|
|
@@ -212,6 +213,7 @@ export { default as PresentPlay } from './PresentPlay';
|
|
|
212
213
|
export { default as PresentStop } from './PresentStop';
|
|
213
214
|
export { default as PrivateItems } from './PrivateItems';
|
|
214
215
|
export { default as Question } from './Question';
|
|
216
|
+
export { default as ReadOnly } from './ReadOnly';
|
|
215
217
|
export { default as Refresh } from './Refresh';
|
|
216
218
|
export { default as RemovePeople } from './RemovePeople';
|
|
217
219
|
export { default as Remove } from './Remove';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// This icon file is generated automatically.
|
|
2
|
+
|
|
3
|
+
import {forwardRef} from 'react';
|
|
4
|
+
import DateRangeSvg from '../ast/DateRange';
|
|
5
|
+
import { Icon } from '../Icon';
|
|
6
|
+
import { IconBaseProps } from '../types';
|
|
7
|
+
|
|
8
|
+
const DateRange = forwardRef<SVGSVGElement, IconBaseProps>(function DateRange(
|
|
9
|
+
props: IconBaseProps,
|
|
10
|
+
ref: React.Ref<SVGSVGElement>
|
|
11
|
+
) {return <Icon {...props} className={props.className} ref={ref} icon={DateRangeSvg} />});
|
|
12
|
+
|
|
13
|
+
export default DateRange;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// This icon file is generated automatically.
|
|
2
|
+
|
|
3
|
+
import {forwardRef} from 'react';
|
|
4
|
+
import ReadOnlySvg from '../ast/ReadOnly';
|
|
5
|
+
import { Icon } from '../Icon';
|
|
6
|
+
import { IconBaseProps } from '../types';
|
|
7
|
+
|
|
8
|
+
const ReadOnly = forwardRef<SVGSVGElement, IconBaseProps>(function ReadOnly(
|
|
9
|
+
props: IconBaseProps,
|
|
10
|
+
ref: React.Ref<SVGSVGElement>
|
|
11
|
+
) {return <Icon {...props} className={props.className} ref={ref} icon={ReadOnlySvg} />});
|
|
12
|
+
|
|
13
|
+
export default ReadOnly;
|
|
@@ -84,6 +84,7 @@ export { default as CrossCircle } from './CrossCircle';
|
|
|
84
84
|
export { default as DatabaseOff } from './DatabaseOff';
|
|
85
85
|
export { default as DatabaseStroke } from './DatabaseStroke';
|
|
86
86
|
export { default as Database } from './Database';
|
|
87
|
+
export { default as DateRange } from './DateRange';
|
|
87
88
|
export { default as Delete } from './Delete';
|
|
88
89
|
export { default as Demo } from './Demo';
|
|
89
90
|
export { default as Dependency } from './Dependency';
|
|
@@ -212,6 +213,7 @@ export { default as PresentPlay } from './PresentPlay';
|
|
|
212
213
|
export { default as PresentStop } from './PresentStop';
|
|
213
214
|
export { default as PrivateItems } from './PrivateItems';
|
|
214
215
|
export { default as Question } from './Question';
|
|
216
|
+
export { default as ReadOnly } from './ReadOnly';
|
|
215
217
|
export { default as Refresh } from './Refresh';
|
|
216
218
|
export { default as RemovePeople } from './RemovePeople';
|
|
217
219
|
export { default as Remove } from './Remove';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
3
|
+
d="M14 3.6H15.6C16.2365 3.6 16.847 3.85286 17.2971 4.30294C17.7471 4.75303 18 5.36348 18 6V15.6C18 16.2365 17.7471 16.847 17.2971 17.2971C16.847 17.7471 16.2365 18 15.6 18H4.4C3.76348 18 3.15303 17.7471 2.70294 17.2971C2.25286 16.847 2 16.2365 2 15.6V6C2 5.36348 2.25286 4.75303 2.70294 4.30294C3.15303 3.85286 3.76348 3.6 4.4 3.6H6V2.8C6 2.58783 6.08429 2.38434 6.23431 2.23431C6.38434 2.08429 6.58783 2 6.8 2C7.01217 2 7.21566 2.08429 7.36569 2.23431C7.51571 2.38434 7.6 2.58783 7.6 2.8V3.6H12.4V2.8C12.4 2.58783 12.4843 2.38434 12.6343 2.23431C12.7843 2.08429 12.9878 2 13.2 2C13.4122 2 13.6157 2.08429 13.7657 2.23431C13.9157 2.38434 14 2.58783 14 2.8V3.6ZM16.1657 16.1657C16.3157 16.0157 16.4 15.8122 16.4 15.6V6C16.4 5.78783 16.3157 5.58434 16.1657 5.43431C16.0157 5.28429 15.8122 5.2 15.6 5.2H14V6C14 6.21217 13.9157 6.41566 13.7657 6.56569C13.6157 6.71571 13.4122 6.8 13.2 6.8C12.9878 6.8 12.7843 6.71571 12.6343 6.56569C12.4843 6.41566 12.4 6.21217 12.4 6V5.2H7.6V6C7.6 6.21217 7.51571 6.41566 7.36569 6.56569C7.21566 6.71571 7.01217 6.8 6.8 6.8C6.58783 6.8 6.38434 6.71571 6.23431 6.56569C6.08429 6.41566 6 6.21217 6 6V5.2H4.4C4.18783 5.2 3.98434 5.28429 3.83431 5.43431C3.68429 5.58434 3.6 5.78783 3.6 6V15.6C3.6 15.8122 3.68429 16.0157 3.83431 16.1657C3.98434 16.3157 4.18783 16.4 4.4 16.4H15.6C15.8122 16.4 16.0157 16.3157 16.1657 16.1657ZM8.78033 8.21967C9.07322 8.51256 9.07322 8.98744 8.78033 9.28033L7.56066 10.5H12.4393L11.2197 9.28033C10.9268 8.98744 10.9268 8.51256 11.2197 8.21967C11.5126 7.92678 11.9874 7.92678 12.2803 8.21967L14.7803 10.7197C15.0732 11.0126 15.0732 11.4874 14.7803 11.7803L12.2803 14.2803C11.9874 14.5732 11.5126 14.5732 11.2197 14.2803C10.9268 13.9874 10.9268 13.5126 11.2197 13.2197L12.4393 12H7.56066L8.78033 13.2197C9.07322 13.5126 9.07322 13.9874 8.78033 14.2803C8.48744 14.5732 8.01256 14.5732 7.71967 14.2803L5.21967 11.7803C4.92678 11.4874 4.92678 11.0126 5.21967 10.7197L7.71967 8.21967C8.01256 7.92678 8.48744 7.92678 8.78033 8.21967Z" />
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
3
|
+
d="M2.21967 2.21967C2.51256 1.92678 2.98744 1.92678 3.28033 2.21967L8.125 7.06434L11.5947 3.59468C12.9231 2.26625 15.0769 2.26625 16.4053 3.59468C17.7338 4.92311 17.7338 7.07691 16.4053 8.40534L12.9357 11.875L17.7803 16.7197C18.0732 17.0126 18.0732 17.4874 17.7803 17.7803C17.4874 18.0732 17.0126 18.0732 16.7197 17.7803L2.21967 3.28033C1.92678 2.98744 1.92678 2.51256 2.21967 2.21967ZM13.9027 8.78662L11.875 10.8143L9.18566 8.125L11.2134 6.09728L13.9027 8.78662ZM15.3447 4.65534C14.602 3.9127 13.398 3.9127 12.6553 4.65534L12.0973 5.21339L14.7866 7.90273L15.3447 7.34468C16.0873 6.60204 16.0873 5.39798 15.3447 4.65534Z" />
|
|
4
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
5
|
+
d="M6.53401 10.7767C6.82691 10.4838 6.82691 10.0089 6.53401 9.71599C6.24112 9.4231 5.76625 9.4231 5.47335 9.71599L2.71967 12.4697C2.57902 12.6103 2.5 12.8011 2.5 13V16.5C2.5 17.0523 2.94772 17.5 3.5 17.5H7C7.19891 17.5 7.38968 17.421 7.53033 17.2803L10.284 14.5267C10.5769 14.2338 10.5769 13.7589 10.284 13.466C9.99112 13.1731 9.51625 13.1731 9.22335 13.466L6.68934 16H4V13.3107L6.53401 10.7767Z" />
|
|
6
|
+
</svg>
|
package/src/loaders.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {css, cx} from "@linaria/core";
|
|
2
2
|
import {styled} from "@linaria/react";
|
|
3
|
-
import {ReactNode, memo} from "react";
|
|
3
|
+
import {ReactNode, memo, useMemo} from "react";
|
|
4
4
|
import {IconBaseProps} from "./icons/types";
|
|
5
5
|
import {border, layout, space, textStyles, themeVars} from "./design-system";
|
|
6
6
|
import {getShiftStyle} from "./icons/get-shift-style";
|
|
@@ -104,35 +104,40 @@ const ContainerSpinner = styled.div<{containerSize: number}>`
|
|
|
104
104
|
line-height: ${(props) => props.containerSize}px;
|
|
105
105
|
`;
|
|
106
106
|
|
|
107
|
-
type
|
|
107
|
+
type Props = {
|
|
108
108
|
size?: number;
|
|
109
109
|
containerSize?: number;
|
|
110
110
|
width?: number;
|
|
111
111
|
color?: string;
|
|
112
112
|
backgroundColor?: string;
|
|
113
113
|
};
|
|
114
|
-
export const Spinner = memo<SpinnerProps>(function Spinner({
|
|
115
|
-
size = 20,
|
|
116
|
-
containerSize = 15,
|
|
117
|
-
width = 2,
|
|
118
|
-
color = "#000000",
|
|
119
|
-
backgroundColor = "#FFFFFF",
|
|
120
|
-
}) {
|
|
121
|
-
const spinnerStyle = {
|
|
122
|
-
width: size,
|
|
123
|
-
height: size,
|
|
124
|
-
...getShiftStyle({elementSize: size, containerSize}),
|
|
125
|
-
borderWidth: width,
|
|
126
|
-
borderColor: backgroundColor,
|
|
127
|
-
borderTopColor: color,
|
|
128
|
-
};
|
|
129
114
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
115
|
+
export const Spinner = memo<Props>(
|
|
116
|
+
({
|
|
117
|
+
size = 20,
|
|
118
|
+
containerSize = 15,
|
|
119
|
+
width = 2,
|
|
120
|
+
color = themeVars.iconColor,
|
|
121
|
+
backgroundColor = themeVars.badgeBgColor,
|
|
122
|
+
}) => {
|
|
123
|
+
const style = useMemo(() => {
|
|
124
|
+
return {
|
|
125
|
+
width: size,
|
|
126
|
+
height: size,
|
|
127
|
+
...getShiftStyle({elementSize: size, containerSize}),
|
|
128
|
+
borderWidth: width,
|
|
129
|
+
borderColor: backgroundColor,
|
|
130
|
+
borderTopColor: color,
|
|
131
|
+
};
|
|
132
|
+
}, [backgroundColor, color, containerSize, size, width]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<ContainerSpinner containerSize={containerSize}>
|
|
136
|
+
<div style={style} className={spinner} />
|
|
137
|
+
</ContainerSpinner>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
136
141
|
|
|
137
142
|
export const LoadingTreeMenuItem = ({
|
|
138
143
|
color,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {textStyles, themeVars} from "../../src/design-system";
|
|
2
2
|
import {css} from "@linaria/core";
|
|
3
|
-
import {useCallback, useMemo, useRef, useState
|
|
3
|
+
import {ComponentProps, forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState} from "react";
|
|
4
4
|
import {formatValue, getStep, parseValue} from "./index";
|
|
5
5
|
import _ from "lodash";
|
|
6
6
|
import {add, sub} from "./decimal";
|
|
@@ -10,9 +10,9 @@ import {stopPropagation} from "@fibery/react/src/stop-propagation";
|
|
|
10
10
|
import cx from "classnames";
|
|
11
11
|
import {invalidInputStyle} from "./edit-unit-styles";
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const invalidCss = css``;
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const inputContainerCss = css`
|
|
16
16
|
position: relative;
|
|
17
17
|
input {
|
|
18
18
|
background-color: ${themeVars.transparent};
|
|
@@ -26,31 +26,30 @@ export const inputContainerClassName = css`
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
&.${
|
|
29
|
+
&.${invalidCss} {
|
|
30
30
|
${invalidInputStyle}
|
|
31
31
|
}
|
|
32
32
|
`;
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
>(
|
|
34
|
+
type NumberFormat = "Number" | "Money" | "Percent";
|
|
35
|
+
|
|
36
|
+
type Props = ComponentProps<typeof InputNumber> & {
|
|
37
|
+
value: string | number | null;
|
|
38
|
+
numberFormat: NumberFormat;
|
|
39
|
+
format?: (value: string | number) => string;
|
|
40
|
+
numberPrecision: number;
|
|
41
|
+
placeholder: string;
|
|
42
|
+
autoFocus: boolean;
|
|
43
|
+
valueWrapperClassName?: string;
|
|
44
|
+
decimalSeparator?: string;
|
|
45
|
+
invalid?: boolean;
|
|
46
|
+
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
47
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
|
48
|
+
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
49
|
+
onChange: (value: string | number | null) => void;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const NumberInlineInput = forwardRef<HTMLInputElement, Props>(
|
|
54
53
|
(
|
|
55
54
|
{
|
|
56
55
|
value,
|
|
@@ -69,23 +68,15 @@ export const NumberInlineInput = forwardRef<
|
|
|
69
68
|
},
|
|
70
69
|
ref
|
|
71
70
|
) => {
|
|
72
|
-
const isEditing = true;
|
|
73
|
-
const handleFocus: React.FocusEventHandler<HTMLInputElement> = useCallback(
|
|
74
|
-
(e) => {
|
|
75
|
-
if (onFocus) {
|
|
76
|
-
onFocus(e);
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
[onFocus]
|
|
80
|
-
);
|
|
81
|
-
|
|
82
71
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
83
72
|
const [inputContainerRef, setInputContainerRef] = useState<HTMLDivElement | null>(null);
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
useImperativeHandle(ref, () => inputRef.current!, []);
|
|
75
|
+
|
|
76
|
+
const handleBlur = useCallback(
|
|
77
|
+
(e: React.FocusEvent<HTMLInputElement>) => {
|
|
78
|
+
if (e.relatedTarget && inputContainerRef && !inputContainerRef.contains(e.relatedTarget)) {
|
|
79
|
+
onBlur?.(e);
|
|
89
80
|
}
|
|
90
81
|
},
|
|
91
82
|
[inputContainerRef, onBlur]
|
|
@@ -101,14 +92,7 @@ export const NumberInlineInput = forwardRef<
|
|
|
101
92
|
);
|
|
102
93
|
|
|
103
94
|
const handleStep = useCallback(
|
|
104
|
-
(
|
|
105
|
-
newValue: string | number,
|
|
106
|
-
{
|
|
107
|
-
type,
|
|
108
|
-
}: {
|
|
109
|
-
type: "up" | "down";
|
|
110
|
-
}
|
|
111
|
-
) => {
|
|
95
|
+
(newValue: string | number, {type}: {type: "up" | "down"}) => {
|
|
112
96
|
inputRef.current?.focus();
|
|
113
97
|
const step = getStep(numberFormat);
|
|
114
98
|
if (type === "up") {
|
|
@@ -119,29 +103,26 @@ export const NumberInlineInput = forwardRef<
|
|
|
119
103
|
},
|
|
120
104
|
[numberFormat, onChange, value]
|
|
121
105
|
);
|
|
122
|
-
useImperativeHandle(ref, () => inputRef.current!, []);
|
|
123
106
|
|
|
124
107
|
const formattedValue = useMemo(() => {
|
|
108
|
+
const isEditing = true;
|
|
125
109
|
return formatValue(value, isEditing, numberFormat, format);
|
|
126
|
-
}, [value,
|
|
110
|
+
}, [value, numberFormat, format]);
|
|
127
111
|
|
|
128
112
|
return (
|
|
129
|
-
<div
|
|
130
|
-
ref={setInputContainerRef}
|
|
131
|
-
className={cx(valueWrapperClassName, inputContainerClassName, invalid && invalidClassName)}
|
|
132
|
-
>
|
|
113
|
+
<div ref={setInputContainerRef} className={cx(valueWrapperClassName, inputContainerCss, invalid && invalidCss)}>
|
|
133
114
|
<InputNumber
|
|
134
115
|
autoFocus={autoFocus}
|
|
135
116
|
ref={inputRef}
|
|
136
117
|
value={formattedValue}
|
|
137
|
-
onFocus={handleFocus}
|
|
138
|
-
onBlur={handleBlur}
|
|
139
|
-
onChange={handleChange}
|
|
140
|
-
onKeyDown={onKeyDown}
|
|
141
118
|
type="text"
|
|
142
119
|
placeholder={placeholder}
|
|
143
120
|
upHandler={<StepButton type={"up"} />}
|
|
144
121
|
downHandler={<StepButton type={"down"} />}
|
|
122
|
+
onFocus={onFocus}
|
|
123
|
+
onBlur={handleBlur}
|
|
124
|
+
onChange={handleChange}
|
|
125
|
+
onKeyDown={onKeyDown}
|
|
145
126
|
onStep={handleStep}
|
|
146
127
|
onClick={stopPropagation}
|
|
147
128
|
{...rest}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
|
-
import {
|
|
2
|
+
import {components, GroupHeadingProps} from "react-select";
|
|
3
3
|
import {layout, space, textStyles, themeVars} from "../../design-system";
|
|
4
4
|
import {TooltipIfOverflown} from "../../tooltip-if-overflown";
|
|
5
5
|
import {GroupBase} from "../index";
|
|
@@ -47,6 +47,7 @@ const GroupDivider = () => (
|
|
|
47
47
|
<div className={groupDividerStyle}></div>
|
|
48
48
|
</div>
|
|
49
49
|
);
|
|
50
|
+
|
|
50
51
|
export function GroupHeading<
|
|
51
52
|
Option,
|
|
52
53
|
IsMulti extends boolean = false,
|
|
@@ -77,3 +78,25 @@ export function GroupHeading<
|
|
|
77
78
|
</components.GroupHeading>
|
|
78
79
|
);
|
|
79
80
|
}
|
|
81
|
+
|
|
82
|
+
// probably can be united with "GroupHeading" above.
|
|
83
|
+
export function GroupSeparator<
|
|
84
|
+
Option,
|
|
85
|
+
IsMulti extends boolean = false,
|
|
86
|
+
Group extends GroupBase<Option> = GroupBase<Option>
|
|
87
|
+
>({...props}: GroupHeadingProps<Option, IsMulti, Group>) {
|
|
88
|
+
// Hide separators when searching
|
|
89
|
+
if (!props.data.separator || props.selectProps.inputValue) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<hr
|
|
95
|
+
style={{margin: `${((props.data.separatorMargin || space.s8) - 1) / 2}px 0`}}
|
|
96
|
+
className={css`
|
|
97
|
+
border: none;
|
|
98
|
+
border-bottom: 1px solid ${themeVars.separatorColor};
|
|
99
|
+
`}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {OptionProps, components} from "react-select";
|
|
2
|
-
import {GroupBase} from "../index";
|
|
3
1
|
import {css} from "@linaria/core";
|
|
4
|
-
import {layout, space} from "../../design-system";
|
|
5
|
-
import {ListRowContent, ListRowSurface} from "../../lists/list-row-surface";
|
|
6
2
|
import cn from "classnames";
|
|
7
3
|
import {ComponentProps, useMemo} from "react";
|
|
4
|
+
import {components, OptionProps} from "react-select";
|
|
5
|
+
import {layout, space} from "../../design-system";
|
|
6
|
+
import {ListRowContent, ListRowSurface} from "../../lists/list-row-surface";
|
|
8
7
|
import {TooltipIfOverflown} from "../../tooltip-if-overflown";
|
|
8
|
+
import {GroupBase} from "../index";
|
|
9
9
|
import {isPureTextChildren} from "../util";
|
|
10
10
|
|
|
11
11
|
export const OptionNulledVitualizedStyles = {
|
|
@@ -22,16 +22,22 @@ export const OptionNulledStyles = {
|
|
|
22
22
|
lineHeight: "",
|
|
23
23
|
color: "",
|
|
24
24
|
};
|
|
25
|
+
|
|
25
26
|
const OptionRootClass = css`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
:where(&) {
|
|
28
|
+
padding: 0 ${space.s6}px;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: stretch;
|
|
31
|
+
justify-content: stretch;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
}
|
|
32
35
|
`;
|
|
33
36
|
const OptionWithTextContent = css`
|
|
34
|
-
|
|
37
|
+
:where(&) {
|
|
38
|
+
padding-left: ${space.s6}px;
|
|
39
|
+
padding-right: ${space.s6}px;
|
|
40
|
+
}
|
|
35
41
|
`;
|
|
36
42
|
|
|
37
43
|
/**
|
package/src/select/util.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {GroupBase as ReactSelectGroupBase, OptionsOrGroups, SelectInstance} from "react-select";
|
|
2
2
|
import {isObject} from "lodash";
|
|
3
3
|
import {Children, createContext, ReactNode, RefObject} from "react";
|
|
4
|
+
|
|
4
5
|
/**
|
|
5
6
|
* API used by ReactSelect internal partials (at least VirtualizedMenuList).
|
|
6
7
|
*/
|
|
7
8
|
export const ReactSelectRefContext = createContext<RefObject<SelectInstance>>({current: null});
|
|
8
|
-
export type GroupBase<T> = ReactSelectGroupBase<T> & {separator?: boolean};
|
|
9
|
+
export type GroupBase<T> = ReactSelectGroupBase<T> & {separator?: boolean; separatorMargin?: number};
|
|
9
10
|
export function isGroup<Option, Group extends GroupBase<Option>>(
|
|
10
11
|
optionOrGroup: Option | Group
|
|
11
12
|
): optionOrGroup is Group {
|