@swan-io/lake 2.7.38 → 3.1.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/Card3dPreview.d.ts +1 -1
- package/src/components/Card3dPreview.js +6 -4
- package/src/components/Filters.d.ts +1 -3
- package/src/components/Filters.js +4 -4
- package/src/components/LakeButton.d.ts +7 -3
- package/src/components/ChoicePicker.d.ts +0 -12
- package/src/components/ChoicePicker.js +0 -192
- package/src/components/DatePicker.d.ts +0 -90
- package/src/components/DatePicker.js +0 -619
- package/src/components/FileTile.d.ts +0 -10
- package/src/components/FileTile.js +0 -37
- package/src/components/LakeModal.d.ts +0 -14
- package/src/components/LakeModal.js +0 -135
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ type CardParams = {
|
|
|
20
20
|
cardNumber: string;
|
|
21
21
|
expirationDate: string;
|
|
22
22
|
cvv: string;
|
|
23
|
-
color: "Silver" | "Black";
|
|
23
|
+
color: "Silver" | "Black" | THREE.Texture;
|
|
24
24
|
logo: SVGElement | HTMLImageElement | null;
|
|
25
25
|
logoScale: number;
|
|
26
26
|
assetsUrls: Card3dAssetsUrls;
|
|
@@ -132,7 +132,9 @@ export const Card = forwardRef(({ ownerName, cardNumber, expirationDate, cvv, co
|
|
|
132
132
|
.with("Black", () => {
|
|
133
133
|
materials.card.map = blackTexture;
|
|
134
134
|
})
|
|
135
|
-
.
|
|
135
|
+
.otherwise(texture => {
|
|
136
|
+
materials.card.map = texture;
|
|
137
|
+
});
|
|
136
138
|
// force threejs to update material
|
|
137
139
|
// because sometimes it doesn't apply texture on load randomly
|
|
138
140
|
materials.card.needsUpdate = true;
|
|
@@ -172,9 +174,9 @@ export const Card = forwardRef(({ ownerName, cardNumber, expirationDate, cvv, co
|
|
|
172
174
|
const mainTextMaterial = (_jsx("meshStandardMaterial", { color: match(color)
|
|
173
175
|
.with("Silver", () => 0x000000)
|
|
174
176
|
.with("Black", () => 0xeeeeee)
|
|
175
|
-
.
|
|
177
|
+
.otherwise(() => 0xeeeeee), metalness: 0.1, roughness: 0.55, envMapIntensity: ENV_MAP_INTENSITY }));
|
|
176
178
|
const secondaryTextMaterial = (_jsx("meshStandardMaterial", { color: 0x666666, metalness: 0.1, roughness: 0.55, envMapIntensity: ENV_MAP_INTENSITY }));
|
|
177
|
-
return (_jsx("group", { ref: ref, ...props, dispose: null, children: _jsxs("mesh", { geometry: nodes.card.geometry, material: materials.card, children: [_jsxs("group", { position: [0, 0, FRONT_TEXT_POSITION], children: [_jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", position: [-3.4, -1.95, 0], children: [ownerName, mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMarkProRegular, fontSize: 0.03, anchorX: "left", anchorY: "bottom", position: [3.85, -2.15, 0], children: ["TM", mainTextMaterial] })] }), _jsxs("group", { position: [0, 0, BACK_TEXT_POSITION], children: [_jsxs(Text, { font: assetsUrls.fontMarkProRegular, anchorX: "left", anchorY: "bottom", fontSize: 0.12, rotation: [0, Math.PI, 0], position: [4, 2.38, 0], children: ["support@swan.io", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMarkProRegular, anchorX: "right", anchorY: "bottom", fontSize: 0.12, rotation: [0, Math.PI, 0], position: [-4, 2.38, 0], children: ["IDEMIA 9 1212121L 09/21", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.24, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, 0.68, 0], children: ["Identifier: 0000000000", mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, 0.15, 0], children: ["This card is issued by Swan, pursuant to license", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, -0.15, 0], children: ["
|
|
179
|
+
return (_jsx("group", { ref: ref, ...props, dispose: null, children: _jsxs("mesh", { geometry: nodes.card.geometry, material: materials.card, children: [_jsxs("group", { position: [0, 0, FRONT_TEXT_POSITION], children: [_jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", position: [-3.4, -1.95, 0], children: [ownerName, mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMarkProRegular, fontSize: 0.03, anchorX: "left", anchorY: "bottom", position: [3.85, -2.15, 0], children: ["TM", mainTextMaterial] })] }), _jsxs("group", { position: [0, 0, BACK_TEXT_POSITION], children: [_jsxs(Text, { font: assetsUrls.fontMarkProRegular, anchorX: "left", anchorY: "bottom", fontSize: 0.12, rotation: [0, Math.PI, 0], position: [4, 2.38, 0], children: ["support@swan.io", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMarkProRegular, anchorX: "right", anchorY: "bottom", fontSize: 0.12, rotation: [0, Math.PI, 0], position: [-4, 2.38, 0], children: ["IDEMIA 9 1212121L 09/21", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.24, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, 0.68, 0], children: ["Identifier: 0000000000", mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, 0.15, 0], children: ["This card is issued by Swan, pursuant to license", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.2, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, -0.15, 0], children: ["from Mastercard International.", secondaryTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.48, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, -1.85, 0], children: [cardNumber, mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.29, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [4, -2.3, 0], children: [expirationDate, mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMaisonNeueBook, fontSize: 0.29, anchorX: "left", anchorY: "bottom", rotation: [0, Math.PI, 0], position: [2.55, -2.3, 0], children: ["CVC ", cvv, mainTextMaterial] }), _jsxs(Text, { font: assetsUrls.fontMarkProRegular, anchorX: "center", anchorY: "bottom", fontSize: 0.36, rotation: [0, Math.PI, 0], position: [-2.35, -1.15, 0], children: ["debit", mainTextMaterial] })] }), _jsx("group", {
|
|
178
180
|
// move group to change scale center at top right corner
|
|
179
181
|
position: [
|
|
180
182
|
CARD_WIDTH / 2 - LOGO_MARGIN_RIGHT,
|
|
@@ -190,5 +192,5 @@ export const Card = forwardRef(({ ownerName, cardNumber, expirationDate, cvv, co
|
|
|
190
192
|
}, color: match(color)
|
|
191
193
|
.with("Silver", () => 0x000000)
|
|
192
194
|
.with("Black", () => 0xffffff)
|
|
193
|
-
.
|
|
195
|
+
.otherwise(() => 0xffffff), metalness: 0.1, roughness: 0.35, envMapIntensity: ENV_MAP_INTENSITY, transparent: true, alphaMap: logoData.alphaMap })] })) }), _jsx("mesh", { geometry: nodes.black_band.geometry, material: materials.black_band, position: [0, 1.774, BACK_TEXT_POSITION], rotation: [0, Math.PI / 2, 0] }), _jsx("mesh", { geometry: nodes.chip.geometry, material: materials.chip, position: [-2.78, 0.439, FRONT_TEXT_POSITION], rotation: [0, Math.PI / 2, 0] }), _jsx("mesh", { geometry: nodes.chip_pattern.geometry, material: materials.chip_pattern, position: [-2.778, 0.442, FRONT_TEXT_POSITION + 0.001], rotation: [0, Math.PI / 2, 0] }), _jsx("mesh", { geometry: nodes.mc_center.geometry, material: materials.mastercard_orange, position: [3.052, -1.832, FRONT_TEXT_POSITION], rotation: [Math.PI / 2, 0, 0] }), _jsx("mesh", { geometry: nodes.mc_left.geometry, material: materials.mastercard_red, position: [2.676, -1.773, FRONT_TEXT_POSITION], rotation: [Math.PI / 2, 0, 0] }), _jsx("mesh", { geometry: nodes.mc_right.geometry, material: materials.mastercard_yellow, position: [3.47, -1.773, FRONT_TEXT_POSITION], rotation: [-Math.PI / 2, 0, 0] }), _jsx("mesh", { geometry: nodes.metal_circle.geometry, material: materials.rainbow, position: [-2.33, -1.849, BACK_TEXT_POSITION], rotation: [-Math.PI / 2, Math.PI / 2, 0] }), _jsx("mesh", { geometry: nodes.metal_circle001.geometry, material: materials.rainbow_rough, position: [-2.629, -1.849, BACK_TEXT_POSITION - 0.001], rotation: [-Math.PI / 2, Math.PI / 2, 0], scale: [0.35, 1, 0.35] }), _jsx("mesh", { geometry: nodes.metal_circle002.geometry, material: materials.rainbow_rough, position: [-2.33, -1.849, BACK_TEXT_POSITION - 0.001], rotation: [-Math.PI / 2, Math.PI / 2, 0] }), _jsx("mesh", { geometry: nodes.metal_mastercard.geometry, material: materials.rainbow_mastercard, position: [0.914, -1.298, BACK_TEXT_POSITION - 0.001], rotation: [Math.PI / 2, 0, Math.PI], scale: 0.09 })] }) }));
|
|
194
196
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { DateFormat, DatePickerDate } from "@swan-io/shared-business/src/components/DatePicker";
|
|
1
2
|
import { ValidatorResult } from "react-ux-form";
|
|
2
3
|
import { Simplify } from "type-fest";
|
|
3
|
-
import { DateFormat, DatePickerDate, MonthNames, WeekDayNames } from "./DatePicker";
|
|
4
4
|
type Item<T> = {
|
|
5
5
|
label: string;
|
|
6
6
|
value: T;
|
|
@@ -22,8 +22,6 @@ export type FilterRadioDef<T> = {
|
|
|
22
22
|
export type FilterDateDef<Values = unknown> = {
|
|
23
23
|
type: "date";
|
|
24
24
|
label: string;
|
|
25
|
-
monthNames: MonthNames;
|
|
26
|
-
dayNames: WeekDayNames;
|
|
27
25
|
cancelText: string;
|
|
28
26
|
submitText: string;
|
|
29
27
|
noValueText: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { DatePickerModal, } from "@swan-io/shared-business/src/components/DatePicker";
|
|
2
3
|
import dayjs from "dayjs";
|
|
3
4
|
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
5
|
import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
|
|
@@ -10,7 +11,6 @@ import { useMergeRefs } from "../hooks/useMergeRefs";
|
|
|
10
11
|
import { usePreviousValue } from "../hooks/usePreviousValue";
|
|
11
12
|
import { isNotNullish } from "../utils/nullish";
|
|
12
13
|
import { Box } from "./Box";
|
|
13
|
-
import { DatePickerModal, } from "./DatePicker";
|
|
14
14
|
import { Icon } from "./Icon";
|
|
15
15
|
import { LakeButton } from "./LakeButton";
|
|
16
16
|
import { LakeCheckbox } from "./LakeCheckbox";
|
|
@@ -154,11 +154,11 @@ function FilterCheckbox({ label, items, width, checkAllLabel, value, onValueChan
|
|
|
154
154
|
return (_jsxs(Pressable, { role: "radio", "aria-checked": isSelected, style: ({ hovered }) => [styles.radio, hovered && styles.itemHovered], onPress: onPress, children: [_jsx(LakeCheckbox, { value: isSelected }), _jsx(Space, { width: 12 }), _jsx(Text, { style: styles.itemLabel, children: item.label })] }));
|
|
155
155
|
} }), _jsx(Space, { height: 8 }), _jsx(View, { style: styles.buttonContainer, children: _jsx(LakeButton, { color: "current", onPress: save, children: applyButtonLabel }) }), _jsx(Space, { height: 24 })] }) })] }));
|
|
156
156
|
}
|
|
157
|
-
function FilterDate({ label,
|
|
157
|
+
function FilterDate({ label, initialValue, noValueText, cancelText, submitText, dateFormat, isSelectable, validate, onSave, onPressRemove, autoOpen = false, }) {
|
|
158
158
|
const inputRef = useRef(null);
|
|
159
159
|
const [visible, { close, toggle }] = useDisclosure(autoOpen);
|
|
160
160
|
const value = useMemo(() => (isNotNullish(initialValue) ? dayjs(initialValue).format(dateFormat) : ""), [initialValue, dateFormat]);
|
|
161
|
-
return (_jsxs(View, { style: styles.container, children: [_jsx(FilterTag, { label: label, onPress: toggle, ref: inputRef, onPressRemove: onPressRemove, isActive: visible, value: isNotNullish(initialValue) ? dayjs(initialValue).format(dateFormat) : noValueText }), _jsx(DatePickerModal, { visible: visible,
|
|
161
|
+
return (_jsxs(View, { style: styles.container, children: [_jsx(FilterTag, { label: label, onPress: toggle, ref: inputRef, onPressRemove: onPressRemove, isActive: visible, value: isNotNullish(initialValue) ? dayjs(initialValue).format(dateFormat) : noValueText }), _jsx(DatePickerModal, { visible: visible, format: dateFormat, firstWeekDay: "monday", label: label, cancelLabel: cancelText, confirmLabel: submitText, value: value, isSelectable: isSelectable, validate: validate, onChange: value => {
|
|
162
162
|
const formattedValue = dayjs(value, dateFormat, true).toJSON();
|
|
163
163
|
onSave(formattedValue);
|
|
164
164
|
}, onDissmiss: close })] }));
|
|
@@ -211,7 +211,7 @@ export const FiltersStack = ({ filters, openedFilters, definition, onChangeOpene
|
|
|
211
211
|
onChangeFilters({ ...filters, [filterName]: undefined });
|
|
212
212
|
onChangeOpened(openedFilters.filter(f => f !== filterName));
|
|
213
213
|
} })))
|
|
214
|
-
.with({ type: "date" }, ({ type, label,
|
|
214
|
+
.with({ type: "date" }, ({ type, label, noValueText, cancelText, submitText, dateFormat, isSelectable, validate, }) => (_jsx(FilterDate, { label: label, noValueText: noValueText, cancelText: cancelText, submitText: submitText, dateFormat: dateFormat, autoOpen: lastOpenedFilter === filterName, isSelectable: isSelectable ? date => isSelectable(date, filters) : undefined, validate: validate ? value => validate(value, filters) : undefined, initialValue: getFilterValue(type, filters, filterName), onSave: value => onChangeFilters({ ...filters, [filterName]: value }), onPressRemove: () => {
|
|
215
215
|
onChangeFilters({ ...filters, [filterName]: undefined });
|
|
216
216
|
onChangeOpened(openedFilters.filter(f => f !== filterName));
|
|
217
217
|
} })))
|
|
@@ -5,8 +5,6 @@ import { IconName } from "./Icon";
|
|
|
5
5
|
export type ButtonProps = {
|
|
6
6
|
ariaControls?: string;
|
|
7
7
|
ariaExpanded?: boolean;
|
|
8
|
-
ariaLabel?: string;
|
|
9
|
-
children?: ReactNode;
|
|
10
8
|
color?: ColorVariants;
|
|
11
9
|
disabled?: boolean;
|
|
12
10
|
loading?: boolean;
|
|
@@ -25,7 +23,13 @@ export type ButtonProps = {
|
|
|
25
23
|
target?: string;
|
|
26
24
|
};
|
|
27
25
|
pill?: boolean;
|
|
28
|
-
}
|
|
26
|
+
} & ({
|
|
27
|
+
ariaLabel: string;
|
|
28
|
+
children?: never;
|
|
29
|
+
} | {
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
});
|
|
29
33
|
export declare const LakeButton: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<View>>>;
|
|
30
34
|
type GroupProps = {
|
|
31
35
|
children: ReactNode;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
type Props<T> = {
|
|
3
|
-
items: T[];
|
|
4
|
-
large?: boolean;
|
|
5
|
-
renderItem: (value: T) => ReactNode;
|
|
6
|
-
value?: T;
|
|
7
|
-
getId?: (item: T) => unknown;
|
|
8
|
-
onChange: (value: T) => void;
|
|
9
|
-
disabled?: boolean;
|
|
10
|
-
};
|
|
11
|
-
export declare const ChoicePicker: <T>({ items, getId, large, renderItem, value, disabled, onChange, }: Props<T>) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export {};
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { ScrollView, StyleSheet, View } from "react-native";
|
|
4
|
-
import { match } from "ts-pattern";
|
|
5
|
-
import { commonStyles } from "../constants/commonStyles";
|
|
6
|
-
import { breakpoints, negativeSpacings, spacings } from "../constants/design";
|
|
7
|
-
import { useResponsive } from "../hooks/useResponsive";
|
|
8
|
-
import { clampValue } from "../utils/math";
|
|
9
|
-
import { detectScrollAnimationEnd } from "../utils/viewport";
|
|
10
|
-
import { LakeButton } from "./LakeButton";
|
|
11
|
-
import { LakeRadio } from "./LakeRadio";
|
|
12
|
-
import { Pressable } from "./Pressable";
|
|
13
|
-
import { Space } from "./Space";
|
|
14
|
-
import { Tile } from "./Tile";
|
|
15
|
-
const styles = StyleSheet.create({
|
|
16
|
-
root: {
|
|
17
|
-
alignSelf: "stretch",
|
|
18
|
-
alignItems: "stretch",
|
|
19
|
-
flexGrow: 1,
|
|
20
|
-
overflow: "hidden",
|
|
21
|
-
marginHorizontal: negativeSpacings[12],
|
|
22
|
-
},
|
|
23
|
-
scrollSnap: {
|
|
24
|
-
scrollSnapType: "x mandatory",
|
|
25
|
-
},
|
|
26
|
-
container: {
|
|
27
|
-
alignSelf: "stretch",
|
|
28
|
-
flexDirection: "row",
|
|
29
|
-
flexWrap: "wrap",
|
|
30
|
-
alignItems: "stretch",
|
|
31
|
-
justifyContent: "center",
|
|
32
|
-
},
|
|
33
|
-
mobileContainer: {
|
|
34
|
-
flexWrap: "nowrap",
|
|
35
|
-
justifyContent: "flex-start",
|
|
36
|
-
transitionProperty: "transform",
|
|
37
|
-
transitionDuration: "300ms",
|
|
38
|
-
transitionTimingFunction: "ease-in-out",
|
|
39
|
-
},
|
|
40
|
-
item: {
|
|
41
|
-
flexGrow: 0,
|
|
42
|
-
flexBasis: "33.333%",
|
|
43
|
-
maxWidth: 300,
|
|
44
|
-
padding: spacings[12],
|
|
45
|
-
},
|
|
46
|
-
itemAnimation: {
|
|
47
|
-
transform: "translateZ(0px)",
|
|
48
|
-
animationKeyframes: {
|
|
49
|
-
from: {
|
|
50
|
-
opacity: 0,
|
|
51
|
-
transform: "translateZ(0px) translateX(50px)",
|
|
52
|
-
},
|
|
53
|
-
to: {
|
|
54
|
-
opacity: 1,
|
|
55
|
-
transform: "translateZ(0px) translateX(0px)",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
animationDuration: "200ms",
|
|
59
|
-
animationFillMode: "backwards",
|
|
60
|
-
animationTimingFunction: "ease-in-out",
|
|
61
|
-
},
|
|
62
|
-
itemLarge: {
|
|
63
|
-
flexBasis: "50%",
|
|
64
|
-
maxWidth: "none",
|
|
65
|
-
},
|
|
66
|
-
itemSmallViewport: {
|
|
67
|
-
width: "100%",
|
|
68
|
-
flexBasis: "auto",
|
|
69
|
-
maxWidth: "none",
|
|
70
|
-
scrollSnapAlign: "center",
|
|
71
|
-
},
|
|
72
|
-
tileContents: {
|
|
73
|
-
alignItems: "center",
|
|
74
|
-
alignSelf: "stretch",
|
|
75
|
-
flexGrow: 1,
|
|
76
|
-
},
|
|
77
|
-
tileRenderedContents: {
|
|
78
|
-
alignItems: "center",
|
|
79
|
-
alignSelf: "stretch",
|
|
80
|
-
flexGrow: 1,
|
|
81
|
-
},
|
|
82
|
-
leftButton: {
|
|
83
|
-
position: "absolute",
|
|
84
|
-
top: "50%",
|
|
85
|
-
left: negativeSpacings[24],
|
|
86
|
-
transform: "translateY(-50%)",
|
|
87
|
-
borderTopLeftRadius: 0,
|
|
88
|
-
borderBottomLeftRadius: 0,
|
|
89
|
-
borderWidth: 1,
|
|
90
|
-
borderLeftWidth: 0,
|
|
91
|
-
},
|
|
92
|
-
rightButton: {
|
|
93
|
-
position: "absolute",
|
|
94
|
-
top: "50%",
|
|
95
|
-
right: negativeSpacings[24],
|
|
96
|
-
transform: "translateY(-50%)",
|
|
97
|
-
borderTopRightRadius: 0,
|
|
98
|
-
borderBottomRightRadius: 0,
|
|
99
|
-
borderWidth: 1,
|
|
100
|
-
borderRightWidth: 0,
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
const identity = (x) => x;
|
|
104
|
-
export const ChoicePicker = ({ items, getId = identity, large = false, renderItem, value, disabled = false, onChange, }) => {
|
|
105
|
-
const containerRef = useRef(null);
|
|
106
|
-
const { desktop } = useResponsive(breakpoints.medium);
|
|
107
|
-
const [mobilePosition, setMobilePosition] = useState("start");
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
if (desktop) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// auto scroll to selected value on mobile
|
|
113
|
-
const scrollContainer = containerRef.current;
|
|
114
|
-
const index = items.findIndex(item => value === item);
|
|
115
|
-
if (index !== -1 && scrollContainer instanceof HTMLDivElement) {
|
|
116
|
-
const width = scrollContainer.offsetWidth;
|
|
117
|
-
scrollContainer.scrollTo({ x: index * width, animated: false });
|
|
118
|
-
}
|
|
119
|
-
// if no value is selected, select first item
|
|
120
|
-
if (value == null && items[0] != null) {
|
|
121
|
-
onChange(items[0]);
|
|
122
|
-
}
|
|
123
|
-
// disable exhaustive-deps because we only want to run this effect only when screen size go from desktop to mobile
|
|
124
|
-
}, [desktop]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
125
|
-
const onScroll = () => {
|
|
126
|
-
// prevent scroll event when we change screen size from mobile to desktop
|
|
127
|
-
if (desktop) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const scrollContainer = containerRef.current;
|
|
131
|
-
if (scrollContainer instanceof HTMLDivElement) {
|
|
132
|
-
const scrollLeft = scrollContainer.scrollLeft;
|
|
133
|
-
const width = scrollContainer.offsetWidth;
|
|
134
|
-
const index = clampValue(0, items.length - 1)(Math.round(scrollLeft / width));
|
|
135
|
-
const item = items[index];
|
|
136
|
-
if (item != null) {
|
|
137
|
-
onChange(item);
|
|
138
|
-
}
|
|
139
|
-
match(index)
|
|
140
|
-
.with(0, () => setMobilePosition("start"))
|
|
141
|
-
.with(items.length - 1, () => setMobilePosition("end"))
|
|
142
|
-
.otherwise(() => setMobilePosition("middle"));
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const onPressPrevious = () => {
|
|
146
|
-
const scrollContainer = containerRef.current;
|
|
147
|
-
if (scrollContainer instanceof HTMLDivElement) {
|
|
148
|
-
const scrollLeft = scrollContainer.scrollLeft;
|
|
149
|
-
const width = scrollContainer.offsetWidth;
|
|
150
|
-
const index = Math.round(scrollLeft / width);
|
|
151
|
-
const previousIndex = Math.max(0, index - 1);
|
|
152
|
-
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
153
|
-
scrollContainer.style.scrollSnapType = "none";
|
|
154
|
-
containerRef.current?.scrollTo({ x: previousIndex * width, animated: true });
|
|
155
|
-
detectScrollAnimationEnd(scrollContainer).onResolve(() => {
|
|
156
|
-
// set back scroll snap
|
|
157
|
-
// @ts-expect-error
|
|
158
|
-
scrollContainer.style.scrollSnapType = null;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
const onPressNext = () => {
|
|
163
|
-
const scrollContainer = containerRef.current;
|
|
164
|
-
if (scrollContainer instanceof HTMLDivElement) {
|
|
165
|
-
const scrollLeft = scrollContainer.scrollLeft;
|
|
166
|
-
const width = scrollContainer.offsetWidth;
|
|
167
|
-
const index = Math.round(scrollLeft / width);
|
|
168
|
-
const nextIndex = Math.min(items.length - 1, index + 1);
|
|
169
|
-
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
170
|
-
scrollContainer.style.scrollSnapType = "none";
|
|
171
|
-
containerRef.current?.scrollTo({ x: nextIndex * width, animated: true });
|
|
172
|
-
detectScrollAnimationEnd(scrollContainer).onResolve(() => {
|
|
173
|
-
// set back scroll snap
|
|
174
|
-
// @ts-expect-error
|
|
175
|
-
scrollContainer.style.scrollSnapType = null;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
return (_jsxs(View, { children: [_jsx(View, { style: styles.root, children: _jsx(ScrollView, { ref: containerRef, horizontal: !desktop, onScroll: onScroll, scrollEventThrottle: 200, style: styles.scrollSnap, contentContainerStyle: [
|
|
180
|
-
styles.container,
|
|
181
|
-
!desktop && styles.mobileContainer,
|
|
182
|
-
!desktop && { width: `${items.length * 100}%` },
|
|
183
|
-
], children: items.map((item, index) => (_jsx(Pressable, { disabled: disabled, style: [
|
|
184
|
-
styles.item,
|
|
185
|
-
disabled && commonStyles.disabled,
|
|
186
|
-
desktop && styles.itemAnimation,
|
|
187
|
-
desktop && { animationDelay: `${200 + 100 * index}ms` },
|
|
188
|
-
large && styles.itemLarge,
|
|
189
|
-
!desktop && styles.itemSmallViewport,
|
|
190
|
-
!desktop && { width: `${100 / items.length}%` },
|
|
191
|
-
], onPress: () => onChange(item), children: ({ hovered }) => (_jsx(Tile, { hovered: hovered, selected: value != null && getId(item) === getId(value), flexGrow: 1, children: _jsxs(View, { style: styles.tileContents, children: [_jsx(View, { style: styles.tileRenderedContents, children: renderItem(item) }), desktop && (_jsxs(_Fragment, { children: [_jsx(Space, { height: 24 }), _jsx(LakeRadio, { value: value != null && getId(item) === getId(value) })] }))] }) })) }, String(index)))) }) }), !desktop && (_jsx(LakeButton, { icon: "chevron-left-filled", mode: "secondary", forceBackground: true, onPress: onPressPrevious, disabled: mobilePosition === "start" || disabled, style: styles.leftButton })), !desktop && (_jsx(LakeButton, { icon: "chevron-right-filled", mode: "secondary", forceBackground: true, onPress: onPressNext, disabled: mobilePosition === "end" || disabled, style: styles.rightButton }))] }));
|
|
192
|
-
};
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { Option } from "@swan-io/boxed";
|
|
2
|
-
import { ValidatorResult } from "react-ux-form";
|
|
3
|
-
import { Except } from "type-fest";
|
|
4
|
-
export type MonthNames = readonly [
|
|
5
|
-
string,
|
|
6
|
-
string,
|
|
7
|
-
string,
|
|
8
|
-
string,
|
|
9
|
-
string,
|
|
10
|
-
string,
|
|
11
|
-
string,
|
|
12
|
-
string,
|
|
13
|
-
string,
|
|
14
|
-
string,
|
|
15
|
-
string,
|
|
16
|
-
string
|
|
17
|
-
];
|
|
18
|
-
export type WeekDayNames = readonly [string, string, string, string, string, string, string];
|
|
19
|
-
declare const weekDayIndex: {
|
|
20
|
-
sunday: number;
|
|
21
|
-
monday: number;
|
|
22
|
-
tuesday: number;
|
|
23
|
-
wednesday: number;
|
|
24
|
-
thursday: number;
|
|
25
|
-
friday: number;
|
|
26
|
-
saturday: number;
|
|
27
|
-
};
|
|
28
|
-
export type DatePickerDate = {
|
|
29
|
-
day: number;
|
|
30
|
-
month: number;
|
|
31
|
-
year: number;
|
|
32
|
-
};
|
|
33
|
-
export type DatePickerRange = {
|
|
34
|
-
start: Option<DatePickerDate>;
|
|
35
|
-
end: Option<DatePickerDate>;
|
|
36
|
-
};
|
|
37
|
-
export type DateFormat = "DD/MM/YYYY" | "MM/DD/YYYY";
|
|
38
|
-
export declare const validateDateRangeOrder: (value: {
|
|
39
|
-
start: string;
|
|
40
|
-
end: string;
|
|
41
|
-
}, format: DateFormat) => boolean;
|
|
42
|
-
export declare const isTodayOrFutureDate: (date: DatePickerDate) => boolean;
|
|
43
|
-
export declare const isDateInRange: (minDate: Date, maxDate: Date) => (date: DatePickerDate) => boolean;
|
|
44
|
-
export type DatePickerProps = {
|
|
45
|
-
label: string;
|
|
46
|
-
value?: string;
|
|
47
|
-
error?: string;
|
|
48
|
-
format: DateFormat;
|
|
49
|
-
firstWeekDay: keyof typeof weekDayIndex;
|
|
50
|
-
monthNames: MonthNames;
|
|
51
|
-
weekDayNames: WeekDayNames;
|
|
52
|
-
isSelectable?: (date: DatePickerDate) => boolean;
|
|
53
|
-
onChange: (date: string) => void;
|
|
54
|
-
};
|
|
55
|
-
export declare const DatePicker: ({ label, value, error, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, }: DatePickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
56
|
-
type DatePickerModalProps = Except<DatePickerProps, "error"> & {
|
|
57
|
-
visible: boolean;
|
|
58
|
-
cancelLabel: string;
|
|
59
|
-
confirmLabel: string;
|
|
60
|
-
validate?: (value: string) => ValidatorResult;
|
|
61
|
-
onDissmiss: () => void;
|
|
62
|
-
};
|
|
63
|
-
export declare const DatePickerModal: ({ value, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, visible, label, cancelLabel, confirmLabel, validate, onDissmiss, }: DatePickerModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
64
|
-
export type DateRangePickerProps = {
|
|
65
|
-
value: {
|
|
66
|
-
start: string;
|
|
67
|
-
end: string;
|
|
68
|
-
};
|
|
69
|
-
error?: string;
|
|
70
|
-
format: DateFormat;
|
|
71
|
-
startLabel: string;
|
|
72
|
-
endLabel: string;
|
|
73
|
-
firstWeekDay: keyof typeof weekDayIndex;
|
|
74
|
-
monthNames: MonthNames;
|
|
75
|
-
weekDayNames: WeekDayNames;
|
|
76
|
-
isSelectable?: (date: DatePickerDate) => boolean;
|
|
77
|
-
onChange: (date: {
|
|
78
|
-
start: string;
|
|
79
|
-
end: string;
|
|
80
|
-
}) => void;
|
|
81
|
-
};
|
|
82
|
-
export declare const DateRangePicker: ({ value, error, format, startLabel, endLabel, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, }: DateRangePickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
83
|
-
type DateRangePickerModalProps = DateRangePickerProps & {
|
|
84
|
-
visible: boolean;
|
|
85
|
-
cancelLabel: string;
|
|
86
|
-
confirmLabel: string;
|
|
87
|
-
onDissmiss: () => void;
|
|
88
|
-
};
|
|
89
|
-
export declare const DateRangePickerModal: ({ value, error, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, visible, startLabel, endLabel, cancelLabel, confirmLabel, onDissmiss, }: DateRangePickerModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
90
|
-
export {};
|
|
@@ -1,619 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Option } from "@swan-io/boxed";
|
|
3
|
-
import dayjs from "dayjs";
|
|
4
|
-
import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { StyleSheet, View } from "react-native";
|
|
6
|
-
import { useForm } from "react-ux-form";
|
|
7
|
-
import { Rifm } from "rifm";
|
|
8
|
-
import { P, match } from "ts-pattern";
|
|
9
|
-
import { colors, spacings } from "../constants/design";
|
|
10
|
-
import { useDisclosure } from "../hooks/useDisclosure";
|
|
11
|
-
import { useFirstMountState } from "../hooks/useFirstMountState";
|
|
12
|
-
import { useResponsive } from "../hooks/useResponsive";
|
|
13
|
-
import { noop } from "../utils/function";
|
|
14
|
-
import { isNotNullish, isNotNullishOrEmpty, isNullishOrEmpty } from "../utils/nullish";
|
|
15
|
-
import { getRifmProps } from "../utils/rifm";
|
|
16
|
-
import { BottomPanel } from "./BottomPanel";
|
|
17
|
-
import { Box } from "./Box";
|
|
18
|
-
import { Fill } from "./Fill";
|
|
19
|
-
import { Icon } from "./Icon";
|
|
20
|
-
import { LakeButton } from "./LakeButton";
|
|
21
|
-
import { LakeLabel } from "./LakeLabel";
|
|
22
|
-
import { LakeModal } from "./LakeModal";
|
|
23
|
-
import { LakeSelect } from "./LakeSelect";
|
|
24
|
-
import { LakeText } from "./LakeText";
|
|
25
|
-
import { LakeTextInput } from "./LakeTextInput";
|
|
26
|
-
import { Popover } from "./Popover";
|
|
27
|
-
import { Pressable } from "./Pressable";
|
|
28
|
-
import { Separator } from "./Separator";
|
|
29
|
-
import { Space } from "./Space";
|
|
30
|
-
const styles = StyleSheet.create({
|
|
31
|
-
label: {
|
|
32
|
-
flex: 1,
|
|
33
|
-
},
|
|
34
|
-
arrowContainer: {
|
|
35
|
-
height: 40, // input height
|
|
36
|
-
},
|
|
37
|
-
popover: {
|
|
38
|
-
padding: spacings[12],
|
|
39
|
-
},
|
|
40
|
-
popoverDesktop: {
|
|
41
|
-
padding: spacings[24],
|
|
42
|
-
},
|
|
43
|
-
rangeCalendarSide: {
|
|
44
|
-
flex: 1,
|
|
45
|
-
},
|
|
46
|
-
button: {
|
|
47
|
-
flex: 1,
|
|
48
|
-
},
|
|
49
|
-
monthSelect: {
|
|
50
|
-
width: 130,
|
|
51
|
-
},
|
|
52
|
-
yearSelect: {
|
|
53
|
-
width: 100,
|
|
54
|
-
},
|
|
55
|
-
weekRow: {
|
|
56
|
-
paddingVertical: spacings[4],
|
|
57
|
-
},
|
|
58
|
-
dayName: {
|
|
59
|
-
flex: 1,
|
|
60
|
-
height: 32,
|
|
61
|
-
alignItems: "center",
|
|
62
|
-
justifyContent: "center",
|
|
63
|
-
},
|
|
64
|
-
dayContainer: {
|
|
65
|
-
flex: 1,
|
|
66
|
-
alignItems: "center",
|
|
67
|
-
},
|
|
68
|
-
dayRangeIndicator: {
|
|
69
|
-
position: "absolute",
|
|
70
|
-
top: 0,
|
|
71
|
-
right: 0,
|
|
72
|
-
bottom: 0,
|
|
73
|
-
left: 0,
|
|
74
|
-
backgroundColor: colors.current[100],
|
|
75
|
-
},
|
|
76
|
-
dayStartRangeIndicator: {
|
|
77
|
-
left: "50%",
|
|
78
|
-
},
|
|
79
|
-
dayEndRangeIndicator: {
|
|
80
|
-
right: "50%",
|
|
81
|
-
},
|
|
82
|
-
dayNumber: {
|
|
83
|
-
width: 32,
|
|
84
|
-
height: 32,
|
|
85
|
-
alignItems: "center",
|
|
86
|
-
justifyContent: "center",
|
|
87
|
-
borderRadius: 16,
|
|
88
|
-
},
|
|
89
|
-
dayNumberFocused: {},
|
|
90
|
-
dayNumberHover: {
|
|
91
|
-
backgroundColor: colors.current[100],
|
|
92
|
-
},
|
|
93
|
-
dayNumberPressed: {},
|
|
94
|
-
dayNumberSelected: {
|
|
95
|
-
backgroundColor: colors.current[500],
|
|
96
|
-
},
|
|
97
|
-
todayIndicator: {
|
|
98
|
-
position: "absolute",
|
|
99
|
-
left: 0,
|
|
100
|
-
right: 0,
|
|
101
|
-
bottom: 0,
|
|
102
|
-
width: 4,
|
|
103
|
-
height: 4,
|
|
104
|
-
marginHorizontal: "auto",
|
|
105
|
-
borderRadius: 2,
|
|
106
|
-
backgroundColor: colors.current[500],
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
const MODALE_MOBILE_THRESHOLD = 600;
|
|
110
|
-
const DATE_PICKER_MOBILE_THRESHOLD = 400;
|
|
111
|
-
const DATE_RANGE_PICKER_THRESHOLD = 800;
|
|
112
|
-
const NB_DAYS_IN_WEEK = 7;
|
|
113
|
-
const weekDayIndex = {
|
|
114
|
-
sunday: 0,
|
|
115
|
-
monday: 1,
|
|
116
|
-
tuesday: 2,
|
|
117
|
-
wednesday: 3,
|
|
118
|
-
thursday: 4,
|
|
119
|
-
friday: 5,
|
|
120
|
-
saturday: 6,
|
|
121
|
-
};
|
|
122
|
-
const rifmDateProps = getRifmProps({
|
|
123
|
-
accept: "numeric",
|
|
124
|
-
charMap: { 2: "/", 4: "/" },
|
|
125
|
-
maxLength: 8,
|
|
126
|
-
});
|
|
127
|
-
const parseDate = (value, format) => {
|
|
128
|
-
const date = dayjs.utc(value, format);
|
|
129
|
-
return date.isValid()
|
|
130
|
-
? Option.Some({ day: date.date(), month: date.month(), year: date.year() })
|
|
131
|
-
: Option.None();
|
|
132
|
-
};
|
|
133
|
-
const parseRange = (value, format) => {
|
|
134
|
-
return {
|
|
135
|
-
start: parseDate(value.start, format),
|
|
136
|
-
end: parseDate(value.end, format),
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
const stringifyDate = (value, format) => {
|
|
140
|
-
const date = dayjs.utc().year(value.year).month(value.month).date(value.day);
|
|
141
|
-
return date.format(format);
|
|
142
|
-
};
|
|
143
|
-
export const validateDateRangeOrder = (value, format) => {
|
|
144
|
-
const range = parseRange(value, format);
|
|
145
|
-
if (range.start.isNone() || range.end.isNone()) {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
if (isDateAfter(range.start.value, range.end.value)) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
return true;
|
|
152
|
-
};
|
|
153
|
-
const range = (start, end) => {
|
|
154
|
-
const result = [];
|
|
155
|
-
for (let i = start; i <= end; i++) {
|
|
156
|
-
result.push(i);
|
|
157
|
-
}
|
|
158
|
-
return result;
|
|
159
|
-
};
|
|
160
|
-
const groupEvery = (input, groupSize) => {
|
|
161
|
-
const result = [];
|
|
162
|
-
const nbGroups = Math.ceil(input.length / groupSize);
|
|
163
|
-
for (let i = 0; i < nbGroups; i++) {
|
|
164
|
-
result.push(input.slice(i * groupSize, (i + 1) * groupSize));
|
|
165
|
-
}
|
|
166
|
-
return result;
|
|
167
|
-
};
|
|
168
|
-
const padEnd = (input, length, value) => {
|
|
169
|
-
const itemsToAppend = new Array(length - input.length).fill(value);
|
|
170
|
-
return [...input, ...itemsToAppend];
|
|
171
|
-
};
|
|
172
|
-
export const isTodayOrFutureDate = (date) => {
|
|
173
|
-
const yesterday = new Date();
|
|
174
|
-
yesterday.setDate(yesterday.getDate() - 1);
|
|
175
|
-
const yesterdayDate = {
|
|
176
|
-
day: yesterday.getDate(),
|
|
177
|
-
month: yesterday.getMonth(),
|
|
178
|
-
year: yesterday.getFullYear(),
|
|
179
|
-
};
|
|
180
|
-
return isDateAfter(date, yesterdayDate);
|
|
181
|
-
};
|
|
182
|
-
export const isDateInRange = (minDate, maxDate) => (date) => {
|
|
183
|
-
const min = {
|
|
184
|
-
day: minDate.getDate(),
|
|
185
|
-
month: minDate.getMonth(),
|
|
186
|
-
year: minDate.getFullYear(),
|
|
187
|
-
};
|
|
188
|
-
const max = {
|
|
189
|
-
day: maxDate.getDate(),
|
|
190
|
-
month: maxDate.getMonth(),
|
|
191
|
-
year: maxDate.getFullYear(),
|
|
192
|
-
};
|
|
193
|
-
return isDateAfter(date, min) && isDateBefore(date, max);
|
|
194
|
-
};
|
|
195
|
-
const isDateToday = (date) => {
|
|
196
|
-
const today = new Date();
|
|
197
|
-
return (date.day === today.getDate() &&
|
|
198
|
-
date.month === today.getMonth() &&
|
|
199
|
-
date.year === today.getFullYear());
|
|
200
|
-
};
|
|
201
|
-
const getMonthDates = (month, year) => {
|
|
202
|
-
const aggregate = (acc, date) => {
|
|
203
|
-
const dateDay = date.getDate();
|
|
204
|
-
const dateMonth = date.getMonth();
|
|
205
|
-
const dateYear = date.getFullYear();
|
|
206
|
-
if (date.getMonth() !== month) {
|
|
207
|
-
return acc;
|
|
208
|
-
}
|
|
209
|
-
return aggregate([...acc, { day: dateDay, month: dateMonth, year: dateYear }], new Date(year, month, dateDay + 1));
|
|
210
|
-
};
|
|
211
|
-
return aggregate([], new Date(year, month, 1));
|
|
212
|
-
};
|
|
213
|
-
const getMonthWeeks = (month, year, firstWeekDay) => {
|
|
214
|
-
const firstWeekDayIndex = weekDayIndex[firstWeekDay];
|
|
215
|
-
const monthFirstWeekDay = new Date(year, month, 1).getDay();
|
|
216
|
-
const monthDates = getMonthDates(month, year).map(date => Option.Some(date));
|
|
217
|
-
const nbDaysToPrepend = monthFirstWeekDay >= firstWeekDayIndex
|
|
218
|
-
? monthFirstWeekDay - firstWeekDayIndex
|
|
219
|
-
: NB_DAYS_IN_WEEK - firstWeekDayIndex + monthFirstWeekDay;
|
|
220
|
-
for (let i = 0; i < nbDaysToPrepend; i++) {
|
|
221
|
-
monthDates.unshift(Option.None());
|
|
222
|
-
}
|
|
223
|
-
const weeks = groupEvery(monthDates, NB_DAYS_IN_WEEK);
|
|
224
|
-
const lastWeek = weeks[weeks.length - 1];
|
|
225
|
-
if (!lastWeek) {
|
|
226
|
-
return weeks;
|
|
227
|
-
}
|
|
228
|
-
weeks[weeks.length - 1] = padEnd(lastWeek, NB_DAYS_IN_WEEK, Option.None());
|
|
229
|
-
return weeks;
|
|
230
|
-
};
|
|
231
|
-
const getWeekDayNames = (dayNames, firstWeekDay = "sunday") => {
|
|
232
|
-
const firstWeekDayIndex = weekDayIndex[firstWeekDay];
|
|
233
|
-
const firstWeekDayNames = dayNames.slice(firstWeekDayIndex);
|
|
234
|
-
const lastWeekDayNames = dayNames.slice(0, firstWeekDayIndex);
|
|
235
|
-
// @ts-expect-error
|
|
236
|
-
return [...firstWeekDayNames, ...lastWeekDayNames];
|
|
237
|
-
};
|
|
238
|
-
const isDateEquals = (date1, date2) => {
|
|
239
|
-
return date1.day === date2.day && date1.month === date2.month && date1.year === date2.year;
|
|
240
|
-
};
|
|
241
|
-
const isDateBefore = (date1, date2) => {
|
|
242
|
-
return (date1.year < date2.year ||
|
|
243
|
-
(date1.year === date2.year && date1.month < date2.month) ||
|
|
244
|
-
(date1.year === date2.year && date1.month === date2.month && date1.day < date2.day));
|
|
245
|
-
};
|
|
246
|
-
const isDateAfter = (date1, date2) => {
|
|
247
|
-
return (date1.year > date2.year ||
|
|
248
|
-
(date1.year === date2.year && date1.month > date2.month) ||
|
|
249
|
-
(date1.year === date2.year && date1.month === date2.month && date1.day > date2.day));
|
|
250
|
-
};
|
|
251
|
-
const isDateRange = (value) => {
|
|
252
|
-
return match(value)
|
|
253
|
-
.with({ start: P._, end: P._ }, () => true)
|
|
254
|
-
.otherwise(() => false);
|
|
255
|
-
};
|
|
256
|
-
const isSelectedDate = (date, value) => {
|
|
257
|
-
return match(value)
|
|
258
|
-
.with(Option.pattern.Some(P.select()), value => isDateEquals(value, date))
|
|
259
|
-
.with(Option.pattern.None, () => false)
|
|
260
|
-
.with(P.when(isDateRange), ({ start, end }) => {
|
|
261
|
-
// if range is invalid, we don't display any selection
|
|
262
|
-
if (start.isSome() && end.isSome() && isDateAfter(start.value, end.value)) {
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
return (start.match({
|
|
266
|
-
Some: start => isDateEquals(start, date),
|
|
267
|
-
None: () => false,
|
|
268
|
-
}) ||
|
|
269
|
-
end.match({
|
|
270
|
-
Some: end => isDateEquals(end, date),
|
|
271
|
-
None: () => false,
|
|
272
|
-
}));
|
|
273
|
-
})
|
|
274
|
-
.exhaustive();
|
|
275
|
-
};
|
|
276
|
-
const getRangeIndicatorType = (date, value) => {
|
|
277
|
-
if (!isDateRange(value)) {
|
|
278
|
-
return "none";
|
|
279
|
-
}
|
|
280
|
-
const { start, end } = value;
|
|
281
|
-
if (start.isNone() || end.isNone()) {
|
|
282
|
-
return "none";
|
|
283
|
-
}
|
|
284
|
-
const startDate = start.value;
|
|
285
|
-
const endDate = end.value;
|
|
286
|
-
// no interval indicator if range is invalid
|
|
287
|
-
if (isDateAfter(startDate, endDate)) {
|
|
288
|
-
return "none";
|
|
289
|
-
}
|
|
290
|
-
if (isDateEquals(startDate, endDate)) {
|
|
291
|
-
return "none";
|
|
292
|
-
}
|
|
293
|
-
if (isDateEquals(date, startDate)) {
|
|
294
|
-
return "start";
|
|
295
|
-
}
|
|
296
|
-
if (isDateEquals(date, endDate)) {
|
|
297
|
-
return "end";
|
|
298
|
-
}
|
|
299
|
-
if (isDateAfter(date, startDate) && isDateBefore(date, endDate)) {
|
|
300
|
-
return "between";
|
|
301
|
-
}
|
|
302
|
-
return "none";
|
|
303
|
-
};
|
|
304
|
-
const computeDateDistanceInDays = (date1, date2) => {
|
|
305
|
-
const date1Date = new Date(date1.year, date1.month, date1.day);
|
|
306
|
-
const date2Date = new Date(date2.year, date2.month, date2.day);
|
|
307
|
-
const diffInMs = Math.abs(date2Date.getTime() - date1Date.getTime());
|
|
308
|
-
return Math.round(diffInMs / (1000 * 3600 * 24));
|
|
309
|
-
};
|
|
310
|
-
const getNewDateRange = (currentRange, selectedDate) => {
|
|
311
|
-
const { start, end } = currentRange;
|
|
312
|
-
// Handle initial selection
|
|
313
|
-
if (start.isNone()) {
|
|
314
|
-
return { start: Option.Some(selectedDate), end: Option.None() };
|
|
315
|
-
}
|
|
316
|
-
if (end.isNone()) {
|
|
317
|
-
if (isDateAfter(selectedDate, start.value)) {
|
|
318
|
-
return { start, end: Option.Some(selectedDate) };
|
|
319
|
-
}
|
|
320
|
-
return { start: Option.Some(selectedDate), end: currentRange.start };
|
|
321
|
-
}
|
|
322
|
-
// Handle selection outside of the current range
|
|
323
|
-
if (isDateBefore(selectedDate, start.value)) {
|
|
324
|
-
return { start: Option.Some(selectedDate), end: currentRange.end };
|
|
325
|
-
}
|
|
326
|
-
if (isDateAfter(selectedDate, end.value)) {
|
|
327
|
-
return { start: currentRange.start, end: Option.Some(selectedDate) };
|
|
328
|
-
}
|
|
329
|
-
// We change the closest date to the new date
|
|
330
|
-
const startDistance = computeDateDistanceInDays(start.value, selectedDate);
|
|
331
|
-
const endDistance = computeDateDistanceInDays(end.value, selectedDate);
|
|
332
|
-
if (startDistance < endDistance) {
|
|
333
|
-
return { start: Option.Some(selectedDate), end: currentRange.end };
|
|
334
|
-
}
|
|
335
|
-
return { start: currentRange.start, end: Option.Some(selectedDate) };
|
|
336
|
-
};
|
|
337
|
-
const getTodayYearMonth = () => ({
|
|
338
|
-
month: new Date().getMonth(),
|
|
339
|
-
year: new Date().getFullYear(),
|
|
340
|
-
});
|
|
341
|
-
const getYearMonth = (value, format) => {
|
|
342
|
-
if (isNullishOrEmpty(value)) {
|
|
343
|
-
return Option.None();
|
|
344
|
-
}
|
|
345
|
-
return parseDate(value, format);
|
|
346
|
-
};
|
|
347
|
-
const isYearMonthBefore = (date1, date2) => {
|
|
348
|
-
return date1.year < date2.year || (date1.year === date2.year && date1.month < date2.month);
|
|
349
|
-
};
|
|
350
|
-
const isYearMonthEquals = (date1, date2) => {
|
|
351
|
-
return date1.year === date2.year && date1.month === date2.month;
|
|
352
|
-
};
|
|
353
|
-
const minYearMonth = (date1, date2) => {
|
|
354
|
-
return isYearMonthBefore(date1, date2) ? date1 : date2;
|
|
355
|
-
};
|
|
356
|
-
const maxYearMonth = (date1, date2) => {
|
|
357
|
-
return isYearMonthBefore(date1, date2) ? date2 : date1;
|
|
358
|
-
};
|
|
359
|
-
const incrementYearMonth = ({ month, year }) => {
|
|
360
|
-
if (month === 11) {
|
|
361
|
-
return { month: 0, year: year + 1 };
|
|
362
|
-
}
|
|
363
|
-
return { month: month + 1, year };
|
|
364
|
-
};
|
|
365
|
-
const decrementYearMonth = ({ month, year }) => {
|
|
366
|
-
if (month === 0) {
|
|
367
|
-
return { month: 11, year: year - 1 };
|
|
368
|
-
}
|
|
369
|
-
return { month: month - 1, year };
|
|
370
|
-
};
|
|
371
|
-
const YearMonthSelect = ({ monthNames, value, arrowsPosition = "right", hideArrows, minValue, maxValue, onChange, }) => {
|
|
372
|
-
const monthItems = useMemo(() => monthNames.map((name, index) => ({ name, value: index })), [monthNames]);
|
|
373
|
-
const yearItems = useMemo(() => range(value.year - 5, value.year + 5).map(year => ({
|
|
374
|
-
name: year.toString(),
|
|
375
|
-
value: year,
|
|
376
|
-
})), [value.year]);
|
|
377
|
-
const selectMonth = (month) => {
|
|
378
|
-
onChange({ year: value.year, month });
|
|
379
|
-
};
|
|
380
|
-
const selectYear = (year) => {
|
|
381
|
-
onChange({ year, month: value.month });
|
|
382
|
-
};
|
|
383
|
-
const setPreviousMonth = () => {
|
|
384
|
-
onChange(decrementYearMonth(value));
|
|
385
|
-
};
|
|
386
|
-
const setNextMonth = () => {
|
|
387
|
-
onChange(incrementYearMonth(value));
|
|
388
|
-
};
|
|
389
|
-
const isPreviousDisabled = !minValue
|
|
390
|
-
? false
|
|
391
|
-
: value.year <= minValue.year && value.month <= minValue.month;
|
|
392
|
-
const isNextDisabled = !maxValue
|
|
393
|
-
? false
|
|
394
|
-
: value.year >= maxValue.year && value.month >= maxValue.month;
|
|
395
|
-
return (_jsxs(Box, { direction: "row", alignItems: "center", children: [arrowsPosition === "around" && hideArrows !== true && (_jsxs(_Fragment, { children: [_jsx(LakeButton, { size: "small", mode: "tertiary", icon: "arrow-left-filled", disabled: isPreviousDisabled, onPress: setPreviousMonth }), _jsx(Fill, { minWidth: 12 })] })), _jsx(LakeSelect, { items: monthItems, value: value.month, onValueChange: selectMonth, mode: "borderless", size: "small", hideErrors: true, style: styles.monthSelect }), _jsx(LakeSelect, { items: yearItems, value: value.year, onValueChange: selectYear, mode: "borderless", size: "small", hideErrors: true, style: styles.yearSelect }), hideArrows !== true && (_jsxs(_Fragment, { children: [_jsx(Fill, { minWidth: 12 }), arrowsPosition === "right" && (_jsxs(_Fragment, { children: [_jsx(LakeButton, { size: "small", mode: "tertiary", icon: "arrow-left-filled", disabled: isPreviousDisabled, onPress: setPreviousMonth }), _jsx(Space, { width: 12 })] })), _jsx(LakeButton, { size: "small", mode: "tertiary", icon: "arrow-right-filled", disabled: isNextDisabled, onPress: setNextMonth })] }))] }));
|
|
396
|
-
};
|
|
397
|
-
const MonthCalendar = ({ month, year, value, firstWeekDay, weekDayNames, isSelectable, onChange, }) => {
|
|
398
|
-
const dayNames = useMemo(() => getWeekDayNames(weekDayNames, firstWeekDay), [weekDayNames, firstWeekDay]);
|
|
399
|
-
const weeks = useMemo(() => getMonthWeeks(month, year, firstWeekDay), [month, year, firstWeekDay]);
|
|
400
|
-
return (_jsxs(View, { children: [_jsx(Box, { direction: "row", alignItems: "center", style: styles.weekRow, children: dayNames.map(dayName => (_jsx(View, { style: styles.dayName, children: _jsx(LakeText, { variant: "medium", color: colors.gray[600], children: dayName.substring(0, 1) }) }, dayName))) }), weeks.map((week, weekIndex) => (_jsx(Box, { direction: "row", alignItems: "center", style: styles.weekRow, children: week.map((date, dateIndex) => {
|
|
401
|
-
const isDisabled = date.match({
|
|
402
|
-
Some: date => isNotNullish(isSelectable) && !isSelectable(date),
|
|
403
|
-
None: () => true,
|
|
404
|
-
});
|
|
405
|
-
const isSelected = date.match({
|
|
406
|
-
Some: date => isSelectedDate(date, value),
|
|
407
|
-
None: () => false,
|
|
408
|
-
});
|
|
409
|
-
const isToday = date.match({
|
|
410
|
-
Some: date => isDateToday(date),
|
|
411
|
-
None: () => false,
|
|
412
|
-
});
|
|
413
|
-
const rangeIndicator = date.match({
|
|
414
|
-
Some: date => getRangeIndicatorType(date, value),
|
|
415
|
-
None: () => "none",
|
|
416
|
-
});
|
|
417
|
-
return (_jsxs(View, { style: styles.dayContainer, children: [rangeIndicator !== "none" && (_jsx(View, { style: [
|
|
418
|
-
styles.dayRangeIndicator,
|
|
419
|
-
rangeIndicator === "start" && styles.dayStartRangeIndicator,
|
|
420
|
-
rangeIndicator === "end" && styles.dayEndRangeIndicator,
|
|
421
|
-
] })), _jsxs(Pressable, { disabled: isDisabled, onPress: () => date.match({ Some: onChange, None: noop }), style: ({ focused, hovered, pressed }) => [
|
|
422
|
-
styles.dayNumber,
|
|
423
|
-
focused && styles.dayNumberFocused,
|
|
424
|
-
hovered && styles.dayNumberHover,
|
|
425
|
-
pressed && styles.dayNumberPressed,
|
|
426
|
-
isSelected && styles.dayNumberSelected,
|
|
427
|
-
], children: [_jsx(LakeText, { variant: "smallRegular", color: isSelected
|
|
428
|
-
? colors.current.contrast
|
|
429
|
-
: isDisabled
|
|
430
|
-
? colors.gray[300]
|
|
431
|
-
: isToday
|
|
432
|
-
? colors.current[500]
|
|
433
|
-
: colors.gray[900], children: date.match({ Some: ({ day }) => day.toString(), None: () => " " }) }), isToday && _jsx(View, { style: styles.todayIndicator })] })] }, dateIndex));
|
|
434
|
-
}) }, weekIndex)))] }));
|
|
435
|
-
};
|
|
436
|
-
const DatePickerPopoverContent = ({ value, format, firstWeekDay, monthNames, weekDayNames, desktop, isSelectable, onChange, }) => {
|
|
437
|
-
const [monthYear, setMonthYear] = useState(() => getYearMonth(value, format).getWithDefault(getTodayYearMonth()));
|
|
438
|
-
// Automatically change displayed year and month when user change the value with text input
|
|
439
|
-
useEffect(() => {
|
|
440
|
-
const yearMonth = getYearMonth(value, format);
|
|
441
|
-
if (yearMonth.isSome()) {
|
|
442
|
-
setMonthYear(yearMonth.value);
|
|
443
|
-
}
|
|
444
|
-
}, [value, format]);
|
|
445
|
-
const handleChange = useCallback((date) => {
|
|
446
|
-
const formatted = stringifyDate(date, format);
|
|
447
|
-
onChange(formatted);
|
|
448
|
-
}, [format, onChange]);
|
|
449
|
-
return (_jsxs(_Fragment, { children: [_jsx(YearMonthSelect, { monthNames: monthNames, value: monthYear, hideArrows: !desktop, onChange: setMonthYear }), _jsx(Space, { height: 24 }), _jsx(MonthCalendar, { month: monthYear.month, year: monthYear.year, value: isNotNullishOrEmpty(value) ? parseDate(value, format) : Option.None(), firstWeekDay: firstWeekDay, weekDayNames: weekDayNames, isSelectable: isSelectable, onChange: handleChange })] }));
|
|
450
|
-
};
|
|
451
|
-
export const DatePicker = ({ label, value, error, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, }) => {
|
|
452
|
-
const { desktop } = useResponsive(DATE_PICKER_MOBILE_THRESHOLD);
|
|
453
|
-
const ref = useRef(null);
|
|
454
|
-
const [isOpened, { open, close }] = useDisclosure(false);
|
|
455
|
-
const popoverId = useId();
|
|
456
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", alignItems: "end", children: _jsx(LakeLabel, { label: label, style: styles.label, actions: _jsx(LakeButton, { mode: "secondary", icon: "calendar-ltr-regular", size: "small", onPress: open }), render: id => (_jsx(Rifm, { value: value ?? "", onChange: onChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { ref: ref, id: id, placeholder: format, value: value, error: error, onChange: onChange, ariaExpanded: isOpened })) })) }) }), _jsx(Popover, { id: popoverId, role: "dialog", onDismiss: close, referenceRef: ref, visible: isOpened, children: _jsx(View, { style: desktop ? styles.popoverDesktop : styles.popover, children: _jsx(DatePickerPopoverContent, { value: value, format: format, firstWeekDay: firstWeekDay, monthNames: monthNames, weekDayNames: weekDayNames, desktop: desktop, isSelectable: isSelectable, onChange: onChange }) }) })] }));
|
|
457
|
-
};
|
|
458
|
-
export const DatePickerModal = ({ value, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, visible, label, cancelLabel, confirmLabel, validate, onDissmiss, }) => {
|
|
459
|
-
const { desktop } = useResponsive(DATE_PICKER_MOBILE_THRESHOLD);
|
|
460
|
-
const { Field, submitForm, setFieldValue, resetField } = useForm({
|
|
461
|
-
date: {
|
|
462
|
-
initialValue: value ?? "",
|
|
463
|
-
validate,
|
|
464
|
-
},
|
|
465
|
-
});
|
|
466
|
-
const handleCancel = () => {
|
|
467
|
-
setFieldValue("date", value ?? "");
|
|
468
|
-
onDissmiss();
|
|
469
|
-
};
|
|
470
|
-
const handleConfirm = () => {
|
|
471
|
-
submitForm(({ date }) => {
|
|
472
|
-
if (isNotNullishOrEmpty(date)) {
|
|
473
|
-
onChange(date);
|
|
474
|
-
}
|
|
475
|
-
onDissmiss();
|
|
476
|
-
});
|
|
477
|
-
};
|
|
478
|
-
useEffect(() => {
|
|
479
|
-
if (!visible) {
|
|
480
|
-
resetField("date");
|
|
481
|
-
}
|
|
482
|
-
}, [visible, resetField]);
|
|
483
|
-
return (_jsxs(DateModal, { visible: visible, maxWidth: 500, onPressClose: handleCancel, children: [_jsx(Field, { name: "date", children: ({ ref, value, error, onBlur, onChange }) => (_jsxs(_Fragment, { children: [_jsx(LakeLabel, { label: label, render: id => (_jsx(Rifm, { value: value, onChange: onChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { ref: ref, id: id, placeholder: format, value: value, error: error, onBlur: onBlur, onChange: onChange })) })) }), _jsx(DatePickerPopoverContent, { value: value, format: format, firstWeekDay: firstWeekDay, monthNames: monthNames, weekDayNames: weekDayNames, desktop: desktop, isSelectable: isSelectable, onChange: onChange })] })) }), _jsx(Space, { height: 24 }), _jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeButton, { mode: "secondary", size: "small", onPress: handleCancel, style: styles.button, children: cancelLabel }), _jsx(Space, { width: 24 }), _jsx(LakeButton, { color: "current", size: "small", onPress: handleConfirm, style: styles.button, children: confirmLabel })] })] }));
|
|
484
|
-
};
|
|
485
|
-
const DateModal = ({ children, visible, maxWidth, withCloseButton, onPressClose, }) => {
|
|
486
|
-
const { desktop } = useResponsive(MODALE_MOBILE_THRESHOLD);
|
|
487
|
-
if (desktop) {
|
|
488
|
-
return (_jsx(LakeModal, { visible: visible, maxWidth: maxWidth, onPressClose: withCloseButton === true ? onPressClose : undefined, children: children }));
|
|
489
|
-
}
|
|
490
|
-
return (_jsx(BottomPanel, { visible: visible, onPressClose: onPressClose, children: _jsx(View, { style: styles.popover, children: children }) }));
|
|
491
|
-
};
|
|
492
|
-
const DateRangePickerModalContent = ({ value, format, firstWeekDay, monthNames, weekDayNames, desktop, displayTwoCalendar, isSelectable, onChange, }) => {
|
|
493
|
-
const isFirstMount = useFirstMountState();
|
|
494
|
-
const [periods, setPeriods] = useState(() => {
|
|
495
|
-
const startYearMonth = getYearMonth(value.start, format).getWithDefault(getTodayYearMonth());
|
|
496
|
-
const endYearMonth = getYearMonth(value.end, format).getWithDefault(incrementYearMonth(startYearMonth));
|
|
497
|
-
return {
|
|
498
|
-
start: startYearMonth,
|
|
499
|
-
end: isYearMonthEquals(startYearMonth, endYearMonth)
|
|
500
|
-
? incrementYearMonth(startYearMonth)
|
|
501
|
-
: endYearMonth,
|
|
502
|
-
};
|
|
503
|
-
});
|
|
504
|
-
// Automatically change displayed year and month when start date changes
|
|
505
|
-
useEffect(() => {
|
|
506
|
-
if (isFirstMount) {
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
const startYearMonth = getYearMonth(value.start, format);
|
|
510
|
-
if (startYearMonth.isSome()) {
|
|
511
|
-
setPeriods(periods => {
|
|
512
|
-
const isStartAndEndSameMonth = isYearMonthEquals(startYearMonth.value, periods.end);
|
|
513
|
-
if (isStartAndEndSameMonth) {
|
|
514
|
-
return {
|
|
515
|
-
start: decrementYearMonth(periods.end),
|
|
516
|
-
end: periods.end,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
// change end period if it becomes before start period
|
|
520
|
-
const endPeriod = maxYearMonth(periods.end, incrementYearMonth(startYearMonth.value));
|
|
521
|
-
return {
|
|
522
|
-
start: startYearMonth.value,
|
|
523
|
-
end: endPeriod,
|
|
524
|
-
};
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
}, [isFirstMount, value.start, format]);
|
|
528
|
-
// Automatically change displayed year and month when end date changes
|
|
529
|
-
useEffect(() => {
|
|
530
|
-
if (isFirstMount) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
const endYearMonth = getYearMonth(value.end, format);
|
|
534
|
-
if (endYearMonth.isSome()) {
|
|
535
|
-
setPeriods(periods => {
|
|
536
|
-
const isStartAndEndSameMonth = isYearMonthEquals(periods.start, endYearMonth.value);
|
|
537
|
-
if (isStartAndEndSameMonth) {
|
|
538
|
-
return {
|
|
539
|
-
start: periods.start,
|
|
540
|
-
end: incrementYearMonth(periods.start),
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
// change start period if it becomes after end period
|
|
544
|
-
const startPeriod = minYearMonth(periods.start, decrementYearMonth(endYearMonth.value));
|
|
545
|
-
return {
|
|
546
|
-
start: startPeriod,
|
|
547
|
-
end: endYearMonth.value,
|
|
548
|
-
};
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
}, [isFirstMount, value.end, format]);
|
|
552
|
-
const setStartPeriod = useCallback((yearMonth) => {
|
|
553
|
-
setPeriods(periods => ({
|
|
554
|
-
start: yearMonth,
|
|
555
|
-
end: maxYearMonth(periods.end, incrementYearMonth(yearMonth)),
|
|
556
|
-
}));
|
|
557
|
-
}, []);
|
|
558
|
-
const setEndPeriod = useCallback((yearMonth) => {
|
|
559
|
-
setPeriods(periods => ({
|
|
560
|
-
start: minYearMonth(periods.start, decrementYearMonth(yearMonth)),
|
|
561
|
-
end: yearMonth,
|
|
562
|
-
}));
|
|
563
|
-
}, []);
|
|
564
|
-
const dateRange = useMemo(() => parseRange(value, format), [value, format]);
|
|
565
|
-
const handleSelectDate = (date) => {
|
|
566
|
-
const newRange = getNewDateRange(dateRange, date);
|
|
567
|
-
const newValue = {
|
|
568
|
-
start: newRange.start.match({
|
|
569
|
-
Some: date => stringifyDate(date, format),
|
|
570
|
-
None: () => value.start,
|
|
571
|
-
}),
|
|
572
|
-
end: newRange.end.match({
|
|
573
|
-
Some: date => stringifyDate(date, format),
|
|
574
|
-
None: () => value.end,
|
|
575
|
-
}),
|
|
576
|
-
};
|
|
577
|
-
onChange(newValue);
|
|
578
|
-
};
|
|
579
|
-
if (!displayTwoCalendar) {
|
|
580
|
-
return (_jsxs(_Fragment, { children: [_jsx(YearMonthSelect, { monthNames: monthNames, value: periods.start, hideArrows: !desktop, onChange: setStartPeriod }), _jsx(Space, { height: 24 }), _jsx(MonthCalendar, { month: periods.start.month, year: periods.start.year, value: dateRange, firstWeekDay: firstWeekDay, weekDayNames: weekDayNames, isSelectable: isSelectable, onChange: handleSelectDate })] }));
|
|
581
|
-
}
|
|
582
|
-
return (_jsx(View, { children: _jsxs(Box, { direction: "row", alignItems: "start", children: [_jsxs(View, { style: styles.rangeCalendarSide, children: [_jsx(YearMonthSelect, { monthNames: monthNames, value: periods.start, maxValue: decrementYearMonth(periods.end), arrowsPosition: "around", onChange: setStartPeriod }), _jsx(Space, { height: 24 }), _jsx(MonthCalendar, { month: periods.start.month, year: periods.start.year, value: dateRange, firstWeekDay: firstWeekDay, weekDayNames: weekDayNames, isSelectable: isSelectable, onChange: handleSelectDate })] }), _jsx(Separator, { space: 24, horizontal: true }), _jsxs(View, { style: styles.rangeCalendarSide, children: [_jsx(YearMonthSelect, { monthNames: monthNames, value: periods.end, minValue: incrementYearMonth(periods.start), arrowsPosition: "around", onChange: setEndPeriod }), _jsx(Space, { height: 24 }), _jsx(MonthCalendar, { month: periods.end.month, year: periods.end.year, value: dateRange, firstWeekDay: firstWeekDay, weekDayNames: weekDayNames, isSelectable: isSelectable, onChange: handleSelectDate })] })] }) }));
|
|
583
|
-
};
|
|
584
|
-
export const DateRangePicker = ({ value, error, format, startLabel, endLabel, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, }) => {
|
|
585
|
-
const { desktop } = useResponsive(DATE_PICKER_MOBILE_THRESHOLD);
|
|
586
|
-
const { desktop: displayTwoCalendar } = useResponsive(DATE_RANGE_PICKER_THRESHOLD);
|
|
587
|
-
const ref = useRef(null);
|
|
588
|
-
const [isOpened, { open, close }] = useDisclosure(false);
|
|
589
|
-
const handleStartChange = useCallback((start) => {
|
|
590
|
-
onChange({ start, end: value.end });
|
|
591
|
-
}, [value, onChange]);
|
|
592
|
-
const handleEndChange = useCallback((end) => {
|
|
593
|
-
onChange({ start: value.start, end });
|
|
594
|
-
}, [value, onChange]);
|
|
595
|
-
return (_jsxs(View, { children: [_jsxs(Box, { direction: "row", alignItems: "end", children: [_jsx(LakeLabel, { label: startLabel, style: styles.label, render: id => (_jsx(Rifm, { value: value.start, onChange: handleStartChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { ref: ref, id: id, placeholder: format, value: value, onChange: onChange, error: error, hideErrors: true, ariaExpanded: isOpened })) })) }), _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: id => (_jsx(Rifm, { value: value.end, onChange: handleEndChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { id: id, placeholder: format, value: value, onChange: onChange, error: error, hideErrors: true, ariaExpanded: isOpened })) })) }), _jsx(Space, { width: 12 }), _jsx(LakeButton, { mode: "secondary", icon: "calendar-ltr-regular", size: "small", onPress: open })] }), _jsx(Space, { height: 4 }), _jsx(LakeText, { variant: "smallRegular", color: colors.negative[500], children: error ?? " " }), _jsx(DateModal, { visible: isOpened, maxWidth: 900, withCloseButton: true, onPressClose: close, children: _jsx(DateRangePickerModalContent, { value: value, format: format, firstWeekDay: firstWeekDay, monthNames: monthNames, weekDayNames: weekDayNames, desktop: desktop, displayTwoCalendar: displayTwoCalendar, isSelectable: isSelectable, onChange: onChange }) })] }));
|
|
596
|
-
};
|
|
597
|
-
export const DateRangePickerModal = ({ value, error, format, firstWeekDay, monthNames, weekDayNames, isSelectable, onChange, visible, startLabel, endLabel, cancelLabel, confirmLabel, onDissmiss, }) => {
|
|
598
|
-
const { desktop } = useResponsive(MODALE_MOBILE_THRESHOLD);
|
|
599
|
-
const { desktop: displayTwoCalendar } = useResponsive(DATE_RANGE_PICKER_THRESHOLD);
|
|
600
|
-
const [localeValue, setLocaleValue] = useState(value);
|
|
601
|
-
useEffect(() => {
|
|
602
|
-
setLocaleValue(value);
|
|
603
|
-
}, [value]);
|
|
604
|
-
const handleStartChange = (start) => {
|
|
605
|
-
setLocaleValue({ start, end: localeValue.end });
|
|
606
|
-
};
|
|
607
|
-
const handleEndChange = (end) => {
|
|
608
|
-
setLocaleValue({ start: localeValue.start, end });
|
|
609
|
-
};
|
|
610
|
-
const handleCancel = () => {
|
|
611
|
-
setLocaleValue(value);
|
|
612
|
-
onDissmiss();
|
|
613
|
-
};
|
|
614
|
-
const handleConfirm = () => {
|
|
615
|
-
onChange(localeValue);
|
|
616
|
-
onDissmiss();
|
|
617
|
-
};
|
|
618
|
-
return (_jsxs(DateModal, { visible: visible, maxWidth: 900, onPressClose: handleCancel, children: [_jsxs(View, { children: [_jsxs(Box, { direction: "row", alignItems: "end", children: [_jsx(LakeLabel, { label: startLabel, style: styles.label, render: id => (_jsx(Rifm, { value: localeValue.start, onChange: handleStartChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { id: id, placeholder: format, value: value, onChange: onChange, error: error, hideErrors: true })) })) }), _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: id => (_jsx(Rifm, { value: localeValue.end, onChange: handleEndChange, ...rifmDateProps, children: ({ value, onChange }) => (_jsx(LakeTextInput, { id: id, placeholder: format, value: value, onChange: onChange, error: error, hideErrors: true })) })) })] }), _jsx(Space, { height: 4 }), _jsx(LakeText, { variant: "smallRegular", color: colors.negative[500], children: error ?? " " })] }), _jsx(DateRangePickerModalContent, { value: localeValue, format: format, firstWeekDay: firstWeekDay, monthNames: monthNames, weekDayNames: weekDayNames, desktop: desktop, displayTwoCalendar: displayTwoCalendar, isSelectable: isSelectable, onChange: setLocaleValue }), _jsx(Space, { height: 24 }), _jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeButton, { mode: "secondary", size: "small", onPress: handleCancel, style: styles.button, children: cancelLabel }), _jsx(Space, { width: 24 }), _jsx(LakeButton, { color: "current", size: "small", onPress: handleConfirm, style: styles.button, children: confirmLabel })] })] }));
|
|
619
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
variant?: "none" | "pending" | "verified" | "refused";
|
|
3
|
-
name: string;
|
|
4
|
-
url?: string;
|
|
5
|
-
onRemove?: () => void;
|
|
6
|
-
title?: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
};
|
|
9
|
-
export declare const FileTile: ({ variant, name, url, onRemove, title, description }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
-
export {};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { StyleSheet } from "react-native";
|
|
3
|
-
import { match } from "ts-pattern";
|
|
4
|
-
import { commonStyles } from "../constants/commonStyles";
|
|
5
|
-
import { backgroundColor, colors, shadows, spacings } from "../constants/design";
|
|
6
|
-
import { getIconNameFromFilename } from "../utils/file";
|
|
7
|
-
import { isNotNullish, isNotNullishOrEmpty } from "../utils/nullish";
|
|
8
|
-
import { Box } from "./Box";
|
|
9
|
-
import { LakeAlert } from "./LakeAlert";
|
|
10
|
-
import { LakeButton } from "./LakeButton";
|
|
11
|
-
import { LakeText } from "./LakeText";
|
|
12
|
-
import { Space } from "./Space";
|
|
13
|
-
import { Tag } from "./Tag";
|
|
14
|
-
const styles = StyleSheet.create({
|
|
15
|
-
base: {
|
|
16
|
-
backgroundColor: backgroundColor.accented,
|
|
17
|
-
borderRadius: 8,
|
|
18
|
-
boxShadow: shadows.tile,
|
|
19
|
-
overflow: "hidden",
|
|
20
|
-
},
|
|
21
|
-
content: {
|
|
22
|
-
height: 56,
|
|
23
|
-
paddingLeft: spacings[20],
|
|
24
|
-
paddingRight: spacings[8],
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
export const FileTile = ({ variant = "none", name, url, onRemove, title, description }) => (_jsxs(Box, { style: styles.base, children: [_jsxs(Box, { alignItems: "center", direction: "row", style: styles.content, children: [_jsx(Tag, { icon: getIconNameFromFilename(name), iconSize: 20, color: match(variant)
|
|
28
|
-
.with("none", "pending", () => "shakespear")
|
|
29
|
-
.with("verified", () => "positive")
|
|
30
|
-
.with("refused", () => "negative")
|
|
31
|
-
.exhaustive() }), _jsx(Space, { width: 16 }), _jsx(LakeText, { numberOfLines: 1, color: colors.gray[700], style: commonStyles.fill, children: name }), _jsx(Space, { width: 12 }), isNotNullishOrEmpty(url) && (_jsx(LakeButton, { mode: "tertiary", size: "small", icon: "open-filled", onPress: () => {
|
|
32
|
-
window.open(url, "_blank");
|
|
33
|
-
} })), isNotNullish(onRemove) && (_jsx(LakeButton, { mode: "tertiary", size: "small", icon: "delete-regular", color: "negative", onPress: onRemove }))] }), variant !== "none" && (_jsx(LakeAlert, { anchored: true, title: title, variant: match(variant)
|
|
34
|
-
.with("pending", () => "info")
|
|
35
|
-
.with("verified", () => "success")
|
|
36
|
-
.with("refused", () => "error")
|
|
37
|
-
.exhaustive(), children: description }))] }));
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
import { ColorVariants } from "../constants/design";
|
|
3
|
-
import { IconName } from "./Icon";
|
|
4
|
-
import { Context } from "./ResponsiveContainer";
|
|
5
|
-
export type LakeModalProps = {
|
|
6
|
-
visible: boolean;
|
|
7
|
-
title?: string;
|
|
8
|
-
icon?: IconName;
|
|
9
|
-
color?: ColorVariants;
|
|
10
|
-
children: ReactNode | ((context: Context) => ReactNode);
|
|
11
|
-
maxWidth?: number;
|
|
12
|
-
onPressClose?: () => void;
|
|
13
|
-
};
|
|
14
|
-
export declare const LakeModal: ({ visible, icon, title, color, children, maxWidth, onPressClose, }: LakeModalProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense, useEffect, useState } from "react";
|
|
3
|
-
import { ScrollView, StyleSheet, View } from "react-native";
|
|
4
|
-
import { commonStyles } from "../constants/commonStyles";
|
|
5
|
-
import { backgroundColor, breakpoints, colors, negativeSpacings, shadows, spacings, } from "../constants/design";
|
|
6
|
-
import { useBodyClassName } from "../hooks/useBodyClassName";
|
|
7
|
-
import { FocusTrap } from "./FocusTrap";
|
|
8
|
-
import { Icon } from "./Icon";
|
|
9
|
-
import { LakeButton } from "./LakeButton";
|
|
10
|
-
import { LakeHeading } from "./LakeHeading";
|
|
11
|
-
import { LoadingView } from "./LoadingView";
|
|
12
|
-
import { Portal } from "./Portal";
|
|
13
|
-
import { Pressable } from "./Pressable";
|
|
14
|
-
import { ResponsiveContainer } from "./ResponsiveContainer";
|
|
15
|
-
import { Space } from "./Space";
|
|
16
|
-
import { TransitionView } from "./TransitionView";
|
|
17
|
-
const BACKGROUND_COLOR = "rgba(0, 0, 0, 0.6)";
|
|
18
|
-
const styles = StyleSheet.create({
|
|
19
|
-
root: {
|
|
20
|
-
...StyleSheet.absoluteFillObject,
|
|
21
|
-
},
|
|
22
|
-
container: {
|
|
23
|
-
...StyleSheet.absoluteFillObject,
|
|
24
|
-
},
|
|
25
|
-
inert: {
|
|
26
|
-
pointerEvents: "none",
|
|
27
|
-
},
|
|
28
|
-
fill: {
|
|
29
|
-
...StyleSheet.absoluteFillObject,
|
|
30
|
-
animationFillMode: "forwards",
|
|
31
|
-
},
|
|
32
|
-
overlay: {
|
|
33
|
-
...StyleSheet.absoluteFillObject,
|
|
34
|
-
backgroundColor: BACKGROUND_COLOR,
|
|
35
|
-
},
|
|
36
|
-
overlayEnter: {
|
|
37
|
-
animationKeyframes: {
|
|
38
|
-
"0%": { opacity: 0 },
|
|
39
|
-
},
|
|
40
|
-
animationDuration: "200ms",
|
|
41
|
-
animationTimingFunction: "ease-in-out",
|
|
42
|
-
},
|
|
43
|
-
overlayLeave: {
|
|
44
|
-
animationKeyframes: {
|
|
45
|
-
"100%": { opacity: 0 },
|
|
46
|
-
},
|
|
47
|
-
animationDuration: "200ms",
|
|
48
|
-
animationTimingFunction: "ease-in-out",
|
|
49
|
-
},
|
|
50
|
-
modalEnter: {
|
|
51
|
-
animationKeyframes: {
|
|
52
|
-
"0%": {
|
|
53
|
-
opacity: 0,
|
|
54
|
-
transform: "translateY(-20px)",
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
animationDuration: "300ms",
|
|
58
|
-
animationTimingFunction: "ease-in-out",
|
|
59
|
-
},
|
|
60
|
-
modalLeave: {
|
|
61
|
-
animationKeyframes: {
|
|
62
|
-
"100%": {
|
|
63
|
-
opacity: 0,
|
|
64
|
-
transform: "translateY(-20px)",
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
animationDuration: "300ms",
|
|
68
|
-
animationTimingFunction: "ease-in-out",
|
|
69
|
-
},
|
|
70
|
-
modalContainer: {
|
|
71
|
-
...StyleSheet.absoluteFillObject,
|
|
72
|
-
},
|
|
73
|
-
modalContentContainer: {
|
|
74
|
-
padding: spacings[24],
|
|
75
|
-
justifyContent: "center",
|
|
76
|
-
flexGrow: 1,
|
|
77
|
-
},
|
|
78
|
-
modalContentContainerSmall: {
|
|
79
|
-
padding: spacings[8],
|
|
80
|
-
flexGrow: 1,
|
|
81
|
-
justifyContent: "center",
|
|
82
|
-
},
|
|
83
|
-
modal: {
|
|
84
|
-
backgroundColor: backgroundColor.default,
|
|
85
|
-
borderRadius: 24,
|
|
86
|
-
padding: spacings[48],
|
|
87
|
-
boxShadow: shadows.modal,
|
|
88
|
-
alignSelf: "stretch",
|
|
89
|
-
width: "100%",
|
|
90
|
-
marginHorizontal: "auto",
|
|
91
|
-
},
|
|
92
|
-
modalSmall: {
|
|
93
|
-
borderRadius: 16,
|
|
94
|
-
backgroundColor: backgroundColor.default,
|
|
95
|
-
width: "100%",
|
|
96
|
-
padding: spacings[24],
|
|
97
|
-
alignSelf: "stretch",
|
|
98
|
-
marginHorizontal: "auto",
|
|
99
|
-
},
|
|
100
|
-
modalHeader: {
|
|
101
|
-
display: "flex",
|
|
102
|
-
flexDirection: "row",
|
|
103
|
-
justifyContent: "space-between",
|
|
104
|
-
},
|
|
105
|
-
modalIconTitle: {
|
|
106
|
-
...commonStyles.fill,
|
|
107
|
-
},
|
|
108
|
-
trap: {
|
|
109
|
-
...commonStyles.fill,
|
|
110
|
-
},
|
|
111
|
-
pressableOverlay: {
|
|
112
|
-
...StyleSheet.absoluteFillObject,
|
|
113
|
-
},
|
|
114
|
-
closeButton: {
|
|
115
|
-
top: negativeSpacings[16],
|
|
116
|
-
right: negativeSpacings[16],
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
export const LakeModal = ({ visible, icon, title, color = "current", children, maxWidth = 570, onPressClose, }) => {
|
|
120
|
-
const [rootElement, setRootElement] = useState(() => undefined);
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
const rootElement = document.createElement("div");
|
|
123
|
-
document.body.append(rootElement);
|
|
124
|
-
setRootElement(rootElement);
|
|
125
|
-
return () => {
|
|
126
|
-
rootElement.remove();
|
|
127
|
-
setRootElement(undefined);
|
|
128
|
-
};
|
|
129
|
-
}, []);
|
|
130
|
-
useBodyClassName("ModalOpen", { enabled: visible });
|
|
131
|
-
if (rootElement == null) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
return (_jsx(Portal, { container: rootElement, children: _jsxs(View, { "aria-modal": true, style: [styles.container, !visible && styles.inert], children: [_jsx(TransitionView, { style: styles.fill, enter: styles.overlayEnter, leave: styles.overlayLeave, children: visible ? _jsx(View, { style: styles.overlay }) : null }), _jsx(Suspense, { fallback: _jsx(LoadingView, { color: backgroundColor.accented, delay: 0 }), children: _jsx(TransitionView, { style: styles.fill, enter: styles.modalEnter, leave: styles.modalLeave, children: visible ? (_jsx(ResponsiveContainer, { style: styles.root, breakpoint: breakpoints.tiny, children: ({ large, small }) => (_jsx(FocusTrap, { autoFocus: true, focusLock: true, returnFocus: true, onEscapeKey: onPressClose, style: styles.trap, children: _jsxs(ScrollView, { style: styles.modalContainer, contentContainerStyle: large ? styles.modalContentContainer : styles.modalContentContainerSmall, children: [onPressClose != null ? (_jsx(Pressable, { onPress: onPressClose, style: styles.pressableOverlay })) : null, _jsxs(View, { role: "dialog", style: [large ? styles.modal : styles.modalSmall, { maxWidth }], children: [_jsxs(View, { style: styles.modalHeader, children: [_jsxs(View, { style: styles.modalIconTitle, children: [icon != null ? (_jsx(Icon, { name: icon, size: large ? 40 : 32, color: colors[color][500] })) : null, icon != null && title != null ? _jsx(Space, { height: 12 }) : null, title != null ? (_jsxs(_Fragment, { children: [_jsx(LakeHeading, { level: 2, variant: "h3", children: title }), _jsx(Space, { height: 12 })] })) : null] }), onPressClose != null ? (_jsx(LakeButton, { style: styles.closeButton, mode: "tertiary", onPress: onPressClose, icon: "lake-close" })) : null] }), typeof children == "function" ? children({ large, small }) : children] })] }) })) })) : null }) })] }) }));
|
|
135
|
-
};
|