@swan-io/shared-business 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/ChoicePicker.d.ts +12 -0
- package/src/components/ChoicePicker.js +193 -0
- package/src/components/ConfirmModal.d.ts +16 -0
- package/src/components/ConfirmModal.js +16 -0
- package/src/components/DatePicker.d.ts +86 -0
- package/src/components/DatePicker.js +643 -0
- package/src/components/FileTile.d.ts +10 -0
- package/src/components/FileTile.js +38 -0
- package/src/components/LakeModal.d.ts +14 -0
- package/src/components/LakeModal.js +136 -0
- package/src/components/SupportingDocument.js +1 -1
- package/src/components/UploadArea.d.ts +5 -3
- package/src/components/UploadArea.js +38 -38
- package/src/locales/de.json +8 -0
- package/src/locales/en.json +8 -0
- package/src/locales/es.json +8 -0
- package/src/locales/fr.json +8 -0
- package/src/locales/it.json +8 -0
- package/src/locales/nl.json +8 -0
- package/src/locales/pt.json +8 -0
- package/src/utils/date.d.ts +0 -3
- package/src/utils/date.js +0 -24
- package/src/utils/i18n.d.ts +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { LakeButton } from "@swan-io/lake/src/components/LakeButton";
|
|
3
|
+
import { LakeRadio } from "@swan-io/lake/src/components/LakeRadio";
|
|
4
|
+
import { Pressable } from "@swan-io/lake/src/components/Pressable";
|
|
5
|
+
import { Space } from "@swan-io/lake/src/components/Space";
|
|
6
|
+
import { Tile } from "@swan-io/lake/src/components/Tile";
|
|
7
|
+
import { commonStyles } from "@swan-io/lake/src/constants/commonStyles";
|
|
8
|
+
import { breakpoints, negativeSpacings, spacings } from "@swan-io/lake/src/constants/design";
|
|
9
|
+
import { useResponsive } from "@swan-io/lake/src/hooks/useResponsive";
|
|
10
|
+
import { clampValue } from "@swan-io/lake/src/utils/math";
|
|
11
|
+
import { detectScrollAnimationEnd } from "@swan-io/lake/src/utils/viewport";
|
|
12
|
+
import { useEffect, useRef, useState } from "react";
|
|
13
|
+
import { ScrollView, StyleSheet, View } from "react-native";
|
|
14
|
+
import { match } from "ts-pattern";
|
|
15
|
+
import { t } from "../utils/i18n";
|
|
16
|
+
const styles = StyleSheet.create({
|
|
17
|
+
root: {
|
|
18
|
+
alignSelf: "stretch",
|
|
19
|
+
alignItems: "stretch",
|
|
20
|
+
flexGrow: 1,
|
|
21
|
+
overflow: "hidden",
|
|
22
|
+
marginHorizontal: negativeSpacings[12],
|
|
23
|
+
},
|
|
24
|
+
scrollSnap: {
|
|
25
|
+
scrollSnapType: "x mandatory",
|
|
26
|
+
},
|
|
27
|
+
container: {
|
|
28
|
+
alignSelf: "stretch",
|
|
29
|
+
flexDirection: "row",
|
|
30
|
+
flexWrap: "wrap",
|
|
31
|
+
alignItems: "stretch",
|
|
32
|
+
justifyContent: "center",
|
|
33
|
+
},
|
|
34
|
+
mobileContainer: {
|
|
35
|
+
flexWrap: "nowrap",
|
|
36
|
+
justifyContent: "flex-start",
|
|
37
|
+
transitionProperty: "transform",
|
|
38
|
+
transitionDuration: "300ms",
|
|
39
|
+
transitionTimingFunction: "ease-in-out",
|
|
40
|
+
},
|
|
41
|
+
item: {
|
|
42
|
+
flexGrow: 0,
|
|
43
|
+
flexBasis: "33.333%",
|
|
44
|
+
maxWidth: 300,
|
|
45
|
+
padding: spacings[12],
|
|
46
|
+
},
|
|
47
|
+
itemAnimation: {
|
|
48
|
+
transform: "translateZ(0px)",
|
|
49
|
+
animationKeyframes: {
|
|
50
|
+
from: {
|
|
51
|
+
opacity: 0,
|
|
52
|
+
transform: "translateZ(0px) translateX(50px)",
|
|
53
|
+
},
|
|
54
|
+
to: {
|
|
55
|
+
opacity: 1,
|
|
56
|
+
transform: "translateZ(0px) translateX(0px)",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
animationDuration: "200ms",
|
|
60
|
+
animationFillMode: "backwards",
|
|
61
|
+
animationTimingFunction: "ease-in-out",
|
|
62
|
+
},
|
|
63
|
+
itemLarge: {
|
|
64
|
+
flexBasis: "50%",
|
|
65
|
+
maxWidth: "none",
|
|
66
|
+
},
|
|
67
|
+
itemSmallViewport: {
|
|
68
|
+
width: "100%",
|
|
69
|
+
flexBasis: "auto",
|
|
70
|
+
maxWidth: "none",
|
|
71
|
+
scrollSnapAlign: "center",
|
|
72
|
+
},
|
|
73
|
+
tileContents: {
|
|
74
|
+
alignItems: "center",
|
|
75
|
+
alignSelf: "stretch",
|
|
76
|
+
flexGrow: 1,
|
|
77
|
+
},
|
|
78
|
+
tileRenderedContents: {
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
alignSelf: "stretch",
|
|
81
|
+
flexGrow: 1,
|
|
82
|
+
},
|
|
83
|
+
leftButton: {
|
|
84
|
+
position: "absolute",
|
|
85
|
+
top: "50%",
|
|
86
|
+
left: negativeSpacings[24],
|
|
87
|
+
transform: "translateY(-50%)",
|
|
88
|
+
borderTopLeftRadius: 0,
|
|
89
|
+
borderBottomLeftRadius: 0,
|
|
90
|
+
borderWidth: 1,
|
|
91
|
+
borderLeftWidth: 0,
|
|
92
|
+
},
|
|
93
|
+
rightButton: {
|
|
94
|
+
position: "absolute",
|
|
95
|
+
top: "50%",
|
|
96
|
+
right: negativeSpacings[24],
|
|
97
|
+
transform: "translateY(-50%)",
|
|
98
|
+
borderTopRightRadius: 0,
|
|
99
|
+
borderBottomRightRadius: 0,
|
|
100
|
+
borderWidth: 1,
|
|
101
|
+
borderRightWidth: 0,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const identity = (x) => x;
|
|
105
|
+
export const ChoicePicker = ({ items, getId = identity, large = false, renderItem, value, disabled = false, onChange, }) => {
|
|
106
|
+
const containerRef = useRef(null);
|
|
107
|
+
const { desktop } = useResponsive(breakpoints.medium);
|
|
108
|
+
const [mobilePosition, setMobilePosition] = useState("start");
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (desktop) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// auto scroll to selected value on mobile
|
|
114
|
+
const scrollContainer = containerRef.current;
|
|
115
|
+
const index = items.findIndex(item => value === item);
|
|
116
|
+
if (index !== -1 && scrollContainer instanceof HTMLDivElement) {
|
|
117
|
+
const width = scrollContainer.offsetWidth;
|
|
118
|
+
scrollContainer.scrollTo({ x: index * width, animated: false });
|
|
119
|
+
}
|
|
120
|
+
// if no value is selected, select first item
|
|
121
|
+
if (value == null && items[0] != null) {
|
|
122
|
+
onChange(items[0]);
|
|
123
|
+
}
|
|
124
|
+
// disable exhaustive-deps because we only want to run this effect only when screen size go from desktop to mobile
|
|
125
|
+
}, [desktop]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
126
|
+
const onScroll = () => {
|
|
127
|
+
// prevent scroll event when we change screen size from mobile to desktop
|
|
128
|
+
if (desktop) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const scrollContainer = containerRef.current;
|
|
132
|
+
if (scrollContainer instanceof HTMLDivElement) {
|
|
133
|
+
const scrollLeft = scrollContainer.scrollLeft;
|
|
134
|
+
const width = scrollContainer.offsetWidth;
|
|
135
|
+
const index = clampValue(0, items.length - 1)(Math.round(scrollLeft / width));
|
|
136
|
+
const item = items[index];
|
|
137
|
+
if (item != null) {
|
|
138
|
+
onChange(item);
|
|
139
|
+
}
|
|
140
|
+
match(index)
|
|
141
|
+
.with(0, () => setMobilePosition("start"))
|
|
142
|
+
.with(items.length - 1, () => setMobilePosition("end"))
|
|
143
|
+
.otherwise(() => setMobilePosition("middle"));
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const onPressPrevious = () => {
|
|
147
|
+
const scrollContainer = containerRef.current;
|
|
148
|
+
if (scrollContainer instanceof HTMLDivElement) {
|
|
149
|
+
const scrollLeft = scrollContainer.scrollLeft;
|
|
150
|
+
const width = scrollContainer.offsetWidth;
|
|
151
|
+
const index = Math.round(scrollLeft / width);
|
|
152
|
+
const previousIndex = Math.max(0, index - 1);
|
|
153
|
+
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
154
|
+
scrollContainer.style.scrollSnapType = "none";
|
|
155
|
+
containerRef.current?.scrollTo({ x: previousIndex * width, animated: true });
|
|
156
|
+
detectScrollAnimationEnd(scrollContainer).onResolve(() => {
|
|
157
|
+
// set back scroll snap
|
|
158
|
+
// @ts-expect-error
|
|
159
|
+
scrollContainer.style.scrollSnapType = null;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const onPressNext = () => {
|
|
164
|
+
const scrollContainer = containerRef.current;
|
|
165
|
+
if (scrollContainer instanceof HTMLDivElement) {
|
|
166
|
+
const scrollLeft = scrollContainer.scrollLeft;
|
|
167
|
+
const width = scrollContainer.offsetWidth;
|
|
168
|
+
const index = Math.round(scrollLeft / width);
|
|
169
|
+
const nextIndex = Math.min(items.length - 1, index + 1);
|
|
170
|
+
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
171
|
+
scrollContainer.style.scrollSnapType = "none";
|
|
172
|
+
containerRef.current?.scrollTo({ x: nextIndex * width, animated: true });
|
|
173
|
+
detectScrollAnimationEnd(scrollContainer).onResolve(() => {
|
|
174
|
+
// set back scroll snap
|
|
175
|
+
// @ts-expect-error
|
|
176
|
+
scrollContainer.style.scrollSnapType = null;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
return (_jsxs(View, { children: [_jsx(View, { style: styles.root, children: _jsx(ScrollView, { ref: containerRef, horizontal: !desktop, onScroll: onScroll, scrollEventThrottle: 200, style: styles.scrollSnap, contentContainerStyle: [
|
|
181
|
+
styles.container,
|
|
182
|
+
!desktop && styles.mobileContainer,
|
|
183
|
+
!desktop && { width: `${items.length * 100}%` },
|
|
184
|
+
], children: items.map((item, index) => (_jsx(Pressable, { disabled: disabled, style: [
|
|
185
|
+
styles.item,
|
|
186
|
+
disabled && commonStyles.disabled,
|
|
187
|
+
desktop && styles.itemAnimation,
|
|
188
|
+
desktop && { animationDelay: `${200 + 100 * index}ms` },
|
|
189
|
+
large && styles.itemLarge,
|
|
190
|
+
!desktop && styles.itemSmallViewport,
|
|
191
|
+
!desktop && { width: `${100 / items.length}%` },
|
|
192
|
+
], 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, ariaLabel: t("common.previous") })), !desktop && (_jsx(LakeButton, { icon: "chevron-right-filled", mode: "secondary", forceBackground: true, onPress: onPressNext, disabled: mobilePosition === "end" || disabled, style: styles.rightButton, ariaLabel: t("common.next") }))] }));
|
|
193
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IconName } from "@swan-io/lake/src/components/Icon";
|
|
2
|
+
import { ColorVariants } from "@swan-io/lake/src/constants/design";
|
|
3
|
+
type Props = {
|
|
4
|
+
visible: boolean;
|
|
5
|
+
title: string;
|
|
6
|
+
message?: string;
|
|
7
|
+
confirmText: string;
|
|
8
|
+
cancelText?: string;
|
|
9
|
+
color?: ColorVariants;
|
|
10
|
+
icon: IconName;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
onConfirm: () => void;
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const ConfirmModal: ({ visible, title, message, confirmText, cancelText, color, icon, loading, onConfirm, onCancel, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@swan-io/lake/src/components/Box";
|
|
3
|
+
import { LakeButton } from "@swan-io/lake/src/components/LakeButton";
|
|
4
|
+
import { LakeText } from "@swan-io/lake/src/components/LakeText";
|
|
5
|
+
import { Space } from "@swan-io/lake/src/components/Space";
|
|
6
|
+
import { LakeModal } from "@swan-io/shared-business/src/components/LakeModal";
|
|
7
|
+
import { StyleSheet } from "react-native";
|
|
8
|
+
import { t } from "../utils/i18n";
|
|
9
|
+
const styles = StyleSheet.create({
|
|
10
|
+
confirmButton: {
|
|
11
|
+
flex: 1,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export const ConfirmModal = ({ visible, title, message, confirmText, cancelText, color = "partner", icon, loading, onConfirm, onCancel, }) => {
|
|
15
|
+
return (_jsxs(LakeModal, { visible: visible, title: title, icon: icon, color: color, children: [message != null && _jsx(LakeText, { children: message }), _jsx(Space, { height: 48 }), _jsxs(Box, { direction: "row", children: [_jsx(LakeButton, { style: styles.confirmButton, mode: "secondary", onPress: onCancel, children: cancelText ?? t("common.cancel") }), _jsx(Space, { width: 24 }), _jsx(LakeButton, { style: styles.confirmButton, color: color, onPress: onConfirm, loading: loading, children: confirmText })] })] }));
|
|
16
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
isSelectable?: (date: DatePickerDate) => boolean;
|
|
51
|
+
onChange: (date: string) => void;
|
|
52
|
+
};
|
|
53
|
+
export declare const DatePicker: ({ label, value, error, format, firstWeekDay, isSelectable, onChange, }: DatePickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
type DatePickerModalProps = Except<DatePickerProps, "error"> & {
|
|
55
|
+
visible: boolean;
|
|
56
|
+
cancelLabel: string;
|
|
57
|
+
confirmLabel: string;
|
|
58
|
+
validate?: (value: string) => ValidatorResult;
|
|
59
|
+
onDissmiss: () => void;
|
|
60
|
+
};
|
|
61
|
+
export declare const DatePickerModal: ({ value, format, firstWeekDay, isSelectable, onChange, visible, label, cancelLabel, confirmLabel, validate, onDissmiss, }: DatePickerModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
export type DateRangePickerProps = {
|
|
63
|
+
value: {
|
|
64
|
+
start: string;
|
|
65
|
+
end: string;
|
|
66
|
+
};
|
|
67
|
+
error?: string;
|
|
68
|
+
format: DateFormat;
|
|
69
|
+
startLabel: string;
|
|
70
|
+
endLabel: string;
|
|
71
|
+
firstWeekDay: keyof typeof weekDayIndex;
|
|
72
|
+
isSelectable?: (date: DatePickerDate) => boolean;
|
|
73
|
+
onChange: (date: {
|
|
74
|
+
start: string;
|
|
75
|
+
end: string;
|
|
76
|
+
}) => void;
|
|
77
|
+
};
|
|
78
|
+
export declare const DateRangePicker: ({ value, error, format, startLabel, endLabel, firstWeekDay, isSelectable, onChange, }: DateRangePickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
79
|
+
type DateRangePickerModalProps = DateRangePickerProps & {
|
|
80
|
+
visible: boolean;
|
|
81
|
+
cancelLabel: string;
|
|
82
|
+
confirmLabel: string;
|
|
83
|
+
onDissmiss: () => void;
|
|
84
|
+
};
|
|
85
|
+
export declare const DateRangePickerModal: ({ value, error, format, firstWeekDay, isSelectable, onChange, visible, startLabel, endLabel, cancelLabel, confirmLabel, onDissmiss, }: DateRangePickerModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
export {};
|