@papernote/ui 1.10.5 → 1.10.7
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/dist/components/SwipeableListItem.d.ts +85 -0
- package/dist/components/SwipeableListItem.d.ts.map +1 -0
- package/dist/components/Toast.d.ts +7 -1
- package/dist/components/Toast.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +92 -4
- package/dist/index.esm.js +376 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +376 -2
- package/dist/index.js.map +1 -1
- package/dist/styles.css +135 -0
- package/package.json +1 -1
- package/src/components/SwipeableListItem.stories.tsx +514 -0
- package/src/components/SwipeableListItem.tsx +520 -0
- package/src/components/Toast.stories.tsx +449 -0
- package/src/components/Toast.tsx +23 -1
- package/src/components/index.ts +4 -1
package/dist/index.esm.js
CHANGED
|
@@ -3346,7 +3346,7 @@ const toastStyles = {
|
|
|
3346
3346
|
icon: jsx(Info, { className: "h-5 w-5 text-primary-600" }),
|
|
3347
3347
|
},
|
|
3348
3348
|
};
|
|
3349
|
-
function Toast({ id, type, title, message, duration = 5000, onClose }) {
|
|
3349
|
+
function Toast({ id, type, title, message, duration = 5000, onClose, action }) {
|
|
3350
3350
|
const [isExiting, setIsExiting] = useState(false);
|
|
3351
3351
|
const styles = toastStyles[type];
|
|
3352
3352
|
const handleClose = useCallback(() => {
|
|
@@ -3355,13 +3355,19 @@ function Toast({ id, type, title, message, duration = 5000, onClose }) {
|
|
|
3355
3355
|
onClose(id);
|
|
3356
3356
|
}, 300); // Match animation duration
|
|
3357
3357
|
}, [id, onClose]);
|
|
3358
|
+
const handleAction = useCallback(() => {
|
|
3359
|
+
if (action) {
|
|
3360
|
+
action.onClick();
|
|
3361
|
+
handleClose();
|
|
3362
|
+
}
|
|
3363
|
+
}, [action, handleClose]);
|
|
3358
3364
|
useEffect(() => {
|
|
3359
3365
|
const timer = setTimeout(() => {
|
|
3360
3366
|
handleClose();
|
|
3361
3367
|
}, duration);
|
|
3362
3368
|
return () => clearTimeout(timer);
|
|
3363
3369
|
}, [duration, handleClose]);
|
|
3364
|
-
return (jsx("div", { className: `${styles.bg} ${styles.border} bg-subtle-grain rounded-lg shadow-lg p-4 min-w-[320px] max-w-md transition-all duration-300 ${isExiting ? 'opacity-0 translate-x-full' : 'opacity-100 translate-x-0 animate-slide-in-right'}`, children: jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex-shrink-0 mt-0.5", children: styles.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("h4", { className: "text-sm font-medium text-ink-900 mb-1", children: title }), jsx("p", { className: "text-sm text-ink-600", children: message })] }), jsx("button", { onClick: handleClose, className: "flex-shrink-0 text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close notification", children: jsx(X, { className: "h-4 w-4" }) })] }) }));
|
|
3370
|
+
return (jsx("div", { className: `${styles.bg} ${styles.border} bg-subtle-grain rounded-lg shadow-lg p-4 min-w-[320px] max-w-md transition-all duration-300 ${isExiting ? 'opacity-0 translate-x-full' : 'opacity-100 translate-x-0 animate-slide-in-right'}`, children: jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex-shrink-0 mt-0.5", children: styles.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("h4", { className: "text-sm font-medium text-ink-900 mb-1", children: title }), jsx("p", { className: "text-sm text-ink-600", children: message }), action && (jsx("button", { onClick: handleAction, className: "mt-2 text-sm font-medium text-accent-600 hover:text-accent-700 transition-colors", children: action.label }))] }), jsx("button", { onClick: handleClose, className: "flex-shrink-0 text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close notification", children: jsx(X, { className: "h-4 w-4" }) })] }) }));
|
|
3365
3371
|
}
|
|
3366
3372
|
const positionStyles = {
|
|
3367
3373
|
'top-right': 'top-20 right-6',
|
|
@@ -8096,6 +8102,373 @@ function SwipeableCard({ children, onSwipeRight, onSwipeLeft, rightAction = {
|
|
|
8096
8102
|
}, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onMouseDown: handleMouseDown, role: "button", "aria-label": `Swipeable card. ${onSwipeRight ? `Swipe right to ${rightAction.label}.` : ''} ${onSwipeLeft ? `Swipe left to ${leftAction.label}.` : ''}`, tabIndex: disabled ? -1 : 0, children: children })] }));
|
|
8097
8103
|
}
|
|
8098
8104
|
|
|
8105
|
+
// Color classes for action backgrounds
|
|
8106
|
+
const getColorClasses = (color) => {
|
|
8107
|
+
const colorMap = {
|
|
8108
|
+
destructive: { bg: 'bg-gradient-to-r from-error-500 to-error-600', hover: 'hover:from-error-600 hover:to-error-700' },
|
|
8109
|
+
warning: { bg: 'bg-gradient-to-r from-warning-500 to-warning-600', hover: 'hover:from-warning-600 hover:to-warning-700' },
|
|
8110
|
+
success: { bg: 'bg-gradient-to-r from-success-500 to-success-600', hover: 'hover:from-success-600 hover:to-success-700' },
|
|
8111
|
+
primary: { bg: 'bg-gradient-to-r from-accent-500 to-accent-600', hover: 'hover:from-accent-600 hover:to-accent-700' },
|
|
8112
|
+
neutral: { bg: 'bg-gradient-to-r from-paper-400 to-paper-500', hover: 'hover:from-paper-500 hover:to-paper-600' },
|
|
8113
|
+
};
|
|
8114
|
+
return colorMap[color] || { bg: color, hover: '' };
|
|
8115
|
+
};
|
|
8116
|
+
/**
|
|
8117
|
+
* SwipeableListItem - List item with swipe-to-reveal action buttons
|
|
8118
|
+
*
|
|
8119
|
+
* Features:
|
|
8120
|
+
* - Multiple actions per side (like email apps)
|
|
8121
|
+
* - Full swipe to trigger primary action
|
|
8122
|
+
* - Keyboard accessibility (Arrow keys + Tab + Enter)
|
|
8123
|
+
* - Async callback support with loading state
|
|
8124
|
+
* - Haptic feedback on mobile
|
|
8125
|
+
* - Smooth animations and visual polish
|
|
8126
|
+
*
|
|
8127
|
+
* @example Single action per side
|
|
8128
|
+
* ```tsx
|
|
8129
|
+
* <SwipeableListItem
|
|
8130
|
+
* rightActions={[
|
|
8131
|
+
* { id: 'approve', icon: Check, color: 'success', label: 'Approve', onClick: handleApprove }
|
|
8132
|
+
* ]}
|
|
8133
|
+
* leftActions={[
|
|
8134
|
+
* { id: 'delete', icon: Trash, color: 'destructive', label: 'Delete', onClick: handleDelete }
|
|
8135
|
+
* ]}
|
|
8136
|
+
* >
|
|
8137
|
+
* <div className="p-4">List item content</div>
|
|
8138
|
+
* </SwipeableListItem>
|
|
8139
|
+
* ```
|
|
8140
|
+
*
|
|
8141
|
+
* @example Multiple actions (email-style)
|
|
8142
|
+
* ```tsx
|
|
8143
|
+
* <SwipeableListItem
|
|
8144
|
+
* leftActions={[
|
|
8145
|
+
* { id: 'delete', icon: Trash, color: 'destructive', label: 'Delete', onClick: handleDelete },
|
|
8146
|
+
* { id: 'archive', icon: Archive, color: 'warning', label: 'Archive', onClick: handleArchive },
|
|
8147
|
+
* ]}
|
|
8148
|
+
* rightActions={[
|
|
8149
|
+
* { id: 'read', icon: Mail, color: 'primary', label: 'Read', onClick: handleRead },
|
|
8150
|
+
* { id: 'star', icon: Star, color: 'warning', label: 'Star', onClick: handleStar },
|
|
8151
|
+
* ]}
|
|
8152
|
+
* fullSwipe
|
|
8153
|
+
* >
|
|
8154
|
+
* <EmailListItem />
|
|
8155
|
+
* </SwipeableListItem>
|
|
8156
|
+
* ```
|
|
8157
|
+
*/
|
|
8158
|
+
function SwipeableListItem({ children, leftActions = [], rightActions = [], actionWidth = 72, fullSwipe = false, fullSwipeThreshold = 0.5, disabled = false, className = '', onSwipeChange, }) {
|
|
8159
|
+
const containerRef = useRef(null);
|
|
8160
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
8161
|
+
const [offsetX, setOffsetX] = useState(0);
|
|
8162
|
+
const [activeDirection, setActiveDirection] = useState(null);
|
|
8163
|
+
const [loadingActionId, setLoadingActionId] = useState(null);
|
|
8164
|
+
const [focusedActionIndex, setFocusedActionIndex] = useState(-1);
|
|
8165
|
+
const startX = useRef(0);
|
|
8166
|
+
const startY = useRef(0);
|
|
8167
|
+
const startTime = useRef(0);
|
|
8168
|
+
const isHorizontalSwipe = useRef(null);
|
|
8169
|
+
// Calculate total widths
|
|
8170
|
+
const leftActionsWidth = leftActions.length * actionWidth;
|
|
8171
|
+
const rightActionsWidth = rightActions.length * actionWidth;
|
|
8172
|
+
// Trigger haptic feedback
|
|
8173
|
+
const triggerHaptic = useCallback((style = 'medium') => {
|
|
8174
|
+
if ('vibrate' in navigator) {
|
|
8175
|
+
const patterns = {
|
|
8176
|
+
light: 10,
|
|
8177
|
+
medium: 25,
|
|
8178
|
+
heavy: [50, 30, 50],
|
|
8179
|
+
};
|
|
8180
|
+
navigator.vibrate(patterns[style]);
|
|
8181
|
+
}
|
|
8182
|
+
}, []);
|
|
8183
|
+
// Reset position
|
|
8184
|
+
const resetPosition = useCallback(() => {
|
|
8185
|
+
setOffsetX(0);
|
|
8186
|
+
setActiveDirection(null);
|
|
8187
|
+
setFocusedActionIndex(-1);
|
|
8188
|
+
onSwipeChange?.(null);
|
|
8189
|
+
}, [onSwipeChange]);
|
|
8190
|
+
// Execute action with async support
|
|
8191
|
+
const executeAction = useCallback(async (action) => {
|
|
8192
|
+
setLoadingActionId(action.id);
|
|
8193
|
+
triggerHaptic('heavy');
|
|
8194
|
+
try {
|
|
8195
|
+
await action.onClick();
|
|
8196
|
+
}
|
|
8197
|
+
finally {
|
|
8198
|
+
setLoadingActionId(null);
|
|
8199
|
+
resetPosition();
|
|
8200
|
+
}
|
|
8201
|
+
}, [triggerHaptic, resetPosition]);
|
|
8202
|
+
// Handle drag start
|
|
8203
|
+
const handleDragStart = useCallback((clientX, clientY) => {
|
|
8204
|
+
if (disabled || loadingActionId)
|
|
8205
|
+
return;
|
|
8206
|
+
setIsDragging(true);
|
|
8207
|
+
startX.current = clientX;
|
|
8208
|
+
startY.current = clientY;
|
|
8209
|
+
startTime.current = Date.now();
|
|
8210
|
+
isHorizontalSwipe.current = null;
|
|
8211
|
+
}, [disabled, loadingActionId]);
|
|
8212
|
+
// Handle drag move
|
|
8213
|
+
const handleDragMove = useCallback((clientX, clientY) => {
|
|
8214
|
+
if (!isDragging || disabled || loadingActionId)
|
|
8215
|
+
return;
|
|
8216
|
+
const deltaX = clientX - startX.current;
|
|
8217
|
+
const deltaY = clientY - startY.current;
|
|
8218
|
+
// Determine if this is a horizontal swipe on first significant movement
|
|
8219
|
+
if (isHorizontalSwipe.current === null) {
|
|
8220
|
+
const absDeltaX = Math.abs(deltaX);
|
|
8221
|
+
const absDeltaY = Math.abs(deltaY);
|
|
8222
|
+
if (absDeltaX > 10 || absDeltaY > 10) {
|
|
8223
|
+
isHorizontalSwipe.current = absDeltaX > absDeltaY;
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
8226
|
+
// Only process horizontal swipes
|
|
8227
|
+
if (isHorizontalSwipe.current !== true)
|
|
8228
|
+
return;
|
|
8229
|
+
let newOffset = deltaX;
|
|
8230
|
+
// Swiping left (reveals left actions on right side)
|
|
8231
|
+
if (deltaX < 0) {
|
|
8232
|
+
if (leftActions.length === 0) {
|
|
8233
|
+
newOffset = deltaX * 0.2; // Heavy resistance if no actions
|
|
8234
|
+
}
|
|
8235
|
+
else {
|
|
8236
|
+
const maxSwipe = fullSwipe
|
|
8237
|
+
? -(containerRef.current?.offsetWidth || 300)
|
|
8238
|
+
: -leftActionsWidth;
|
|
8239
|
+
newOffset = Math.max(maxSwipe, deltaX);
|
|
8240
|
+
// Apply resistance past the action buttons
|
|
8241
|
+
if (newOffset < -leftActionsWidth && !fullSwipe) {
|
|
8242
|
+
const overSwipe = newOffset + leftActionsWidth;
|
|
8243
|
+
newOffset = -leftActionsWidth + overSwipe * 0.3;
|
|
8244
|
+
}
|
|
8245
|
+
}
|
|
8246
|
+
if (activeDirection !== 'left') {
|
|
8247
|
+
setActiveDirection('left');
|
|
8248
|
+
onSwipeChange?.('left');
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
8251
|
+
// Swiping right (reveals right actions on left side)
|
|
8252
|
+
else if (deltaX > 0) {
|
|
8253
|
+
if (rightActions.length === 0) {
|
|
8254
|
+
newOffset = deltaX * 0.2; // Heavy resistance if no actions
|
|
8255
|
+
}
|
|
8256
|
+
else {
|
|
8257
|
+
const maxSwipe = fullSwipe
|
|
8258
|
+
? (containerRef.current?.offsetWidth || 300)
|
|
8259
|
+
: rightActionsWidth;
|
|
8260
|
+
newOffset = Math.min(maxSwipe, deltaX);
|
|
8261
|
+
// Apply resistance past the action buttons
|
|
8262
|
+
if (newOffset > rightActionsWidth && !fullSwipe) {
|
|
8263
|
+
const overSwipe = newOffset - rightActionsWidth;
|
|
8264
|
+
newOffset = rightActionsWidth + overSwipe * 0.3;
|
|
8265
|
+
}
|
|
8266
|
+
}
|
|
8267
|
+
if (activeDirection !== 'right') {
|
|
8268
|
+
setActiveDirection('right');
|
|
8269
|
+
onSwipeChange?.('right');
|
|
8270
|
+
}
|
|
8271
|
+
}
|
|
8272
|
+
setOffsetX(newOffset);
|
|
8273
|
+
}, [isDragging, disabled, loadingActionId, leftActions.length, rightActions.length, leftActionsWidth, rightActionsWidth, fullSwipe, activeDirection, onSwipeChange]);
|
|
8274
|
+
// Handle drag end
|
|
8275
|
+
const handleDragEnd = useCallback(() => {
|
|
8276
|
+
if (!isDragging)
|
|
8277
|
+
return;
|
|
8278
|
+
setIsDragging(false);
|
|
8279
|
+
const velocity = Math.abs(offsetX) / (Date.now() - startTime.current);
|
|
8280
|
+
const containerWidth = containerRef.current?.offsetWidth || 300;
|
|
8281
|
+
// Check for full swipe trigger
|
|
8282
|
+
if (fullSwipe) {
|
|
8283
|
+
const swipePercentage = Math.abs(offsetX) / containerWidth;
|
|
8284
|
+
if (swipePercentage >= fullSwipeThreshold || velocity > 0.5) {
|
|
8285
|
+
if (offsetX < 0 && leftActions.length > 0) {
|
|
8286
|
+
executeAction(leftActions[0]);
|
|
8287
|
+
return;
|
|
8288
|
+
}
|
|
8289
|
+
else if (offsetX > 0 && rightActions.length > 0) {
|
|
8290
|
+
executeAction(rightActions[0]);
|
|
8291
|
+
return;
|
|
8292
|
+
}
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
// Snap to open or closed position
|
|
8296
|
+
const threshold = actionWidth * 0.5;
|
|
8297
|
+
if (Math.abs(offsetX) >= threshold || velocity > 0.3) {
|
|
8298
|
+
// Snap open
|
|
8299
|
+
if (offsetX < 0 && leftActions.length > 0) {
|
|
8300
|
+
setOffsetX(-leftActionsWidth);
|
|
8301
|
+
setActiveDirection('left');
|
|
8302
|
+
onSwipeChange?.('left');
|
|
8303
|
+
}
|
|
8304
|
+
else if (offsetX > 0 && rightActions.length > 0) {
|
|
8305
|
+
setOffsetX(rightActionsWidth);
|
|
8306
|
+
setActiveDirection('right');
|
|
8307
|
+
onSwipeChange?.('right');
|
|
8308
|
+
}
|
|
8309
|
+
else {
|
|
8310
|
+
resetPosition();
|
|
8311
|
+
}
|
|
8312
|
+
}
|
|
8313
|
+
else {
|
|
8314
|
+
resetPosition();
|
|
8315
|
+
}
|
|
8316
|
+
}, [isDragging, offsetX, fullSwipe, fullSwipeThreshold, leftActions, rightActions, leftActionsWidth, rightActionsWidth, actionWidth, executeAction, resetPosition, onSwipeChange]);
|
|
8317
|
+
// Touch event handlers
|
|
8318
|
+
const handleTouchStart = (e) => {
|
|
8319
|
+
handleDragStart(e.touches[0].clientX, e.touches[0].clientY);
|
|
8320
|
+
};
|
|
8321
|
+
const handleTouchMove = (e) => {
|
|
8322
|
+
handleDragMove(e.touches[0].clientX, e.touches[0].clientY);
|
|
8323
|
+
if (isHorizontalSwipe.current === true) {
|
|
8324
|
+
e.preventDefault();
|
|
8325
|
+
}
|
|
8326
|
+
};
|
|
8327
|
+
const handleTouchEnd = () => {
|
|
8328
|
+
handleDragEnd();
|
|
8329
|
+
};
|
|
8330
|
+
// Mouse event handlers
|
|
8331
|
+
const handleMouseDown = (e) => {
|
|
8332
|
+
handleDragStart(e.clientX, e.clientY);
|
|
8333
|
+
};
|
|
8334
|
+
useEffect(() => {
|
|
8335
|
+
if (!isDragging)
|
|
8336
|
+
return;
|
|
8337
|
+
const handleMouseMove = (e) => {
|
|
8338
|
+
handleDragMove(e.clientX, e.clientY);
|
|
8339
|
+
};
|
|
8340
|
+
const handleMouseUp = () => {
|
|
8341
|
+
handleDragEnd();
|
|
8342
|
+
};
|
|
8343
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
8344
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
8345
|
+
return () => {
|
|
8346
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
8347
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
8348
|
+
};
|
|
8349
|
+
}, [isDragging, handleDragMove, handleDragEnd]);
|
|
8350
|
+
// Close on outside click
|
|
8351
|
+
useEffect(() => {
|
|
8352
|
+
if (activeDirection === null)
|
|
8353
|
+
return;
|
|
8354
|
+
const handleClickOutside = (e) => {
|
|
8355
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
8356
|
+
resetPosition();
|
|
8357
|
+
}
|
|
8358
|
+
};
|
|
8359
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
8360
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
8361
|
+
}, [activeDirection, resetPosition]);
|
|
8362
|
+
// Keyboard navigation
|
|
8363
|
+
const handleKeyDown = useCallback((e) => {
|
|
8364
|
+
if (disabled || loadingActionId)
|
|
8365
|
+
return;
|
|
8366
|
+
const currentActions = activeDirection === 'left' ? leftActions :
|
|
8367
|
+
activeDirection === 'right' ? rightActions : [];
|
|
8368
|
+
switch (e.key) {
|
|
8369
|
+
case 'ArrowRight':
|
|
8370
|
+
e.preventDefault();
|
|
8371
|
+
if (activeDirection === null && rightActions.length > 0) {
|
|
8372
|
+
setOffsetX(rightActionsWidth);
|
|
8373
|
+
setActiveDirection('right');
|
|
8374
|
+
setFocusedActionIndex(0);
|
|
8375
|
+
onSwipeChange?.('right');
|
|
8376
|
+
triggerHaptic('light');
|
|
8377
|
+
}
|
|
8378
|
+
else if (activeDirection === 'right' && focusedActionIndex < rightActions.length - 1) {
|
|
8379
|
+
setFocusedActionIndex(prev => prev + 1);
|
|
8380
|
+
}
|
|
8381
|
+
else if (activeDirection === 'left') {
|
|
8382
|
+
resetPosition();
|
|
8383
|
+
}
|
|
8384
|
+
break;
|
|
8385
|
+
case 'ArrowLeft':
|
|
8386
|
+
e.preventDefault();
|
|
8387
|
+
if (activeDirection === null && leftActions.length > 0) {
|
|
8388
|
+
setOffsetX(-leftActionsWidth);
|
|
8389
|
+
setActiveDirection('left');
|
|
8390
|
+
setFocusedActionIndex(0);
|
|
8391
|
+
onSwipeChange?.('left');
|
|
8392
|
+
triggerHaptic('light');
|
|
8393
|
+
}
|
|
8394
|
+
else if (activeDirection === 'left' && focusedActionIndex < leftActions.length - 1) {
|
|
8395
|
+
setFocusedActionIndex(prev => prev + 1);
|
|
8396
|
+
}
|
|
8397
|
+
else if (activeDirection === 'right') {
|
|
8398
|
+
resetPosition();
|
|
8399
|
+
}
|
|
8400
|
+
break;
|
|
8401
|
+
case 'Tab':
|
|
8402
|
+
if (activeDirection !== null && currentActions.length > 0) {
|
|
8403
|
+
e.preventDefault();
|
|
8404
|
+
if (e.shiftKey) {
|
|
8405
|
+
setFocusedActionIndex(prev => prev <= 0 ? currentActions.length - 1 : prev - 1);
|
|
8406
|
+
}
|
|
8407
|
+
else {
|
|
8408
|
+
setFocusedActionIndex(prev => prev >= currentActions.length - 1 ? 0 : prev + 1);
|
|
8409
|
+
}
|
|
8410
|
+
}
|
|
8411
|
+
break;
|
|
8412
|
+
case 'Enter':
|
|
8413
|
+
case ' ':
|
|
8414
|
+
if (activeDirection !== null && focusedActionIndex >= 0 && focusedActionIndex < currentActions.length) {
|
|
8415
|
+
e.preventDefault();
|
|
8416
|
+
executeAction(currentActions[focusedActionIndex]);
|
|
8417
|
+
}
|
|
8418
|
+
break;
|
|
8419
|
+
case 'Escape':
|
|
8420
|
+
if (activeDirection !== null) {
|
|
8421
|
+
e.preventDefault();
|
|
8422
|
+
resetPosition();
|
|
8423
|
+
}
|
|
8424
|
+
break;
|
|
8425
|
+
}
|
|
8426
|
+
}, [disabled, loadingActionId, activeDirection, leftActions, rightActions, leftActionsWidth, rightActionsWidth, focusedActionIndex, executeAction, resetPosition, onSwipeChange, triggerHaptic]);
|
|
8427
|
+
// Render action button
|
|
8428
|
+
const renderActionButton = (action, index, side) => {
|
|
8429
|
+
const { bg, hover } = getColorClasses(action.color);
|
|
8430
|
+
const isLoading = loadingActionId === action.id;
|
|
8431
|
+
const isFocused = activeDirection === side && focusedActionIndex === index;
|
|
8432
|
+
const IconComponent = action.icon;
|
|
8433
|
+
return (jsxs("button", { onClick: (e) => {
|
|
8434
|
+
e.stopPropagation();
|
|
8435
|
+
executeAction(action);
|
|
8436
|
+
}, disabled: !!loadingActionId, className: `
|
|
8437
|
+
flex flex-col items-center justify-center gap-1
|
|
8438
|
+
h-full text-white
|
|
8439
|
+
${bg} ${hover}
|
|
8440
|
+
transition-all duration-150 ease-out
|
|
8441
|
+
focus:outline-none
|
|
8442
|
+
${isFocused ? 'ring-2 ring-white ring-inset scale-105' : ''}
|
|
8443
|
+
${isLoading ? 'opacity-75' : 'active:scale-95'}
|
|
8444
|
+
disabled:cursor-not-allowed
|
|
8445
|
+
`, style: { width: actionWidth }, "aria-label": action.label, children: [jsx("div", { className: `transition-transform duration-200 ${isFocused ? 'scale-110' : ''}`, children: isLoading ? (jsx(Loader2, { className: "h-5 w-5 animate-spin" })) : (jsx(IconComponent, { className: "h-5 w-5" })) }), jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide opacity-90", children: action.label })] }, action.id));
|
|
8446
|
+
};
|
|
8447
|
+
// Build aria-label
|
|
8448
|
+
const ariaLabel = [
|
|
8449
|
+
'Swipeable list item.',
|
|
8450
|
+
rightActions.length > 0 ? `Swipe right for ${rightActions.map(a => a.label).join(', ')}.` : '',
|
|
8451
|
+
leftActions.length > 0 ? `Swipe left for ${leftActions.map(a => a.label).join(', ')}.` : '',
|
|
8452
|
+
].filter(Boolean).join(' ');
|
|
8453
|
+
// Calculate visual progress for full swipe indicator
|
|
8454
|
+
const fullSwipeProgress = fullSwipe
|
|
8455
|
+
? Math.min(1, Math.abs(offsetX) / ((containerRef.current?.offsetWidth || 300) * fullSwipeThreshold))
|
|
8456
|
+
: 0;
|
|
8457
|
+
return (jsxs("div", { ref: containerRef, className: `relative overflow-hidden ${className}`, children: [rightActions.length > 0 && (jsx("div", { className: "absolute left-0 top-0 bottom-0 flex shadow-inner", style: { width: rightActionsWidth }, children: rightActions.map((action, index) => renderActionButton(action, index, 'right')) })), leftActions.length > 0 && (jsx("div", { className: "absolute right-0 top-0 bottom-0 flex shadow-inner", style: { width: leftActionsWidth }, children: leftActions.map((action, index) => renderActionButton(action, index, 'left')) })), fullSwipe && fullSwipeProgress > 0.3 && (jsx("div", { className: `
|
|
8458
|
+
absolute inset-0 pointer-events-none
|
|
8459
|
+
${offsetX > 0 ? 'bg-gradient-to-r from-success-500/20 to-transparent' : 'bg-gradient-to-l from-error-500/20 to-transparent'}
|
|
8460
|
+
`, style: { opacity: fullSwipeProgress } })), jsx("div", { className: `
|
|
8461
|
+
relative bg-white
|
|
8462
|
+
${isDragging ? 'cursor-grabbing' : 'cursor-grab'}
|
|
8463
|
+
${isDragging ? '' : 'transition-transform duration-200 ease-out'}
|
|
8464
|
+
${disabled ? 'opacity-50 pointer-events-none' : ''}
|
|
8465
|
+
${isDragging ? 'shadow-lg' : activeDirection ? 'shadow-md' : ''}
|
|
8466
|
+
`, style: {
|
|
8467
|
+
transform: `translateX(${offsetX}px)`,
|
|
8468
|
+
touchAction: 'pan-y',
|
|
8469
|
+
}, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onMouseDown: handleMouseDown, onKeyDown: handleKeyDown, role: "button", "aria-label": ariaLabel, tabIndex: disabled ? -1 : 0, children: children })] }));
|
|
8470
|
+
}
|
|
8471
|
+
|
|
8099
8472
|
/**
|
|
8100
8473
|
* NotificationBanner - Dismissible banner for important alerts
|
|
8101
8474
|
*
|
|
@@ -58476,5 +58849,5 @@ function Responsive({ mobile, tablet, desktop, }) {
|
|
|
58476
58849
|
return jsx(Fragment, { children: mobile || tablet || desktop });
|
|
58477
58850
|
}
|
|
58478
58851
|
|
|
58479
|
-
export { Accordion, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, AdminModal, Alert, AlertDialog, AppLayout, Autocomplete, Avatar, BREAKPOINTS, Badge, BottomNavigation, BottomNavigationSpacer, BottomSheet, BottomSheetActions, BottomSheetContent, BottomSheetHeader, Box, Breadcrumbs, Button, ButtonGroup, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardView, Carousel, Checkbox, CheckboxList, Chip, ChipGroup, Collapsible, ColorPicker, Combobox, ComingSoon, CommandPalette, CompactStat, ConfirmDialog, ContextMenu, ControlBar, CurrencyDisplay, CurrencyInput, Dashboard, DashboardContent, DashboardHeader, DataGrid, DataTable, DataTableCardView, DateDisplay, DatePicker, DateRangePicker, DateTimePicker, DesktopOnly, Drawer, DrawerFooter, DropZone, Dropdown, DropdownTrigger, EmptyState, ErrorBoundary, ExpandablePanel, ExpandablePanelContainer, ExpandablePanelSpacer, ExpandableRowButton, ExpandableToolbar, ExpandedRowEditForm, ExportButton, FORMULA_CATEGORIES, FORMULA_DEFINITIONS, FORMULA_NAMES, FieldArray, FileUpload, FilterBar, FilterControls, FilterStatusBanner, FloatingActionButton, Form, FormContext, FormControl, FormWizard, Grid, GridItem, Hide, HorizontalScroll, HoverCard, InfiniteScroll, Input, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MultiSelect, NotificationBanner, NotificationBar, NotificationBell, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, Popover, Progress, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, SwipeActions, SwipeableCard, Switch, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, Text, Textarea, ThemeToggle, TimePicker, Timeline, TimezoneSelector, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, getLocalTimezone, isValidTimezone, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
|
|
58852
|
+
export { Accordion, ActionBar, ActionBarCenter, ActionBarLeft, ActionBarRight, ActionButton, AdminModal, Alert, AlertDialog, AppLayout, Autocomplete, Avatar, BREAKPOINTS, Badge, BottomNavigation, BottomNavigationSpacer, BottomSheet, BottomSheetActions, BottomSheetContent, BottomSheetHeader, Box, Breadcrumbs, Button, ButtonGroup, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardView, Carousel, Checkbox, CheckboxList, Chip, ChipGroup, Collapsible, ColorPicker, Combobox, ComingSoon, CommandPalette, CompactStat, ConfirmDialog, ContextMenu, ControlBar, CurrencyDisplay, CurrencyInput, Dashboard, DashboardContent, DashboardHeader, DataGrid, DataTable, DataTableCardView, DateDisplay, DatePicker, DateRangePicker, DateTimePicker, DesktopOnly, Drawer, DrawerFooter, DropZone, Dropdown, DropdownTrigger, EmptyState, ErrorBoundary, ExpandablePanel, ExpandablePanelContainer, ExpandablePanelSpacer, ExpandableRowButton, ExpandableToolbar, ExpandedRowEditForm, ExportButton, FORMULA_CATEGORIES, FORMULA_DEFINITIONS, FORMULA_NAMES, FieldArray, FileUpload, FilterBar, FilterControls, FilterStatusBanner, FloatingActionButton, Form, FormContext, FormControl, FormWizard, Grid, GridItem, Hide, HorizontalScroll, HoverCard, InfiniteScroll, Input, KanbanBoard, Layout, Loading, LoadingOverlay, Logo, MarkdownEditor, MaskedInput, Menu, MenuDivider, MobileHeader, MobileHeaderSpacer, MobileLayout, MobileOnly, MobileProvider, Modal, ModalFooter, MultiSelect, NotificationBanner, NotificationBar, NotificationBell, NotificationIndicator, NumberInput, Page, PageHeader, PageLayout, PageNavigation, Pagination, PasswordInput, Popover, Progress, PullToRefresh, QueryTransparency, RadioGroup, Rating, Responsive, RichTextEditor, SearchBar, SearchableList, Select, Separator, Show, Sidebar, SidebarGroup, Skeleton, SkeletonCard$1 as SkeletonCard, SkeletonTable, Slider, Spreadsheet, SpreadsheetReport, Stack, StatCard, StatItem, StatsCardGrid, StatsGrid, StatusBadge, StatusBar, StepIndicator, Stepper, SwipeActions, SwipeableCard, SwipeableListItem, Switch, Tabs, TabsContent, TabsList, TabsRoot, TabsTrigger, Text, Textarea, ThemeToggle, TimePicker, Timeline, TimezoneSelector, Toast, ToastContainer, Tooltip, Transfer, TreeView, TwoColumnContent, UserProfileButton, addErrorMessage, addInfoMessage, addSuccessMessage, addWarningMessage, calculateColumnWidth, createActionsSection, createFiltersSection, createMultiSheetExcel, createPageControlsSection, createQueryDetailsSection, exportDataTableToExcel, exportToExcel, formatStatisticValue, formatStatistics, getFormula, getFormulasByCategory, getLocalTimezone, isValidTimezone, loadColumnOrder, loadColumnWidths, reorderArray, saveColumnOrder, saveColumnWidths, searchFormulas, statusManager, useBreadcrumbReset, useBreakpoint, useBreakpointValue, useColumnReorder, useColumnResize, useCommandPalette, useConfirmDialog, useFABScroll, useFormContext, useIsDesktop, useIsMobile, useIsTablet, useIsTouchDevice, useMediaQuery, useMobileContext, useOrientation, usePrefersMobile, useResponsiveCallback, useSafeAreaInsets, useViewportSize, withMobileContext };
|
|
58480
58853
|
//# sourceMappingURL=index.esm.js.map
|