@entropy-softworks/ui 2026.9.1 → 2026.9.2
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.
|
@@ -7,12 +7,17 @@ import React from "react";
|
|
|
7
7
|
*
|
|
8
8
|
* # Sizing
|
|
9
9
|
*
|
|
10
|
-
* The pad
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* The pad fills the width it is given instead of sitting at a fixed 288px. On a
|
|
11
|
+
* modern phone a fixed pad reads as a small control marooned in the middle of a
|
|
12
|
+
* large screen — and this is the primary way into the app for a device-only
|
|
13
|
+
* vault, so it should feel like the screen's purpose rather than a widget on it.
|
|
14
|
+
* Keys grow with the available width and stop at `MAX_KEY` so a tablet does not
|
|
15
|
+
* get comedy-sized buttons.
|
|
16
|
+
*
|
|
17
|
+
* The width comes from the pad's own `onLayout`, not from the viewport. It is
|
|
18
|
+
* usually inside a card inside a padded screen, and every one of those layers
|
|
19
|
+
* takes width the window knows nothing about — sizing off the window overflowed
|
|
20
|
+
* the card it sat in.
|
|
16
21
|
*
|
|
17
22
|
* # Why the keys have a fill
|
|
18
23
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pin-pad.d.ts","sourceRoot":"","sources":["../../src/components/pin-pad.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAOxE
|
|
1
|
+
{"version":3,"file":"pin-pad.d.ts","sourceRoot":"","sources":["../../src/components/pin-pad.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAOxE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAWD,wBAAgB,MAAM,CAAC,EACrB,MAAU,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,KAAK,EACL,OAAc,GACf,EAAE,WAAW,qBAwJb;AAmDD,eAAe,MAAM,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { Platform, Pressable, Text,
|
|
2
|
+
import { Platform, Pressable, Text, View } from "react-native";
|
|
3
3
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
4
4
|
import * as Haptics from "expo-haptics";
|
|
5
5
|
import { useColorScheme } from "nativewind";
|
|
@@ -9,18 +9,25 @@ const KEYS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
|
|
9
9
|
const MIN_KEY = 76;
|
|
10
10
|
const MAX_KEY = 104;
|
|
11
11
|
const GAP = 16;
|
|
12
|
-
/**
|
|
13
|
-
const
|
|
12
|
+
/** Assumed width for the single frame before the first measurement lands. */
|
|
13
|
+
const ASSUMED_WIDTH = 320;
|
|
14
14
|
export function PinPad({ length = 6, onComplete, onChange, onBiometric, resetKey, error, haptics = true, }) {
|
|
15
15
|
const { colorScheme } = useColorScheme();
|
|
16
16
|
const isDark = colorScheme === "dark";
|
|
17
17
|
const iconColor = isDark ? "#FFFFFF" : "#000000";
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const [available, setAvailable] = useState(0);
|
|
19
|
+
const onMeasure = (event) => {
|
|
20
|
+
const measured = event.nativeEvent.layout.width;
|
|
21
|
+
if (measured > 0 && Math.abs(measured - available) > 1) {
|
|
22
|
+
setAvailable(measured);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// Three keys and two gaps have to fit in the width this pad was actually
|
|
26
|
+
// given. Clamped at both ends: MIN_KEY keeps the pad usable in a narrow
|
|
27
|
+
// column even if that means it overflows a little, MAX_KEY stops a tablet
|
|
28
|
+
// from rendering three dinner plates.
|
|
29
|
+
const width = available || ASSUMED_WIDTH;
|
|
30
|
+
const keySize = Math.max(MIN_KEY, Math.min(MAX_KEY, Math.floor((width - 2 * GAP) / 3)));
|
|
24
31
|
const padWidth = keySize * 3 + GAP * 2;
|
|
25
32
|
const dotSize = Math.round(keySize * 0.17);
|
|
26
33
|
const [pin, setPin] = useState("");
|
|
@@ -103,7 +110,7 @@ export function PinPad({ length = 6, onComplete, onChange, onBiometric, resetKey
|
|
|
103
110
|
window.addEventListener("keydown", onKey);
|
|
104
111
|
return () => window.removeEventListener("keydown", onKey);
|
|
105
112
|
}, [press, back, onComplete, length]);
|
|
106
|
-
return (<View className="items-center gap-8">
|
|
113
|
+
return (<View className="w-full items-center gap-8" onLayout={onMeasure}>
|
|
107
114
|
{/* dots */}
|
|
108
115
|
<View className="flex-row" style={{ gap: Math.round(dotSize * 0.9) }}>
|
|
109
116
|
{Array.from({ length }).map((_, i) => (<View key={i} style={{ width: dotSize, height: dotSize, borderRadius: dotSize / 2 }} className={i < pin.length
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pin-pad.jsx","sourceRoot":"","sources":["../../src/components/pin-pad.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,
|
|
1
|
+
{"version":3,"file":"pin-pad.jsx","sourceRoot":"","sources":["../../src/components/pin-pad.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { Platform, Pressable, Text,
|
|
2
|
+
import { type LayoutChangeEvent, Platform, Pressable, Text, View } from "react-native";
|
|
3
3
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
4
4
|
import * as Haptics from "expo-haptics";
|
|
5
5
|
import { useColorScheme } from "nativewind";
|
|
@@ -13,12 +13,17 @@ import { Delete, Fingerprint } from "lucide-react-native";
|
|
|
13
13
|
*
|
|
14
14
|
* # Sizing
|
|
15
15
|
*
|
|
16
|
-
* The pad
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* The pad fills the width it is given instead of sitting at a fixed 288px. On a
|
|
17
|
+
* modern phone a fixed pad reads as a small control marooned in the middle of a
|
|
18
|
+
* large screen — and this is the primary way into the app for a device-only
|
|
19
|
+
* vault, so it should feel like the screen's purpose rather than a widget on it.
|
|
20
|
+
* Keys grow with the available width and stop at `MAX_KEY` so a tablet does not
|
|
21
|
+
* get comedy-sized buttons.
|
|
22
|
+
*
|
|
23
|
+
* The width comes from the pad's own `onLayout`, not from the viewport. It is
|
|
24
|
+
* usually inside a card inside a padded screen, and every one of those layers
|
|
25
|
+
* takes width the window knows nothing about — sizing off the window overflowed
|
|
26
|
+
* the card it sat in.
|
|
22
27
|
*
|
|
23
28
|
* # Why the keys have a fill
|
|
24
29
|
*
|
|
@@ -49,8 +54,8 @@ const KEYS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] as const;
|
|
|
49
54
|
const MIN_KEY = 76;
|
|
50
55
|
const MAX_KEY = 104;
|
|
51
56
|
const GAP = 16;
|
|
52
|
-
/**
|
|
53
|
-
const
|
|
57
|
+
/** Assumed width for the single frame before the first measurement lands. */
|
|
58
|
+
const ASSUMED_WIDTH = 320;
|
|
54
59
|
|
|
55
60
|
export function PinPad({
|
|
56
61
|
length = 6,
|
|
@@ -64,16 +69,20 @@ export function PinPad({
|
|
|
64
69
|
const { colorScheme } = useColorScheme();
|
|
65
70
|
const isDark = colorScheme === "dark";
|
|
66
71
|
const iconColor = isDark ? "#FFFFFF" : "#000000";
|
|
67
|
-
const
|
|
72
|
+
const [available, setAvailable] = useState(0);
|
|
73
|
+
const onMeasure = (event: LayoutChangeEvent) => {
|
|
74
|
+
const measured = event.nativeEvent.layout.width;
|
|
75
|
+
if (measured > 0 && Math.abs(measured - available) > 1) {
|
|
76
|
+
setAvailable(measured);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
68
79
|
|
|
69
|
-
// Three keys and two gaps have to fit
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
Math.min(MAX_KEY, Math.floor((width - SIDE_PADDING - 2 * GAP) / 3))
|
|
76
|
-
);
|
|
80
|
+
// Three keys and two gaps have to fit in the width this pad was actually
|
|
81
|
+
// given. Clamped at both ends: MIN_KEY keeps the pad usable in a narrow
|
|
82
|
+
// column even if that means it overflows a little, MAX_KEY stops a tablet
|
|
83
|
+
// from rendering three dinner plates.
|
|
84
|
+
const width = available || ASSUMED_WIDTH;
|
|
85
|
+
const keySize = Math.max(MIN_KEY, Math.min(MAX_KEY, Math.floor((width - 2 * GAP) / 3)));
|
|
77
86
|
const padWidth = keySize * 3 + GAP * 2;
|
|
78
87
|
const dotSize = Math.round(keySize * 0.17);
|
|
79
88
|
|
|
@@ -165,7 +174,7 @@ export function PinPad({
|
|
|
165
174
|
}, [press, back, onComplete, length]);
|
|
166
175
|
|
|
167
176
|
return (
|
|
168
|
-
<View className="items-center gap-8">
|
|
177
|
+
<View className="w-full items-center gap-8" onLayout={onMeasure}>
|
|
169
178
|
{/* dots */}
|
|
170
179
|
<View className="flex-row" style={{ gap: Math.round(dotSize * 0.9) }}>
|
|
171
180
|
{Array.from({ length }).map((_, i) => (
|