@papernote/ui 1.10.7 → 1.10.9

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.
@@ -27,7 +27,7 @@ export interface SwipeableListItemProps {
27
27
  rightActions?: SwipeListAction[];
28
28
  /** Width per action button in pixels (default: 72) */
29
29
  actionWidth?: number;
30
- /** Enable full swipe to trigger first action (default: false) */
30
+ /** Enable full swipe to trigger first action (default: true) */
31
31
  fullSwipe?: boolean;
32
32
  /** Full swipe threshold as percentage of container width (default: 0.5) */
33
33
  fullSwipeThreshold?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"SwipeableListItem.d.ts","sourceRoot":"","sources":["../../src/components/SwipeableListItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC9E,6BAA6B;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wBAAwB;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;CAC9D;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,WAAgB,EAChB,YAAiB,EACjB,WAAgB,EAChB,SAAiB,EACjB,kBAAwB,EACxB,QAAgB,EAChB,SAAc,EACd,aAAa,GACd,EAAE,sBAAsB,2CAsZxB;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"SwipeableListItem.d.ts","sourceRoot":"","sources":["../../src/components/SwipeableListItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC9E,6BAA6B;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wBAAwB;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;CAC9D;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,WAAgB,EAChB,YAAiB,EACjB,WAAgB,EAChB,SAAgB,EAChB,kBAAwB,EACxB,QAAgB,EAChB,SAAc,EACd,aAAa,GACd,EAAE,sBAAsB,2CAgexB;AAED,eAAe,iBAAiB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3032,7 +3032,7 @@ interface SwipeableListItemProps {
3032
3032
  rightActions?: SwipeListAction[];
3033
3033
  /** Width per action button in pixels (default: 72) */
3034
3034
  actionWidth?: number;
3035
- /** Enable full swipe to trigger first action (default: false) */
3035
+ /** Enable full swipe to trigger first action (default: true) */
3036
3036
  fullSwipe?: boolean;
3037
3037
  /** Full swipe threshold as percentage of container width (default: 0.5) */
3038
3038
  fullSwipeThreshold?: number;
package/dist/index.esm.js CHANGED
@@ -8155,17 +8155,19 @@ const getColorClasses = (color) => {
8155
8155
  * </SwipeableListItem>
8156
8156
  * ```
8157
8157
  */
8158
- function SwipeableListItem({ children, leftActions = [], rightActions = [], actionWidth = 72, fullSwipe = false, fullSwipeThreshold = 0.5, disabled = false, className = '', onSwipeChange, }) {
8158
+ function SwipeableListItem({ children, leftActions = [], rightActions = [], actionWidth = 72, fullSwipe = true, fullSwipeThreshold = 0.5, disabled = false, className = '', onSwipeChange, }) {
8159
8159
  const containerRef = useRef(null);
8160
8160
  const [isDragging, setIsDragging] = useState(false);
8161
8161
  const [offsetX, setOffsetX] = useState(0);
8162
8162
  const [activeDirection, setActiveDirection] = useState(null);
8163
8163
  const [loadingActionId, setLoadingActionId] = useState(null);
8164
8164
  const [focusedActionIndex, setFocusedActionIndex] = useState(-1);
8165
+ const [isCommitted, setIsCommitted] = useState(false); // Tracks if past full-swipe threshold
8165
8166
  const startX = useRef(0);
8166
8167
  const startY = useRef(0);
8167
8168
  const startTime = useRef(0);
8168
8169
  const isHorizontalSwipe = useRef(null);
8170
+ const wasCommitted = useRef(false); // Track previous committed state for haptic
8169
8171
  // Calculate total widths
8170
8172
  const leftActionsWidth = leftActions.length * actionWidth;
8171
8173
  const rightActionsWidth = rightActions.length * actionWidth;
@@ -8185,6 +8187,8 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8185
8187
  setOffsetX(0);
8186
8188
  setActiveDirection(null);
8187
8189
  setFocusedActionIndex(-1);
8190
+ setIsCommitted(false);
8191
+ wasCommitted.current = false;
8188
8192
  onSwipeChange?.(null);
8189
8193
  }, [onSwipeChange]);
8190
8194
  // Execute action with async support
@@ -8227,6 +8231,7 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8227
8231
  if (isHorizontalSwipe.current !== true)
8228
8232
  return;
8229
8233
  let newOffset = deltaX;
8234
+ const containerWidth = containerRef.current?.offsetWidth || 300;
8230
8235
  // Swiping left (reveals left actions on right side)
8231
8236
  if (deltaX < 0) {
8232
8237
  if (leftActions.length === 0) {
@@ -8234,7 +8239,7 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8234
8239
  }
8235
8240
  else {
8236
8241
  const maxSwipe = fullSwipe
8237
- ? -(containerRef.current?.offsetWidth || 300)
8242
+ ? -containerWidth
8238
8243
  : -leftActionsWidth;
8239
8244
  newOffset = Math.max(maxSwipe, deltaX);
8240
8245
  // Apply resistance past the action buttons
@@ -8255,7 +8260,7 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8255
8260
  }
8256
8261
  else {
8257
8262
  const maxSwipe = fullSwipe
8258
- ? (containerRef.current?.offsetWidth || 300)
8263
+ ? containerWidth
8259
8264
  : rightActionsWidth;
8260
8265
  newOffset = Math.min(maxSwipe, deltaX);
8261
8266
  // Apply resistance past the action buttons
@@ -8269,8 +8274,19 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8269
8274
  onSwipeChange?.('right');
8270
8275
  }
8271
8276
  }
8277
+ // Check if we've crossed the full-swipe threshold
8278
+ if (fullSwipe) {
8279
+ const swipePercentage = Math.abs(newOffset) / containerWidth;
8280
+ const nowCommitted = swipePercentage >= fullSwipeThreshold;
8281
+ // Haptic feedback when crossing threshold (both directions)
8282
+ if (nowCommitted !== wasCommitted.current) {
8283
+ triggerHaptic(nowCommitted ? 'heavy' : 'light');
8284
+ wasCommitted.current = nowCommitted;
8285
+ }
8286
+ setIsCommitted(nowCommitted);
8287
+ }
8272
8288
  setOffsetX(newOffset);
8273
- }, [isDragging, disabled, loadingActionId, leftActions.length, rightActions.length, leftActionsWidth, rightActionsWidth, fullSwipe, activeDirection, onSwipeChange]);
8289
+ }, [isDragging, disabled, loadingActionId, leftActions.length, rightActions.length, leftActionsWidth, rightActionsWidth, fullSwipe, fullSwipeThreshold, activeDirection, onSwipeChange, triggerHaptic]);
8274
8290
  // Handle drag end
8275
8291
  const handleDragEnd = useCallback(() => {
8276
8292
  if (!isDragging)
@@ -8278,10 +8294,21 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8278
8294
  setIsDragging(false);
8279
8295
  const velocity = Math.abs(offsetX) / (Date.now() - startTime.current);
8280
8296
  const containerWidth = containerRef.current?.offsetWidth || 300;
8281
- // Check for full swipe trigger
8282
- if (fullSwipe) {
8297
+ // Check for full swipe trigger - use isCommitted state for more reliable detection
8298
+ if (fullSwipe && isCommitted) {
8299
+ if (offsetX < 0 && leftActions.length > 0) {
8300
+ executeAction(leftActions[0]);
8301
+ return;
8302
+ }
8303
+ else if (offsetX > 0 && rightActions.length > 0) {
8304
+ executeAction(rightActions[0]);
8305
+ return;
8306
+ }
8307
+ }
8308
+ // Also check velocity-based trigger for quick swipes
8309
+ if (fullSwipe && velocity > 0.5) {
8283
8310
  const swipePercentage = Math.abs(offsetX) / containerWidth;
8284
- if (swipePercentage >= fullSwipeThreshold || velocity > 0.5) {
8311
+ if (swipePercentage >= fullSwipeThreshold * 0.5) { // Lower threshold for fast swipes
8285
8312
  if (offsetX < 0 && leftActions.length > 0) {
8286
8313
  executeAction(leftActions[0]);
8287
8314
  return;
@@ -8292,6 +8319,9 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8292
8319
  }
8293
8320
  }
8294
8321
  }
8322
+ // Reset committed state
8323
+ setIsCommitted(false);
8324
+ wasCommitted.current = false;
8295
8325
  // Snap to open or closed position
8296
8326
  const threshold = actionWidth * 0.5;
8297
8327
  if (Math.abs(offsetX) >= threshold || velocity > 0.3) {
@@ -8313,7 +8343,7 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8313
8343
  else {
8314
8344
  resetPosition();
8315
8345
  }
8316
- }, [isDragging, offsetX, fullSwipe, fullSwipeThreshold, leftActions, rightActions, leftActionsWidth, rightActionsWidth, actionWidth, executeAction, resetPosition, onSwipeChange]);
8346
+ }, [isDragging, offsetX, fullSwipe, fullSwipeThreshold, isCommitted, leftActions, rightActions, leftActionsWidth, rightActionsWidth, actionWidth, executeAction, resetPosition, onSwipeChange]);
8317
8347
  // Touch event handlers
8318
8348
  const handleTouchStart = (e) => {
8319
8349
  handleDragStart(e.touches[0].clientX, e.touches[0].clientY);
@@ -8454,7 +8484,24 @@ function SwipeableListItem({ children, leftActions = [], rightActions = [], acti
8454
8484
  const fullSwipeProgress = fullSwipe
8455
8485
  ? Math.min(1, Math.abs(offsetX) / ((containerRef.current?.offsetWidth || 300) * fullSwipeThreshold))
8456
8486
  : 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: `
8487
+ 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 && isCommitted && isDragging && (jsxs("div", { className: `
8488
+ absolute inset-0 z-10 flex items-center
8489
+ ${offsetX > 0 ? 'justify-start pl-6' : 'justify-end pr-6'}
8490
+ ${offsetX > 0 && rightActions.length > 0
8491
+ ? getColorClasses(rightActions[0].color).bg
8492
+ : offsetX < 0 && leftActions.length > 0
8493
+ ? getColorClasses(leftActions[0].color).bg
8494
+ : ''}
8495
+ transition-opacity duration-150
8496
+ `, children: [offsetX > 0 && rightActions.length > 0 && (() => {
8497
+ const action = rightActions[0];
8498
+ const IconComponent = action.icon;
8499
+ return (jsxs("div", { className: "flex items-center gap-3 text-white animate-pulse", children: [jsx(IconComponent, { className: "h-8 w-8" }), jsxs("span", { className: "text-lg font-semibold uppercase tracking-wide", children: ["Release to ", action.label] })] }));
8500
+ })(), offsetX < 0 && leftActions.length > 0 && (() => {
8501
+ const action = leftActions[0];
8502
+ const IconComponent = action.icon;
8503
+ return (jsxs("div", { className: "flex items-center gap-3 text-white animate-pulse", children: [jsxs("span", { className: "text-lg font-semibold uppercase tracking-wide", children: ["Release to ", action.label] }), jsx(IconComponent, { className: "h-8 w-8" })] }));
8504
+ })()] })), fullSwipe && fullSwipeProgress > 0.3 && !isCommitted && (jsx("div", { className: `
8458
8505
  absolute inset-0 pointer-events-none
8459
8506
  ${offsetX > 0 ? 'bg-gradient-to-r from-success-500/20 to-transparent' : 'bg-gradient-to-l from-error-500/20 to-transparent'}
8460
8507
  `, style: { opacity: fullSwipeProgress } })), jsx("div", { className: `