@swan-io/lake 1.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/HISTORY.md +3 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/package.json +60 -0
- package/src/components/Alert.d.ts +10 -0
- package/src/components/Alert.js +36 -0
- package/src/components/AppOpeningAnimation.d.ts +10 -0
- package/src/components/AppOpeningAnimation.js +50 -0
- package/src/components/AutoWidthImage.d.ts +8 -0
- package/src/components/AutoWidthImage.js +26 -0
- package/src/components/Avatar.d.ts +7 -0
- package/src/components/Avatar.js +42 -0
- package/src/components/BorderedButton.d.ts +16 -0
- package/src/components/BorderedButton.js +98 -0
- package/src/components/BorderedIcon.d.ts +12 -0
- package/src/components/BorderedIcon.js +25 -0
- package/src/components/BottomPanel.d.ts +9 -0
- package/src/components/BottomPanel.js +94 -0
- package/src/components/Box.d.ts +65 -0
- package/src/components/Box.js +31 -0
- package/src/components/Breadcrumbs.d.ts +18 -0
- package/src/components/Breadcrumbs.js +362 -0
- package/src/components/Button.d.ts +15 -0
- package/src/components/Button.js +83 -0
- package/src/components/Caption.d.ts +6 -0
- package/src/components/Caption.js +11 -0
- package/src/components/Checkbox.d.ts +12 -0
- package/src/components/Checkbox.js +83 -0
- package/src/components/ChoicePicker.d.ts +11 -0
- package/src/components/ChoicePicker.js +99 -0
- package/src/components/Combobox.d.ts +29 -0
- package/src/components/Combobox.js +182 -0
- package/src/components/FailureIcon.d.ts +8 -0
- package/src/components/FailureIcon.js +4 -0
- package/src/components/FileTile.d.ts +11 -0
- package/src/components/FileTile.js +37 -0
- package/src/components/Fill.d.ts +8 -0
- package/src/components/Fill.js +24 -0
- package/src/components/FilterChooser.d.ts +15 -0
- package/src/components/FilterChooser.js +52 -0
- package/src/components/Filters.d.ts +57 -0
- package/src/components/Filters.js +229 -0
- package/src/components/FixedListView.d.ts +104 -0
- package/src/components/FixedListView.js +821 -0
- package/src/components/FixedListViewCells.d.ts +55 -0
- package/src/components/FixedListViewCells.js +157 -0
- package/src/components/Flag.d.ts +8 -0
- package/src/components/Flag.js +36 -0
- package/src/components/FlowPresentation.d.ts +12 -0
- package/src/components/FlowPresentation.js +70 -0
- package/src/components/FocusTrap.d.ts +16 -0
- package/src/components/FocusTrap.js +90 -0
- package/src/components/Form.d.ts +8 -0
- package/src/components/Form.js +17 -0
- package/src/components/FullViewportLayer.d.ts +7 -0
- package/src/components/FullViewportLayer.js +91 -0
- package/src/components/Grid.d.ts +13 -0
- package/src/components/Grid.js +33 -0
- package/src/components/Heading.d.ts +61 -0
- package/src/components/Heading.js +27 -0
- package/src/components/Icon.d.ts +191 -0
- package/src/components/Icon.js +11 -0
- package/src/components/Input.d.ts +34 -0
- package/src/components/Input.js +115 -0
- package/src/components/InputError.d.ts +8 -0
- package/src/components/InputError.js +16 -0
- package/src/components/Label.d.ts +10 -0
- package/src/components/Label.js +19 -0
- package/src/components/LakeAlert.d.ts +14 -0
- package/src/components/LakeAlert.js +75 -0
- package/src/components/LakeButton.d.ts +36 -0
- package/src/components/LakeButton.js +171 -0
- package/src/components/LakeCheckbox.d.ts +16 -0
- package/src/components/LakeCheckbox.js +54 -0
- package/src/components/LakeCombobox.d.ts +28 -0
- package/src/components/LakeCombobox.js +166 -0
- package/src/components/LakeCopyButton.d.ts +10 -0
- package/src/components/LakeCopyButton.js +16 -0
- package/src/components/LakeDownloadButton.d.ts +8 -0
- package/src/components/LakeDownloadButton.js +6 -0
- package/src/components/LakeHeading.d.ts +10 -0
- package/src/components/LakeHeading.js +19 -0
- package/src/components/LakeLabel.d.ts +19 -0
- package/src/components/LakeLabel.js +43 -0
- package/src/components/LakeModal.d.ts +14 -0
- package/src/components/LakeModal.js +132 -0
- package/src/components/LakeRadio.d.ts +9 -0
- package/src/components/LakeRadio.js +44 -0
- package/src/components/LakeScrollView.d.ts +10 -0
- package/src/components/LakeScrollView.js +35 -0
- package/src/components/LakeSearchField.d.ts +10 -0
- package/src/components/LakeSearchField.js +111 -0
- package/src/components/LakeSelect.d.ts +30 -0
- package/src/components/LakeSelect.js +183 -0
- package/src/components/LakeSlider.d.ts +12 -0
- package/src/components/LakeSlider.js +31 -0
- package/src/components/LakeStepper.d.ts +21 -0
- package/src/components/LakeStepper.js +134 -0
- package/src/components/LakeText.d.ts +19 -0
- package/src/components/LakeText.js +20 -0
- package/src/components/LakeTextInput.d.ts +36 -0
- package/src/components/LakeTextInput.js +154 -0
- package/src/components/LakeTooltip.d.ts +24 -0
- package/src/components/LakeTooltip.js +188 -0
- package/src/components/Link.d.ts +138 -0
- package/src/components/Link.js +23 -0
- package/src/components/ListRightPanel.d.ts +17 -0
- package/src/components/ListRightPanel.js +79 -0
- package/src/components/LoadingView.d.ts +9 -0
- package/src/components/LoadingView.js +24 -0
- package/src/components/Modal.d.ts +12 -0
- package/src/components/Modal.js +80 -0
- package/src/components/MultiSelect.d.ts +27 -0
- package/src/components/MultiSelect.js +223 -0
- package/src/components/MultilineInput.d.ts +15 -0
- package/src/components/MultilineInput.js +55 -0
- package/src/components/Picker.d.ts +26 -0
- package/src/components/Picker.js +116 -0
- package/src/components/PlainListView.d.ts +36 -0
- package/src/components/PlainListView.js +208 -0
- package/src/components/Popover.d.ts +23 -0
- package/src/components/Popover.js +147 -0
- package/src/components/PopoverContent.d.ts +8 -0
- package/src/components/PopoverContent.js +9 -0
- package/src/components/Portal.d.ts +7 -0
- package/src/components/Portal.js +9 -0
- package/src/components/Pressable.d.ts +348 -0
- package/src/components/Pressable.js +91 -0
- package/src/components/ProgressBar.d.ts +11 -0
- package/src/components/ProgressBar.js +46 -0
- package/src/components/ProjectEnvTag.d.ts +7 -0
- package/src/components/ProjectEnvTag.js +12 -0
- package/src/components/QuickActions.d.ts +15 -0
- package/src/components/QuickActions.js +38 -0
- package/src/components/RadioGroup.d.ts +16 -0
- package/src/components/RadioGroup.js +34 -0
- package/src/components/ReadOnlyFieldList.d.ts +6 -0
- package/src/components/ReadOnlyFieldList.js +8 -0
- package/src/components/ResponsiveContainer.d.ts +13 -0
- package/src/components/ResponsiveContainer.js +23 -0
- package/src/components/RightPanel.d.ts +10 -0
- package/src/components/RightPanel.js +102 -0
- package/src/components/SegmentedControl.d.ts +19 -0
- package/src/components/SegmentedControl.js +74 -0
- package/src/components/Separator.d.ts +10 -0
- package/src/components/Separator.js +19 -0
- package/src/components/SidebarNavigationTracker.d.ts +13 -0
- package/src/components/SidebarNavigationTracker.js +93 -0
- package/src/components/Slider.d.ts +11 -0
- package/src/components/Slider.js +119 -0
- package/src/components/SmsOpeningAnimation.d.ts +8 -0
- package/src/components/SmsOpeningAnimation.js +52 -0
- package/src/components/Space.d.ts +10 -0
- package/src/components/Space.js +23 -0
- package/src/components/Stack.d.ts +12 -0
- package/src/components/Stack.js +23 -0
- package/src/components/StepDots.d.ts +7 -0
- package/src/components/StepDots.js +24 -0
- package/src/components/Stepper.d.ts +9 -0
- package/src/components/Stepper.js +67 -0
- package/src/components/SuccessIcon.d.ts +8 -0
- package/src/components/SuccessIcon.js +4 -0
- package/src/components/Svg.d.ts +145 -0
- package/src/components/Svg.js +24 -0
- package/src/components/SwanLogo.d.ts +8 -0
- package/src/components/SwanLogo.js +11 -0
- package/src/components/Switch.d.ts +9 -0
- package/src/components/Switch.js +74 -0
- package/src/components/TabView.d.ts +16 -0
- package/src/components/TabView.js +398 -0
- package/src/components/Table.d.ts +34 -0
- package/src/components/Table.js +79 -0
- package/src/components/Tag.d.ts +17 -0
- package/src/components/Tag.js +76 -0
- package/src/components/Tile.d.ts +34 -0
- package/src/components/Tile.js +130 -0
- package/src/components/TilePlaceholder.d.ts +6 -0
- package/src/components/TilePlaceholder.js +51 -0
- package/src/components/ToastStack.d.ts +2 -0
- package/src/components/ToastStack.js +96 -0
- package/src/components/Tooltip.d.ts +18 -0
- package/src/components/Tooltip.js +162 -0
- package/src/components/TransitionGroupView.d.ts +12 -0
- package/src/components/TransitionGroupView.js +43 -0
- package/src/components/TransitionView.d.ts +12 -0
- package/src/components/TransitionView.js +43 -0
- package/src/components/WithCurrentColor.d.ts +12 -0
- package/src/components/WithCurrentColor.js +65 -0
- package/src/components/WithPartnerAccentColor.d.ts +7 -0
- package/src/components/WithPartnerAccentColor.js +91 -0
- package/src/constants/colors.d.ts +42 -0
- package/src/constants/colors.js +42 -0
- package/src/constants/commonStyles.d.ts +66 -0
- package/src/constants/commonStyles.js +45 -0
- package/src/constants/design.d.ts +168 -0
- package/src/constants/design.js +564 -0
- package/src/constants/insets.d.ts +10 -0
- package/src/constants/insets.js +22 -0
- package/src/constants/typography.d.ts +26 -0
- package/src/constants/typography.js +54 -0
- package/src/hooks/useAnimatedValue.d.ts +2 -0
- package/src/hooks/useAnimatedValue.js +3 -0
- package/src/hooks/useBodyClassName.d.ts +3 -0
- package/src/hooks/useBodyClassName.js +14 -0
- package/src/hooks/useBoolean.d.ts +8 -0
- package/src/hooks/useBoolean.js +12 -0
- package/src/hooks/useComputedColors.d.ts +10 -0
- package/src/hooks/useComputedColors.js +42 -0
- package/src/hooks/useDebounce.d.ts +1 -0
- package/src/hooks/useDebounce.js +12 -0
- package/src/hooks/useDisclosure.d.ts +8 -0
- package/src/hooks/useDisclosure.js +12 -0
- package/src/hooks/useFirstMountState.d.ts +1 -0
- package/src/hooks/useFirstMountState.js +9 -0
- package/src/hooks/useForceableState.d.ts +1 -0
- package/src/hooks/useForceableState.js +6 -0
- package/src/hooks/useHover.d.ts +11 -0
- package/src/hooks/useHover.js +4 -0
- package/src/hooks/useInterval.d.ts +1 -0
- package/src/hooks/useInterval.js +11 -0
- package/src/hooks/useLazyRef.d.ts +2 -0
- package/src/hooks/useLazyRef.js +9 -0
- package/src/hooks/useMergeRefs.d.ts +2 -0
- package/src/hooks/useMergeRefs.js +5 -0
- package/src/hooks/useNativeProp.d.ts +2 -0
- package/src/hooks/useNativeProp.js +9 -0
- package/src/hooks/useOutsideClick.d.ts +8 -0
- package/src/hooks/useOutsideClick.js +54 -0
- package/src/hooks/usePersistedState.d.ts +1 -0
- package/src/hooks/usePersistedState.js +21 -0
- package/src/hooks/usePressEvents.d.ts +31 -0
- package/src/hooks/usePressEvents.js +4 -0
- package/src/hooks/usePreviousValue.d.ts +1 -0
- package/src/hooks/usePreviousValue.js +8 -0
- package/src/hooks/useResponsive.d.ts +7 -0
- package/src/hooks/useResponsive.js +20 -0
- package/src/hooks/useUpdateEffect.d.ts +2 -0
- package/src/hooks/useUpdateEffect.js +10 -0
- package/src/hooks/useUrqlMutation.d.ts +4 -0
- package/src/hooks/useUrqlMutation.js +30 -0
- package/src/hooks/useUrqlQuery.d.ts +19 -0
- package/src/hooks/useUrqlQuery.js +73 -0
- package/src/state/toasts.d.ts +21 -0
- package/src/state/toasts.js +61 -0
- package/src/utils/__tests__/array.test.d.ts +1 -0
- package/src/utils/__tests__/array.test.js +127 -0
- package/src/utils/__tests__/base64.test.d.ts +1 -0
- package/src/utils/__tests__/base64.test.js +27 -0
- package/src/utils/__tests__/function.test.d.ts +1 -0
- package/src/utils/__tests__/function.test.js +36 -0
- package/src/utils/__tests__/object.test.d.ts +1 -0
- package/src/utils/__tests__/object.test.js +17 -0
- package/src/utils/__tests__/rifm.test.d.ts +1 -0
- package/src/utils/__tests__/rifm.test.js +124 -0
- package/src/utils/__tests__/string.test.d.ts +1 -0
- package/src/utils/__tests__/string.test.js +16 -0
- package/src/utils/a11y.d.ts +1 -0
- package/src/utils/a11y.js +18 -0
- package/src/utils/array.d.ts +6 -0
- package/src/utils/array.js +71 -0
- package/src/utils/base64.d.ts +2 -0
- package/src/utils/base64.js +20 -0
- package/src/utils/file.d.ts +2 -0
- package/src/utils/file.js +10 -0
- package/src/utils/flagCountry.d.ts +1 -0
- package/src/utils/flagCountry.js +1 -0
- package/src/utils/function.d.ts +4 -0
- package/src/utils/function.js +19 -0
- package/src/utils/math.d.ts +16 -0
- package/src/utils/math.js +47 -0
- package/src/utils/nullish.d.ts +10 -0
- package/src/utils/nullish.js +8 -0
- package/src/utils/object.d.ts +2 -0
- package/src/utils/object.js +7 -0
- package/src/utils/popper.d.ts +3 -0
- package/src/utils/popper.js +30 -0
- package/src/utils/rifm.d.ts +14 -0
- package/src/utils/rifm.js +53 -0
- package/src/utils/string.d.ts +4 -0
- package/src/utils/string.js +233 -0
- package/src/utils/timer.d.ts +11 -0
- package/src/utils/timer.js +46 -0
- package/src/utils/types.d.ts +8 -0
- package/src/utils/types.js +1 -0
- package/src/utils/userAgent.d.ts +4 -0
- package/src/utils/userAgent.js +9 -0
- package/src/utils/viewport.d.ts +1 -0
- package/src/utils/viewport.js +12 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
export const useBodyClassName = (className, { enabled = true } = {}) => {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
if (enabled) {
|
|
5
|
+
document.body.classList.add(className);
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
document.body.classList.remove(className);
|
|
9
|
+
}
|
|
10
|
+
return () => {
|
|
11
|
+
document.body.classList.remove(className);
|
|
12
|
+
};
|
|
13
|
+
}, [className, enabled]);
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
export const useBoolean = (initialValue) => {
|
|
3
|
+
const [value, setValue] = useState(initialValue);
|
|
4
|
+
return [
|
|
5
|
+
value,
|
|
6
|
+
useMemo(() => ({
|
|
7
|
+
on: () => setValue(true),
|
|
8
|
+
off: () => setValue(false),
|
|
9
|
+
toggle: () => setValue(prevValue => !prevValue),
|
|
10
|
+
}), []),
|
|
11
|
+
];
|
|
12
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { darken, getLuminance, meetsContrastGuidelines, tint } from "polished";
|
|
2
|
+
import { invariantColors } from "../constants/design";
|
|
3
|
+
import { identity, memoize } from "../utils/function";
|
|
4
|
+
const isValidColor = (color) => {
|
|
5
|
+
try {
|
|
6
|
+
getLuminance(color);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const getComputedColors = (color) => {
|
|
14
|
+
const safeColor = isValidColor(color) ? color : invariantColors.gray; // fallback
|
|
15
|
+
const luminance = getLuminance(safeColor);
|
|
16
|
+
if (luminance <= 0.75) {
|
|
17
|
+
return {
|
|
18
|
+
original: safeColor,
|
|
19
|
+
progress: safeColor,
|
|
20
|
+
text: invariantColors.white,
|
|
21
|
+
hovered: tint(0.2, safeColor),
|
|
22
|
+
pressed: darken(0.05, safeColor),
|
|
23
|
+
disabled: tint(0.5, safeColor),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const delta = 0.5 + (1 - luminance);
|
|
27
|
+
let text = darken(delta / 1.5, safeColor);
|
|
28
|
+
if (!meetsContrastGuidelines(safeColor, text).AALarge) {
|
|
29
|
+
text = darken(delta, safeColor);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
original: safeColor,
|
|
33
|
+
progress: text,
|
|
34
|
+
text,
|
|
35
|
+
hovered: darken(delta / 12, safeColor),
|
|
36
|
+
pressed: darken(delta / 6, safeColor),
|
|
37
|
+
disabled: safeColor, // update this with Clement's values
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const memoizedGetComputedColors = memoize(getComputedColors, identity);
|
|
41
|
+
// Not really a hook: we rely on lodash to perform cross-components memoization
|
|
42
|
+
export const useComputedColors = (color) => memoizedGetComputedColors(color);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useDebounce: <T>(func: (arg: T) => void, duration: number) => (arg: T) => () => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
import { isNotNullish } from "../utils/nullish";
|
|
3
|
+
export const useDebounce = (func, duration) => {
|
|
4
|
+
const timeoutRef = useRef(undefined);
|
|
5
|
+
return useCallback((arg) => {
|
|
6
|
+
if (isNotNullish(timeoutRef.current)) {
|
|
7
|
+
clearTimeout(timeoutRef.current);
|
|
8
|
+
}
|
|
9
|
+
timeoutRef.current = window.setTimeout(func, duration, arg);
|
|
10
|
+
return () => clearTimeout(timeoutRef.current);
|
|
11
|
+
}, [func, duration]);
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
export const useDisclosure = (initialValue) => {
|
|
3
|
+
const [value, setValue] = useState(initialValue);
|
|
4
|
+
return [
|
|
5
|
+
value,
|
|
6
|
+
useMemo(() => ({
|
|
7
|
+
open: () => setValue(true),
|
|
8
|
+
close: () => setValue(false),
|
|
9
|
+
toggle: () => setValue(prevValue => !prevValue),
|
|
10
|
+
}), []),
|
|
11
|
+
];
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useFirstMountState: () => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useForceableState: (forced: boolean) => [boolean, (value: boolean) => void];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/exports/Pressable/index.js#L220
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
export const useForceableState = (forced) => {
|
|
4
|
+
const [value, setValue] = useState(false);
|
|
5
|
+
return [value || forced, setValue];
|
|
6
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MutableRefObject } from "react";
|
|
2
|
+
type HoverEventsConfig = {
|
|
3
|
+
contain?: boolean;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
onHoverChange?: (bool: boolean) => void;
|
|
6
|
+
onHoverEnd?: (event: unknown) => void;
|
|
7
|
+
onHoverStart?: (event: unknown) => void;
|
|
8
|
+
onHoverUpdate?: (event: unknown) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const useHover: <T>(ref: MutableRefObject<T | null>, config: HoverEventsConfig) => void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useInterval: (handler: (id: number) => void, timeout: number) => void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
export const useInterval = (handler, timeout) => {
|
|
3
|
+
const handlerRef = useRef(handler);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
handlerRef.current = handler;
|
|
6
|
+
}, [handler]);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const id = window.setInterval(() => handlerRef.current(id), timeout);
|
|
9
|
+
return () => window.clearInterval(id);
|
|
10
|
+
}, [timeout]);
|
|
11
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/modules/useMergeRefs/index.js
|
|
2
|
+
// https://github.com/theKashey/use-callback-ref (for typing)
|
|
3
|
+
// @ts-expect-error
|
|
4
|
+
import originalUseMergeRefs from "react-native-web/dist/cjs/modules/useMergeRefs";
|
|
5
|
+
export const useMergeRefs = originalUseMergeRefs;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useLayoutEffect } from "react";
|
|
2
|
+
import { isNotNullish } from "../utils/nullish";
|
|
3
|
+
export const useNativeProp = (ref, name, value) => {
|
|
4
|
+
useLayoutEffect(() => {
|
|
5
|
+
if (isNotNullish(value) && ref.current instanceof Element) {
|
|
6
|
+
ref.current.setAttribute(name, String(value));
|
|
7
|
+
}
|
|
8
|
+
}, [ref, name, value]);
|
|
9
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RefObject } from "react";
|
|
2
|
+
type Params = {
|
|
3
|
+
containerRef: RefObject<unknown>;
|
|
4
|
+
onClickOutside?: (event: MouseEvent | TouchEvent) => void;
|
|
5
|
+
onFocusOutside?: (event: FocusEvent) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const useOutsideClick: ({ containerRef, onClickOutside, onFocusOutside }: Params) => void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { isNotNullish, isNullish } from "../utils/nullish";
|
|
3
|
+
export const useOutsideClick = ({ containerRef, onClickOutside, onFocusOutside }) => {
|
|
4
|
+
const hasTouchStartedRef = useRef(false);
|
|
5
|
+
const isTargetInside = useCallback((event) => {
|
|
6
|
+
const target = event.target;
|
|
7
|
+
// NOTE: Let's be careful with the `instanceof` check if we're ever to render a portal to another window
|
|
8
|
+
return (isNotNullish(containerRef.current) &&
|
|
9
|
+
containerRef.current instanceof HTMLElement &&
|
|
10
|
+
containerRef.current.contains(target));
|
|
11
|
+
}, [containerRef]);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (isNullish(onClickOutside)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const onTouchStart = (event) => {
|
|
17
|
+
if (!isTargetInside(event)) {
|
|
18
|
+
hasTouchStartedRef.current = true;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const onTouchEnd = (event) => {
|
|
22
|
+
if (!isTargetInside(event) && hasTouchStartedRef.current) {
|
|
23
|
+
onClickOutside?.(event);
|
|
24
|
+
}
|
|
25
|
+
hasTouchStartedRef.current = false;
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener("mousedown", onTouchStart, true);
|
|
28
|
+
document.addEventListener("mouseup", onTouchEnd, true);
|
|
29
|
+
document.addEventListener("touchstart", onTouchStart, true);
|
|
30
|
+
document.addEventListener("touchend", onTouchEnd, true);
|
|
31
|
+
return () => {
|
|
32
|
+
document.removeEventListener("mousedown", onTouchStart, true);
|
|
33
|
+
document.removeEventListener("mouseup", onTouchEnd, true);
|
|
34
|
+
document.removeEventListener("touchstart", onTouchStart, true);
|
|
35
|
+
document.removeEventListener("touchend", onTouchEnd, true);
|
|
36
|
+
};
|
|
37
|
+
}, [isTargetInside, onClickOutside]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (isNullish(onFocusOutside)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const onFocusIn = (event) => {
|
|
43
|
+
if (!isTargetInside(event)) {
|
|
44
|
+
onFocusOutside?.(event);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
// We use the `focusin` event so that we can intercept during the capturing phase
|
|
48
|
+
// see: https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event
|
|
49
|
+
document.addEventListener("focusin", onFocusIn, true);
|
|
50
|
+
return () => {
|
|
51
|
+
document.removeEventListener("focusin", onFocusIn, true);
|
|
52
|
+
};
|
|
53
|
+
}, [isTargetInside, onFocusOutside]);
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const usePersistedState: <T>(key: string, defaultValue: T) => readonly [T, (state: T) => void];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Option, Result } from "@swan-io/boxed";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
export const usePersistedState = (key, defaultValue) => {
|
|
4
|
+
const [state, setState] = useState(() => {
|
|
5
|
+
return Result.fromExecution(() => localStorage.getItem(key))
|
|
6
|
+
.toOption()
|
|
7
|
+
.flatMap(Option.fromNullable)
|
|
8
|
+
.map(value => JSON.parse(value))
|
|
9
|
+
.getWithDefault(defaultValue);
|
|
10
|
+
});
|
|
11
|
+
const setPersistedState = useCallback((state) => {
|
|
12
|
+
setState(state);
|
|
13
|
+
try {
|
|
14
|
+
localStorage.setItem(key, JSON.stringify(state));
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
// ignore
|
|
18
|
+
}
|
|
19
|
+
}, [key]);
|
|
20
|
+
return [state, setPersistedState];
|
|
21
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MutableRefObject } from "react";
|
|
2
|
+
import { GestureResponderEvent } from "react-native";
|
|
3
|
+
type ClickEvent = unknown;
|
|
4
|
+
type KeyboardEvent = unknown;
|
|
5
|
+
type ResponderEvent = unknown;
|
|
6
|
+
export type PressResponderConfig = {
|
|
7
|
+
cancelable?: boolean;
|
|
8
|
+
delayLongPress?: number;
|
|
9
|
+
delayPressEnd?: number;
|
|
10
|
+
delayPressStart?: number;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
onLongPress?: (event: ResponderEvent) => void;
|
|
13
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
14
|
+
onPressChange?: (event: ResponderEvent) => void;
|
|
15
|
+
onPressEnd?: (event: ResponderEvent) => void;
|
|
16
|
+
onPressMove?: (event: ResponderEvent) => void;
|
|
17
|
+
onPressStart?: (event: ResponderEvent) => void;
|
|
18
|
+
};
|
|
19
|
+
type EventHandlers = {
|
|
20
|
+
onClick?: (event: ClickEvent) => void;
|
|
21
|
+
onContextMenu?: (event: ClickEvent) => void;
|
|
22
|
+
onKeyDown?: (event: KeyboardEvent) => void;
|
|
23
|
+
onResponderGrant?: (event: ResponderEvent) => void;
|
|
24
|
+
onResponderMove?: (event: ResponderEvent) => void;
|
|
25
|
+
onResponderRelease?: (event: ResponderEvent) => void;
|
|
26
|
+
onResponderTerminate?: (event: ResponderEvent) => void;
|
|
27
|
+
onResponderTerminationRequest?: (event: ResponderEvent) => boolean;
|
|
28
|
+
onStartShouldSetResponder?: (event: ResponderEvent) => boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare const usePressEvents: <T>(ref: MutableRefObject<T | null>, config: PressResponderConfig) => EventHandlers;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// https://github.com/necolas/react-native-web/blob/0.17.5/packages/react-native-web/src/modules/usePressEvents/index.js
|
|
2
|
+
// @ts-expect-error
|
|
3
|
+
import originalUsePressEvents from "react-native-web/dist/cjs/modules/usePressEvents";
|
|
4
|
+
export const usePressEvents = originalUsePressEvents;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const usePreviousValue: <T>(value: T) => T;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Based on https://github.com/necolas/react-native-web/blob/0.17.7/packages/react-native-web/src/exports/useWindowDimensions/index.js
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
|
+
import { Dimensions } from "react-native";
|
|
4
|
+
import { isNotNullish } from "../utils/nullish";
|
|
5
|
+
const BREAKPOINT = 992;
|
|
6
|
+
const isDesktop = ({ width }, breakpoint) => width >= breakpoint;
|
|
7
|
+
export const useResponsive = (breakpoint = BREAKPOINT) => {
|
|
8
|
+
const desktop = useSyncExternalStore(onStoreChange => {
|
|
9
|
+
const subscription = Dimensions.addEventListener("change", ({ window }) => {
|
|
10
|
+
if (isNotNullish(window)) {
|
|
11
|
+
onStoreChange();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
subscription.remove();
|
|
16
|
+
};
|
|
17
|
+
}, () => isDesktop(Dimensions.get("window"), breakpoint));
|
|
18
|
+
const media = useCallback((values) => desktop ? values.desktop ?? values.mobile : values.mobile, [desktop]);
|
|
19
|
+
return { desktop, media };
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useFirstMountState } from "./useFirstMountState";
|
|
3
|
+
export const useUpdateEffect = (effect, deps) => {
|
|
4
|
+
const isFirstMount = useFirstMountState();
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!isFirstMount) {
|
|
7
|
+
return effect();
|
|
8
|
+
}
|
|
9
|
+
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
|
10
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AsyncData, Future, Result } from "@swan-io/boxed";
|
|
2
|
+
import { DocumentNode } from "graphql";
|
|
3
|
+
import { AnyVariables, CombinedError, TypedDocumentNode } from "urql";
|
|
4
|
+
export declare const useUrqlMutation: <Data, Variables extends AnyVariables>(query: string | DocumentNode | TypedDocumentNode<Data, Variables>) => readonly [AsyncData<Result<Data, Error>>, (input: Variables) => Future<Result<Data, Error | CombinedError>>];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AsyncData, Future, Result } from "@swan-io/boxed";
|
|
2
|
+
import { useCallback, useMemo } from "react";
|
|
3
|
+
import { CombinedError, useMutation } from "urql";
|
|
4
|
+
import { isNotNullish, isNullish } from "../utils/nullish";
|
|
5
|
+
const toResult = ({ data, error, }) => {
|
|
6
|
+
if (isNotNullish(error)) {
|
|
7
|
+
return Result.Error(error);
|
|
8
|
+
}
|
|
9
|
+
if (isNullish(data)) {
|
|
10
|
+
return Result.Error(new CombinedError({ networkError: new Error("No Content") }));
|
|
11
|
+
}
|
|
12
|
+
return Result.Ok(data);
|
|
13
|
+
};
|
|
14
|
+
export const useUrqlMutation = (query) => {
|
|
15
|
+
const [{ fetching, data, error }, execute] = useMutation(query);
|
|
16
|
+
return [
|
|
17
|
+
useMemo(() => {
|
|
18
|
+
if (fetching) {
|
|
19
|
+
return AsyncData.Loading();
|
|
20
|
+
}
|
|
21
|
+
if (isNullish(data) && isNullish(error)) {
|
|
22
|
+
return AsyncData.NotAsked();
|
|
23
|
+
}
|
|
24
|
+
return AsyncData.Done(toResult({ data, error }));
|
|
25
|
+
}, [fetching, data, error]),
|
|
26
|
+
useCallback((input) => Future.fromPromise(execute(input))
|
|
27
|
+
.mapError(error => error) // Only used to cast error
|
|
28
|
+
.mapResult(toResult), [execute]),
|
|
29
|
+
];
|
|
30
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AsyncData, Result } from "@swan-io/boxed";
|
|
2
|
+
import { DependencyList } from "react";
|
|
3
|
+
import { AnyVariables, UseQueryArgs } from "urql";
|
|
4
|
+
type Query<Data> = {
|
|
5
|
+
data: AsyncData<Result<Data, Error>>;
|
|
6
|
+
nextData: AsyncData<Result<Data, Error>>;
|
|
7
|
+
isForceReloading: boolean;
|
|
8
|
+
reload: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const useUrqlQuery: <Data, Variables extends AnyVariables>(args: UseQueryArgs<Variables, Data>, dependencyList: DependencyList) => Query<Data>;
|
|
11
|
+
type PaginatedQuery<Data> = {
|
|
12
|
+
data: AsyncData<Result<Data, Error>>;
|
|
13
|
+
nextData: AsyncData<Result<Data, Error>>;
|
|
14
|
+
isForceReloading: boolean;
|
|
15
|
+
reload: () => void;
|
|
16
|
+
setAfter: (cursor: string | undefined) => void;
|
|
17
|
+
};
|
|
18
|
+
export declare const useUrqlPaginatedQuery: <Data, Variables extends AnyVariables>(args: UseQueryArgs<Variables, Data>, dependencyList: DependencyList) => PaginatedQuery<Data>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AsyncData, Result } from "@swan-io/boxed";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useQuery } from "urql";
|
|
4
|
+
import { isNotNullish, isNullish } from "../utils/nullish";
|
|
5
|
+
const NO_SUSPENSE_CONTEXT = { suspense: false };
|
|
6
|
+
export const useUrqlQuery = (args, dependencyList) => {
|
|
7
|
+
const [haveParamsChanged, setHaveParamsChanged] = useState(false);
|
|
8
|
+
const [isForceReloading, setIsForceReloading] = useState(false);
|
|
9
|
+
const [{ data, fetching, error }, reexecute] = useQuery({
|
|
10
|
+
...args,
|
|
11
|
+
context: NO_SUSPENSE_CONTEXT,
|
|
12
|
+
});
|
|
13
|
+
const reload = useCallback(() => {
|
|
14
|
+
setIsForceReloading(true);
|
|
15
|
+
reexecute();
|
|
16
|
+
}, [reexecute]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setHaveParamsChanged(true);
|
|
19
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
20
|
+
}, dependencyList);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!fetching) {
|
|
23
|
+
setHaveParamsChanged(false);
|
|
24
|
+
setIsForceReloading(false);
|
|
25
|
+
}
|
|
26
|
+
}, [fetching]);
|
|
27
|
+
const okResult = useMemo(() => AsyncData.Done(Result.Ok(data)), [data]);
|
|
28
|
+
const errorResult = useMemo(() => AsyncData.Done(Result.Error(error)), [error]);
|
|
29
|
+
const shouldResetState = isForceReloading || haveParamsChanged;
|
|
30
|
+
if (((isNullish(data) && isNullish(error)) || shouldResetState) && fetching) {
|
|
31
|
+
return {
|
|
32
|
+
data: AsyncData.Loading(),
|
|
33
|
+
nextData: AsyncData.Loading(),
|
|
34
|
+
isForceReloading,
|
|
35
|
+
reload,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (isNotNullish(error)) {
|
|
39
|
+
return {
|
|
40
|
+
data: errorResult,
|
|
41
|
+
nextData: errorResult,
|
|
42
|
+
isForceReloading,
|
|
43
|
+
reload,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (isNotNullish(data)) {
|
|
47
|
+
return {
|
|
48
|
+
data: okResult,
|
|
49
|
+
nextData: fetching ? AsyncData.Loading() : okResult,
|
|
50
|
+
isForceReloading,
|
|
51
|
+
reload,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
data: AsyncData.NotAsked(),
|
|
56
|
+
nextData: AsyncData.NotAsked(),
|
|
57
|
+
isForceReloading,
|
|
58
|
+
reload,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export const useUrqlPaginatedQuery = (args, dependencyList) => {
|
|
62
|
+
const [after, setAfter] = useState(undefined);
|
|
63
|
+
const { data, nextData, isForceReloading, reload: originalReload, } = useUrqlQuery({ ...args, variables: { ...args.variables, after } }, dependencyList);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
setAfter(undefined);
|
|
66
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
+
}, dependencyList);
|
|
68
|
+
const reload = useCallback(() => {
|
|
69
|
+
setAfter(undefined);
|
|
70
|
+
originalReload();
|
|
71
|
+
}, [originalReload]);
|
|
72
|
+
return { data, nextData, isForceReloading, reload, setAfter };
|
|
73
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Animated } from "react-native";
|
|
2
|
+
import { ControllableTimeout } from "../utils/timer";
|
|
3
|
+
export type ToastVariant = "success" | "info" | "warning" | "error";
|
|
4
|
+
type ToastContent = {
|
|
5
|
+
variant: ToastVariant;
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
autoClose?: boolean;
|
|
9
|
+
};
|
|
10
|
+
type Toast = {
|
|
11
|
+
uid: string;
|
|
12
|
+
variant: ToastVariant;
|
|
13
|
+
title: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
progress?: Animated.Value;
|
|
16
|
+
timeout?: ControllableTimeout;
|
|
17
|
+
};
|
|
18
|
+
export declare const useToasts: () => Toast[];
|
|
19
|
+
export declare const hideToast: (uid: string) => void;
|
|
20
|
+
export declare const showToast: ({ variant, title, description, autoClose }: ToastContent) => string;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { atom, useAtom } from "react-atomic-state";
|
|
2
|
+
import { Animated, Easing } from "react-native";
|
|
3
|
+
import { createControllableTimeout } from "../utils/timer";
|
|
4
|
+
const toasts = atom([]);
|
|
5
|
+
export const useToasts = () => useAtom(toasts);
|
|
6
|
+
export const hideToast = (uid) => {
|
|
7
|
+
const toast = toasts.get().find(toast => toast.uid === uid);
|
|
8
|
+
if (!toast) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
toast.timeout?.clear();
|
|
12
|
+
toast.progress?.stopAnimation();
|
|
13
|
+
toasts.set(toasts => toasts.filter(toast => toast.uid !== uid));
|
|
14
|
+
};
|
|
15
|
+
export const showToast = ({ variant, title, description, autoClose }) => {
|
|
16
|
+
const uid = `${variant} - ${title} - ${description ?? ""}`;
|
|
17
|
+
const toast = toasts.get().find(toast => toast.uid === uid);
|
|
18
|
+
if (toast != null) {
|
|
19
|
+
if (toast.timeout && toast.progress) {
|
|
20
|
+
toast.timeout.clear();
|
|
21
|
+
Animated.timing(toast.progress, {
|
|
22
|
+
duration: 100,
|
|
23
|
+
easing: Easing.linear,
|
|
24
|
+
toValue: 1,
|
|
25
|
+
useNativeDriver: false,
|
|
26
|
+
}).start(() => {
|
|
27
|
+
toast.timeout?.reset();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return uid;
|
|
31
|
+
}
|
|
32
|
+
// by default, only info and success toasts are auto-closing
|
|
33
|
+
const isAutoClosingToast = autoClose != null ? autoClose : variant === "info" || variant === "success";
|
|
34
|
+
const progress = isAutoClosingToast ? new Animated.Value(1) : undefined;
|
|
35
|
+
const timeout = progress
|
|
36
|
+
? createControllableTimeout({
|
|
37
|
+
duration: 10000,
|
|
38
|
+
onStart: duration => {
|
|
39
|
+
Animated.timing(progress, {
|
|
40
|
+
duration,
|
|
41
|
+
easing: Easing.linear,
|
|
42
|
+
toValue: 0,
|
|
43
|
+
useNativeDriver: false,
|
|
44
|
+
}).start();
|
|
45
|
+
},
|
|
46
|
+
onReset: duration => {
|
|
47
|
+
Animated.timing(progress, {
|
|
48
|
+
duration,
|
|
49
|
+
easing: Easing.linear,
|
|
50
|
+
toValue: 0,
|
|
51
|
+
useNativeDriver: false,
|
|
52
|
+
}).start();
|
|
53
|
+
},
|
|
54
|
+
onEnd: () => {
|
|
55
|
+
hideToast(uid);
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
: undefined;
|
|
59
|
+
toasts.set(toasts => [{ uid, variant, title, description, progress, timeout }, ...toasts]);
|
|
60
|
+
return uid;
|
|
61
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|