@papernote/ui 1.3.1 → 1.5.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.
Files changed (100) hide show
  1. package/dist/components/BottomNavigation.d.ts +98 -0
  2. package/dist/components/BottomNavigation.d.ts.map +1 -0
  3. package/dist/components/Checkbox.d.ts +2 -0
  4. package/dist/components/Checkbox.d.ts.map +1 -1
  5. package/dist/components/CheckboxList.d.ts +81 -0
  6. package/dist/components/CheckboxList.d.ts.map +1 -0
  7. package/dist/components/Chip.d.ts +92 -1
  8. package/dist/components/Chip.d.ts.map +1 -1
  9. package/dist/components/ConfirmDialog.d.ts +43 -1
  10. package/dist/components/ConfirmDialog.d.ts.map +1 -1
  11. package/dist/components/DataTable.d.ts +10 -1
  12. package/dist/components/DataTable.d.ts.map +1 -1
  13. package/dist/components/DataTableCardView.d.ts +99 -0
  14. package/dist/components/DataTableCardView.d.ts.map +1 -0
  15. package/dist/components/ExpandablePanel.d.ts +142 -0
  16. package/dist/components/ExpandablePanel.d.ts.map +1 -0
  17. package/dist/components/FloatingActionButton.d.ts +98 -0
  18. package/dist/components/FloatingActionButton.d.ts.map +1 -0
  19. package/dist/components/Input.d.ts +45 -1
  20. package/dist/components/Input.d.ts.map +1 -1
  21. package/dist/components/MobileHeader.d.ts +98 -0
  22. package/dist/components/MobileHeader.d.ts.map +1 -0
  23. package/dist/components/MobileLayout.d.ts +121 -0
  24. package/dist/components/MobileLayout.d.ts.map +1 -0
  25. package/dist/components/Modal.d.ts +50 -1
  26. package/dist/components/Modal.d.ts.map +1 -1
  27. package/dist/components/PullToRefresh.d.ts +87 -0
  28. package/dist/components/PullToRefresh.d.ts.map +1 -0
  29. package/dist/components/QueryTransparency.d.ts +1 -1
  30. package/dist/components/QueryTransparency.d.ts.map +1 -1
  31. package/dist/components/SearchableList.d.ts +83 -0
  32. package/dist/components/SearchableList.d.ts.map +1 -0
  33. package/dist/components/Select.d.ts +16 -2
  34. package/dist/components/Select.d.ts.map +1 -1
  35. package/dist/components/Sidebar.d.ts +40 -1
  36. package/dist/components/Sidebar.d.ts.map +1 -1
  37. package/dist/components/SwipeActions.d.ts +93 -0
  38. package/dist/components/SwipeActions.d.ts.map +1 -0
  39. package/dist/components/Switch.d.ts +1 -0
  40. package/dist/components/Switch.d.ts.map +1 -1
  41. package/dist/components/Textarea.d.ts +13 -0
  42. package/dist/components/Textarea.d.ts.map +1 -1
  43. package/dist/components/index.d.ts +27 -3
  44. package/dist/components/index.d.ts.map +1 -1
  45. package/dist/context/MobileContext.d.ts +168 -0
  46. package/dist/context/MobileContext.d.ts.map +1 -0
  47. package/dist/hooks/useResponsive.d.ts +158 -0
  48. package/dist/hooks/useResponsive.d.ts.map +1 -0
  49. package/dist/index.d.ts +1653 -56
  50. package/dist/index.esm.js +2832 -194
  51. package/dist/index.esm.js.map +1 -1
  52. package/dist/index.js +2865 -192
  53. package/dist/index.js.map +1 -1
  54. package/dist/styles.css +404 -1
  55. package/dist/types/index.d.ts +2 -0
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/package.json +1 -1
  58. package/src/components/BottomNavigation.stories.tsx +142 -0
  59. package/src/components/BottomNavigation.tsx +225 -0
  60. package/src/components/Checkbox.stories.tsx +162 -0
  61. package/src/components/Checkbox.tsx +22 -6
  62. package/src/components/CheckboxList.stories.tsx +311 -0
  63. package/src/components/CheckboxList.tsx +433 -0
  64. package/src/components/Chip.stories.tsx +389 -0
  65. package/src/components/Chip.tsx +182 -3
  66. package/src/components/ConfirmDialog.tsx +56 -4
  67. package/src/components/DataTable.tsx +60 -1
  68. package/src/components/DataTableCardView.stories.tsx +307 -0
  69. package/src/components/DataTableCardView.tsx +419 -0
  70. package/src/components/ExpandablePanel.stories.tsx +620 -0
  71. package/src/components/ExpandablePanel.tsx +383 -0
  72. package/src/components/FloatingActionButton.stories.tsx +197 -0
  73. package/src/components/FloatingActionButton.tsx +301 -0
  74. package/src/components/Grid.stories.tsx +16 -16
  75. package/src/components/Input.stories.tsx +214 -0
  76. package/src/components/Input.tsx +81 -4
  77. package/src/components/MobileHeader.stories.tsx +205 -0
  78. package/src/components/MobileHeader.tsx +233 -0
  79. package/src/components/MobileLayout.stories.tsx +338 -0
  80. package/src/components/MobileLayout.tsx +313 -0
  81. package/src/components/Modal.stories.tsx +183 -0
  82. package/src/components/Modal.tsx +84 -3
  83. package/src/components/PullToRefresh.stories.tsx +321 -0
  84. package/src/components/PullToRefresh.tsx +294 -0
  85. package/src/components/QueryTransparency.tsx +1 -1
  86. package/src/components/SearchableList.stories.tsx +437 -0
  87. package/src/components/SearchableList.tsx +326 -0
  88. package/src/components/Select.stories.tsx +190 -0
  89. package/src/components/Select.tsx +353 -137
  90. package/src/components/Sidebar.tsx +191 -8
  91. package/src/components/SwipeActions.stories.tsx +327 -0
  92. package/src/components/SwipeActions.tsx +387 -0
  93. package/src/components/Switch.stories.tsx +158 -0
  94. package/src/components/Switch.tsx +12 -3
  95. package/src/components/Textarea.tsx +31 -1
  96. package/src/components/index.ts +63 -3
  97. package/src/context/MobileContext.tsx +296 -0
  98. package/src/hooks/useResponsive.ts +360 -0
  99. package/src/types/index.ts +4 -0
  100. package/tailwind.config.js +56 -1
