@swan-io/shared-business 2.7.37 → 3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swan-io/shared-business",
3
- "version": "2.7.37",
3
+ "version": "3.0.0",
4
4
  "engines": {
5
5
  "node": ">=18.0.0",
6
6
  "yarn": "^1.22.0"
@@ -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,90 @@
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 {};