@swan-io/lake 8.3.1 → 8.3.2
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 +1 -2
- package/src/components/FilterChooser.d.ts +1 -1
- package/src/components/FilterChooser.js +3 -2
- package/src/components/MultiSelect.js +6 -4
- package/src/components/Pressable.d.ts +6 -0
- package/src/components/Pressable.js +3 -0
- package/src/components/Switch.js +13 -33
- package/src/components/ToastStack.js +7 -12
- package/src/hooks/useAnimatedValue.d.ts +3 -0
- package/src/hooks/useAnimatedValue.js +3 -0
- package/src/state/toasts.d.ts +10 -4
- package/src/state/toasts.js +59 -41
- package/src/utils/timer.d.ts +6 -0
- package/src/utils/timer.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swan-io/lake",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.2",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=18.0.0",
|
|
6
6
|
"yarn": "^1.22.0"
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"@react-three/fiber": "^8.16.6",
|
|
31
31
|
"@swan-io/boxed": "^2.3.0",
|
|
32
32
|
"@swan-io/chicane": "^2.0.0",
|
|
33
|
-
"@swan-io/graphql-client": "^0.1.3",
|
|
34
33
|
"@swan-io/use-form": "^2.0.4",
|
|
35
34
|
"dayjs": "^1.11.11",
|
|
36
35
|
"polished": "^4.3.1",
|
|
@@ -2,7 +2,7 @@ export declare function FilterChooser<FilterName extends string>({ filters, open
|
|
|
2
2
|
filters: Partial<Record<FilterName, unknown>>;
|
|
3
3
|
openFilters: FilterName[];
|
|
4
4
|
label: string;
|
|
5
|
-
title
|
|
5
|
+
title?: string;
|
|
6
6
|
availableFilters: {
|
|
7
7
|
label: string;
|
|
8
8
|
name: FilterName;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRef } from "react";
|
|
3
3
|
import { FlatList, Pressable, StyleSheet, View } from "react-native";
|
|
4
4
|
import { colors } from "../constants/design";
|
|
5
5
|
import { useDisclosure } from "../hooks/useDisclosure";
|
|
6
|
+
import { isNotNullishOrEmpty } from "../utils/nullish";
|
|
6
7
|
import { Box } from "./Box";
|
|
7
8
|
import { Icon } from "./Icon";
|
|
8
9
|
import { LakeButton } from "./LakeButton";
|
|
@@ -37,7 +38,7 @@ const styles = StyleSheet.create({
|
|
|
37
38
|
export function FilterChooser({ filters, openFilters, label, title, availableFilters, large = true, onAddFilter, }) {
|
|
38
39
|
const inputRef = useRef(null);
|
|
39
40
|
const [visible, { close, toggle }] = useDisclosure(false);
|
|
40
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", justifyContent: "start", alignItems: "center", children: _jsx(LakeButton, { size: "small", mode: "secondary", color: "gray", onPress: toggle, ref: inputRef, icon: large ? "chevron-down-filled" : "filter-filled", iconPosition: "end", ariaLabel: label, children: large ? label : null }) }), _jsx(Popover, { role: "listbox", matchReferenceMinWidth: true, onDismiss: close, referenceRef: inputRef, returnFocus: false, visible: visible, children: _jsxs(View, { style: styles.list, children: [_jsx(LakeText, { style: styles.availableFiltersTitle, children: title }), _jsx(Space, { height: 8 }), _jsx(FlatList, { role: "list", data: availableFilters, keyExtractor: (_, index) => `filter-item-${index}`, renderItem: ({ item }) => {
|
|
41
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", justifyContent: "start", alignItems: "center", children: _jsx(LakeButton, { size: "small", mode: "secondary", color: "gray", onPress: toggle, ref: inputRef, icon: large ? "chevron-down-filled" : "filter-filled", iconPosition: "end", ariaLabel: label, children: large ? label : null }) }), _jsx(Popover, { role: "listbox", matchReferenceMinWidth: true, onDismiss: close, referenceRef: inputRef, returnFocus: false, visible: visible, children: _jsxs(View, { style: styles.list, children: [isNotNullishOrEmpty(title) ? (_jsxs(_Fragment, { children: [_jsx(LakeText, { style: styles.availableFiltersTitle, children: title }), _jsx(Space, { height: 8 })] })) : null, _jsx(FlatList, { role: "list", data: availableFilters, keyExtractor: (_, index) => `filter-item-${index}`, renderItem: ({ item }) => {
|
|
41
42
|
const isSet = Boolean(filters[item.name]) || openFilters.includes(item.name);
|
|
42
43
|
return (_jsxs(Pressable, { style: ({ hovered }) => [styles.item, hovered && styles.itemHovered], role: "button", disabled: isSet, onPress: () => {
|
|
43
44
|
onAddFilter(item.name);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Array, Dict, Option } from "@swan-io/boxed";
|
|
3
3
|
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { FlatList, Pressable, SectionList, StyleSheet, Text, View, } from "react-native";
|
|
4
|
+
import { FlatList, Pressable, SectionList, StyleSheet, Text, TextInput, View, } from "react-native";
|
|
5
5
|
import { backgroundColor, colors, radii, shadows, texts } from "../constants/design";
|
|
6
|
+
import { useBoolean } from "../hooks/useBoolean";
|
|
6
7
|
import { useDisclosure } from "../hooks/useDisclosure";
|
|
7
8
|
import { groupBy } from "../utils/array";
|
|
8
9
|
import { isNotNullish, isNotNullishOrEmpty } from "../utils/nullish";
|
|
@@ -11,7 +12,7 @@ import { Box } from "./Box";
|
|
|
11
12
|
import { Icon } from "./Icon";
|
|
12
13
|
import { InputError } from "./InputError";
|
|
13
14
|
import { Popover } from "./Popover";
|
|
14
|
-
import { PressableText
|
|
15
|
+
import { PressableText } from "./Pressable";
|
|
15
16
|
import { Space } from "./Space";
|
|
16
17
|
import { Tag } from "./Tag";
|
|
17
18
|
const MAX_INPUT_HEIGHT = 120;
|
|
@@ -85,7 +86,7 @@ const styles = StyleSheet.create({
|
|
|
85
86
|
paddingLeft: 40,
|
|
86
87
|
placeholderTextColor: colors.gray[300],
|
|
87
88
|
},
|
|
88
|
-
|
|
89
|
+
filterFocused: {
|
|
89
90
|
borderColor: colors.gray[200],
|
|
90
91
|
},
|
|
91
92
|
searchIcon: {
|
|
@@ -136,6 +137,7 @@ const styles = StyleSheet.create({
|
|
|
136
137
|
});
|
|
137
138
|
export const MultiSelect = memo(({ color = "gray", disabled = false, emptyResultText, enableGroups = true, filterPlaceholder, items, onValueChange, placeholder, renderTagGroup, error, style, values, id, }) => {
|
|
138
139
|
const [filter, setFilter] = useState("");
|
|
140
|
+
const [filterFocused, setFilterFocused] = useBoolean(false);
|
|
139
141
|
const shouldScrollToBottomRef = useRef(false);
|
|
140
142
|
const selectedTagListRef = useRef(null);
|
|
141
143
|
const inputRef = useRef(null);
|
|
@@ -188,7 +190,7 @@ export const MultiSelect = memo(({ color = "gray", disabled = false, emptyResult
|
|
|
188
190
|
const sections = useMemo(() => {
|
|
189
191
|
return Array.filterMap(Dict.entries(groupBy(filteredItems, ({ group }) => group)), ([groupName, data]) => data === undefined ? Option.None() : Option.Some({ title: groupName, data }));
|
|
190
192
|
}, [filteredItems]);
|
|
191
|
-
const ListHeaderComponent = useMemo(() => (_jsxs(Box, { direction: "row", alignItems: "center", style: styles.filterContainer, children: [_jsx(
|
|
193
|
+
const ListHeaderComponent = useMemo(() => (_jsxs(Box, { direction: "row", alignItems: "center", style: styles.filterContainer, children: [_jsx(TextInput, { autoComplete: "off", inputMode: "search", multiline: false, rows: 1, onChangeText: filterValue => setFilter(filterValue), placeholder: filterPlaceholder, value: filter, onFocus: setFilterFocused.on, onBlur: setFilterFocused.off, style: [styles.filterInput, filterFocused && styles.filterFocused] }), _jsx(Icon, { name: "search-filled", color: colors[color].primary, size: 20, style: styles.searchIcon })] })), [filter, filterFocused, setFilterFocused, filterPlaceholder, color]);
|
|
192
194
|
const ListEmptyComponent = useMemo(() => (_jsxs(Box, { justifyContent: "center", alignItems: "center", style: styles.emptyList, children: [_jsx(Icon, { name: "clipboard-search-regular", size: 24, color: colors.gray.primary }), isNotNullishOrEmpty(emptyResultText) && (_jsxs(_Fragment, { children: [_jsx(Space, { height: 8 }), _jsx(Text, { style: styles.emptyListText, children: emptyResultText })] }))] })), [emptyResultText]);
|
|
193
195
|
return (_jsxs(View, { style: style, children: [_jsxs(Pressable, { id: id, ref: inputRef, "aria-haspopup": "listbox", role: "button", "aria-expanded": visible, disabled: disabled, onPress: open, style: ({ hovered, focused }) => [
|
|
194
196
|
styles.base,
|
|
@@ -33,6 +33,9 @@ type ExtraProps = {
|
|
|
33
33
|
};
|
|
34
34
|
export type PressableViewProps = Except<Props<ViewProps>, "children">;
|
|
35
35
|
export type PressableTextProps = Props<TextProps>;
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated
|
|
38
|
+
*/
|
|
36
39
|
export type PressableTextInputProps = Except<Props<TextInputProps>, "children" | "editable" | "keyboardType" | "numberOfLines">;
|
|
37
40
|
export declare const Pressable: FC<PressableProps & ExtraProps & {
|
|
38
41
|
ref?: Ref<View> | undefined;
|
|
@@ -151,6 +154,9 @@ export declare const PressableText: FC<{
|
|
|
151
154
|
} & {
|
|
152
155
|
ref?: Ref<Text> | undefined;
|
|
153
156
|
}>;
|
|
157
|
+
/**
|
|
158
|
+
* @deprecated
|
|
159
|
+
*/
|
|
154
160
|
export declare const PressableTextInput: FC<{
|
|
155
161
|
allowFontScaling?: boolean | undefined;
|
|
156
162
|
autoCapitalize?: "none" | "sentences" | "words" | "characters" | undefined;
|
|
@@ -100,6 +100,9 @@ Component, config = {}) => {
|
|
|
100
100
|
};
|
|
101
101
|
export const Pressable = memo(getPressable(View, { applyPressStyle: true }));
|
|
102
102
|
export const PressableText = memo(getPressable(Text, { applyPressStyle: true }));
|
|
103
|
+
/**
|
|
104
|
+
* @deprecated
|
|
105
|
+
*/
|
|
103
106
|
export const PressableTextInput = memo(getPressable(TextInput, { applyPressStyle: false }));
|
|
104
107
|
PressableText.displayName = "PressableText";
|
|
105
108
|
PressableTextInput.displayName = "PressableTextInput";
|
package/src/components/Switch.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, memo
|
|
3
|
-
import {
|
|
2
|
+
import { forwardRef, memo } from "react";
|
|
3
|
+
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
4
|
import { backgroundColor, colors, shadows } from "../constants/design";
|
|
5
|
-
import { useAnimatedValue } from "../hooks/useAnimatedValue";
|
|
6
|
-
import { interpolate } from "../utils/math";
|
|
7
5
|
import { Icon } from "./Icon";
|
|
8
6
|
const WIDTH = 36;
|
|
9
7
|
const BUTTON_SIZE = 16;
|
|
@@ -16,7 +14,7 @@ const styles = StyleSheet.create({
|
|
|
16
14
|
width: WIDTH,
|
|
17
15
|
boxSizing: "content-box",
|
|
18
16
|
transitionProperty: "background-color",
|
|
19
|
-
transitionDuration: "
|
|
17
|
+
transitionDuration: "250ms",
|
|
20
18
|
boxShadow: "inset 0 0 0 1px rgba(0, 0, 0, 0.2)",
|
|
21
19
|
},
|
|
22
20
|
active: {
|
|
@@ -39,11 +37,18 @@ const styles = StyleSheet.create({
|
|
|
39
37
|
position: "absolute",
|
|
40
38
|
top: PADDING,
|
|
41
39
|
width: BUTTON_SIZE,
|
|
40
|
+
transform: `translateX(${PADDING}px)`,
|
|
41
|
+
transitionProperty: "transform",
|
|
42
|
+
transitionDuration: "250ms",
|
|
43
|
+
transitionTimingFunction: "ease",
|
|
44
|
+
},
|
|
45
|
+
buttonActive: {
|
|
46
|
+
transform: `translateX(${WIDTH - BUTTON_SIZE - PADDING}px)`,
|
|
42
47
|
},
|
|
43
48
|
icon: {
|
|
44
49
|
opacity: 0,
|
|
45
50
|
transitionProperty: "opacity",
|
|
46
|
-
transitionDuration: "
|
|
51
|
+
transitionDuration: "250ms",
|
|
47
52
|
},
|
|
48
53
|
shadow: {
|
|
49
54
|
position: "absolute",
|
|
@@ -53,34 +58,9 @@ const styles = StyleSheet.create({
|
|
|
53
58
|
boxShadow: shadows.tile,
|
|
54
59
|
opacity: 0,
|
|
55
60
|
transitionProperty: "opacity",
|
|
56
|
-
transitionDuration: "
|
|
61
|
+
transitionDuration: "250ms",
|
|
57
62
|
},
|
|
58
63
|
});
|
|
59
64
|
export const Switch = memo(forwardRef(({ value, disabled = false, onValueChange }, ref) => {
|
|
60
|
-
|
|
61
|
-
const animation = useAnimatedValue(animatedValue);
|
|
62
|
-
const buttonRef = useRef(null);
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
const interpolateValue = interpolate({
|
|
65
|
-
inputRange: [0, 1],
|
|
66
|
-
outputRange: [PADDING, WIDTH - BUTTON_SIZE - PADDING],
|
|
67
|
-
});
|
|
68
|
-
const id = animation.addListener(({ value }) => {
|
|
69
|
-
if (buttonRef.current instanceof HTMLElement) {
|
|
70
|
-
buttonRef.current.style.transform = `translateX(${interpolateValue(value)}px)`;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
return () => {
|
|
74
|
-
animation.removeListener(id);
|
|
75
|
-
};
|
|
76
|
-
}, [animation]);
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
Animated.spring(animation, {
|
|
79
|
-
bounciness: 6,
|
|
80
|
-
speed: 25,
|
|
81
|
-
toValue: animatedValue,
|
|
82
|
-
useNativeDriver: false,
|
|
83
|
-
}).start();
|
|
84
|
-
}, [animation, animatedValue]);
|
|
85
|
-
return (_jsx(Pressable, { ref: ref, role: "switch", "aria-checked": value, disabled: disabled, onPress: () => onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(!value), children: ({ hovered }) => (_jsxs(_Fragment, { children: [_jsx(View, { style: [styles.shadow, hovered && styles.opaque] }), _jsx(View, { style: [styles.base, value && styles.active, disabled && styles.disabled], children: _jsx(View, { ref: buttonRef, style: styles.button, children: _jsx(Icon, { color: colors.positive[400], name: "checkmark-filled", size: 10, style: [styles.icon, value && styles.opaque] }) }) })] })) }));
|
|
65
|
+
return (_jsx(Pressable, { ref: ref, role: "switch", "aria-checked": value, disabled: disabled, onPress: () => onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(!value), children: ({ hovered }) => (_jsxs(_Fragment, { children: [_jsx(View, { style: [styles.shadow, hovered && styles.opaque] }), _jsx(View, { style: [styles.base, value && styles.active, disabled && styles.disabled], children: _jsx(View, { style: [styles.button, value && styles.buttonActive], children: _jsx(Icon, { color: colors.positive[400], name: "checkmark-filled", size: 10, style: [styles.icon, value && styles.opaque] }) }) })] })) }));
|
|
86
66
|
}));
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Array, Option } from "@swan-io/boxed";
|
|
3
|
-
import { ClientError } from "@swan-io/graphql-client";
|
|
4
3
|
import { t } from "@swan-io/shared-business/src/utils/i18n";
|
|
5
4
|
import { memo, useEffect, useRef, useState } from "react";
|
|
6
5
|
import { Clipboard, StyleSheet, View } from "react-native";
|
|
7
6
|
import { P, match } from "ts-pattern";
|
|
8
7
|
import { animations, colors, shadows } from "../constants/design";
|
|
9
|
-
import { getErrorToRequestId, hideToast, useToasts } from "../state/toasts";
|
|
8
|
+
import { getErrorToRequestId, hideToast, useToasts, } from "../state/toasts";
|
|
10
9
|
import { isNotNullishOrEmpty, isNullish } from "../utils/nullish";
|
|
11
10
|
import { Box } from "./Box";
|
|
12
11
|
import { Icon } from "./Icon";
|
|
@@ -66,6 +65,10 @@ const styles = StyleSheet.create({
|
|
|
66
65
|
flexShrink: 1,
|
|
67
66
|
},
|
|
68
67
|
});
|
|
68
|
+
const errorsToArray = (errors) => {
|
|
69
|
+
const asArray = Array.isArray(errors) ? errors : [errors];
|
|
70
|
+
return Array.filterMap(asArray, error => error instanceof Error ? Option.Some(error) : Option.None());
|
|
71
|
+
};
|
|
69
72
|
const Toast = memo(({ variant, uid, title, description, error, progress, onClose }) => {
|
|
70
73
|
const progressBarRef = useRef(null);
|
|
71
74
|
const [visibleState, setVisibleState] = useState("copy");
|
|
@@ -74,14 +77,7 @@ const Toast = memo(({ variant, uid, title, description, error, progress, onClose
|
|
|
74
77
|
if (error == undefined) {
|
|
75
78
|
return Option.None();
|
|
76
79
|
}
|
|
77
|
-
return Array.findMap(
|
|
78
|
-
if (error instanceof Error) {
|
|
79
|
-
return Option.fromNullable(getErrorToRequestId().get(error));
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
return Option.None();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
80
|
+
return Array.findMap(errorsToArray(error), error => Option.fromNullable(getErrorToRequestId().get(error)));
|
|
85
81
|
});
|
|
86
82
|
const colorVariation = match(variant)
|
|
87
83
|
.returnType()
|
|
@@ -94,12 +90,11 @@ const Toast = memo(({ variant, uid, title, description, error, progress, onClose
|
|
|
94
90
|
if (isNullish(progress)) {
|
|
95
91
|
return;
|
|
96
92
|
}
|
|
97
|
-
|
|
93
|
+
return progress.subscribe(value => {
|
|
98
94
|
if (progressBarRef.current instanceof HTMLElement) {
|
|
99
95
|
progressBarRef.current.style.transform = `scaleX(${value})`;
|
|
100
96
|
}
|
|
101
97
|
});
|
|
102
|
-
return () => progress.removeListener(id);
|
|
103
98
|
}, [progress]);
|
|
104
99
|
return (_jsx(View, { style: styles.toastWrapper, children: _jsxs(View, { style: [
|
|
105
100
|
styles.toast,
|
package/src/state/toasts.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
declare const createProgress: ({ duration, onEnd }: {
|
|
2
|
+
duration: number;
|
|
3
|
+
onEnd: () => void;
|
|
4
|
+
}) => {
|
|
5
|
+
clear: () => void;
|
|
6
|
+
reset: () => void;
|
|
7
|
+
subscribe: (callback: (value: number) => void) => () => void;
|
|
8
|
+
};
|
|
9
|
+
export type ToastProgress = ReturnType<typeof createProgress>;
|
|
3
10
|
export type ToastVariant = "success" | "info" | "warning" | "error";
|
|
4
11
|
type ToastContent = {
|
|
5
12
|
variant: ToastVariant;
|
|
@@ -14,8 +21,7 @@ type Toast = {
|
|
|
14
21
|
title: string;
|
|
15
22
|
description?: string;
|
|
16
23
|
error?: unknown;
|
|
17
|
-
progress?:
|
|
18
|
-
timeout?: ControllableTimeout;
|
|
24
|
+
progress?: ToastProgress;
|
|
19
25
|
};
|
|
20
26
|
export declare const useToasts: () => Toast[];
|
|
21
27
|
export declare const hideToast: (uid: string) => void;
|
package/src/state/toasts.js
CHANGED
|
@@ -1,17 +1,62 @@
|
|
|
1
1
|
import { atom, useAtom } from "react-atomic-state";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// based on https://gist.github.com/ncou/3a0a1f89c8e22416d0d607f621a948a9
|
|
3
|
+
const createProgress = ({ duration, onEnd }) => {
|
|
4
|
+
const callbacks = new Set();
|
|
5
|
+
let endDate = 0;
|
|
6
|
+
let timerId = 0;
|
|
7
|
+
const step = () => {
|
|
8
|
+
const value = (endDate - Date.now()) / duration;
|
|
9
|
+
callbacks.forEach(callback => callback(value));
|
|
10
|
+
animationRequest = window.requestAnimationFrame(step);
|
|
11
|
+
};
|
|
12
|
+
let animationRequest = window.requestAnimationFrame(step);
|
|
13
|
+
const start = () => {
|
|
14
|
+
endDate = Date.now() + duration;
|
|
15
|
+
timerId = window.setTimeout(() => {
|
|
16
|
+
clear();
|
|
17
|
+
onEnd();
|
|
18
|
+
}, duration);
|
|
19
|
+
};
|
|
20
|
+
const reset = () => {
|
|
21
|
+
window.clearTimeout(timerId);
|
|
22
|
+
start();
|
|
23
|
+
};
|
|
24
|
+
const subscribe = (callback) => {
|
|
25
|
+
callbacks.add(callback);
|
|
26
|
+
return () => {
|
|
27
|
+
callbacks.delete(callback);
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const clear = () => {
|
|
31
|
+
document.removeEventListener("visibilitychange", onDocumentVisible);
|
|
32
|
+
window.clearTimeout(timerId);
|
|
33
|
+
window.cancelAnimationFrame(animationRequest);
|
|
34
|
+
};
|
|
35
|
+
const onDocumentVisible = () => {
|
|
36
|
+
document.removeEventListener("visibilitychange", onDocumentVisible);
|
|
37
|
+
reset();
|
|
38
|
+
};
|
|
39
|
+
if (document.hidden) {
|
|
40
|
+
document.addEventListener("visibilitychange", onDocumentVisible);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
reset();
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
clear,
|
|
47
|
+
reset,
|
|
48
|
+
subscribe,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
4
51
|
const toasts = atom([]);
|
|
5
52
|
export const useToasts = () => useAtom(toasts);
|
|
6
53
|
export const hideToast = (uid) => {
|
|
7
|
-
var _a
|
|
54
|
+
var _a;
|
|
8
55
|
const toast = toasts.get().find(toast => toast.uid === uid);
|
|
9
|
-
if (
|
|
10
|
-
|
|
56
|
+
if (toast != null) {
|
|
57
|
+
(_a = toast.progress) === null || _a === void 0 ? void 0 : _a.clear();
|
|
58
|
+
toasts.set(toasts => toasts.filter(toast => toast.uid !== uid));
|
|
11
59
|
}
|
|
12
|
-
(_a = toast.timeout) === null || _a === void 0 ? void 0 : _a.clear();
|
|
13
|
-
(_b = toast.progress) === null || _b === void 0 ? void 0 : _b.stopAnimation();
|
|
14
|
-
toasts.set(toasts => toasts.filter(toast => toast.uid !== uid));
|
|
15
60
|
};
|
|
16
61
|
let errorToRequestId = new WeakMap();
|
|
17
62
|
export const registerErrorToRequestId = (value) => {
|
|
@@ -21,50 +66,23 @@ export const getErrorToRequestId = () => {
|
|
|
21
66
|
return errorToRequestId;
|
|
22
67
|
};
|
|
23
68
|
export const showToast = ({ variant, title, description, error, autoClose }) => {
|
|
69
|
+
var _a;
|
|
24
70
|
const uid = `${variant} - ${title} - ${description !== null && description !== void 0 ? description : ""}`;
|
|
25
71
|
const toast = toasts.get().find(toast => toast.uid === uid);
|
|
26
72
|
if (toast != null) {
|
|
27
|
-
|
|
28
|
-
toast.timeout.clear();
|
|
29
|
-
Animated.timing(toast.progress, {
|
|
30
|
-
duration: 100,
|
|
31
|
-
easing: Easing.linear,
|
|
32
|
-
toValue: 1,
|
|
33
|
-
useNativeDriver: false,
|
|
34
|
-
}).start(() => {
|
|
35
|
-
var _a;
|
|
36
|
-
(_a = toast.timeout) === null || _a === void 0 ? void 0 : _a.reset();
|
|
37
|
-
});
|
|
38
|
-
}
|
|
73
|
+
(_a = toast.progress) === null || _a === void 0 ? void 0 : _a.reset();
|
|
39
74
|
return uid;
|
|
40
75
|
}
|
|
41
76
|
// by default, only info and success toasts are auto-closing
|
|
42
|
-
const
|
|
43
|
-
const progress =
|
|
44
|
-
|
|
45
|
-
? createControllableTimeout({
|
|
77
|
+
const shouldAutoClose = autoClose !== null && autoClose !== void 0 ? autoClose : (variant === "info" || variant === "success");
|
|
78
|
+
const progress = shouldAutoClose
|
|
79
|
+
? createProgress({
|
|
46
80
|
duration: 10000,
|
|
47
|
-
onStart: duration => {
|
|
48
|
-
Animated.timing(progress, {
|
|
49
|
-
duration,
|
|
50
|
-
easing: Easing.linear,
|
|
51
|
-
toValue: 0,
|
|
52
|
-
useNativeDriver: false,
|
|
53
|
-
}).start();
|
|
54
|
-
},
|
|
55
|
-
onReset: duration => {
|
|
56
|
-
Animated.timing(progress, {
|
|
57
|
-
duration,
|
|
58
|
-
easing: Easing.linear,
|
|
59
|
-
toValue: 0,
|
|
60
|
-
useNativeDriver: false,
|
|
61
|
-
}).start();
|
|
62
|
-
},
|
|
63
81
|
onEnd: () => {
|
|
64
82
|
hideToast(uid);
|
|
65
83
|
},
|
|
66
84
|
})
|
|
67
85
|
: undefined;
|
|
68
|
-
toasts.set(toasts => [{ uid, variant, title, description, error, progress
|
|
86
|
+
toasts.set(toasts => [{ uid, variant, title, description, error, progress }, ...toasts]);
|
|
69
87
|
return uid;
|
|
70
88
|
};
|
package/src/utils/timer.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated
|
|
3
|
+
*/
|
|
1
4
|
export type ControllableTimeout = {
|
|
2
5
|
readonly duration: number;
|
|
3
6
|
readonly clear: () => void;
|
|
4
7
|
readonly reset: () => void;
|
|
5
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated
|
|
11
|
+
*/
|
|
6
12
|
export declare const createControllableTimeout: (config: {
|
|
7
13
|
duration: number;
|
|
8
14
|
onStart: (duration: number) => void;
|