@fibery/ui-kit 1.19.0 → 1.20.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/package.json +7 -5
- package/src/a11y-color.ts +9 -6
- package/src/antd/styles.ts +6 -6
- package/src/button/button.tsx +5 -5
- package/src/design-system.ts +19 -18
- package/src/emoji-picker/emoji-picker-content-with-color.tsx +1 -1
- package/src/emoji-picker/emoji-picker.tsx +2 -2
- package/src/emoji-picker/icon-emoji-picker.tsx +1 -1
- package/src/emoji-picker/primitives/category.tsx +2 -2
- package/src/emoji-picker/primitives/emoji.tsx +26 -3
- package/src/emoji-picker/primitives/footer.tsx +1 -1
- package/src/emoji-picker/primitives/header.tsx +1 -1
- package/src/emoji-picker/primitives/layout.ts +2 -2
- package/src/emoji-picker/primitives/search.tsx +5 -5
- package/src/error-alert.tsx +3 -3
- package/src/form-field-loader.tsx +3 -3
- package/src/icons/ast/AlertTriangle.ts +8 -0
- package/src/icons/ast/DragBlockHandleSingle.ts +8 -0
- package/src/icons/ast/Eye.ts +8 -0
- package/src/icons/ast/NetworkAdd.ts +8 -0
- package/src/icons/ast/RemovePeople.ts +8 -0
- package/src/icons/ast/RicheditorMarkTextBackgroundColor.ts +8 -0
- package/src/icons/ast/index.tsx +6 -1
- package/src/icons/react/AlertTriangle.tsx +12 -0
- package/src/icons/react/DragBlockHandleSingle.tsx +12 -0
- package/src/icons/react/Eye.tsx +12 -0
- package/src/icons/react/NetworkAdd.tsx +12 -0
- package/src/icons/react/RemovePeople.tsx +12 -0
- package/src/icons/react/RicheditorMarkTextBackgroundColor.tsx +12 -0
- package/src/icons/react/index.tsx +6 -1
- package/src/lists/list-item.tsx +139 -0
- package/src/loaders.tsx +10 -10
- package/src/loading-sausage.tsx +2 -2
- package/src/media-query-utils.ts +1 -1
- package/src/select/custom-select-partials/group-heading.tsx +2 -2
- package/src/select/custom-select-partials/menu.tsx +9 -1
- package/src/select/custom-select-partials/no-option-message.tsx +1 -1
- package/src/select/custom-select-partials/option.tsx +64 -7
- package/src/select/index.tsx +1 -1
- package/src/select/select-in-popover.tsx +10 -10
- package/src/select/styles.ts +12 -4
- package/src/toast/primitives.tsx +21 -8
- package/src/toast/toast-action.tsx +9 -1
- package/src/toast/toast-provider.tsx +15 -0
- package/src/toast/{toast-queue.tsx → toast-queue.ts} +21 -25
- package/src/toast/toast.tsx +35 -28
- package/src/toast/toaster.tsx +11 -48
- package/src/toast/utils/toastify-item-name.ts +6 -0
- package/src/tooltip.tsx +2 -2
- package/src/icons/ast/RicheditorMarkHighlight.ts +0 -8
- package/src/icons/react/RicheditorMarkHighlight.tsx +0 -12
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {makePubSub} from "@fibery/helpers/utils/pub-sub";
|
|
2
2
|
import type {ToastProps} from "./toast";
|
|
3
3
|
|
|
4
|
-
type AddToastProps = Pick<
|
|
4
|
+
export type AddToastProps = Pick<
|
|
5
|
+
ToastProps,
|
|
6
|
+
"type" | "title" | "subTitle" | "icon" | "action" | "duration" | "open"
|
|
7
|
+
> & {
|
|
5
8
|
id?: string;
|
|
6
9
|
};
|
|
7
10
|
type TypedAddProps = Omit<AddToastProps, "type">;
|
|
@@ -12,8 +15,15 @@ type QueuedToast = AddToastProps & {
|
|
|
12
15
|
|
|
13
16
|
type ToastQueueOpts = {
|
|
14
17
|
maxActiveToasts?: number;
|
|
18
|
+
onToastAdd?: (params: AddToastProps) => void;
|
|
19
|
+
|
|
20
|
+
/** Overrides toast props.
|
|
21
|
+
* This option is added only to support mapping of billing errors as it was done in toast actions.
|
|
22
|
+
* Ideally this should be removed and billing errors moved to thunk middlewares
|
|
23
|
+
*/
|
|
24
|
+
transformToast?: (params: AddToastProps) => AddToastProps;
|
|
15
25
|
};
|
|
16
|
-
export const makeToastQueue = ({maxActiveToasts = 1}: ToastQueueOpts = {}) => {
|
|
26
|
+
export const makeToastQueue = ({maxActiveToasts = 1, onToastAdd, transformToast}: ToastQueueOpts = {}) => {
|
|
17
27
|
const pubSub = makePubSub();
|
|
18
28
|
let queue: QueuedToast[] = [];
|
|
19
29
|
let activeToasts: QueuedToast[] = [];
|
|
@@ -35,39 +45,24 @@ export const makeToastQueue = ({maxActiveToasts = 1}: ToastQueueOpts = {}) => {
|
|
|
35
45
|
pubSub.publish();
|
|
36
46
|
};
|
|
37
47
|
|
|
38
|
-
const closeActive = (id: string) => {
|
|
39
|
-
activeToasts = activeToasts.map((t) => (t.id === id ? {...t, open: false} : t));
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const removeFromQueue = (id: string) => {
|
|
43
|
-
queue = queue.filter((queued) => queued.id !== id);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
48
|
/**
|
|
47
49
|
* Add toast to queue
|
|
48
50
|
*
|
|
49
51
|
* Pass custom `id` if you don't want toasts to be duplicated.
|
|
50
52
|
* If toast with same id already exists it will be closed to display fresh more relevant one
|
|
51
53
|
*/
|
|
52
|
-
const add = ({id, ...toast}: AddToastProps
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
!replace &&
|
|
56
|
-
(queue.some((queued) => queued.id === id) || activeToasts.some((active) => active.id === id))
|
|
57
|
-
) {
|
|
58
|
-
return id;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (id && replace) {
|
|
62
|
-
closeActive(id);
|
|
63
|
-
removeFromQueue(id);
|
|
54
|
+
const add = ({id, ...toast}: AddToastProps) => {
|
|
55
|
+
if (activeToasts.length > 0) {
|
|
56
|
+
activeToasts[0].open = false;
|
|
64
57
|
}
|
|
65
58
|
|
|
66
59
|
const t: QueuedToast = {
|
|
67
60
|
id: id || makeId(),
|
|
68
|
-
...toast,
|
|
61
|
+
...(transformToast ? transformToast(toast) : toast),
|
|
69
62
|
};
|
|
70
63
|
|
|
64
|
+
onToastAdd?.(t);
|
|
65
|
+
|
|
71
66
|
queue.push(t);
|
|
72
67
|
|
|
73
68
|
updateActiveToasts();
|
|
@@ -77,9 +72,10 @@ export const makeToastQueue = ({maxActiveToasts = 1}: ToastQueueOpts = {}) => {
|
|
|
77
72
|
|
|
78
73
|
return {
|
|
79
74
|
add,
|
|
80
|
-
/**
|
|
75
|
+
/** Mark active toast as closed. Can be used to play exit animation */
|
|
81
76
|
close(id: string) {
|
|
82
|
-
|
|
77
|
+
activeToasts = activeToasts.map((t) => (t.id === id ? {...t, open: false} : t));
|
|
78
|
+
|
|
83
79
|
pubSub.publish();
|
|
84
80
|
},
|
|
85
81
|
/** remove toast from active ones, should be called after exit animation */
|
package/src/toast/toast.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import {useCallbackRef} from "@fibery/react/src/use-callback-ref";
|
|
1
2
|
import {useControllableState} from "@fibery/react/src/use-controllable-state";
|
|
2
3
|
import {css} from "@linaria/core";
|
|
3
|
-
import {
|
|
4
|
+
import {useEffect, useRef} from "react";
|
|
4
5
|
import {space, themeVars} from "../design-system";
|
|
5
6
|
import Spinner from "../icons/react/Spinner";
|
|
6
7
|
import SuccessIcon from "../icons/react/Success";
|
|
@@ -15,21 +16,23 @@ export type ToastProps = {
|
|
|
15
16
|
subTitle?: React.ReactNode;
|
|
16
17
|
icon?: React.ReactNode;
|
|
17
18
|
action?: ToastActionElement;
|
|
18
|
-
|
|
19
|
+
duration?: number;
|
|
20
|
+
dismissable?: boolean;
|
|
19
21
|
open?: boolean;
|
|
20
22
|
onOpenChange?: (open: boolean) => void;
|
|
21
23
|
|
|
22
24
|
type?: ToastType;
|
|
23
25
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
+
/** It gets called after exit animation */
|
|
27
|
+
onHide?: () => void;
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
const defaultPropsPerType: Record<ToastType, Partial<ToastProps>> = {
|
|
29
31
|
info: {},
|
|
30
32
|
error: {
|
|
31
33
|
icon: <WarningTriangle color={themeVars.textColor} />,
|
|
32
|
-
|
|
34
|
+
duration: 10000,
|
|
35
|
+
dismissable: true,
|
|
33
36
|
},
|
|
34
37
|
loading: {
|
|
35
38
|
icon: <Spinner color={themeVars.textColor} />,
|
|
@@ -40,17 +43,19 @@ const defaultPropsPerType: Record<ToastType, Partial<ToastProps>> = {
|
|
|
40
43
|
};
|
|
41
44
|
|
|
42
45
|
const rootCss = css`
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
min-height: 44px;
|
|
43
48
|
min-width: 320px;
|
|
44
49
|
display: flex;
|
|
45
50
|
align-items: center;
|
|
46
|
-
gap: ${space.
|
|
51
|
+
gap: ${space.s12}px;
|
|
47
52
|
`;
|
|
48
53
|
|
|
49
54
|
const messageCss = css`
|
|
50
55
|
display: flex;
|
|
51
56
|
flex-grow: 1;
|
|
52
57
|
flex-direction: column;
|
|
53
|
-
gap: ${space.
|
|
58
|
+
gap: ${space.s4}px;
|
|
54
59
|
`;
|
|
55
60
|
|
|
56
61
|
const iconCss = css`
|
|
@@ -65,10 +70,11 @@ export const Toast: React.FC<ToastProps> = (props) => {
|
|
|
65
70
|
subTitle,
|
|
66
71
|
icon,
|
|
67
72
|
action,
|
|
68
|
-
|
|
73
|
+
duration = 5000,
|
|
74
|
+
dismissable,
|
|
69
75
|
open,
|
|
70
76
|
onOpenChange,
|
|
71
|
-
|
|
77
|
+
onHide,
|
|
72
78
|
} = {
|
|
73
79
|
...defaultProps,
|
|
74
80
|
...props,
|
|
@@ -76,31 +82,32 @@ export const Toast: React.FC<ToastProps> = (props) => {
|
|
|
76
82
|
|
|
77
83
|
const [isOpen, setOpen] = useControllableState({value: open, defaultValue: true, onChange: onOpenChange});
|
|
78
84
|
|
|
79
|
-
const
|
|
85
|
+
const ref = useRef<HTMLLIElement>(null);
|
|
86
|
+
const onHideCb = useCallbackRef(onHide);
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const toastEl = ref.current;
|
|
90
|
+
if (!isOpen) {
|
|
91
|
+
const emergencyTimeout = setTimeout(onHideCb, 400); // just in case if animationend doesn't fire for some reasons or toast got closed before render
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
? {
|
|
85
|
-
onOpenChange: setOpen,
|
|
93
|
+
toastEl?.addEventListener("animationend", onHideCb, {
|
|
86
94
|
// https://github.com/radix-ui/primitives/issues/1020
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
: {
|
|
94
|
-
onOpenChange(v: boolean) {
|
|
95
|
-
setOpen(v);
|
|
96
|
-
if (!v) {
|
|
97
|
-
onRemove?.();
|
|
98
|
-
}
|
|
99
|
-
},
|
|
95
|
+
capture: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return () => {
|
|
99
|
+
toastEl?.removeEventListener("animationend", onHideCb, {capture: true});
|
|
100
|
+
clearTimeout(emergencyTimeout);
|
|
100
101
|
};
|
|
102
|
+
}
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
104
|
+
return () => {};
|
|
105
|
+
}, [isOpen, onHideCb]);
|
|
106
|
+
|
|
107
|
+
const close = dismissable ? <ToastClose /> : null;
|
|
101
108
|
|
|
102
109
|
return (
|
|
103
|
-
<ToastRoot open={open} duration={
|
|
110
|
+
<ToastRoot ref={ref} open={open} duration={duration} className={rootCss} onOpenChange={setOpen}>
|
|
104
111
|
{icon ? <div className={iconCss}>{icon}</div> : null}
|
|
105
112
|
<div className={messageCss}>
|
|
106
113
|
<ToastTitle>{title}</ToastTitle>
|
package/src/toast/toaster.tsx
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import React, {useEffect, useSyncExternalStore} from "react";
|
|
1
|
+
import {useSyncExternalStore} from "react";
|
|
3
2
|
import {createInlineTheme} from "../create-inline-theme";
|
|
4
3
|
import {colors, getThemeColors} from "../design-system";
|
|
5
4
|
import {ThemeProvider, useThemeMode} from "../theme-provider";
|
|
6
|
-
import {
|
|
5
|
+
import {ToastViewport} from "./primitives";
|
|
7
6
|
import {Toast} from "./toast";
|
|
8
|
-
import
|
|
9
|
-
import {useCallbackRef} from "@fibery/react/src/use-callback-ref";
|
|
10
|
-
import {reactNodeToString} from "@fibery/react/src/react-node-to-string";
|
|
7
|
+
import {useToast} from "./toast-provider";
|
|
11
8
|
|
|
12
9
|
const ToastThemeProvider: React.FC<React.PropsWithChildren> = ({children}) => {
|
|
13
10
|
const themeMode = useThemeMode();
|
|
@@ -21,52 +18,18 @@ const ToastThemeProvider: React.FC<React.PropsWithChildren> = ({children}) => {
|
|
|
21
18
|
);
|
|
22
19
|
};
|
|
23
20
|
|
|
24
|
-
const
|
|
21
|
+
export const Toaster: React.FC = () => {
|
|
22
|
+
const toastQueue = useToast();
|
|
25
23
|
|
|
26
|
-
type OnTrackToast = (args: {title: string; type: string}) => void;
|
|
27
|
-
const TrackToast: React.FC<
|
|
28
|
-
React.PropsWithChildren<{title: React.ReactNode; type: string; onTrackToast: OnTrackToast}>
|
|
29
|
-
> = ({title, type, onTrackToast, children}) => {
|
|
30
|
-
const onTrackCb = useCallbackRef(onTrackToast);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
onTrackCb({title: reactNodeToString(title), type});
|
|
33
|
-
}, [onTrackCb, title, type]);
|
|
34
|
-
|
|
35
|
-
return children as JSX.Element;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const Toaster: React.FC<React.PropsWithChildren<{toastQueue: ToastQueue; onTrackToast: OnTrackToast}>> = ({
|
|
39
|
-
toastQueue,
|
|
40
|
-
children,
|
|
41
|
-
onTrackToast,
|
|
42
|
-
}) => {
|
|
43
24
|
const toasts = useSyncExternalStore(toastQueue.subscribe, toastQueue.getActiveToasts);
|
|
44
25
|
|
|
45
26
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
<ToastThemeProvider>
|
|
51
|
-
{toasts.map(({id, open = true, ...toastProps}) => (
|
|
52
|
-
<TrackToast key={id} title={toastProps.title} type={toastProps.type || "info"} onTrackToast={onTrackToast}>
|
|
53
|
-
<Toast
|
|
54
|
-
{...toastProps}
|
|
55
|
-
open={open}
|
|
56
|
-
onOpenChange={(v) => {
|
|
57
|
-
if (!v) {
|
|
58
|
-
toastQueue.close(id);
|
|
59
|
-
}
|
|
60
|
-
}}
|
|
61
|
-
onRemove={() => toastQueue.remove(id)}
|
|
62
|
-
/>
|
|
63
|
-
</TrackToast>
|
|
64
|
-
))}
|
|
27
|
+
<ToastThemeProvider>
|
|
28
|
+
{toasts.map(({id, open, type = "info", ...toastProps}) => (
|
|
29
|
+
<Toast key={`${id}-${type}`} type={type} {...toastProps} open={open} onHide={() => toastQueue.remove(id)} />
|
|
30
|
+
))}
|
|
65
31
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</ToastProvider>
|
|
69
|
-
</Provider>
|
|
32
|
+
<ToastViewport />
|
|
33
|
+
</ToastThemeProvider>
|
|
70
34
|
);
|
|
71
35
|
};
|
|
72
|
-
export const useToast = useCtx;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const charLimit = 60;
|
|
2
|
+
|
|
3
|
+
const trimDynamicToastText = (str: string) => (str.length > charLimit ? `${str.slice(0, 60)}...` : str);
|
|
4
|
+
|
|
5
|
+
export const toastifyItemName = ({prefix, name, postfix}: {prefix?: string; name?: string; postfix?: string}) =>
|
|
6
|
+
`${prefix ? `${prefix} ` : ""}"${name ? trimDynamicToastText(name) : "Untitled"}"${postfix ? ` ${postfix}` : ""}`;
|
package/src/tooltip.tsx
CHANGED
|
@@ -27,7 +27,7 @@ const tooltipStyle = css`
|
|
|
27
27
|
min-height: 30px;
|
|
28
28
|
max-width: 300px;
|
|
29
29
|
background-color: ${themeVars.tooltipBgColor};
|
|
30
|
-
padding: ${space.
|
|
30
|
+
padding: ${space.s6}px ${space.s8}px;
|
|
31
31
|
border-radius: ${border.radius6}px;
|
|
32
32
|
box-shadow: ${themeVars.actionMenuShadow};
|
|
33
33
|
|
|
@@ -132,7 +132,7 @@ export const Tooltip = forwardRef<HTMLButtonElement, TooltipProps>(
|
|
|
132
132
|
</>
|
|
133
133
|
),
|
|
134
134
|
side,
|
|
135
|
-
sideOffset = space.
|
|
135
|
+
sideOffset = space.s4,
|
|
136
136
|
align,
|
|
137
137
|
alignOffset,
|
|
138
138
|
instant,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// This icon file is generated automatically.
|
|
3
|
-
|
|
4
|
-
import { IconDefinition } from '../types';
|
|
5
|
-
|
|
6
|
-
const RicheditorMarkHighlight: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"g","properties":{},"children":[{"type":"element","tagName":"path","properties":{"d":"m5.07 16.9.898-.625h2.418c.643 0 1.197-.177 1.66-.607.15-.14.135-.377-.012-.522L5.23 10.438c-.156-.153-.408-.154-.535.023-.34.476-.509 1.087-.509 1.698v2.37l-.89.873c-.382.39-.17.154-.597.624 1.517.595 2.37.874 2.37.874ZM10.653 14.177c.255.25.51.25.764 0l5.997-7.786a1.474 1.474 0 0 0 0-1.746l-1.909-1.87a1.247 1.247 0 0 0-1.781 0l-7.61 6.313c-.255.249-.255.498 0 .748l4.54 4.341Z"},"children":[]}]}],"metadata":""}]},"name":"richeditor-mark-highlight"};
|
|
7
|
-
|
|
8
|
-
export default RicheditorMarkHighlight;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// This icon file is generated automatically.
|
|
2
|
-
|
|
3
|
-
import RicheditorMarkHighlightSvg from '../ast/RicheditorMarkHighlight';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
|
-
import { IconBaseProps } from '../types';
|
|
6
|
-
|
|
7
|
-
const RicheditorMarkHighlight = (
|
|
8
|
-
props: IconBaseProps,
|
|
9
|
-
): JSX.Element => <Icon {...props} icon={RicheditorMarkHighlightSvg} />;
|
|
10
|
-
|
|
11
|
-
RicheditorMarkHighlight.displayName = 'RicheditorMarkHighlight';
|
|
12
|
-
export default RicheditorMarkHighlight;
|