ps99-api 2.6.5 → 2.7.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.
@@ -13,6 +13,7 @@ import { FixedSizeGrid, FixedSizeList } from "./ReactWindowMock";
13
13
  import AutoSizer from "./AutoSizer";
14
14
  import { useScrollPersistence } from "../context/ScrollContext";
15
15
  import { useCollapsibleHeader } from '../hooks/useCollapsibleHeader';
16
+ import { usePullToRefresh } from '../hooks/usePullToRefresh';
16
17
  import { formatGigantix } from "../utils/gigantix";
17
18
 
18
19
  // const FixedSizeGrid = Grid;
@@ -491,6 +492,20 @@ const CollectionConfigIndex: React.FC<CollectionConfigIndexProps> = () => {
491
492
  disabled: isShortContent // Disable collapsing if content is short
492
493
  });
493
494
 
495
+ // Pull To Refresh Logic
496
+ const { isRefreshing, pullDistance, onTouchStart, onTouchMove, onTouchEnd, updateScrollTop } = usePullToRefresh({
497
+ onRefresh: async () => {
498
+ // Simple reload to fetch new data
499
+ window.location.reload();
500
+ }
501
+ });
502
+
503
+ // Update scroll top for PTR
504
+ const onScroll = (scrollInfo: { scrollOffset?: number, scrollTop?: number, scrollHeight?: number, clientHeight?: number }) => {
505
+ handleScroll(scrollInfo);
506
+ updateScrollTop(scrollInfo.scrollTop ?? scrollInfo.scrollOffset ?? 0);
507
+ };
508
+
494
509
  // Loading State
