@swan-io/lake 12.4.3 → 13.0.1
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 +6 -11
- package/src/components/Toggle.d.ts +5 -9
- package/src/components/Toggle.js +62 -57
- package/src/components/WithCurrentColor.d.ts +1 -1
- package/src/constants/design.d.ts +0 -7
- package/src/constants/design.js +0 -4
- package/src/components/Card3dPreview.d.ts +0 -36
- package/src/components/Card3dPreview.js +0 -197
- package/src/components/FilterChooser.d.ts +0 -15
- package/src/components/FilterChooser.js +0 -51
- package/src/components/LakeSearchField.d.ts +0 -12
- package/src/components/LakeSearchField.js +0 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swan-io/lake",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.1",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": "^22.12.0"
|
|
6
6
|
},
|
|
@@ -25,28 +25,23 @@
|
|
|
25
25
|
],
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@react-three/drei": "^10.1.2",
|
|
29
|
-
"@react-three/fiber": "^9.1.2",
|
|
30
28
|
"@swan-io/boxed": "^3.2.0",
|
|
31
29
|
"@swan-io/chicane": "^2.2.0",
|
|
32
30
|
"@swan-io/use-form": "^3.1.0",
|
|
33
31
|
"dayjs": "^1.11.13",
|
|
34
32
|
"polished": "^4.3.1",
|
|
35
33
|
"prism-react-renderer": "^2.4.1",
|
|
36
|
-
"react": "^19.1.
|
|
37
|
-
"react-dom": "^19.1.
|
|
34
|
+
"react": "^19.1.1",
|
|
35
|
+
"react-dom": "^19.1.1",
|
|
38
36
|
"react-native-web": "^0.20.0",
|
|
39
37
|
"rifm": "^0.12.1",
|
|
40
|
-
"
|
|
41
|
-
"ts-pattern": "^5.7.1",
|
|
38
|
+
"ts-pattern": "^5.8.0",
|
|
42
39
|
"uuid": "^11.1.0"
|
|
43
40
|
},
|
|
44
41
|
"devDependencies": {
|
|
45
|
-
"@types/react": "^19.1.
|
|
46
|
-
"@types/react-dom": "^19.1.
|
|
42
|
+
"@types/react": "^19.1.10",
|
|
43
|
+
"@types/react-dom": "^19.1.7",
|
|
47
44
|
"@types/react-native": "^0.72.8",
|
|
48
|
-
"@types/three": "^0.176.0",
|
|
49
|
-
"jsdom": "^26.1.0",
|
|
50
45
|
"type-fest": "^4.41.0"
|
|
51
46
|
}
|
|
52
47
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
type Props = {
|
|
2
|
+
compact?: boolean;
|
|
2
3
|
value: boolean;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
onLabel: string;
|
|
7
|
-
offLabel: string;
|
|
4
|
+
labelOff: string;
|
|
5
|
+
labelOn: string;
|
|
6
|
+
onToggle: (value: boolean) => void;
|
|
8
7
|
};
|
|
9
|
-
|
|
10
|
-
* @deprecated
|
|
11
|
-
*/
|
|
12
|
-
export declare const Toggle: ({ onToggle, value, disabled, mode, onLabel, offLabel, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const Toggle: ({ compact, value, labelOff, labelOn, onToggle }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
13
9
|
export {};
|
package/src/components/Toggle.js
CHANGED
|
@@ -1,76 +1,81 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
-
import { StyleSheet, View } from "react-native";
|
|
4
|
-
import {
|
|
5
|
-
import { colors } from "../constants/design";
|
|
6
|
-
import { Box } from "./Box";
|
|
3
|
+
import { StyleSheet, Text, View } from "react-native";
|
|
4
|
+
import { match, P } from "ts-pattern";
|
|
5
|
+
import { colors, radii, spacings, texts } from "../constants/design";
|
|
7
6
|
import { Icon } from "./Icon";
|
|
8
|
-
import { LakeText } from "./LakeText";
|
|
9
7
|
import { Pressable } from "./Pressable";
|
|
10
|
-
const HEIGHT = 26;
|
|
11
8
|
const BORDER_WIDTH = 1;
|
|
12
9
|
const styles = StyleSheet.create({
|
|
13
|
-
|
|
14
|
-
userSelect: "none",
|
|
10
|
+
base: {
|
|
15
11
|
flexDirection: "row",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
width: "min-content",
|
|
20
|
-
borderColor: colors.gray[100],
|
|
12
|
+
alignItems: "center",
|
|
13
|
+
borderColor: colors.gray[200],
|
|
14
|
+
borderRadius: radii[8],
|
|
21
15
|
borderWidth: BORDER_WIDTH,
|
|
16
|
+
height: 28,
|
|
22
17
|
},
|
|
23
18
|
handle: {
|
|
24
19
|
position: "absolute",
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
// Allow handle to bleed on container border
|
|
21
|
+
bottom: -BORDER_WIDTH,
|
|
22
|
+
left: -BORDER_WIDTH,
|
|
23
|
+
right: -BORDER_WIDTH,
|
|
27
24
|
top: -BORDER_WIDTH,
|
|
28
|
-
|
|
25
|
+
borderWidth: BORDER_WIDTH,
|
|
26
|
+
borderRadius: radii[8],
|
|
29
27
|
transitionDuration: "300ms",
|
|
28
|
+
transitionProperty: "transform, width",
|
|
30
29
|
transitionTimingFunction: "ease-in-out",
|
|
31
|
-
borderWidth: BORDER_WIDTH,
|
|
32
30
|
},
|
|
33
|
-
|
|
34
|
-
paddingHorizontal: 8,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
item: {
|
|
32
|
+
paddingHorizontal: spacings[8],
|
|
33
|
+
},
|
|
34
|
+
text: {
|
|
35
|
+
...texts.smallMedium,
|
|
36
|
+
color: colors.gray[700],
|
|
37
|
+
userSelect: "none",
|
|
38
|
+
},
|
|
39
|
+
hidden: {
|
|
40
|
+
visibility: "hidden",
|
|
41
|
+
},
|
|
42
|
+
textOn: {
|
|
43
|
+
color: colors.positive[500],
|
|
44
|
+
},
|
|
45
|
+
textOff: {
|
|
46
|
+
color: colors.negative[500],
|
|
39
47
|
},
|
|
40
48
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}, [value]);
|
|
62
|
-
|
|
63
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies(value):
|
|
64
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies(isMobileMode):
|
|
65
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies(onLabel):
|
|
66
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies(offLabel):
|
|
67
|
-
useEffect(reajustLayout, [reajustLayout, value, isMobileMode, onLabel, offLabel]);
|
|
68
|
-
return (_jsxs(Pressable, { style: [styles.switch, disabled && commonStyles.disabled], onPress: () => onToggle(!value), "aria-disabled": disabled, "aria-checked": value, disabled: disabled, ref: containerRef, role: "switch", onLayout: reajustLayout, children: [_jsx(View, { style: [
|
|
49
|
+
const getItemWidth = (node) => match(node)
|
|
50
|
+
.returnType()
|
|
51
|
+
.with(P.instanceOf(HTMLElement), element => element.offsetWidth)
|
|
52
|
+
.otherwise(() => 0);
|
|
53
|
+
export const Toggle = ({ compact = false, value, labelOff, labelOn, onToggle }) => {
|
|
54
|
+
const [itemsWidth, setItemsWidth] = useState();
|
|
55
|
+
const onViewRef = useRef(null);
|
|
56
|
+
const offViewRef = useRef(null);
|
|
57
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(compact):
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
// batch measurements
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
setItemsWidth({
|
|
62
|
+
on: getItemWidth(onViewRef.current),
|
|
63
|
+
off: getItemWidth(offViewRef.current),
|
|
64
|
+
});
|
|
65
|
+
}, 0);
|
|
66
|
+
}, [compact]);
|
|
67
|
+
const onPress = useCallback(() => {
|
|
68
|
+
onToggle(!value);
|
|
69
|
+
}, [onToggle, value]);
|
|
70
|
+
return (_jsxs(Pressable, { role: "switch", onPress: onPress, "aria-checked": value, style: styles.base, children: [_jsx(View, { role: "presentation", style: [
|
|
69
71
|
styles.handle,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
itemsWidth == null
|
|
73
|
+
? styles.hidden
|
|
74
|
+
: {
|
|
75
|
+
backgroundColor: value ? colors.positive[50] : colors.negative[50],
|
|
76
|
+
borderColor: value ? colors.positive[500] : colors.negative[500],
|
|
77
|
+
transform: `translateX(${value ? 0 : itemsWidth.on + BORDER_WIDTH}px)`,
|
|
78
|
+
width: (value ? itemsWidth.on : itemsWidth.off) + BORDER_WIDTH,
|
|
79
|
+
},
|
|
80
|
+
] }), _jsx(View, { ref: onViewRef, style: styles.item, children: compact ? (_jsx(Icon, { color: value ? colors.positive[500] : colors.gray[500], size: 16, name: "checkmark-circle-regular" })) : (_jsx(Text, { style: [styles.text, value && styles.textOn], children: labelOn })) }), _jsx(View, { ref: offViewRef, style: styles.item, children: compact ? (_jsx(Icon, { color: !value ? colors.negative[500] : colors.gray[500], size: 16, name: "subtract-circle-regular" })) : (_jsx(Text, { style: [styles.text, !value && styles.textOff], children: labelOff })) })] }));
|
|
76
81
|
};
|
|
@@ -6,7 +6,7 @@ type Props = {
|
|
|
6
6
|
style?: ViewProps["style"];
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
};
|
|
9
|
-
export declare const CurrentColorContext: import("react").Context<"
|
|
9
|
+
export declare const CurrentColorContext: import("react").Context<"gray" | "live" | "sandbox" | "positive" | "warning" | "negative" | "current" | "partner" | "swan" | "shakespear" | "darkPink" | "sunglow" | "mediumSladeBlue" | undefined>;
|
|
10
10
|
export declare const useCurrentColor: (containerRef: RefObject<HTMLElement | null>, variant: ColorVariants | undefined) => void;
|
|
11
11
|
export declare const WithCurrentColor: ({ variant, style, children }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
12
12
|
export {};
|
|
@@ -67,13 +67,6 @@ export declare const primaryFontStyle: {
|
|
|
67
67
|
fontFamily: string;
|
|
68
68
|
letterSpacing: number;
|
|
69
69
|
};
|
|
70
|
-
/**
|
|
71
|
-
* @deprecated use primaryFontStyle instead
|
|
72
|
-
*/
|
|
73
|
-
export declare const interFontStyle: {
|
|
74
|
-
fontFamily: string;
|
|
75
|
-
letterSpacing: number;
|
|
76
|
-
};
|
|
77
70
|
export declare const texts: {
|
|
78
71
|
readonly h1: TextStyle;
|
|
79
72
|
readonly h2: TextStyle;
|
package/src/constants/design.js
CHANGED
|
@@ -258,10 +258,6 @@ export const primaryFontStyle = {
|
|
|
258
258
|
fontFamily: fonts.primary,
|
|
259
259
|
letterSpacing: "var(--letter-spacing-primary)",
|
|
260
260
|
};
|
|
261
|
-
/**
|
|
262
|
-
* @deprecated use primaryFontStyle instead
|
|
263
|
-
*/
|
|
264
|
-
export const interFontStyle = primaryFontStyle;
|
|
265
261
|
const asTextStyle = (x) => {
|
|
266
262
|
return x;
|
|
267
263
|
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { JSX, Ref } from "react";
|
|
2
|
-
import * as THREE from "three";
|
|
3
|
-
export type Card3dAssetsUrls = {
|
|
4
|
-
envNx: string;
|
|
5
|
-
envNy: string;
|
|
6
|
-
envNz: string;
|
|
7
|
-
envPx: string;
|
|
8
|
-
envPy: string;
|
|
9
|
-
envPz: string;
|
|
10
|
-
fontMaisonNeueBook: string;
|
|
11
|
-
fontMarkProRegular: string;
|
|
12
|
-
bandRoughness: string;
|
|
13
|
-
cardGltf: string;
|
|
14
|
-
chipTexture: string;
|
|
15
|
-
colorBlack: string;
|
|
16
|
-
colorSilver: string;
|
|
17
|
-
};
|
|
18
|
-
type CardParams = {
|
|
19
|
-
ref?: Ref<THREE.Group>;
|
|
20
|
-
ownerName: string;
|
|
21
|
-
cardNumber: string;
|
|
22
|
-
expirationDate: string;
|
|
23
|
-
cvv: string;
|
|
24
|
-
color: "Silver" | "Black" | THREE.Texture;
|
|
25
|
-
logo: SVGElement | HTMLImageElement | null;
|
|
26
|
-
logoScale: number;
|
|
27
|
-
assetsUrls: Card3dAssetsUrls;
|
|
28
|
-
onSvgError?: (code: string) => void;
|
|
29
|
-
};
|
|
30
|
-
type Props = CardParams & {
|
|
31
|
-
autoRotationDuration?: number;
|
|
32
|
-
};
|
|
33
|
-
declare const _default: (props: Props) => import("react/jsx-runtime").JSX.Element;
|
|
34
|
-
export default _default;
|
|
35
|
-
type CardProps = JSX.IntrinsicElements["group"] & CardParams;
|
|
36
|
-
export declare const Card: ({ ref, ownerName, cardNumber, expirationDate, cvv, color, logo, logoScale, assetsUrls, onSvgError, ...props }: CardProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Environment, OrbitControls, Text, useGLTF, useTexture } from "@react-three/drei";
|
|
3
|
-
import { Canvas, useFrame } from "@react-three/fiber";
|
|
4
|
-
import { Result } from "@swan-io/boxed";
|
|
5
|
-
import { useEffect, useRef, useState } from "react";
|
|
6
|
-
import * as THREE from "three";
|
|
7
|
-
import { match, P } from "ts-pattern";
|
|
8
|
-
import { isNotNullish, isNullish } from "../utils/nullish";
|
|
9
|
-
import { createSvgImage, getMonochromeSvg } from "../utils/svg";
|
|
10
|
-
/*
|
|
11
|
-
This Module exports 2 components:
|
|
12
|
-
- default export: A react-three-fiber scene which can be integrated in any react project
|
|
13
|
-
(We use default export to be able to use React.lazy to load this component asynchronously without impacting bundle size)
|
|
14
|
-
- Card component: which can be use for developers who already have a react-three-fiber scene or want to create there own scene
|
|
15
|
-
|
|
16
|
-
Here are some details about choices made for this components:
|
|
17
|
-
:one: Textures
|
|
18
|
-
Textures aren't integrated in gltf export because it will force all developers to put texture files in their public folder.
|
|
19
|
-
By using ?url import and `useTexture` hook, Vite will put textures in dist folder for all developers who use this component.
|
|
20
|
-
|
|
21
|
-
:two: Logo integration
|
|
22
|
-
There is an SVGLoader for threejs but it doesn't support all svg features.
|
|
23
|
-
So to be sure to support all svg features, we transform the SVG into Image element to create a texture.
|
|
24
|
-
And this texture is used as an alpha map on a plane.
|
|
25
|
-
|
|
26
|
-
:three: Mastercard shiny text on back of card
|
|
27
|
-
To reproduce the shiny effect on the back of the card, we inject a custom shader in rainbow_mastercard material.
|
|
28
|
-
This custom shader chunk change the diffuse color depending on camera position.
|
|
29
|
-
*/
|
|
30
|
-
// Uses alpha channel instead of green to make pixel transparent on logo plane
|
|
31
|
-
const logoAlphaMapFragmentShader = `
|
|
32
|
-
diffuseColor.a *= texture2D(alphaMap, vAlphaMapUv).a;
|
|
33
|
-
`;
|
|
34
|
-
const shinyColorFragmentShader = `
|
|
35
|
-
float red = cameraPosition.x * cameraPosition.z;
|
|
36
|
-
float green = cameraPosition.y * cameraPosition.z;
|
|
37
|
-
float blue = 0.1;
|
|
38
|
-
|
|
39
|
-
red = sin(red / 5.0) + 1.0 / 2.0;
|
|
40
|
-
green = sin(green / 5.0) + 1.0 / 2.0;
|
|
41
|
-
|
|
42
|
-
vec3 shinyColor = vec3(red, green, blue);
|
|
43
|
-
float shinyFactor = 0.35;
|
|
44
|
-
|
|
45
|
-
diffuseColor.rgb = mix(diffuseColor.rgb, shinyColor, shinyFactor);
|
|
46
|
-
`;
|
|
47
|
-
const ENV_MAP_INTENSITY = 3;
|
|
48
|
-
const CARD_WIDTH = 8.56;
|
|
49
|
-
const CARD_HEIGHT = 5.4;
|
|
50
|
-
const FRONT_TEXT_POSITION = 0.04;
|
|
51
|
-
const BACK_TEXT_POSITION = -FRONT_TEXT_POSITION;
|
|
52
|
-
const LOGO_MARGIN_TOP = 0.3;
|
|
53
|
-
const LOGO_MARGIN_RIGHT = 0.3;
|
|
54
|
-
const LOGO_MAX_WIDTH = 5; // in cm
|
|
55
|
-
const LOGO_MAX_HEIGHT = 1; // in cm
|
|
56
|
-
const computeCardLogoSize = (logoSize) => {
|
|
57
|
-
const logoRatio = logoSize.width / logoSize.height;
|
|
58
|
-
const cardSpaceRatio = LOGO_MAX_WIDTH / LOGO_MAX_HEIGHT;
|
|
59
|
-
// if logo is wider than available space
|
|
60
|
-
// logo will have the same width than available space
|
|
61
|
-
if (logoRatio >= cardSpaceRatio) {
|
|
62
|
-
const width = LOGO_MAX_WIDTH;
|
|
63
|
-
const height = width / logoRatio;
|
|
64
|
-
return { width, height };
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
// if logo is higher than available space
|
|
68
|
-
// logo will have the same height than available space
|
|
69
|
-
const height = LOGO_MAX_HEIGHT;
|
|
70
|
-
const width = height * logoRatio;
|
|
71
|
-
return { width, height };
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
// Use export default for React.lazy
|
|
75
|
-
export default (props) => {
|
|
76
|
-
return (_jsxs(Canvas, { camera: { position: [0, 0, 12], fov: 50 }, children: [_jsx(OrbitControls, { enablePan: false, enableZoom: false }), _jsx(CardScene, { ...props })] }));
|
|
77
|
-
};
|
|
78
|
-
const CardScene = ({ autoRotationDuration, ...props }) => {
|
|
79
|
-
const { assetsUrls } = props;
|
|
80
|
-
const card = useRef(null);
|
|
81
|
-
useFrame(({ clock }) => {
|
|
82
|
-
if (autoRotationDuration != null && card.current != null) {
|
|
83
|
-
card.current.rotation.y = (clock.getElapsedTime() / autoRotationDuration) * Math.PI * 2;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
return (_jsxs(_Fragment, { children: [_jsx("ambientLight", { color: 0xffffff, intensity: 1 }), _jsx("pointLight", { intensity: 0.2, decay: 2, position: [-10, -10, -21] }), _jsx("pointLight", { intensity: 0.2, decay: 2, position: [10, 10, 21] }), _jsx(Environment, { files: [
|
|
87
|
-
assetsUrls.envPx,
|
|
88
|
-
assetsUrls.envNx,
|
|
89
|
-
assetsUrls.envPy,
|
|
90
|
-
assetsUrls.envNy,
|
|
91
|
-
assetsUrls.envPz,
|
|
92
|
-
assetsUrls.envNz,
|
|
93
|
-
] }), _jsx(Card, { ref: card, ...props })] }));
|
|
94
|
-
};
|
|
95
|
-
// Set color space to sRGB for textures
|
|
96
|
-
const setTextureColorSpace = (texture) => {
|
|
97
|
-
if (!Array.isArray(texture)) {
|
|
98
|
-
texture.colorSpace = THREE.SRGBColorSpace;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
export const Card = ({ ref, ownerName, cardNumber, expirationDate, cvv, color, logo, logoScale, assetsUrls, onSvgError, ...props }) => {
|
|
102
|
-
const { nodes, materials } = useGLTF(assetsUrls.cardGltf);
|
|
103
|
-
const [logoData, setLogoData] = useState(null);
|
|
104
|
-
const silverTexture = useTexture(assetsUrls.colorSilver, setTextureColorSpace);
|
|
105
|
-
const blackTexture = useTexture(assetsUrls.colorBlack, setTextureColorSpace);
|
|
106
|
-
const chipTexture = useTexture(assetsUrls.chipTexture, setTextureColorSpace);
|
|
107
|
-
const bandRoughnessTexture = useTexture(assetsUrls.bandRoughness); // keep default color space because it's grayscale
|
|
108
|
-
// Set environment map intensity for all materials
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
Object.values(materials).forEach(material => {
|
|
111
|
-
material.envMapIntensity = ENV_MAP_INTENSITY;
|
|
112
|
-
});
|
|
113
|
-
}, [materials]);
|
|
114
|
-
// Set rainbow mastercard text custom fragment shader
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
materials.rainbow_mastercard.onBeforeCompile = shader => {
|
|
117
|
-
shader.fragmentShader = shader.fragmentShader.replace("#include <color_fragment>", shinyColorFragmentShader);
|
|
118
|
-
};
|
|
119
|
-
}, [materials.rainbow_mastercard]);
|
|
120
|
-
// Set band roughness and chip texture
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
materials.black_band.roughness = 0.8;
|
|
123
|
-
materials.black_band.roughnessMap = bandRoughnessTexture;
|
|
124
|
-
materials.chip.map = chipTexture;
|
|
125
|
-
}, [materials.black_band, materials.chip, bandRoughnessTexture, chipTexture]);
|
|
126
|
-
// Set color texture
|
|
127
|
-
useEffect(() => {
|
|
128
|
-
match(color)
|
|
129
|
-
.with("Silver", () => {
|
|
130
|
-
materials.card.map = silverTexture;
|
|
131
|
-
})
|
|
132
|
-
.with("Black", () => {
|
|
133
|
-
materials.card.map = blackTexture;
|
|
134
|
-
})
|
|
135
|
-
.otherwise(texture => {
|
|
136
|
-
materials.card.map = texture;
|
|
137
|
-
});
|
|
138
|
-
// force threejs to update material
|
|
139
|
-
// because sometimes it doesn't apply texture on load randomly
|
|
140
|
-
materials.card.needsUpdate = true;
|
|
141
|
-
}, [color, materials.card, silverTexture, blackTexture]);
|
|
142
|
-
// this avoid to have onSvgError as dependency of the effect below which should run only on logo change
|
|
143
|
-
const handleSvgError = useRef(onSvgError);
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
handleSvgError.current = onSvgError;
|
|
146
|
-
}, [onSvgError]);
|
|
147
|
-
// Handle logo
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
var _a;
|
|
150
|
-
if (isNullish(logo)) {
|
|
151
|
-
setLogoData(null);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const image = match(logo)
|
|
155
|
-
.with(P.instanceOf(HTMLImageElement), image => Result.Ok(image))
|
|
156
|
-
.otherwise(logo => {
|
|
157
|
-
// We transform the logo to white to be able to use it as alpha map
|
|
158
|
-
const whiteLogo = getMonochromeSvg(logo, "white");
|
|
159
|
-
// Convert to Image element to be able to use it as texture
|
|
160
|
-
return createSvgImage(whiteLogo);
|
|
161
|
-
});
|
|
162
|
-
if (image.isError()) {
|
|
163
|
-
(_a = handleSvgError.current) === null || _a === void 0 ? void 0 : _a.call(handleSvgError, image.getError());
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
// Compute logo size depending on constraints
|
|
167
|
-
const { width: logoWidth, height: logoHeight } = computeCardLogoSize(image.get());
|
|
168
|
-
const alphaMap = new THREE.Texture(image.get());
|
|
169
|
-
alphaMap.needsUpdate = true;
|
|
170
|
-
setLogoData({
|
|
171
|
-
size: [logoWidth, logoHeight],
|
|
172
|
-
alphaMap,
|
|
173
|
-
});
|
|
174
|
-
}, [logo]);
|
|
175
|
-
const mainTextMaterial = (_jsx("meshStandardMaterial", { color: match(color)
|
|
176
|
-
.with("Silver", () => 0x000000)
|
|
177
|
-
.with("Black", () => 0xeeeeee)
|
|
178
|
-
.otherwise(() => 0xeeeeee), metalness: 0.1, roughness: 0.55, envMapIntensity: ENV_MAP_INTENSITY }));
|
|
179
|
-
const secondaryTextMaterial = (_jsx("meshStandardMaterial", { color: 0x666666, metalness: 0.1, roughness: 0.55, envMapIntensity: ENV_MAP_INTENSITY }));
|
|
180
|
-
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", {
|
|
181
|
-
// move group to change scale center at top right corner
|
|
182
|
-
position: [
|
|
183
|
-
CARD_WIDTH / 2 - LOGO_MARGIN_RIGHT,
|
|
184
|
-
CARD_HEIGHT / 2 - LOGO_MARGIN_TOP,
|
|
185
|
-
FRONT_TEXT_POSITION,
|
|
186
|
-
], scale: logoScale, children: isNotNullish(logoData) && (_jsxs("mesh", { position: [-logoData.size[0] / 2, -logoData.size[1] / 2, 0], children: [_jsx("planeGeometry", { args: logoData.size }), _jsx("meshStandardMaterial", { ref: material => {
|
|
187
|
-
if (!material) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
material.onBeforeCompile = shader => {
|
|
191
|
-
shader.fragmentShader = shader.fragmentShader.replace("#include <alphamap_fragment>", logoAlphaMapFragmentShader);
|
|
192
|
-
};
|
|
193
|
-
}, color: match(color)
|
|
194
|
-
.with("Silver", () => 0x000000)
|
|
195
|
-
.with("Black", () => 0xffffff)
|
|
196
|
-
.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 })] }) }));
|
|
197
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated
|
|
3
|
-
*/
|
|
4
|
-
export declare const FilterChooser: <FilterName extends string>({ filters, openFilters, label, title, availableFilters, large, onAddFilter, }: {
|
|
5
|
-
filters: Partial<Record<FilterName, unknown>>;
|
|
6
|
-
openFilters: FilterName[];
|
|
7
|
-
label: string;
|
|
8
|
-
title?: string;
|
|
9
|
-
availableFilters: {
|
|
10
|
-
label: string;
|
|
11
|
-
name: FilterName;
|
|
12
|
-
}[];
|
|
13
|
-
large?: boolean;
|
|
14
|
-
onAddFilter: (filterName: FilterName) => void;
|
|
15
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRef } from "react";
|
|
3
|
-
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
|
-
import { colors } from "../constants/design";
|
|
5
|
-
import { useDisclosure } from "../hooks/useDisclosure";
|
|
6
|
-
import { isNotNullishOrEmpty } from "../utils/nullish";
|
|
7
|
-
import { FlatList } from "./FlatList";
|
|
8
|
-
import { Icon } from "./Icon";
|
|
9
|
-
import { LakeButton } from "./LakeButton";
|
|
10
|
-
import { LakeText } from "./LakeText";
|
|
11
|
-
import { Popover } from "./Popover";
|
|
12
|
-
import { Space } from "./Space";
|
|
13
|
-
const styles = StyleSheet.create({
|
|
14
|
-
selected: {
|
|
15
|
-
color: colors.gray[500],
|
|
16
|
-
},
|
|
17
|
-
list: {
|
|
18
|
-
paddingVertical: 20,
|
|
19
|
-
marginVertical: 4,
|
|
20
|
-
minWidth: 250,
|
|
21
|
-
overflow: "hidden",
|
|
22
|
-
},
|
|
23
|
-
item: {
|
|
24
|
-
display: "flex",
|
|
25
|
-
paddingHorizontal: 24,
|
|
26
|
-
flexDirection: "row",
|
|
27
|
-
justifyContent: "space-between",
|
|
28
|
-
alignItems: "center",
|
|
29
|
-
paddingVertical: 8,
|
|
30
|
-
},
|
|
31
|
-
itemHovered: {
|
|
32
|
-
backgroundColor: colors.gray[50],
|
|
33
|
-
},
|
|
34
|
-
availableFiltersTitle: {
|
|
35
|
-
paddingHorizontal: 24,
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
/**
|
|
39
|
-
* @deprecated
|
|
40
|
-
*/
|
|
41
|
-
export const FilterChooser = ({ filters, openFilters, label, title, availableFilters, large = true, onAddFilter, }) => {
|
|
42
|
-
const inputRef = useRef(null);
|
|
43
|
-
const [visible, { close, toggle }] = useDisclosure(false);
|
|
44
|
-
return (_jsxs(_Fragment, { children: [_jsx(LakeButton, { size: "small", mode: "secondary", color: "gray", onPress: toggle, ref: inputRef, icon: large ? "chevron-down-filled" : "filter-filled", iconPosition: "end", ariaLabel: label, children: large ? label : null }), _jsx(Popover, { role: "listbox", matchReferenceMinWidth: true, onDismiss: close, referenceRef: inputRef, returnFocus: false, visible: visible, children: _jsxs(View, { style: styles.list, children: [isNotNullishOrEmpty(title) ? (_jsxs(_Fragment, { children: [_jsx(LakeText, { style: styles.availableFiltersTitle, children: title }), _jsx(Space, { height: 8 })] })) : null, _jsx(FlatList, { role: "list", data: availableFilters, keyExtractor: (_, index) => `filter-item-${index}`, renderItem: ({ item }) => {
|
|
45
|
-
const isSet = Boolean(filters[item.name]) || openFilters.includes(item.name);
|
|
46
|
-
return (_jsxs(Pressable, { style: ({ hovered }) => [styles.item, hovered && styles.itemHovered], role: "button", disabled: isSet, onPress: () => {
|
|
47
|
-
onAddFilter(item.name);
|
|
48
|
-
close();
|
|
49
|
-
}, children: [_jsx(LakeText, { variant: "smallRegular", style: isSet && styles.selected, children: item.label }), isSet && _jsx(Icon, { color: colors.positive[500], name: "checkmark-filled", size: 14 })] }));
|
|
50
|
-
} })] }) })] }));
|
|
51
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
type Props = {
|
|
3
|
-
initialValue: string;
|
|
4
|
-
placeholder: string;
|
|
5
|
-
onChangeText: (text: string) => void;
|
|
6
|
-
debounceDuration?: number;
|
|
7
|
-
maxWidth?: number;
|
|
8
|
-
children?: ReactNode;
|
|
9
|
-
renderEnd?: () => ReactNode;
|
|
10
|
-
};
|
|
11
|
-
export declare const LakeSearchField: ({ initialValue, placeholder, onChangeText, debounceDuration, maxWidth: maxWidthProp, renderEnd, children, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export {};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useRef, useState } from "react";
|
|
3
|
-
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
|
-
import { animations, backgroundColor, colors, radii, spacings } from "../constants/design";
|
|
5
|
-
import { useBoolean } from "../hooks/useBoolean";
|
|
6
|
-
import { useDebounce } from "../hooks/useDebounce";
|
|
7
|
-
import { isNotNullishOrEmpty } from "../utils/nullish";
|
|
8
|
-
import { Icon } from "./Icon";
|
|
9
|
-
import { LakeButton } from "./LakeButton";
|
|
10
|
-
import { LakeTextInput } from "./LakeTextInput";
|
|
11
|
-
import { ResponsiveContainer } from "./ResponsiveContainer";
|
|
12
|
-
import { TransitionView } from "./TransitionView";
|
|
13
|
-
const BREAKPOINT = 350;
|
|
14
|
-
const styles = StyleSheet.create({
|
|
15
|
-
container: {
|
|
16
|
-
flexDirection: "row",
|
|
17
|
-
alignItems: "center",
|
|
18
|
-
justifyContent: "flex-end",
|
|
19
|
-
flexGrow: 1,
|
|
20
|
-
flexShrink: 1,
|
|
21
|
-
},
|
|
22
|
-
input: {
|
|
23
|
-
transition: "300ms ease-in-out border-color",
|
|
24
|
-
justifyContent: "flex-end",
|
|
25
|
-
},
|
|
26
|
-
focus: {
|
|
27
|
-
outlineStyle: "none",
|
|
28
|
-
borderColor: colors.current.primary,
|
|
29
|
-
},
|
|
30
|
-
clearButton: {
|
|
31
|
-
padding: spacings[8],
|
|
32
|
-
borderRadius: radii[4],
|
|
33
|
-
},
|
|
34
|
-
openSearchFieldContainer: {
|
|
35
|
-
position: "absolute",
|
|
36
|
-
right: 0,
|
|
37
|
-
top: 0,
|
|
38
|
-
bottom: 0,
|
|
39
|
-
},
|
|
40
|
-
openSearchField: {
|
|
41
|
-
position: "absolute",
|
|
42
|
-
right: 0,
|
|
43
|
-
top: 0,
|
|
44
|
-
bottom: 0,
|
|
45
|
-
boxShadow: `0 0 20px 20px ${backgroundColor.default}`,
|
|
46
|
-
borderRadius: radii[6],
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
const CollapsibleSearchField = ({ inputRef, placeholder, initialValue, onChange, setFocused, hasFocus, clear, currentValue, renderEnd, }) => {
|
|
50
|
-
return (_jsxs(View, { children: [_jsx(LakeButton, { mode: "secondary", size: "small", ariaLabel: placeholder, icon: "search-filled", onPress: setFocused.on, pill: currentValue !== "" }), _jsx(TransitionView, { style: styles.openSearchFieldContainer, ...animations.fadeAndSlideInFromRight, children: hasFocus ? (_jsx(View, { style: styles.openSearchField, children: _jsx(ExpandedSearchField, { inputRef: inputRef, placeholder: placeholder, initialValue: initialValue, onChange: onChange, setFocused: setFocused, hasFocus: hasFocus, clear: clear, currentValue: currentValue, renderEnd: renderEnd }) })) : null })] }));
|
|
51
|
-
};
|
|
52
|
-
const ExpandedSearchField = ({ inputRef, placeholder, initialValue, onChange, setFocused, hasFocus, clear, renderEnd, currentValue, }) => {
|
|
53
|
-
const timeoutRef = useRef(null);
|
|
54
|
-
return (_jsx(LakeTextInput, { ref: inputRef, autoFocus: hasFocus, icon: "search-filled", placeholder: placeholder, defaultValue: initialValue, inputMode: "search", onChangeText: onChange, hideErrors: true, renderEnd: () => (_jsxs(_Fragment, { children: [isNotNullishOrEmpty(currentValue) && (_jsx(Pressable, { role: "button", style: styles.clearButton, onPress: () => {
|
|
55
|
-
if (timeoutRef.current != null) {
|
|
56
|
-
clearTimeout(timeoutRef.current);
|
|
57
|
-
}
|
|
58
|
-
clear();
|
|
59
|
-
}, children: _jsx(Icon, { name: "dismiss-filled", size: 12, color: colors.gray[500] }) })), renderEnd === null || renderEnd === void 0 ? void 0 : renderEnd()] })), onFocus: () => {
|
|
60
|
-
if (timeoutRef.current != null) {
|
|
61
|
-
clearTimeout(timeoutRef.current);
|
|
62
|
-
}
|
|
63
|
-
setFocused.on();
|
|
64
|
-
}, onBlur: () => {
|
|
65
|
-
timeoutRef.current = window.setTimeout(() => {
|
|
66
|
-
setFocused.off();
|
|
67
|
-
}, 300);
|
|
68
|
-
}, style: [styles.input, hasFocus && styles.focus] }));
|
|
69
|
-
};
|
|
70
|
-
export const LakeSearchField = ({ initialValue, placeholder, onChangeText, debounceDuration = 500, maxWidth: maxWidthProp = 350, renderEnd, children, }) => {
|
|
71
|
-
const [hasFocus, setFocused] = useBoolean(false);
|
|
72
|
-
const inputRef = useRef(null);
|
|
73
|
-
const [currentValue, setCurrentValue] = useState(initialValue);
|
|
74
|
-
const onChange = useDebounce((value) => {
|
|
75
|
-
onChangeText(value);
|
|
76
|
-
setCurrentValue(value.trim());
|
|
77
|
-
}, debounceDuration);
|
|
78
|
-
const clear = useCallback(() => {
|
|
79
|
-
if (inputRef.current != null) {
|
|
80
|
-
inputRef.current.clear();
|
|
81
|
-
inputRef.current.focus();
|
|
82
|
-
onChangeText("");
|
|
83
|
-
setCurrentValue("");
|
|
84
|
-
}
|
|
85
|
-
}, [onChangeText]);
|
|
86
|
-
const props = {
|
|
87
|
-
inputRef,
|
|
88
|
-
placeholder,
|
|
89
|
-
initialValue,
|
|
90
|
-
onChange,
|
|
91
|
-
setFocused,
|
|
92
|
-
hasFocus,
|
|
93
|
-
clear,
|
|
94
|
-
currentValue,
|
|
95
|
-
renderEnd,
|
|
96
|
-
};
|
|
97
|
-
const maxWidth = Math.max(maxWidthProp, BREAKPOINT);
|
|
98
|
-
return (_jsx(ResponsiveContainer, { breakpoint: BREAKPOINT, style: [styles.container, { maxWidth }], children: ({ large }) => {
|
|
99
|
-
const Component = large ? ExpandedSearchField : CollapsibleSearchField;
|
|
100
|
-
return (_jsxs(_Fragment, { children: [children, _jsx(Component, { ...props })] }));
|
|
101
|
-
} }));
|
|
102
|
-
};
|