@swan-io/lake 15.5.2 → 16.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/package.json +1 -1
- package/src/components/Accordion.js +2 -2
- package/src/components/ContextMenu.js +2 -2
- package/src/components/LakeAlert.d.ts +2 -1
- package/src/components/LakeAlert.js +7 -3
- package/src/components/LakeCopyButton.d.ts +0 -1
- package/src/components/LakeCopyButton.js +1 -1
- package/src/components/LakeSelect.js +1 -2
- package/src/components/LakeStepper.d.ts +0 -2
- package/src/components/LakeStepper.js +0 -39
- package/src/components/MultiSelect.js +1 -2
- package/src/components/Popover.d.ts +0 -1
- package/src/components/Popover.js +1 -1
- package/src/components/Tile.js +2 -2
- package/src/components/SortBottomPanel.d.ts +0 -20
- package/src/components/SortBottomPanel.js +0 -41
- package/src/components/TimePicker.d.ts +0 -55
- package/src/components/TimePicker.js +0 -171
- package/src/hooks/useDisclosure.d.ts +0 -5
- package/src/hooks/useDisclosure.js +0 -12
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useId } from "react";
|
|
3
3
|
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
4
|
import { backgroundColor, colors, spacings } from "../constants/design";
|
|
5
|
-
import {
|
|
5
|
+
import { useBoolean } from "../hooks/useBoolean";
|
|
6
6
|
import { Icon } from "./Icon";
|
|
7
7
|
import { LakeText } from "./LakeText";
|
|
8
8
|
import { Space } from "./Space";
|
|
@@ -40,6 +40,6 @@ const styles = StyleSheet.create({
|
|
|
40
40
|
});
|
|
41
41
|
export const Accordion = ({ children, trigger, style, contentContainerStyle }) => {
|
|
42
42
|
const id = useId();
|
|
43
|
-
const [isOpen, { toggle }] =
|
|
43
|
+
const [isOpen, { toggle }] = useBoolean(false);
|
|
44
44
|
return (_jsxs(View, { children: [_jsxs(Pressable, { "aria-controls": id, role: "button", "aria-expanded": isOpen, onPress: toggle, style: [styles.trigger, style], children: [_jsx(Icon, { name: "chevron-right-filled", size: 12, color: colors.gray[500], style: [styles.arrow, isOpen && styles.arrowOpen] }), _jsx(Space, { width: 20 }), typeof trigger === "string" ? (_jsx(LakeText, { variant: "smallMedium", color: colors.gray[900], children: trigger })) : (trigger)] }), _jsx(View, { "aria-labelledby": id, "aria-hidden": !isOpen, role: "region", style: [styles.contentContainer, isOpen && styles.contentContainerDisplayed], children: _jsx(View, { style: styles.contentInner, children: _jsx(View, { style: [styles.content, contentContainerStyle], children: children }) }) })] }));
|
|
45
45
|
};
|
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { createContext, useContext, useRef } from "react";
|
|
3
3
|
import { StyleSheet, View } from "react-native";
|
|
4
4
|
import { colors, spacings } from "../constants/design";
|
|
5
|
-
import {
|
|
5
|
+
import { useBoolean } from "../hooks/useBoolean";
|
|
6
6
|
import { noop } from "../utils/function";
|
|
7
7
|
import { LakeButton } from "./LakeButton";
|
|
8
8
|
import { LakeText } from "./LakeText";
|
|
@@ -28,7 +28,7 @@ const styles = StyleSheet.create({
|
|
|
28
28
|
});
|
|
29
29
|
const ContextMenuContext = createContext({ close: noop });
|
|
30
30
|
export const ContextMenu = ({ children, ariaLabel, withPill = false, verticalPlacement, }) => {
|
|
31
|
-
const [isOpen, { open, close }] =
|
|
31
|
+
const [isOpen, { on: open, off: close }] = useBoolean(false);
|
|
32
32
|
const triggerRef = useRef(null);
|
|
33
33
|
return (_jsxs(ContextMenuContext.Provider, { value: { close }, children: [_jsx(LakeButton, { ref: triggerRef, mode: "tertiary", size: "small", icon: "more-horizontal-filled", color: "gray", onPress: open, ariaLabel: ariaLabel, pill: withPill }), _jsx(Popover, { referenceRef: triggerRef, visible: isOpen, onDismiss: close, verticalPlacement: verticalPlacement, children: _jsx(View, { style: styles.container, children: children }) })] }));
|
|
34
34
|
};
|
|
@@ -3,6 +3,7 @@ import { StyleProp, ViewStyle } from "react-native";
|
|
|
3
3
|
type AlertVariant = "info" | "warning" | "error" | "success" | "neutral";
|
|
4
4
|
type Props = {
|
|
5
5
|
anchored?: boolean;
|
|
6
|
+
inPopover?: boolean;
|
|
6
7
|
variant: AlertVariant;
|
|
7
8
|
children?: ReactNode;
|
|
8
9
|
callToAction?: ReactNode;
|
|
@@ -11,5 +12,5 @@ type Props = {
|
|
|
11
12
|
style?: StyleProp<ViewStyle>;
|
|
12
13
|
tag?: string;
|
|
13
14
|
};
|
|
14
|
-
export declare const LakeAlert: ({ anchored, variant, title, subtitle, children, style, callToAction, tag, }: Props) => import("react").JSX.Element;
|
|
15
|
+
export declare const LakeAlert: ({ anchored, inPopover, variant, title, subtitle, children, style, callToAction, tag, }: Props) => import("react").JSX.Element;
|
|
15
16
|
export {};
|
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { StyleSheet, View } from "react-native";
|
|
3
3
|
import { match } from "ts-pattern";
|
|
4
4
|
import { commonStyles } from "../constants/commonStyles";
|
|
5
|
-
import { breakpoints, colors, shadows } from "../constants/design";
|
|
5
|
+
import { breakpoints, colors, radii, shadows } from "../constants/design";
|
|
6
6
|
import { useResponsive } from "../hooks/useResponsive";
|
|
7
7
|
import { isNotNullish, isNotNullishOrEmpty } from "../utils/nullish";
|
|
8
8
|
import { Box } from "./Box";
|
|
@@ -14,7 +14,7 @@ const styles = StyleSheet.create({
|
|
|
14
14
|
base: {
|
|
15
15
|
paddingVertical: 20,
|
|
16
16
|
paddingHorizontal: 24,
|
|
17
|
-
borderRadius: 4,
|
|
17
|
+
borderRadius: radii[4],
|
|
18
18
|
borderWidth: 0.5,
|
|
19
19
|
borderLeftWidth: 4,
|
|
20
20
|
boxShadow: shadows.tile,
|
|
@@ -25,6 +25,9 @@ const styles = StyleSheet.create({
|
|
|
25
25
|
marginBottom: -1,
|
|
26
26
|
boxShadow: "none",
|
|
27
27
|
},
|
|
28
|
+
inPopover: {
|
|
29
|
+
borderRadius: radii[8],
|
|
30
|
+
},
|
|
28
31
|
content: {
|
|
29
32
|
marginLeft: 40,
|
|
30
33
|
},
|
|
@@ -71,7 +74,7 @@ const alertLeftBorder = {
|
|
|
71
74
|
neutral: colors.gray[500],
|
|
72
75
|
};
|
|
73
76
|
const isText = (node) => typeof node === "string" || typeof node === "number";
|
|
74
|
-
export const LakeAlert = ({ anchored = false, variant, title, subtitle, children, style, callToAction, tag, }) => {
|
|
77
|
+
export const LakeAlert = ({ anchored = false, inPopover = false, variant, title, subtitle, children, style, callToAction, tag, }) => {
|
|
75
78
|
const color = alertColor[variant];
|
|
76
79
|
const icon = alertIcon[variant];
|
|
77
80
|
const { desktop } = useResponsive(breakpoints.medium);
|
|
@@ -90,6 +93,7 @@ export const LakeAlert = ({ anchored = false, variant, title, subtitle, children
|
|
|
90
93
|
borderColor: alertBorder[variant],
|
|
91
94
|
},
|
|
92
95
|
anchored ? styles.anchored : { borderLeftColor: alertLeftBorder[variant] },
|
|
96
|
+
inPopover && styles.inPopover,
|
|
93
97
|
style,
|
|
94
98
|
], children: [_jsxs(Box, { direction: "row", alignItems: "center", children: [icon != null ? (_jsxs(_Fragment, { children: [_jsx(Icon, { name: icon, color: color, size: 20 }), _jsx(Space, { width: 20 })] })) : null, _jsxs(View, { style: commonStyles.fill, children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", children: [_jsx(LakeText, { color: color, variant: "medium", children: title }), tag && _jsx(Tag, { color: translateColorTag(), children: tag })] }), isNotNullishOrEmpty(subtitle) && _jsx(LakeText, { color: color, children: subtitle })] }), isNotNullish(callToAction) && _jsx(View, { style: styles.callToAction, children: callToAction })] }), isNotNullish(children) && (_jsxs(View, { style: desktop && icon != null && styles.content, children: [_jsx(Space, { height: 12 }), isText(children) ? _jsx(LakeText, { color: color, children: children }) : children] }))] }));
|
|
95
99
|
};
|
|
@@ -4,7 +4,7 @@ import { setClipboardText } from "../utils/clipboard";
|
|
|
4
4
|
import { Icon } from "./Icon";
|
|
5
5
|
import { LakeTooltip } from "./LakeTooltip";
|
|
6
6
|
import { Pressable } from "./Pressable";
|
|
7
|
-
|
|
7
|
+
const copyButtondefaultSize = 21;
|
|
8
8
|
export const LakeCopyButton = ({ color, copiedText, copyText, size = copyButtondefaultSize, valueToCopy, }) => {
|
|
9
9
|
const [visibleState, setVisibleState] = useState("copy");
|
|
10
10
|
return (_jsx(LakeTooltip, { describedBy: "copy", onHide: () => setVisibleState("copy"), togglableOnFocus: true, content: visibleState === "copy" ? copyText : copiedText, children: _jsx(Pressable, { onPress: event => {
|
|
@@ -5,7 +5,6 @@ import { t } from "../../../shared-business/src/utils/i18n";
|
|
|
5
5
|
import { commonStyles } from "../constants/commonStyles";
|
|
6
6
|
import { backgroundColor, colors, invariantColors, radii, shadows, spacings, texts, } from "../constants/design";
|
|
7
7
|
import { useBoolean } from "../hooks/useBoolean";
|
|
8
|
-
import { useDisclosure } from "../hooks/useDisclosure";
|
|
9
8
|
import { useMergeRefs } from "../hooks/useMergeRefs";
|
|
10
9
|
import { getFocusableElements } from "../utils/a11y";
|
|
11
10
|
import { setHighlightApi } from "../utils/highlights";
|
|
@@ -163,7 +162,7 @@ export const LakeSelect = ({ ref, title, items, valueStyle, size, color = "curre
|
|
|
163
162
|
const currentlyTypedRef = useRef(undefined);
|
|
164
163
|
const listItemRefs = useRef(Array(items.length));
|
|
165
164
|
const mergedRef = useMergeRefs(inputRef, ref);
|
|
166
|
-
const [visible, {
|
|
165
|
+
const [visible, { on: open, off: close }] = useBoolean(false);
|
|
167
166
|
const hasValue = isNotNullish(value);
|
|
168
167
|
const isSmall = size === "small";
|
|
169
168
|
const itemValue = items.find(i => i.value === value);
|
|
@@ -16,7 +16,5 @@ type Props = {
|
|
|
16
16
|
style?: StyleProp<ViewStyle>;
|
|
17
17
|
};
|
|
18
18
|
export declare const LakeStepper: ({ steps, activeStepId, style }: Props) => import("react").JSX.Element;
|
|
19
|
-
/** @deprecated Use <MobileStepper> instead */
|
|
20
|
-
export declare const MobileStepTitle: ({ steps, activeStepId }: Props) => import("react").JSX.Element | null;
|
|
21
19
|
export declare const MobileStepper: ({ steps, activeStepId }: Props) => import("react").JSX.Element | null;
|
|
22
20
|
export {};
|
|
@@ -4,9 +4,7 @@ import { Link } from "@swan-io/chicane";
|
|
|
4
4
|
import { StyleSheet, View } from "react-native";
|
|
5
5
|
import { match, P } from "ts-pattern";
|
|
6
6
|
import { colors } from "../constants/design";
|
|
7
|
-
import { Box } from "./Box";
|
|
8
7
|
import { Grid } from "./Grid";
|
|
9
|
-
import { LakeHeading } from "./LakeHeading";
|
|
10
8
|
import { LakeText } from "./LakeText";
|
|
11
9
|
import { Space } from "./Space";
|
|
12
10
|
const linkStyle = {
|
|
@@ -44,23 +42,6 @@ const styles = StyleSheet.create({
|
|
|
44
42
|
progressBarError: {
|
|
45
43
|
backgroundColor: colors.negative[500],
|
|
46
44
|
},
|
|
47
|
-
mobileContainer: {
|
|
48
|
-
borderBottomWidth: 1,
|
|
49
|
-
borderBottomColor: colors.gray[100],
|
|
50
|
-
paddingBottom: 10,
|
|
51
|
-
},
|
|
52
|
-
mobileNumber: {
|
|
53
|
-
minWidth: 28,
|
|
54
|
-
height: 24,
|
|
55
|
-
borderRadius: 12,
|
|
56
|
-
borderWidth: 1,
|
|
57
|
-
borderColor: colors.current[100],
|
|
58
|
-
backgroundColor: colors.current[50],
|
|
59
|
-
},
|
|
60
|
-
mobileNumberError: {
|
|
61
|
-
borderColor: colors.negative[100],
|
|
62
|
-
backgroundColor: colors.negative[50],
|
|
63
|
-
},
|
|
64
45
|
});
|
|
65
46
|
const stylesMobile = StyleSheet.create({
|
|
66
47
|
progress: {
|
|
@@ -127,26 +108,6 @@ export const LakeStepper = ({ steps, activeStepId, style }) => {
|
|
|
127
108
|
}) })] }, currentId));
|
|
128
109
|
}) }));
|
|
129
110
|
};
|
|
130
|
-
/** @deprecated Use <MobileStepper> instead */
|
|
131
|
-
export const MobileStepTitle = ({ steps, activeStepId }) => {
|
|
132
|
-
const currentStep = Option.fromNullable(steps
|
|
133
|
-
.flatMap((step, index) => match(step)
|
|
134
|
-
.with({ id: P.string }, step => ({ ...step, number: `${index + 1}` }))
|
|
135
|
-
.with({ children: P.array(P.any) }, ({ children }) => children.map((child, subIndex) => ({
|
|
136
|
-
...child,
|
|
137
|
-
number: `${index + 1}.${subIndex + 1}`,
|
|
138
|
-
})))
|
|
139
|
-
.exhaustive())
|
|
140
|
-
.find(({ id }) => id === activeStepId)).map(({ label, hasErrors, number }) => ({
|
|
141
|
-
number,
|
|
142
|
-
label,
|
|
143
|
-
isErrorState: hasErrors === true,
|
|
144
|
-
}));
|
|
145
|
-
return currentStep.match({
|
|
146
|
-
Some: ({ number, label, isErrorState }) => (_jsxs(Box, { direction: "row", alignItems: "center", justifyContent: "center", style: styles.mobileContainer, children: [_jsx(Box, { alignItems: "center", justifyContent: "center", style: [styles.mobileNumber, isErrorState && styles.mobileNumberError], children: _jsx(LakeText, { variant: "smallMedium", color: isErrorState ? colors.negative[500] : colors.current[500], children: number }) }), _jsx(Space, { width: 8 }), _jsx(LakeHeading, { level: 4, variant: "h4", color: isErrorState ? colors.negative[500] : colors.current[500], children: label })] })),
|
|
147
|
-
None: () => null,
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
111
|
export const MobileStepper = ({ steps, activeStepId }) => {
|
|
151
112
|
const total = steps.length;
|
|
152
113
|
const currentStep = Option.fromNullable(steps
|
|
@@ -4,7 +4,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { Pressable, StyleSheet, Text, TextInput, View, } from "react-native";
|
|
5
5
|
import { backgroundColor, colors, radii, shadows, texts } from "../constants/design";
|
|
6
6
|
import { useBoolean } from "../hooks/useBoolean";
|
|
7
|
-
import { useDisclosure } from "../hooks/useDisclosure";
|
|
8
7
|
import { groupBy } from "../utils/array";
|
|
9
8
|
import { isNotNullish, isNotNullishOrEmpty } from "../utils/nullish";
|
|
10
9
|
import { safeSplitAround } from "../utils/string";
|
|
@@ -143,7 +142,7 @@ export const MultiSelect = ({ color = "gray", disabled = false, emptyResultText,
|
|
|
143
142
|
const shouldScrollToBottomRef = useRef(false);
|
|
144
143
|
const selectedTagListRef = useRef(null);
|
|
145
144
|
const inputRef = useRef(null);
|
|
146
|
-
const [visible, { open, close }] =
|
|
145
|
+
const [visible, { on: open, off: close }] = useBoolean(false);
|
|
147
146
|
const tint50 = colors[color][50];
|
|
148
147
|
const tint100 = colors[color][100];
|
|
149
148
|
useEffect(() => {
|
|
@@ -55,7 +55,7 @@ const animation = {
|
|
|
55
55
|
{ animationDuration: "100ms" },
|
|
56
56
|
],
|
|
57
57
|
};
|
|
58
|
-
|
|
58
|
+
const VIEWPORT_WIDTH_THRESHOLD = 600;
|
|
59
59
|
export const Popover = memo(({ children, id, label, placement, verticalPlacement, role = "dialog", describedBy, matchReferenceWidth = false, matchReferenceMinWidth = false, onDismiss = noop, onEscapeKey = onDismiss, referenceRef, returnFocus = true, autoFocus = true, visible, underlay = true, forcedMode, }) => {
|
|
60
60
|
const [rootElement, setRootElement] = useState(null);
|
|
61
61
|
const underlayRef = useRef(null);
|
package/src/components/Tile.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { Children, cloneElement, Fragment, useCallback, useId, useState, } from "react";
|
|
3
3
|
import { Pressable, StyleSheet, Text, View, } from "react-native";
|
|
4
4
|
import { backgroundColor, colors, negativeSpacings, radii, shadows, spacings, texts, } from "../constants/design";
|
|
5
|
-
import {
|
|
5
|
+
import { useBoolean } from "../hooks/useBoolean";
|
|
6
6
|
import { isNotNullish } from "../utils/nullish";
|
|
7
7
|
import { Box } from "./Box";
|
|
8
8
|
import { Icon } from "./Icon";
|
|
@@ -99,7 +99,7 @@ const styles = StyleSheet.create({
|
|
|
99
99
|
});
|
|
100
100
|
export const Tile = ({ icon, title, description, headerEnd, footer, flexBasis, children, numberOfLines, paddingVertical = 32, paddingHorizontal = 32, hovered = false, flexGrow = 0, flexShrink = 0, disabled = false, selected, collapsible = false, defaultExpanded = true, style, }) => {
|
|
101
101
|
const contentId = useId();
|
|
102
|
-
const [expanded, { toggle }] =
|
|
102
|
+
const [expanded, { toggle }] = useBoolean(defaultExpanded);
|
|
103
103
|
const hasHeader = isNotNullish(title) || isNotNullish(headerEnd) || isNotNullish(description) || collapsible;
|
|
104
104
|
const header = (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTitleIcon, children: [isNotNullish(icon) && (_jsxs(_Fragment, { children: [icon, _jsx(Space, { height: 12 })] })), isNotNullish(title) && (_jsx(Text, { style: styles.title, role: "heading", "aria-level": 3, children: title }))] }), collapsible ? (_jsx(Icon, { name: "chevron-down-filled", size: 16, color: colors.gray[500], style: [styles.chevron, expanded && styles.chevronOpen] })) : isNotNullish(headerEnd) ? (_jsx(View, { children: headerEnd })) : null] }), isNotNullish(description) && (_jsxs(_Fragment, { children: [isNotNullish(title) && _jsx(Space, { height: 12 }), _jsx(View, { style: styles.descriptionContainer, children: _jsx(LakeText, { numberOfLines: numberOfLines, style: styles.description, children: description }) })] }))] }));
|
|
105
105
|
const content = (_jsxs(_Fragment, { children: [children, isNotNullish(footer) && (_jsx(View, { style: [styles.footer, { marginTop: paddingVertical, marginBottom: -paddingVertical }], children: footer }))] }));
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type SortDirection = "Asc" | "Desc";
|
|
2
|
-
export type SortField<T> = {
|
|
3
|
-
name: T;
|
|
4
|
-
ascLabel: string;
|
|
5
|
-
descLabel: string;
|
|
6
|
-
};
|
|
7
|
-
export type SortValue<T extends string> = {
|
|
8
|
-
field: T;
|
|
9
|
-
direction: SortDirection;
|
|
10
|
-
};
|
|
11
|
-
type Props<T extends string> = {
|
|
12
|
-
visible: boolean;
|
|
13
|
-
title: string;
|
|
14
|
-
fields: SortField<T>[];
|
|
15
|
-
value: SortValue<T> | undefined;
|
|
16
|
-
onChange: (value: SortValue<T>) => void;
|
|
17
|
-
onClose: () => void;
|
|
18
|
-
};
|
|
19
|
-
export declare const SortBottomPanel: <T extends string>({ visible, title, value, fields, onChange, onClose, }: Props<T>) => import("react").JSX.Element;
|
|
20
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment } from "react";
|
|
3
|
-
import { StyleSheet, View } from "react-native";
|
|
4
|
-
import { commonStyles } from "../constants/commonStyles";
|
|
5
|
-
import { colors, spacings } from "../constants/design";
|
|
6
|
-
import { BottomPanel } from "./BottomPanel";
|
|
7
|
-
import { Icon } from "./Icon";
|
|
8
|
-
import { LakeHeading } from "./LakeHeading";
|
|
9
|
-
import { LakeRadio } from "./LakeRadio";
|
|
10
|
-
import { LakeText } from "./LakeText";
|
|
11
|
-
import { Pressable } from "./Pressable";
|
|
12
|
-
import { Separator } from "./Separator";
|
|
13
|
-
import { Space } from "./Space";
|
|
14
|
-
const styles = StyleSheet.create({
|
|
15
|
-
head: {
|
|
16
|
-
padding: 24,
|
|
17
|
-
paddingBottom: 12,
|
|
18
|
-
},
|
|
19
|
-
option: {
|
|
20
|
-
display: "flex",
|
|
21
|
-
flexDirection: "row",
|
|
22
|
-
alignItems: "center",
|
|
23
|
-
paddingHorizontal: spacings[24],
|
|
24
|
-
paddingVertical: spacings[12],
|
|
25
|
-
},
|
|
26
|
-
arrowDown: {
|
|
27
|
-
transform: "rotate(180deg)",
|
|
28
|
-
},
|
|
29
|
-
text: {
|
|
30
|
-
...commonStyles.fill,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
export const SortBottomPanel = ({ visible, title, value, fields, onChange, onClose, }) => {
|
|
34
|
-
return (_jsxs(BottomPanel, { visible: visible, onPressClose: onClose, children: [_jsx(View, { style: styles.head, children: _jsx(LakeHeading, { level: 3, variant: "h3", children: title }) }), fields.map((field, index) => (_jsxs(Fragment, { children: [_jsxs(Pressable, { style: styles.option, onPress: () => {
|
|
35
|
-
onChange({ field: field.name, direction: "Asc" });
|
|
36
|
-
onClose();
|
|
37
|
-
}, children: [_jsx(Icon, { name: "arrow-up-filled", size: 12, color: colors.gray[600], style: styles.arrowDown }), _jsx(Space, { width: 12 }), _jsx(LakeText, { variant: "smallRegular", color: colors.gray[600], style: styles.text, children: field.ascLabel }), _jsx(Space, { width: 16 }), _jsx(LakeRadio, { value: (value === null || value === void 0 ? void 0 : value.field) === field.name && value.direction === "Asc" })] }), _jsxs(Pressable, { style: styles.option, onPress: () => {
|
|
38
|
-
onChange({ field: field.name, direction: "Desc" });
|
|
39
|
-
onClose();
|
|
40
|
-
}, children: [_jsx(Icon, { name: "arrow-up-filled", size: 12, color: colors.gray[600] }), _jsx(Space, { width: 12 }), _jsx(LakeText, { variant: "smallRegular", color: colors.gray[600], style: styles.text, children: field.descLabel }), _jsx(Space, { width: 16 }), _jsx(LakeRadio, { value: (value === null || value === void 0 ? void 0 : value.field) === field.name && value.direction === "Desc" })] }), index < fields.length - 1 && _jsx(Separator, {})] }, field.name))), _jsx(Space, { height: 12 })] }));
|
|
41
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Option } from "@swan-io/boxed";
|
|
2
|
-
type Time = {
|
|
3
|
-
hour: number;
|
|
4
|
-
minute: number;
|
|
5
|
-
};
|
|
6
|
-
export declare const parseTime: (time: string) => Option<Time>;
|
|
7
|
-
export declare const parseTimeRange: (value: {
|
|
8
|
-
start?: string;
|
|
9
|
-
end?: string;
|
|
10
|
-
}) => {
|
|
11
|
-
start: Option<Time>;
|
|
12
|
-
end: Option<Time>;
|
|
13
|
-
};
|
|
14
|
-
export declare const validateTime: (time: Time) => boolean;
|
|
15
|
-
export declare const validateTimeRange: (range: {
|
|
16
|
-
start: Option<Time>;
|
|
17
|
-
end: Option<Time>;
|
|
18
|
-
}) => boolean;
|
|
19
|
-
export type TimePickerProps = {
|
|
20
|
-
value?: string;
|
|
21
|
-
onChangeText: (value: string) => void;
|
|
22
|
-
suggestionStart?: Time;
|
|
23
|
-
suggestionEnd?: Time;
|
|
24
|
-
intervalInMinutes?: number;
|
|
25
|
-
nbMaxSuggestions?: number;
|
|
26
|
-
suggestionSuffix?: (option: Time) => string;
|
|
27
|
-
readOnly?: boolean;
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
error?: string;
|
|
30
|
-
hideErrors?: boolean;
|
|
31
|
-
noSuggestionLabel: string;
|
|
32
|
-
};
|
|
33
|
-
export declare const TimePicker: ({ value, onChangeText, suggestionStart, suggestionEnd, intervalInMinutes, nbMaxSuggestions, suggestionSuffix, readOnly, disabled, error, hideErrors, noSuggestionLabel, }: TimePickerProps) => import("react").JSX.Element;
|
|
34
|
-
export type TimeRangePickerProps = {
|
|
35
|
-
value: {
|
|
36
|
-
start?: string;
|
|
37
|
-
end?: string;
|
|
38
|
-
};
|
|
39
|
-
onChange: (value: {
|
|
40
|
-
start?: string;
|
|
41
|
-
end?: string;
|
|
42
|
-
}) => void;
|
|
43
|
-
intervalInMinutes?: number;
|
|
44
|
-
nbMaxSuggestions?: number;
|
|
45
|
-
readOnly?: boolean;
|
|
46
|
-
disabled?: boolean;
|
|
47
|
-
error?: string;
|
|
48
|
-
startLabel: string;
|
|
49
|
-
endLabel: string;
|
|
50
|
-
minutesLabel: string;
|
|
51
|
-
hoursLabel: string;
|
|
52
|
-
noSuggestionLabel: string;
|
|
53
|
-
};
|
|
54
|
-
export declare const TimeRangePicker: ({ value, onChange, intervalInMinutes, nbMaxSuggestions, readOnly, disabled, error, startLabel, endLabel, minutesLabel, hoursLabel, noSuggestionLabel, }: TimeRangePickerProps) => import("react").JSX.Element;
|
|
55
|
-
export {};
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { AsyncData, Option, Result } from "@swan-io/boxed";
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { StyleSheet, View } from "react-native";
|
|
5
|
-
import { Rifm } from "rifm";
|
|
6
|
-
import { colors } from "../constants/design";
|
|
7
|
-
import { noop } from "../utils/function";
|
|
8
|
-
import { isNotNullishOrEmpty, isNullish } from "../utils/nullish";
|
|
9
|
-
import { getRifmProps } from "../utils/rifm";
|
|
10
|
-
import { Box } from "./Box";
|
|
11
|
-
import { Fill } from "./Fill";
|
|
12
|
-
import { Icon } from "./Icon";
|
|
13
|
-
import { LakeCombobox } from "./LakeCombobox";
|
|
14
|
-
import { LakeLabel } from "./LakeLabel";
|
|
15
|
-
import { LakeText } from "./LakeText";
|
|
16
|
-
import { Space } from "./Space";
|
|
17
|
-
const styles = StyleSheet.create({
|
|
18
|
-
label: {
|
|
19
|
-
flex: 1,
|
|
20
|
-
},
|
|
21
|
-
arrowContainer: {
|
|
22
|
-
height: 40, // input height
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
const DEFAULT_START_TIME = { hour: 0, minute: 0 };
|
|
26
|
-
const DEFAULT_END_TIME = { hour: 23, minute: 59 };
|
|
27
|
-
const DEFAULT_INTERVAL_IN_MINUTES = 15;
|
|
28
|
-
const rifmTimeProps = getRifmProps({
|
|
29
|
-
accept: "numeric",
|
|
30
|
-
charMap: { 2: ":" },
|
|
31
|
-
maxLength: 4,
|
|
32
|
-
});
|
|
33
|
-
export const parseTime = (time) => {
|
|
34
|
-
const [hours, minutes] = time.split(":").map(Number);
|
|
35
|
-
if (isNullish(hours) || Number.isNaN(hours) || isNullish(minutes) || Number.isNaN(minutes)) {
|
|
36
|
-
return Option.None();
|
|
37
|
-
}
|
|
38
|
-
return Option.Some({ hour: hours, minute: minutes });
|
|
39
|
-
};
|
|
40
|
-
export const parseTimeRange = (value) => {
|
|
41
|
-
const start = isNotNullishOrEmpty(value.start) ? parseTime(value.start) : Option.None();
|
|
42
|
-
const end = isNotNullishOrEmpty(value.end) ? parseTime(value.end) : Option.None();
|
|
43
|
-
return { start, end };
|
|
44
|
-
};
|
|
45
|
-
const parseTypingHours = (time) => {
|
|
46
|
-
const [hours, minutes] = time.split(":");
|
|
47
|
-
if (isNullish(hours) || (hours === null || hours === void 0 ? void 0 : hours.length) !== 2 || isNotNullishOrEmpty(minutes)) {
|
|
48
|
-
return Option.None();
|
|
49
|
-
}
|
|
50
|
-
const parsedHours = Number(hours);
|
|
51
|
-
if (parsedHours < 0 || parsedHours > 23) {
|
|
52
|
-
return Option.None();
|
|
53
|
-
}
|
|
54
|
-
if (Number.isNaN(parsedHours)) {
|
|
55
|
-
return Option.None();
|
|
56
|
-
}
|
|
57
|
-
return Option.Some(parsedHours);
|
|
58
|
-
};
|
|
59
|
-
const stringifyTime = (time) => {
|
|
60
|
-
return `${time.hour.toString().padStart(2, "0")}:${time.minute.toString().padStart(2, "0")}`;
|
|
61
|
-
};
|
|
62
|
-
const timeToMinutes = (time) => {
|
|
63
|
-
return time.hour * 60 + time.minute;
|
|
64
|
-
};
|
|
65
|
-
const minutesToTime = (minutes) => {
|
|
66
|
-
return {
|
|
67
|
-
hour: Math.floor(minutes / 60),
|
|
68
|
-
minute: minutes % 60,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
const addMinutes = (time, minutes) => {
|
|
72
|
-
return minutesToTime(timeToMinutes(time) + minutes);
|
|
73
|
-
};
|
|
74
|
-
const floorTime = (time, intervalInMinutes) => {
|
|
75
|
-
const minutes = timeToMinutes(time);
|
|
76
|
-
const flooredMinutes = Math.floor(minutes / intervalInMinutes) * intervalInMinutes;
|
|
77
|
-
return minutesToTime(flooredMinutes);
|
|
78
|
-
};
|
|
79
|
-
export const validateTime = (time) => {
|
|
80
|
-
return time.hour >= 0 && time.hour <= 23 && time.minute >= 0 && time.minute <= 59;
|
|
81
|
-
};
|
|
82
|
-
const isTimeBefore = (time1, time2) => {
|
|
83
|
-
return timeToMinutes(time1) < timeToMinutes(time2);
|
|
84
|
-
};
|
|
85
|
-
export const validateTimeRange = (range) => {
|
|
86
|
-
if (range.start.isNone() || range.end.isNone()) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
return isTimeBefore(range.start.value, range.end.value);
|
|
90
|
-
};
|
|
91
|
-
const minTime = (...times) => {
|
|
92
|
-
const timesInMinutes = times.map(timeToMinutes);
|
|
93
|
-
const minTimeInMinutes = Math.min(...timesInMinutes);
|
|
94
|
-
return minutesToTime(minTimeInMinutes);
|
|
95
|
-
};
|
|
96
|
-
const maxTime = (...times) => {
|
|
97
|
-
const timesInMinutes = times.map(timeToMinutes);
|
|
98
|
-
const maxTimeInMinutes = Math.max(...timesInMinutes);
|
|
99
|
-
return minutesToTime(maxTimeInMinutes);
|
|
100
|
-
};
|
|
101
|
-
const generateTimeList = (start, end, intervalInMinutes) => {
|
|
102
|
-
const startMinutes = timeToMinutes(start);
|
|
103
|
-
const endMinutes = timeToMinutes(end);
|
|
104
|
-
const timeList = [];
|
|
105
|
-
for (let i = startMinutes; i <= endMinutes; i += intervalInMinutes) {
|
|
106
|
-
const time = minutesToTime(i);
|
|
107
|
-
timeList.push(time);
|
|
108
|
-
}
|
|
109
|
-
return timeList;
|
|
110
|
-
};
|
|
111
|
-
const formatTimeDuration = (time, minutesLabel, hoursLabel) => {
|
|
112
|
-
if (time.hour === 0) {
|
|
113
|
-
return `${time.minute} ${minutesLabel}`;
|
|
114
|
-
}
|
|
115
|
-
if (time.minute === 0) {
|
|
116
|
-
return `${time.hour} ${hoursLabel}`;
|
|
117
|
-
}
|
|
118
|
-
return `${time.hour} ${hoursLabel} ${time.minute} ${minutesLabel}`;
|
|
119
|
-
};
|
|
120
|
-
export const TimePicker = ({ value, onChangeText, suggestionStart = DEFAULT_START_TIME, suggestionEnd = DEFAULT_END_TIME, intervalInMinutes = DEFAULT_INTERVAL_IN_MINUTES, nbMaxSuggestions, suggestionSuffix, readOnly, disabled, error, hideErrors, noSuggestionLabel, }) => {
|
|
121
|
-
const typingHour = parseTypingHours(value !== null && value !== void 0 ? value : "");
|
|
122
|
-
const optionsInterval = useMemo(() => typingHour.match({
|
|
123
|
-
Some: hour => [
|
|
124
|
-
maxTime({ hour, minute: 0 }, suggestionStart),
|
|
125
|
-
minTime({ hour, minute: 59 }, suggestionEnd),
|
|
126
|
-
],
|
|
127
|
-
None: () => [suggestionStart, suggestionEnd],
|
|
128
|
-
}), [typingHour, suggestionStart, suggestionEnd]);
|
|
129
|
-
const options = useMemo(() => generateTimeList(optionsInterval[0], optionsInterval[1], intervalInMinutes).slice(0, nbMaxSuggestions), [optionsInterval, intervalInMinutes, nbMaxSuggestions]);
|
|
130
|
-
const items = useMemo(() => AsyncData.Done(Result.Ok(options)), [options]);
|
|
131
|
-
return (_jsx(Rifm, { value: value !== null && value !== void 0 ? value : "", onChange: onChangeText, ...rifmTimeProps, children: ({ value, onChange }) => (_jsx(LakeCombobox, { keyExtractor: stringifyTime, placeholder: "HH:MM", value: value, items: items, itemHeight: 40, nbItemsDisplayed: 4.5, renderItem: item => {
|
|
132
|
-
const text = stringifyTime(item);
|
|
133
|
-
const selected = text === value;
|
|
134
|
-
const suffix = suggestionSuffix === null || suggestionSuffix === void 0 ? void 0 : suggestionSuffix(item);
|
|
135
|
-
const label = isNotNullishOrEmpty(suffix) ? `${text} ${suffix}` : text;
|
|
136
|
-
return (_jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeText, { color: selected ? colors.gray[700] : colors.gray[900], variant: selected ? "smallRegular" : "medium", children: label }), _jsx(Fill, { minWidth: 8 }), selected && (_jsx(Icon, { name: "checkmark-filled", color: colors.positive[500], size: 16 }))] }));
|
|
137
|
-
}, readOnly: readOnly, disabled: disabled, error: error, hideErrors: hideErrors, emptyResult: noSuggestionLabel, onChange: onChange, onValueChange: noop, onSelectItem: item => onChangeText(stringifyTime(item)) })) }));
|
|
138
|
-
};
|
|
139
|
-
export const TimeRangePicker = ({ value, onChange, intervalInMinutes = DEFAULT_INTERVAL_IN_MINUTES, nbMaxSuggestions, readOnly, disabled, error, startLabel, endLabel, minutesLabel, hoursLabel, noSuggestionLabel, }) => {
|
|
140
|
-
var _a;
|
|
141
|
-
const start = parseTime((_a = value.start) !== null && _a !== void 0 ? _a : "");
|
|
142
|
-
const handleStartChange = (startValue) => {
|
|
143
|
-
onChange({
|
|
144
|
-
start: startValue,
|
|
145
|
-
end: value.end,
|
|
146
|
-
});
|
|
147
|
-
};
|
|
148
|
-
const handleEndChange = (endValue) => {
|
|
149
|
-
onChange({
|
|
150
|
-
start: value.start,
|
|
151
|
-
end: endValue,
|
|
152
|
-
});
|
|
153
|
-
};
|
|
154
|
-
return (_jsxs(View, { children: [_jsxs(Box, { direction: "row", alignItems: "end", children: [_jsx(LakeLabel, { label: startLabel, style: styles.label, render: () => (_jsx(TimePicker, { value: value.start, intervalInMinutes: intervalInMinutes, nbMaxSuggestions: nbMaxSuggestions, error: error, hideErrors: true, disabled: disabled, readOnly: readOnly, noSuggestionLabel: noSuggestionLabel, onChangeText: handleStartChange })) }), _jsx(Space, { width: 12 }), _jsx(Box, { style: styles.arrowContainer, justifyContent: "center", children: _jsx(Icon, { name: "arrow-right-filled", size: 20 }) }), _jsx(Space, { width: 12 }), _jsx(LakeLabel, { label: endLabel, style: styles.label, render: () => (_jsx(TimePicker, { value: value.end, intervalInMinutes: intervalInMinutes, nbMaxSuggestions: nbMaxSuggestions, suggestionStart: start
|
|
155
|
-
.map(time => floorTime(time, intervalInMinutes))
|
|
156
|
-
.map(time => addMinutes(time, intervalInMinutes))
|
|
157
|
-
.match({
|
|
158
|
-
Some: time => time,
|
|
159
|
-
None: () => undefined,
|
|
160
|
-
}), suggestionSuffix: time => {
|
|
161
|
-
if (start.isNone()) {
|
|
162
|
-
return "";
|
|
163
|
-
}
|
|
164
|
-
const durationInMinutes = timeToMinutes(time) - timeToMinutes(start.value);
|
|
165
|
-
if (durationInMinutes > 0 && durationInMinutes <= 60) {
|
|
166
|
-
const timeDuration = minutesToTime(durationInMinutes);
|
|
167
|
-
return `(${formatTimeDuration(timeDuration, minutesLabel, hoursLabel)})`;
|
|
168
|
-
}
|
|
169
|
-
return "";
|
|
170
|
-
}, error: error, hideErrors: true, disabled: disabled, readOnly: readOnly, noSuggestionLabel: noSuggestionLabel, onChangeText: handleEndChange })) })] }), _jsx(Space, { height: 4 }), _jsx(LakeText, { variant: "smallRegular", color: colors.negative[500], children: error !== null && error !== void 0 ? error : " " })] }));
|
|
171
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
};
|