495
510
  if (loading) {
496
511
  return (
@@ -656,6 +671,31 @@ const CollectionConfigIndex: React.FC<CollectionConfigIndexProps> = () => {
656
671
  position: 'relative' // Context for absolute header
657
672
  }}
658
673
  >
674
+ {/* Pull To Refresh Indicator */}
675
+ {(pullDistance > 0 || isRefreshing) && (
676
+ <div style={{
677
+ position: 'absolute',
678
+ top: showHeader ? headerHeight + 5 : 0, // Adjust based on header
679
+ left: 0,
680
+ right: 0,
681
+ height: isRefreshing ? 60 : pullDistance,
682
+ display: 'flex',
683
+ alignItems: 'center',
684
+ justifyContent: 'center',
685
+ overflow: 'hidden',
686
+ backgroundColor: '#f5f5f5',
687
+ zIndex: 5,
688
+ transition: isDragging.current ? 'none' : 'height 0.3s ease'
689
+ }}>
690
+ {isRefreshing ? (
691
+ <div className="spinner" style={{ width: 24, height: 24, border: '3px solid #ccc', borderTopColor: '#333', borderRadius: '50%' }}></div>
692
+ ) : (
693
+ <span style={{ opacity: Math.min(pullDistance / 60, 1), transform: `rotate(${pullDistance * 2}deg)` }}>
694
+ ⬇️
695
+ </span>
696
+ )}
697
+ </div>
698
+ )}
659
699
  {/* Header Bar inside Window */}
660
700
  <div
661
701
  ref={headerRef}
@@ -828,8 +868,11 @@ const CollectionConfigIndex: React.FC<CollectionConfigIndexProps> = () => {
828
868
  itemSize={80} // List view row height
829
869
  width={width}
830
870
  initialScrollOffset={initialScrollOffset}
831
- onScroll={handleScroll}
871
+ onScroll={onScroll} // Use wrapped handler
832
872
  bottomPadding={120} // Account for Android footer/nav
873
+ onTouchStart={onTouchStart}
874
+ onTouchMove={onTouchMove}
875
+ onTouchEnd={onTouchEnd}
833
876
  itemData={{
834
877
  items: finalItems,
835
878
  navigate,
@@ -861,9 +904,12 @@ const CollectionConfigIndex: React.FC<CollectionConfigIndexProps> = () => {
861
904
  rowHeight={220}
862
905
  width={width}
863
906
  initialScrollOffset={initialScrollOffset}
864
- onScroll={handleScroll}
907
+ onScroll={onScroll} // Use wrapped handler
865
908
  style={{ overflowX: "hidden" }}
866
909
  bottomPadding={120} // Account for Android footer/nav
910
+ onTouchStart={onTouchStart}
911
+ onTouchMove={onTouchMove}
912
+ onTouchEnd={onTouchEnd}
867
913
  itemData={{
868
914
  items: finalItems,
869
915
  columnCount: colCount,
@@ -1,6 +1,6 @@
1
1
  import React, { useRef, useEffect } from 'react';
2
2
 
3
- export const FixedSizeList = ({ children, itemCount, itemSize, height, width, onScroll, initialScrollOffset, itemData, bottomPadding = 0 }: any) => {
3
+ export const FixedSizeList = ({ children, itemCount, itemSize, height, width, onScroll, initialScrollOffset, itemData, bottomPadding = 0, onTouchStart, onTouchMove, onTouchEnd }: any) => {
4
4
  const items = [];
5
5
  for (let i = 0; i < itemCount; i++) {
6
6
  items.push(
@@ -27,6 +27,9 @@ export const FixedSizeList = ({ children, itemCount, itemSize, height, width, on
27
27
  scrollHeight: e.currentTarget.scrollHeight,
28
28
  clientHeight: e.currentTarget.clientHeight
29
29
  })}
30
+ onTouchStart={onTouchStart}
31
+ onTouchMove={onTouchMove}
32
+ onTouchEnd={onTouchEnd}
30
33
  >
31
34
  {items}
32
35
  {bottomPadding > 0 && <div style={{ height: bottomPadding }} />}
@@ -34,7 +37,7 @@ export const FixedSizeList = ({ children, itemCount, itemSize, height, width, on
34
37
  );
35
38
  };
36
39
 
37
- export const FixedSizeGrid = ({ children, columnCount, rowCount, columnWidth, rowHeight, height, width, onScroll, initialScrollOffset, itemData, style, bottomPadding = 0 }: any) => {
40
+ export const FixedSizeGrid = ({ children, columnCount, rowCount, columnWidth, rowHeight, height, width, onScroll, initialScrollOffset, itemData, style, bottomPadding = 0, onTouchStart, onTouchMove, onTouchEnd }: any) => {
38
41
  const items = [];
39
42
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
40
43
  for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
@@ -73,6 +76,9 @@ export const FixedSizeGrid = ({ children, columnCount, rowCount, columnWidth, ro
73
76
  scrollHeight: e.currentTarget.scrollHeight,
74
77
  clientHeight: e.currentTarget.clientHeight
75
78
  })}
79
+ onTouchStart={onTouchStart}
80
+ onTouchMove={onTouchMove}
81
+ onTouchEnd={onTouchEnd}
76
82
  >
77
83
  <div style={{ height: rowCount * rowHeight + bottomPadding, width: columnWidth * columnCount }}>
78
84
  {items}
@@ -0,0 +1,87 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+
3
+ interface PullToRefreshOptions {
4
+ onRefresh: () => Promise<void> | void;
5
+ threshold?: number; // px to pull down to trigger
6
+ }
7
+
8
+ export const usePullToRefresh = ({ onRefresh, threshold = 80 }: PullToRefreshOptions) => {
9
+ const [isRefreshing, setIsRefreshing] = useState(false);
10
+ const [pullDistance, setPullDistance] = useState(0);
11
+ const startY = useRef<number>(0);
12
+ const isDragging = useRef(false);
13
+
14
+ // We need to know the scroll position to only allow pull when at top
15
+ const scrollTopRef = useRef(0);
16
+
17
+ const onTouchStart = (e: React.TouchEvent) => {
18
+ if (scrollTopRef.current === 0) {
19
+ startY.current = e.touches[0].clientY;
20
+ isDragging.current = true;
21
+ }
22
+ };
23
+
24
+ const onTouchMove = (e: React.TouchEvent) => {
25
+ if (!isDragging.current) return;
26
+ if (scrollTopRef.current > 0) {
27
+ isDragging.current = false;
28
+ setPullDistance(0);
29
+ return;
30
+ }
31
+
32
+ const currentY = e.touches[0].clientY;
33
+ const diff = currentY - startY.current;
34
+
35
+ if (diff > 0) {
36
+ // Resistance effect
37
+ setPullDistance(Math.min(diff * 0.5, threshold * 1.5));
38
+ // Prevent native scroll if we are pulling down?
39
+ // Might interfere with normal scrolling if not careful.
40
+ // But since scrollTop is 0, pulling down usually does nothing in overflow:auto unless overscroll-behavior is set.
41
+ }
42
+ };
43
+
44
+ const onTouchEnd = async () => {
45
+ if (!isDragging.current) return;
46
+ isDragging.current = false;
47
+
48
+ if (pullDistance > threshold) {
49
+ setIsRefreshing(true);
50
+ setPullDistance(threshold); // Snap to threshold
51
+ try {
52
+ await onRefresh();
53
+ } finally {
54
+ setIsRefreshing(false);
55
+ setPullDistance(0);
56
+ }
57
+ } else {
58
+ setPullDistance(0);
59
+ }
60
+ };
61
+
62
+ const updateScrollTop = (val: number) => {
63
+ scrollTopRef.current = val;
64
+ };
65
+
66
+ // Expose boolean for UI
67
+ const [isDraggingState, setIsDraggingState] = useState(false);
68
+
69
+ // Sync ref to state for UI (optional, or just use state)
70
+ // Actually, let's just use state for the critical UI part if needed,
71
+ // but typically we want ref for perf on touchmove.
72
+ // Let's just return a getter or similar?
73
+ // Simplest: just don't use isDragging for the transition logic in the UI, or use pullDistance === 0 check.
74
+
75
+ // Correction: In proper PTR, you want no transition during drag, but transition on release.
76
+ // So we need to know if we are dragging.
77
+
78
+ return {
79
+ isRefreshing,
80
+ pullDistance,
81
+ onTouchStart: (e: React.TouchEvent) => { onTouchStart(e); setIsDraggingState(true); },
82
+ onTouchMove,
83
+ onTouchEnd: async () => { setIsDraggingState(false); await onTouchEnd(); },
84
+ updateScrollTop,
85
+ isDragging: isDraggingState
86
+ };
87
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ps99-api",
3
- "version": "2.6.5",
3
+ "version": "2.7.0",
4
4
  "description": "Pet Simulator Public API wrapper written in Typescript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",