@tapcart/mobile-components 0.9.0 → 0.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.
Files changed (35) hide show
  1. package/dist/components/hooks/use-scroll-direction-safe.d.ts +8 -0
  2. package/dist/components/hooks/use-scroll-direction-safe.d.ts.map +1 -0
  3. package/dist/components/hooks/use-scroll-direction-safe.js +75 -0
  4. package/dist/components/hooks/use-transition-compat.d.ts +12 -0
  5. package/dist/components/hooks/use-transition-compat.d.ts.map +1 -0
  6. package/dist/components/hooks/use-transition-compat.js +42 -0
  7. package/dist/components/hooks/use-transition-compat.test.d.ts +2 -0
  8. package/dist/components/hooks/use-transition-compat.test.d.ts.map +1 -0
  9. package/dist/components/hooks/use-transition-compat.test.js +179 -0
  10. package/dist/components/hooks/use-transition-shim.d.ts +7 -0
  11. package/dist/components/hooks/use-transition-shim.d.ts.map +1 -0
  12. package/dist/components/hooks/use-transition-shim.js +25 -0
  13. package/dist/components/ui/quantity-picker copy.d.ts +25 -0
  14. package/dist/components/ui/quantity-picker copy.d.ts.map +1 -0
  15. package/dist/components/ui/quantity-picker copy.js +125 -0
  16. package/dist/components/ui/quantity-pickerDEP.d.ts +23 -0
  17. package/dist/components/ui/quantity-pickerDEP.d.ts.map +1 -0
  18. package/dist/components/ui/quantity-pickerDEP.js +88 -0
  19. package/dist/components/ui/quantity-pickerDEPRECATED.d.ts +23 -0
  20. package/dist/components/ui/quantity-pickerDEPRECATED.d.ts.map +1 -0
  21. package/dist/components/ui/quantity-pickerDEPRECATED.js +88 -0
  22. package/dist/components/ui/wishlist-select.d.ts +54 -0
  23. package/dist/components/ui/wishlist-select.d.ts.map +1 -1
  24. package/dist/components/ui/wishlist-select.js +88 -12
  25. package/dist/components/ui/wishlist.d.ts +29 -1
  26. package/dist/components/ui/wishlist.d.ts.map +1 -1
  27. package/dist/components/ui/wishlist.js +57 -20
  28. package/dist/lib/state.d.ts +3 -0
  29. package/dist/lib/state.d.ts.map +1 -0
  30. package/dist/lib/state.js +9 -0
  31. package/dist/lib/variablesCart.util.d.ts.map +1 -1
  32. package/dist/lib/variablesCart.util.js +6 -6
  33. package/dist/lib/variablesCart.util.test.js +12 -3
  34. package/dist/styles.css +3 -11
  35. package/package.json +1 -1