@@ -0,0 +1,294 @@
1
+ import React, { useState, useRef, useCallback, useEffect } from 'react';
2
+ import { Loader2, ArrowDown } from 'lucide-react';
3
+
4
+ /**
5
+ * PullToRefresh component props
6
+ */
7
+ export interface PullToRefreshProps {
8
+ /** Content to wrap */
9
+ children: React.ReactNode;
10
+ /** Async refresh handler - should return a Promise */
11
+ onRefresh: () => Promise<void>;
12
+ /** Disable pull-to-refresh */
13
+ disabled?: boolean;
14
+ /** Pull distance required to trigger refresh (default: 80) */
15
+ pullThreshold?: number;
16
+ /** Maximum pull distance (default: 120) */
17
+ maxPull?: number;
18
+ /** Custom loading indicator */
19
+ loadingIndicator?: React.ReactNode;
20
+ /** Custom pull indicator */
21
+ pullIndicator?: React.ReactNode;
22
+ /** Additional class names for container */
23
+ className?: string;
24
+ }
25
+
26
+ type RefreshState = 'idle' | 'pulling' | 'ready' | 'refreshing';
27
+
28
+ /**
29
+ * PullToRefresh - Mobile pull-to-refresh gesture handler
30
+ *
31
+ * Wraps content and provides native-feeling pull-to-refresh functionality.
32
+ * Only activates when scrolled to top of content.
33
+ *
34
+ * @example Basic usage
35
+ * ```tsx
36
+ * <PullToRefresh onRefresh={async () => {
37
+ * await fetchLatestData();
38
+ * }}>
39
+ * <div className="min-h-screen">
40
+ * {content}
41
+ * </div>
42
+ * </PullToRefresh>
43
+ * ```
44
+ *
45
+ * @example With custom threshold
46
+ * ```tsx
47
+ * <PullToRefresh
48
+ * onRefresh={handleRefresh}
49
+ * pullThreshold={100}
50
+ * maxPull={150}
51
+ * >
52
+ * {content}
53
+ * </PullToRefresh>
54
+ * ```
55
+ */
56
+ export default function PullToRefresh({
57
+ children,
58
+ onRefresh,
59
+ disabled = false,
60
+ pullThreshold = 80,
61
+ maxPull = 120,
62
+ loadingIndicator,
63
+ pullIndicator,
64
+ className = '',
65
+ }: PullToRefreshProps) {
66
+ const [state, setState] = useState<RefreshState>('idle');
67
+ const [pullDistance, setPullDistance] = useState(0);
68
+ const containerRef = useRef<HTMLDivElement>(null);
69
+ const startY = useRef(0);
70
+ const currentY = useRef(0);
71
+
72
+ // Check if at top of scroll container
73
+ const isAtTop = useCallback(() => {
74
+ const container = containerRef.current;
75
+ if (!container) return false;
76
+ return container.scrollTop <= 0;
77
+ }, []);
78
+
79
+ // Handle touch start
80
+ const handleTouchStart = useCallback((e: TouchEvent) => {
81
+ if (disabled || state === 'refreshing' || !isAtTop()) return;
82
+
83
+ startY.current = e.touches[0].clientY;
84
+ currentY.current = startY.current;
85
+ }, [disabled, state, isAtTop]);
86
+
87
+ // Handle touch move
88
+ const handleTouchMove = useCallback((e: TouchEvent) => {
89
+ if (disabled || state === 'refreshing') return;
90
+ if (startY.current === 0) return;
91
+
92
+ currentY.current = e.touches[0].clientY;
93
+ const diff = currentY.current - startY.current;
94
+
95
+ // Only allow pulling down when at top
96
+ if (diff > 0 && isAtTop()) {
97
+ // Apply resistance - pull slows down as distance increases
98
+ const resistance = 0.5;
99
+ const adjustedPull = Math.min(diff * resistance, maxPull);
100
+
101
+ setPullDistance(adjustedPull);
102
+ setState(adjustedPull >= pullThreshold ? 'ready' : 'pulling');
103
+
104
+ // Prevent default scroll when pulling
105
+ if (adjustedPull > 0) {
106
+ e.preventDefault();
107
+ }
108
+ }
109
+ }, [disabled, state, isAtTop, maxPull, pullThreshold]);
110
+
111
+ // Handle touch end
112
+ const handleTouchEnd = useCallback(async () => {
113
+ if (disabled || state === 'refreshing') return;
114
+
115
+ if (state === 'ready') {
116
+ setState('refreshing');
117
+ setPullDistance(pullThreshold); // Hold at threshold while refreshing
118
+
119
+ try {
120
+ await onRefresh();
121
+ } catch (error) {
122
+ console.error('Refresh failed:', error);
123
+ }
124
+
125
+ setState('idle');
126
+ }
127
+
128
+ setPullDistance(0);
129
+ startY.current = 0;
130
+ currentY.current = 0;
131
+ }, [disabled, state, pullThreshold, onRefresh]);
132
+
133
+ // Attach touch listeners
134
+ useEffect(() => {
135
+ const container = containerRef.current;
136
+ if (!container) return;
137
+
138
+ container.addEventListener('touchstart', handleTouchStart, { passive: true });
139
+ container.addEventListener('touchmove', handleTouchMove, { passive: false });
140
+ container.addEventListener('touchend', handleTouchEnd);
141
+
142
+ return () => {
143
+ container.removeEventListener('touchstart', handleTouchStart);
144
+ container.removeEventListener('touchmove', handleTouchMove);
145
+ container.removeEventListener('touchend', handleTouchEnd);
146
+ };
147
+ }, [handleTouchStart, handleTouchMove, handleTouchEnd]);
148
+
149
+ // Calculate indicator opacity and rotation
150
+ const progress = Math.min(pullDistance / pullThreshold, 1);
151
+ const rotation = progress * 180;
152
+
153
+ // Default loading indicator
154
+ const defaultLoadingIndicator = (
155
+ <Loader2 className="h-6 w-6 text-accent-600 animate-spin" />
156
+ );
157
+
158
+ // Default pull indicator
159
+ const defaultPullIndicator = (
160
+ <div
161
+ className={`
162
+ transition-transform duration-200
163
+ ${state === 'ready' ? 'text-accent-600' : 'text-ink-400'}
164
+ `}
165
+ style={{ transform: `rotate(${rotation}deg)` }}
166
+ >
167
+ <ArrowDown className="h-6 w-6" />
168
+ </div>
169
+ );
170
+
171
+ return (
172
+ <div
173
+ ref={containerRef}
174
+ className={`relative overflow-auto ${className}`}
175
+ style={{ touchAction: pullDistance > 0 ? 'none' : 'auto' }}
176
+ >
177
+ {/* Pull indicator */}
178
+ <div
179
+ className={`
180
+ absolute left-0 right-0 flex items-center justify-center
181
+ transition-all duration-200 overflow-hidden
182
+ ${state === 'idle' && pullDistance === 0 ? 'opacity-0' : 'opacity-100'}
183
+ `}
184
+ style={{
185
+ height: `${pullDistance}px`,
186
+ top: 0,
187
+ zIndex: 10,
188
+ }}
189
+ >
190
+ <div
191
+ className={`
192
+ w-10 h-10 rounded-full bg-white shadow-md
193
+ flex items-center justify-center
194
+ transition-transform duration-200
195
+ ${state === 'refreshing' ? 'scale-100' : progress < 0.3 ? 'scale-75' : 'scale-100'}
196
+ `}
197
+ >
198
+ {state === 'refreshing'
199
+ ? (loadingIndicator || defaultLoadingIndicator)
200
+ : (pullIndicator || defaultPullIndicator)
201
+ }
202
+ </div>
203
+ </div>
204
+
205
+ {/* Content wrapper */}
206
+ <div
207
+ className="transition-transform duration-200"
208
+ style={{
209
+ transform: `translateY(${pullDistance}px)`,
210
+ }}
211
+ >
212
+ {children}
213
+ </div>
214
+ </div>
215
+ );
216
+ }
217
+
218
+ /**
219
+ * usePullToRefresh - Hook for custom pull-to-refresh implementations
220
+ *
221
+ * @example
222
+ * ```tsx
223
+ * const { pullDistance, isRefreshing, bind } = usePullToRefresh({
224
+ * onRefresh: async () => {
225
+ * await fetchData();
226
+ * }
227
+ * });
228
+ *
229
+ * return (
230
+ * <div {...bind}>
231
+ * {isRefreshing && <Spinner />}
232
+ * {content}
233
+ * </div>
234
+ * );
235
+ * ```
236
+ */
237
+ export function usePullToRefresh({
238
+ onRefresh,
239
+ pullThreshold = 80,
240
+ maxPull = 120,
241
+ disabled = false,
242
+ }: {
243
+ onRefresh: () => Promise<void>;
244
+ pullThreshold?: number;
245
+ maxPull?: number;
246
+ disabled?: boolean;
247
+ }) {
248
+ const [pullDistance, setPullDistance] = useState(0);
249
+ const [isRefreshing, setIsRefreshing] = useState(false);
250
+ const startY = useRef(0);
251
+
252
+ const handleTouchStart = useCallback((e: React.TouchEvent) => {
253
+ if (disabled || isRefreshing) return;
254
+ startY.current = e.touches[0].clientY;
255
+ }, [disabled, isRefreshing]);
256
+
257
+ const handleTouchMove = useCallback((e: React.TouchEvent) => {
258
+ if (disabled || isRefreshing || startY.current === 0) return;
259
+
260
+ const diff = e.touches[0].clientY - startY.current;
261
+ if (diff > 0) {
262
+ const adjustedPull = Math.min(diff * 0.5, maxPull);
263
+ setPullDistance(adjustedPull);
264
+ }
265
+ }, [disabled, isRefreshing, maxPull]);
266
+
267
+ const handleTouchEnd = useCallback(async () => {
268
+ if (disabled || isRefreshing) return;
269
+
270
+ if (pullDistance >= pullThreshold) {
271
+ setIsRefreshing(true);
272
+ try {
273
+ await onRefresh();
274
+ } finally {
275
+ setIsRefreshing(false);
276
+ }
277
+ }
278
+
279
+ setPullDistance(0);
280
+ startY.current = 0;
281
+ }, [disabled, isRefreshing, pullDistance, pullThreshold, onRefresh]);
282
+
283
+ return {
284
+ pullDistance,
285
+ isRefreshing,
286
+ isReady: pullDistance >= pullThreshold,
287
+ progress: Math.min(pullDistance / pullThreshold, 1),
288
+ bind: {
289
+ onTouchStart: handleTouchStart,
290
+ onTouchMove: handleTouchMove,
291
+ onTouchEnd: handleTouchEnd,
292
+ },
293
+ };
294
+ }
@@ -37,7 +37,7 @@ export interface QueryTransparencyInfo {
37
37
  relatedData?: Array<{
38
38
  entity: string;
39
39
  description: string;
40
- type: 'join' | 'include' | 'lookup';
40
+ type: 'primary' | 'join' | 'include' | 'lookup';
41
41
  }>;
42
42
  // Backend calculations
43
43
  calculations?: Array<{