@@ -0,0 +1,8 @@
1
+ type ScrollDirection = "up" | "down" | null;
2
+ interface ScrollData {
3
+ direction: ScrollDirection;
4
+ scrollY: number;
5
+ }
6
+ declare function useScrollDirectionSafe(threshold?: number): ScrollData;
7
+ export { useScrollDirectionSafe };
8
+ //# sourceMappingURL=use-scroll-direction-safe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-scroll-direction-safe.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-scroll-direction-safe.ts"],"names":[],"mappings":"AAWA,KAAK,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;AAE3C,UAAU,UAAU;IAClB,SAAS,EAAE,eAAe,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,iBAAS,sBAAsB,CAAC,SAAS,GAAE,MAAY,GAAG,UAAU,CA2EnE;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAA"}
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { useState, useEffect, useRef } from "react";
3
+ // Safe import of useTransition - will be undefined in older React versions
4
+ let useTransition;
5
+ try {
6
+ useTransition = require("react").useTransition;
7
+ }
8
+ catch (_a) {
9
+ // useTransition not available
10
+ }
11
+ function useScrollDirectionSafe(threshold = 100) {
12
+ const [scrollData, setScrollData] = useState({
13
+ direction: null,
14
+ scrollY: 0,
15
+ });
16
+ // Conditionally use useTransition if available
17
+ const transitionResult = useTransition === null || useTransition === void 0 ? void 0 : useTransition();
18
+ const [isPending, startTransition] = transitionResult || [
19
+ false,
20
+ (callback) => callback(),
21
+ ];
22
+ const scrollElement = useRef(null);
23
+ const hasScrollListener = useRef(false);
24
+ const lastScrollY = useRef(0);
25
+ const isBouncing = useRef(false);
26
+ useEffect(() => {
27
+ var _a;
28
+ if (typeof window === "undefined") {
29
+ return;
30
+ }
31
+ const updateScrollDirection = () => {
32
+ var _a, _b, _c;
33
+ if (document.body.hasAttribute("data-scroll-locked")) {
34
+ return;
35
+ }
36
+ const scrollY = ((_a = scrollElement.current) === null || _a === void 0 ? void 0 : _a.scrollTop) || 0;
37
+ const scrollHeight = ((_b = scrollElement.current) === null || _b === void 0 ? void 0 : _b.scrollHeight) || 0;
38
+ const clientHeight = ((_c = scrollElement.current) === null || _c === void 0 ? void 0 : _c.clientHeight) || 0;
39
+ // Detect if the user is at the top or bottom of the page
40
+ if (scrollY <= 0 || scrollY + clientHeight >= scrollHeight) {
41
+ isBouncing.current = true;
42
+ }
43
+ else {
44
+ isBouncing.current = false;
45
+ }
46
+ // Prevent updating scroll direction if the page is bouncing
47
+ if (!isBouncing.current &&
48
+ Math.abs(scrollY - lastScrollY.current) > threshold) {
49
+ startTransition(() => {
50
+ setScrollData({
51
+ direction: scrollY > lastScrollY.current ? "down" : "up",
52
+ scrollY,
53
+ });
54
+ });
55
+ lastScrollY.current = scrollY > 0 ? scrollY : 0;
56
+ }
57
+ };
58
+ if (!scrollElement.current) {
59
+ scrollElement.current = document.getElementById("tc-vgsl");
60
+ }
61
+ if (!hasScrollListener.current) {
62
+ (_a = scrollElement.current) === null || _a === void 0 ? void 0 : _a.addEventListener("scroll", updateScrollDirection, {
63
+ passive: true,
64
+ });
65
+ hasScrollListener.current = true;
66
+ }
67
+ return () => {
68
+ var _a;
69
+ (_a = scrollElement.current) === null || _a === void 0 ? void 0 : _a.removeEventListener("scroll", updateScrollDirection);
70
+ hasScrollListener.current = false;
71
+ };
72
+ }, [threshold, startTransition]);
73
+ return scrollData;
74
+ }
75
+ export { useScrollDirectionSafe };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * React version-aware useTransition compatibility hook
3
+ * Automatically detects React version and provides appropriate behavior
4
+ *
5
+ * Warning: This hook may cause your app to spontaneously time travel.
6
+ * Side effects include: temporal paradoxes and meeting your past self.
7
+ */
8
+ export declare function useTransitionCompat(): [
9
+ boolean,
10
+ (callback: () => void) => void
11
+ ];
12
+ //# sourceMappingURL=use-transition-compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-transition-compat.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-transition-compat.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,OAAO;IACP,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI;CAC/B,CAuCA"}
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import React from "react";
3
+ /**
4
+ * React version-aware useTransition compatibility hook
5
+ * Automatically detects React version and provides appropriate behavior
6
+ *
7
+ * Warning: This hook may cause your app to spontaneously time travel.
8
+ * Side effects include: temporal paradoxes and meeting your past self.
9
+ */
10
+ export function useTransitionCompat() {
11
+ const [fallbackIsPending, setFallbackIsPending] = React.useState(false);
12
+ const reactVersion = React.version;
13
+ const majorVersion = parseInt((reactVersion === null || reactVersion === void 0 ? void 0 : reactVersion.split(".")[0]) || "0", 10);
14
+ // Use native useTransition if available in React 18+
15
+ const hasNativeUseTransition = majorVersion >= 18 && "useTransition" in React;
16
+ const nativeUseTransition = hasNativeUseTransition
17
+ ? React.useTransition()
18
+ : null;
19
+ const startTransition = React.useCallback((callback) => {
20
+ if (nativeUseTransition) {
21
+ // Use native startTransition
22
+ nativeUseTransition[1](callback);
23
+ return;
24
+ }
25
+ setFallbackIsPending(true);
26
+ if (majorVersion >= 17) {
27
+ setTimeout(() => {
28
+ callback();
29
+ setFallbackIsPending(false);
30
+ }, 0);
31
+ }
32
+ else {
33
+ callback();
34
+ setFallbackIsPending(false);
35
+ }
36
+ }, [majorVersion, nativeUseTransition]);
37
+ // Return native pending state if available, otherwise fallback state
38
+ const isPending = nativeUseTransition
39
+ ? nativeUseTransition[0]
40
+ : fallbackIsPending;
41
+ return [isPending, startTransition];
42
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-transition-compat.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-transition-compat.test.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-transition-compat.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,179 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ /* eslint-disable no-undef */
11
+ import { renderHook, act } from "@testing-library/react";
12
+ import React from "react";
13
+ import { useTransitionCompat } from "./use-transition-compat";
14
+ // Mock React to control version and useTransition availability
15
+ const mockReact = React;
16
+ describe("useTransitionCompat", () => {
17
+ // Store original values to restore after tests
18
+ const originalVersion = React.version;
19
+ const originalUseTransition = React.useTransition;
20
+ afterEach(() => {
21
+ // Restore original React properties
22
+ mockReact.version = originalVersion;
23
+ if (originalUseTransition) {
24
+ mockReact.useTransition = originalUseTransition;
25
+ }
26
+ else {
27
+ delete mockReact.useTransition;
28
+ }
29
+ jest.clearAllTimers();
30
+ jest.useRealTimers();
31
+ });
32
+ describe("React 18+ with native useTransition", () => {
33
+ beforeEach(() => {
34
+ mockReact.version = "18.2.0";
35
+ mockReact.useTransition = jest.fn(() => [false, jest.fn()]);
36
+ });
37
+ it("should use native useTransition when available in React 18+", () => {
38
+ const mockStartTransition = jest.fn();
39
+ mockReact.useTransition = jest.fn(() => [false, mockStartTransition]);
40
+ const { result } = renderHook(() => useTransitionCompat());
41
+ const [isPending, startTransition] = result.current;
42
+ // Should initially not be pending
43
+ expect(isPending).toBe(false);
44
+ // Should call native useTransition
45
+ expect(mockReact.useTransition).toHaveBeenCalled();
46
+ // Test startTransition calls native function
47
+ const callback = jest.fn();
48
+ act(() => {
49
+ startTransition(callback);
50
+ });
51
+ expect(mockStartTransition).toHaveBeenCalledWith(callback);
52
+ });
53
+ it("should handle pending state from native useTransition", () => {
54
+ const mockStartTransition = jest.fn();
55
+ mockReact.useTransition = jest.fn(() => [true, mockStartTransition]);
56
+ const { result } = renderHook(() => useTransitionCompat());
57
+ const [isPending] = result.current;
58
+ expect(isPending).toBe(true);
59
+ });
60
+ });
61
+ describe("React 17 fallback behavior", () => {
62
+ beforeEach(() => {
63
+ mockReact.version = "17.0.2";
64
+ delete mockReact.useTransition;
65
+ jest.useFakeTimers();
66
+ });
67
+ it("should use setTimeout fallback for React 17", () => __awaiter(void 0, void 0, void 0, function* () {
68
+ const { result } = renderHook(() => useTransitionCompat());
69
+ const [initialPending, startTransition] = result.current;
70
+ // Initially should not be pending
71
+ expect(initialPending).toBe(false);
72
+ const callback = jest.fn();
73
+ // Start transition
74
+ act(() => {
75
+ startTransition(callback);
76
+ });
77
+ // Should be pending immediately after calling startTransition
78
+ expect(result.current[0]).toBe(true);
79
+ // Callback should not be called yet
80
+ expect(callback).not.toHaveBeenCalled();
81
+ // Fast-forward timers
82
+ act(() => {
83
+ jest.runAllTimers();
84
+ });
85
+ // Callback should now be called and pending should be false
86
+ expect(callback).toHaveBeenCalledTimes(1);
87
+ expect(result.current[0]).toBe(false);
88
+ }));
89
+ });
90
+ describe("React < 17 immediate execution", () => {
91
+ beforeEach(() => {
92
+ mockReact.version = "16.14.0";
93
+ delete mockReact.useTransition;
94
+ });
95
+ it("should execute callback immediately for React < 17", () => {
96
+ const { result } = renderHook(() => useTransitionCompat());
97
+ const [initialPending, startTransition] = result.current;
98
+ // Initially should not be pending
99
+ expect(initialPending).toBe(false);
100
+ const callback = jest.fn();
101
+ // Start transition
102
+ act(() => {
103
+ startTransition(callback);
104
+ });
105
+ // Callback should be called immediately and not be pending
106
+ expect(callback).toHaveBeenCalledTimes(1);
107
+ expect(result.current[0]).toBe(false);
108
+ });
109
+ });
110
+ describe("Edge cases", () => {
111
+ it("should handle missing React version gracefully", () => {
112
+ mockReact.version = undefined;
113
+ delete mockReact.useTransition;
114
+ const { result } = renderHook(() => useTransitionCompat());
115
+ const [isPending, startTransition] = result.current;
116
+ expect(isPending).toBe(false);
117
+ const callback = jest.fn();
118
+ act(() => {
119
+ startTransition(callback);
120
+ });
121
+ // Should execute immediately when version is unknown
122
+ expect(callback).toHaveBeenCalledTimes(1);
123
+ expect(result.current[0]).toBe(false);
124
+ });
125
+ it("should handle malformed version strings", () => {
126
+ mockReact.version = "not-a-version";
127
+ delete mockReact.useTransition;
128
+ const { result } = renderHook(() => useTransitionCompat());
129
+ const [isPending, startTransition] = result.current;
130
+ expect(isPending).toBe(false);
131
+ const callback = jest.fn();
132
+ act(() => {
133
+ startTransition(callback);
134
+ });
135
+ // Should execute immediately when version parsing fails
136
+ expect(callback).toHaveBeenCalledTimes(1);
137
+ expect(result.current[0]).toBe(false);
138
+ });
139
+ it("should handle React 18+ without useTransition (edge case)", () => {
140
+ mockReact.version = "18.0.0";
141
+ delete mockReact.useTransition;
142
+ jest.useFakeTimers();
143
+ const { result } = renderHook(() => useTransitionCompat());
144
+ const [initialPending, startTransition] = result.current;
145
+ expect(initialPending).toBe(false);
146
+ const callback = jest.fn();
147
+ act(() => {
148
+ startTransition(callback);
149
+ });
150
+ // Should fall back to setTimeout behavior even in React 18 if useTransition is missing
151
+ expect(result.current[0]).toBe(true);
152
+ act(() => {
153
+ jest.runAllTimers();
154
+ });
155
+ expect(callback).toHaveBeenCalledTimes(1);
156
+ expect(result.current[0]).toBe(false);
157
+ });
158
+ });
159
+ describe("Return value consistency", () => {
160
+ it("should always return tuple with boolean and function", () => {
161
+ mockReact.version = "18.2.0";
162
+ mockReact.useTransition = jest.fn(() => [false, jest.fn()]);
163
+ const { result } = renderHook(() => useTransitionCompat());
164
+ const [isPending, startTransition] = result.current;
165
+ expect(typeof isPending).toBe("boolean");
166
+ expect(typeof startTransition).toBe("function");
167
+ });
168
+ it("should maintain referential stability of startTransition function", () => {
169
+ mockReact.version = "17.0.0";
170
+ delete mockReact.useTransition;
171
+ const { result, rerender } = renderHook(() => useTransitionCompat());
172
+ const firstStartTransition = result.current[1];
173
+ rerender();
174
+ const secondStartTransition = result.current[1];
175
+ // Function reference should be stable due to useCallback
176
+ expect(firstStartTransition).toBe(secondStartTransition);
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shim for useTransition that falls back gracefully on older React versions
3
+ * In older versions, we just execute the callback immediately without transition
4
+ * @returns [isPending, startTransition] - same signature as React's useTransition
5
+ */
6
+ export declare function useTransitionShim(): [boolean, (callback: () => void) => void];
7
+ //# sourceMappingURL=use-transition-shim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-transition-shim.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-transition-shim.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC,CAoB7E"}
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { useTransition as useReactTransition } from "react";
3
+ /**
4
+ * Shim for useTransition that falls back gracefully on older React versions
5
+ * In older versions, we just execute the callback immediately without transition
6
+ * @returns [isPending, startTransition] - same signature as React's useTransition
7
+ */
8
+ export function useTransitionShim() {
9
+ // Check if useTransition exists (React 18+)
10
+ if (typeof useReactTransition === "function") {
11
+ try {
12
+ return useReactTransition();
13
+ }
14
+ catch (error) {
15
+ // Fallback if useTransition fails for any reason
16
+ console.warn("useTransition not available, falling back to immediate execution");
17
+ }
18
+ }
19
+ // Fallback for React < 18: execute immediately, never pending
20
+ const startTransition = (callback) => {
21
+ // Execute callback immediately in older React versions
22
+ callback();
23
+ };
24
+ return [false, startTransition];
25
+ }
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ export interface QuantityPickerProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ decreaseIconUrl: string;
4
+ increaseIconUrl: string;
5
+ deleteIconUrl: string;
6
+ isDeleteOnly: boolean;
7
+ iconColor: string;
8
+ onDecreaseClick: React.ReactEventHandler;
9
+ onIncreaseClick: React.ReactEventHandler;
10
+ isDecreaseDisabled?: boolean;
11
+ isIncreaseDisabled?: boolean;
12
+ loading?: boolean;
13
+ value: number;
14
+ onValueSet: (_: number) => void;
15
+ onAdjustQuantity: (_: number) => void;
16
+ className?: string;
17
+ inputStyle?: React.CSSProperties;
18
+ buttonStyle?: React.CSSProperties;
19
+ buttonCornerRadius?: number;
20
+ debounceTime?: number;
21
+ max?: number;
22
+ }
23
+ declare const QuantityPicker: React.ForwardRefExoticComponent<QuantityPickerProps & React.RefAttributes<HTMLDivElement>>;
24
+ export { QuantityPicker };
25
+ //# sourceMappingURL=quantity-picker%20copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quantity-picker copy.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-picker copy.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,mBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5C,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAChC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA0ED,QAAA,MAAM,cAAc,4FAiLnB,CAAA;AAID,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,125 @@
1
+ "use client";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
+ import * as React from "react";
15
+ import { cn } from "../../lib/utils";
16
+ import { Icon } from "./icon";
17
+ import { useTap } from "./tap";
18
+ import { LoadingDots } from "./loading-dots";
19
+ import debounce from "lodash/debounce";
20
+ const IconButton = ({ iconUrl, iconColor, handler, className, style, disabled }) => {
21
+ const { onTap, isPressed, ref: tapRef } = useTap();
22
+ const [isButtonPressed, setIsButtonPressed] = React.useState(false);
23
+ // Handle press state manually for the invisible button
24
+ const handleMouseDown = React.useCallback(() => {
25
+ if (!disabled)
26
+ setIsButtonPressed(true);
27
+ }, [disabled]);
28
+ const handleMouseUp = React.useCallback(() => {
29
+ setIsButtonPressed(false);
30
+ }, []);
31
+ // Calculate the visual styles for the button
32
+ const visualButtonStyle = Object.assign({}, style);
33
+ return (_jsxs("div", Object.assign({ className: "relative h-7 w-7", style: { touchAction: "manipulation" } }, { children: [_jsx("div", Object.assign({ className: cn("absolute inset-0 flex items-center justify-center bg-stateColors-skeleton border border-coreColors-dividingLines", className, disabled ? "opacity-50" : ""), style: visualButtonStyle, "aria-hidden": "true" }, { children: _jsx(Icon, { url: iconUrl, size: "sm", strokeColor: iconColor, strokeWidth: 4, style: {
34
+ opacity: isPressed || isButtonPressed ? 0.7 : 1,
35
+ } }) })), _jsx("button", { onClick: onTap(handler), onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleMouseDown, onTouchEnd: handleMouseUp, onTouchCancel: handleMouseUp, ref: tapRef, className: "absolute cursor-pointer", style: {
36
+ top: "-8px",
37
+ right: "-8px",
38
+ bottom: "-8px",
39
+ left: "-8px",
40
+ opacity: 0,
41
+ zIndex: 10,
42
+ }, disabled: disabled, "aria-label": "Quantity button" })] })));
43
+ };
44
+ const QuantityPicker = React.forwardRef((_a, ref) => {
45
+ var { className, decreaseIconUrl, increaseIconUrl, deleteIconUrl, isDeleteOnly = false, iconColor, onDecreaseClick, onIncreaseClick, isDecreaseDisabled, isIncreaseDisabled, value, onValueSet, onAdjustQuantity, inputStyle, buttonStyle, buttonCornerRadius = 4, max = 99, loading = false, debounceTime = 300 } = _a, props = __rest(_a, ["className", "decreaseIconUrl", "increaseIconUrl", "deleteIconUrl", "isDeleteOnly", "iconColor", "onDecreaseClick", "onIncreaseClick", "isDecreaseDisabled", "isIncreaseDisabled", "value", "onValueSet", "onAdjustQuantity", "inputStyle", "buttonStyle", "buttonCornerRadius", "max", "loading", "debounceTime"]);
46
+ const [isFocused, setIsFocused] = React.useState(false);
47
+ const pendingQuantityAdjustment = React.useRef(0);
48
+ const [localInputValue, setLocalInputValue] = React.useState(value);
49
+ // Update local state when external value changes
50
+ React.useEffect(() => {
51
+ if (pendingQuantityAdjustment.current === 0) {
52
+ setLocalInputValue(value);
53
+ }
54
+ }, [value]);
55
+ // Create debounced function for applying changes
56
+ const debouncedApplyChanges = React.useMemo(() => debounce(() => {
57
+ if (pendingQuantityAdjustment.current !== 0) {
58
+ onAdjustQuantity(pendingQuantityAdjustment.current);
59
+ pendingQuantityAdjustment.current = 0;
60
+ }
61
+ }, debounceTime), [debounceTime, onAdjustQuantity]);
62
+ const leftButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderTopLeftRadius: buttonCornerRadius
63
+ ? `${buttonCornerRadius}px`
64
+ : undefined, borderBottomLeftRadius: buttonCornerRadius
65
+ ? `${buttonCornerRadius}px`
66
+ : undefined });
67
+ const rightButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderTopRightRadius: buttonCornerRadius
68
+ ? `${buttonCornerRadius}px`
69
+ : undefined, borderBottomRightRadius: buttonCornerRadius
70
+ ? `${buttonCornerRadius}px`
71
+ : undefined });
72
+ const singleButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderRadius: buttonCornerRadius ? `${buttonCornerRadius}px` : undefined });
73
+ const adjustQuantity = (amount) => {
74
+ const newAmount = localInputValue + amount;
75
+ const clampedNewAmount = Math.min(Math.max(newAmount, 0), max);
76
+ // Update local value immediately
77
+ setLocalInputValue(clampedNewAmount);
78
+ // Track pending change for batched update
79
+ pendingQuantityAdjustment.current += amount;
80
+ // Trigger debounced API update
81
+ debouncedApplyChanges();
82
+ };
83
+ const handleDecreaseClick = (e) => {
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+ adjustQuantity(-1);
87
+ onDecreaseClick(e);
88
+ };
89
+ const handleIncreaseClick = (e) => {
90
+ e.preventDefault();
91
+ e.stopPropagation();
92
+ adjustQuantity(1);
93
+ onIncreaseClick(e);
94
+ };
95
+ return (_jsxs("div", Object.assign({ className: cn("flex relative", className), ref: ref }, props, { children: [isDeleteOnly ? (_jsx(IconButton, { handler: handleDecreaseClick, iconUrl: deleteIconUrl, iconColor: iconColor, style: singleButtonStyle })) : (_jsxs(_Fragment, { children: [_jsx(IconButton, { handler: handleDecreaseClick, iconUrl: localInputValue === 1 ? deleteIconUrl : decreaseIconUrl, iconColor: iconColor, style: leftButtonStyle, disabled: isDecreaseDisabled || loading }), _jsx("input", { type: "number", pattern: "[0-9]*", disabled: loading, max: max, value: localInputValue, onBlur: (e) => {
96
+ setIsFocused(false);
97
+ const parsedValue = parseInt(e.target.value) || 0;
98
+ const clampedValue = Math.min(parsedValue, max);
99
+ // Cancel any pending debounced operations
100
+ debouncedApplyChanges.cancel();
101
+ // Update and apply changes
102
+ const delta = clampedValue - value;
103
+ pendingQuantityAdjustment.current = delta;
104
+ setLocalInputValue(clampedValue);
105
+ onValueSet(clampedValue);
106
+ }, onFocus: () => {
107
+ setIsFocused(true);
108
+ }, onChange: (e) => {
109
+ const inputValue = e.target.value;
110
+ if (inputValue === "") {
111
+ setLocalInputValue(0);
112
+ }
113
+ else {
114
+ const parsedValue = parseInt(inputValue);
115
+ if (!isNaN(parsedValue)) {
116
+ const clampedValue = Math.min(parsedValue, max);
117
+ setLocalInputValue(clampedValue);
118
+ }
119
+ }
120
+ }, className: "w-8 h-7 focus-visible:outline-no ne text-center bg-coreColors-inputBackground text-textColors-primaryColor border-t border-b border-coreColors-dividingLines", style: Object.assign(Object.assign({}, inputStyle), { borderRadius: (inputStyle === null || inputStyle === void 0 ? void 0 : inputStyle.borderRadius)
121
+ ? `${inputStyle.borderRadius}px`
122
+ : 0 }), inputMode: "numeric" }), _jsx(IconButton, { handler: handleIncreaseClick, iconUrl: increaseIconUrl, iconColor: iconColor, style: rightButtonStyle, disabled: isIncreaseDisabled || loading || localInputValue >= max })] })), _jsx(LoadingDots, { show: loading, size: 1, iconColor: iconColor })] })));
123
+ });
124
+ QuantityPicker.displayName = "QuantityPicker";
125
+ export { QuantityPicker };
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ export interface QuantityPickerProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ decreaseIconUrl: string;
4
+ increaseIconUrl: string;
5
+ deleteIconUrl: string;
6
+ isDeleteOnly: boolean;
7
+ iconColor: string;
8
+ onDecreaseClick: React.ReactEventHandler;
9
+ onIncreaseClick: React.ReactEventHandler;
10
+ isDecreaseDisabled?: boolean;
11
+ isIncreaseDisabled?: boolean;
12
+ loading?: boolean;
13
+ value: number;
14
+ setValue: (_: number) => void;
15
+ className?: string;
16
+ inputStyle?: React.CSSProperties;
17
+ buttonStyle?: React.CSSProperties;
18
+ buttonCornerRadius?: number;
19
+ max?: number;
20
+ }
21
+ declare const QuantityPicker: React.ForwardRefExoticComponent<QuantityPickerProps & React.RefAttributes<HTMLDivElement>>;
22
+ export { QuantityPicker };
23
+ //# sourceMappingURL=quantity-pickerDEP.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quantity-pickerDEP.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-pickerDEP.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,WAAW,mBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5C,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAChC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA0ED,QAAA,MAAM,cAAc,4FA8HnB,CAAA;AAID,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
+ import * as React from "react";
15
+ import { cn } from "../../lib/utils";
16
+ import { Icon } from "./icon";
17
+ import { useTap } from "./tap";
18
+ import { LoadingDots } from "./loading-dots";
19
+ const IconButton = ({ iconUrl, iconColor, handler, className, style, disabled }) => {
20
+ const { onTap, isPressed, ref: tapRef } = useTap();
21
+ const [isButtonPressed, setIsButtonPressed] = React.useState(false);
22
+ // Handle press state manually for the invisible button
23
+ const handleMouseDown = React.useCallback(() => {
24
+ if (!disabled)
25
+ setIsButtonPressed(true);
26
+ }, [disabled]);
27
+ const handleMouseUp = React.useCallback(() => {
28
+ setIsButtonPressed(false);
29
+ }, []);
30
+ // Calculate the visual styles for the button
31
+ const visualButtonStyle = Object.assign({}, style);
32
+ return (_jsxs("div", Object.assign({ className: "relative h-7 w-7", style: { touchAction: "manipulation" } }, { children: [_jsx("div", Object.assign({ className: cn("absolute inset-0 flex items-center justify-center bg-stateColors-skeleton border border-coreColors-dividingLines", className, disabled ? "opacity-50" : ""), style: visualButtonStyle, "aria-hidden": "true" }, { children: _jsx(Icon, { url: iconUrl, size: "sm", strokeColor: iconColor, strokeWidth: 4, style: {
33
+ opacity: isPressed || isButtonPressed ? 0.7 : 1,
34
+ } }) })), _jsx("button", { onClick: onTap(handler), onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleMouseDown, onTouchEnd: handleMouseUp, onTouchCancel: handleMouseUp, ref: tapRef, className: "absolute cursor-pointer", style: {
35
+ top: "-8px",
36
+ right: "-8px",
37
+ bottom: "-8px",
38
+ left: "-8px",
39
+ opacity: 0,
40
+ zIndex: 10,
41
+ }, disabled: disabled, "aria-label": "Quantity button" })] })));
42
+ };
43
+ const QuantityPicker = React.forwardRef((_a, ref) => {
44
+ var { className, decreaseIconUrl, increaseIconUrl, deleteIconUrl, isDeleteOnly = false, iconColor, onDecreaseClick, onIncreaseClick, isDecreaseDisabled, isIncreaseDisabled, value, setValue, inputStyle, buttonStyle, buttonCornerRadius = 4, max = 99, loading = false } = _a, props = __rest(_a, ["className", "decreaseIconUrl", "increaseIconUrl", "deleteIconUrl", "isDeleteOnly", "iconColor", "onDecreaseClick", "onIncreaseClick", "isDecreaseDisabled", "isIncreaseDisabled", "value", "setValue", "inputStyle", "buttonStyle", "buttonCornerRadius", "max", "loading"]);
45
+ const [isFocused, setIsFocused] = React.useState(false);
46
+ const [localValue, setLocalValue] = React.useState(value.toString());
47
+ React.useEffect(() => {
48
+ if (!isFocused) {
49
+ setLocalValue(value.toString());
50
+ }
51
+ }, [value, isFocused]);
52
+ const leftButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderTopLeftRadius: buttonCornerRadius
53
+ ? `${buttonCornerRadius}px`
54
+ : undefined, borderBottomLeftRadius: buttonCornerRadius
55
+ ? `${buttonCornerRadius}px`
56
+ : undefined });
57
+ const rightButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderTopRightRadius: buttonCornerRadius
58
+ ? `${buttonCornerRadius}px`
59
+ : undefined, borderBottomRightRadius: buttonCornerRadius
60
+ ? `${buttonCornerRadius}px`
61
+ : undefined });
62
+ const singleButtonStyle = Object.assign(Object.assign({}, buttonStyle), { borderRadius: buttonCornerRadius ? `${buttonCornerRadius}px` : undefined });
63
+ return (_jsxs("div", Object.assign({ className: cn("flex relative", className), ref: ref }, props, { children: [isDeleteOnly ? (_jsx(IconButton, { handler: onDecreaseClick, iconUrl: deleteIconUrl, iconColor: iconColor, style: singleButtonStyle })) : (_jsxs(_Fragment, { children: [_jsx(IconButton, { handler: onDecreaseClick, iconUrl: value === 1 ? deleteIconUrl : decreaseIconUrl, iconColor: iconColor, style: leftButtonStyle, disabled: isDecreaseDisabled || loading }), _jsx("input", { type: "number", pattern: "[0-9]*", disabled: loading, max: max, value: localValue, onBlur: (e) => {
64
+ setIsFocused(false);
65
+ const parsedValue = parseInt(e.target.value) || 0;
66
+ const clampedValue = Math.min(parsedValue, max);
67
+ setValue(clampedValue);
68
+ setLocalValue(clampedValue.toString());
69
+ }, onFocus: () => {
70
+ setIsFocused(true);
71
+ }, onChange: (e) => {
72
+ const inputValue = e.target.value;
73
+ if (inputValue === "") {
74
+ setLocalValue("");
75
+ }
76
+ else {
77
+ const parsedValue = parseInt(inputValue);
78
+ if (!isNaN(parsedValue)) {
79
+ const clampedValue = Math.min(parsedValue, max);
80
+ setLocalValue(clampedValue.toString());
81
+ }
82
+ }
83
+ }, className: "w-8 h-7 focus-visible:outline-none text-center bg-coreColors-inputBackground text-textColors-primaryColor border-t border-b border-coreColors-dividingLines", style: Object.assign(Object.assign({}, inputStyle), { borderRadius: (inputStyle === null || inputStyle === void 0 ? void 0 : inputStyle.borderRadius)
84
+ ? `${inputStyle.borderRadius}px`
85
+ : 0 }), inputMode: "numeric" }), _jsx(IconButton, { handler: onIncreaseClick, iconUrl: increaseIconUrl, iconColor: iconColor, style: rightButtonStyle, disabled: isIncreaseDisabled || loading })] })), _jsx(LoadingDots, { show: loading, size: 1, iconColor: iconColor })] })));
86
+ });
87
+ QuantityPicker.displayName = "QuantityPicker";
88
+ export { QuantityPicker };