@btndprojects/loam-ui-native 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +55 -2
- package/dist/accordion/index.js +3 -0
- package/dist/adaptive/index.js +2 -0
- package/dist/alert/index.js +3 -0
- package/dist/analyzer/index.js +6 -0
- package/dist/announcement/index.js +6 -0
- package/dist/audio-player/index.js +3 -0
- package/dist/avatar/index.js +3 -0
- package/dist/avatar-group/index.js +3 -0
- package/dist/badge/index.js +5 -0
- package/dist/banner/index.js +2 -0
- package/dist/board/index.js +7 -0
- package/dist/button/index.js +5 -0
- package/dist/camera-permission/index.js +13 -0
- package/dist/capture-result/index.js +18 -0
- package/dist/card/index.js +4 -0
- package/dist/card-form/index.js +10 -0
- package/dist/carousel/index.js +2 -0
- package/dist/chart/index.js +6 -0
- package/dist/charts/index.js +7 -0
- package/dist/checkbox/index.js +7 -0
- package/dist/checklist/index.js +10 -0
- package/dist/checkout/index.js +13 -0
- package/dist/chip/index.js +7 -0
- package/dist/chunk-223Q2T2Q.js +229 -0
- package/dist/chunk-2A5NIQAK.js +254 -0
- package/dist/chunk-2GK6PHGE.js +363 -0
- package/dist/chunk-2WUTXHMU.js +1 -0
- package/dist/chunk-2ZZ7TDJ4.js +324 -0
- package/dist/chunk-35OFI3JG.js +499 -0
- package/dist/chunk-3ARE4TES.js +195 -0
- package/dist/chunk-3ECPOSKO.js +1 -0
- package/dist/chunk-3ENTMPQY.js +229 -0
- package/dist/chunk-3HJH6FF4.js +98 -0
- package/dist/chunk-3NMD74ZU.js +4 -0
- package/dist/chunk-3WPW4KUN.js +182 -0
- package/dist/chunk-423B7SPN.js +993 -0
- package/dist/chunk-47FWHGUN.js +394 -0
- package/dist/chunk-4BXMXBJO.js +141 -0
- package/dist/chunk-4DS5ZWVC.js +83 -0
- package/dist/chunk-4GBOGOTV.js +295 -0
- package/dist/chunk-4SZYY6AN.js +99 -0
- package/dist/chunk-4YIP3CWS.js +237 -0
- package/dist/chunk-4YQOR7JM.js +204 -0
- package/dist/chunk-4ZAHCY44.js +177 -0
- package/dist/chunk-52X2VYTD.js +228 -0
- package/dist/chunk-5P26LBDL.js +1 -0
- package/dist/chunk-5XLRYDAX.js +1 -0
- package/dist/chunk-5YNTNN5A.js +177 -0
- package/dist/chunk-6HT2LP62.js +504 -0
- package/dist/chunk-6N33LFQA.js +35 -0
- package/dist/chunk-6X6PQ443.js +432 -0
- package/dist/chunk-7CFF4HXS.js +126 -0
- package/dist/chunk-7O2FMUPT.js +755 -0
- package/dist/chunk-AI4DWZCD.js +162 -0
- package/dist/chunk-AWJOCGFZ.js +213 -0
- package/dist/chunk-AZRX6SAC.js +285 -0
- package/dist/chunk-B54QE2FN.js +1 -0
- package/dist/chunk-BC475SJH.js +1 -0
- package/dist/chunk-BEDBFJND.js +202 -0
- package/dist/chunk-BTTFCMKG.js +166 -0
- package/dist/chunk-BV3B2HYZ.js +1635 -0
- package/dist/chunk-C2NTXVWM.js +98 -0
- package/dist/chunk-CDRZHT42.js +424 -0
- package/dist/chunk-CJ5EOYZE.js +62 -0
- package/dist/chunk-CUMUNPXY.js +128 -0
- package/dist/chunk-CUS3ZSCU.js +1 -0
- package/dist/chunk-CYRBHQQZ.js +411 -0
- package/dist/chunk-DHWADYZX.js +144 -0
- package/dist/chunk-DKCSPATL.js +1 -0
- package/dist/chunk-DM5QUKKO.js +326 -0
- package/dist/chunk-DYBH2I6W.js +1 -0
- package/dist/chunk-DZ255O2F.js +1 -0
- package/dist/chunk-E35BPSGW.js +1 -0
- package/dist/chunk-EJBDRVIH.js +166 -0
- package/dist/chunk-F43NP42L.js +1 -0
- package/dist/chunk-F5XNPWS7.js +1024 -0
- package/dist/chunk-FMYDDECK.js +1521 -0
- package/dist/chunk-G3HOYSF7.js +1 -0
- package/dist/chunk-GDKZTW34.js +395 -0
- package/dist/chunk-GL7B2DOW.js +134 -0
- package/dist/chunk-GNNJ4GCC.js +471 -0
- package/dist/chunk-GNZ56EUI.js +1 -0
- package/dist/chunk-H6NIOQHK.js +346 -0
- package/dist/chunk-H6PC7RDA.js +1 -0
- package/dist/chunk-HET4LLMX.js +129 -0
- package/dist/chunk-HPPW424V.js +334 -0
- package/dist/chunk-HQOQIBJI.js +1 -0
- package/dist/chunk-HU2EQNRZ.js +44 -0
- package/dist/chunk-HZ6CNMAF.js +870 -0
- package/dist/chunk-IIQTAY3J.js +715 -0
- package/dist/chunk-ITTYJZWX.js +271 -0
- package/dist/chunk-IVNRBPGC.js +1018 -0
- package/dist/chunk-J4KLDGI4.js +1 -0
- package/dist/chunk-J7Q7VCVC.js +254 -0
- package/dist/chunk-JDEOITQ2.js +1 -0
- package/dist/chunk-JNH2TYIN.js +96 -0
- package/dist/chunk-JNY4ZHAR.js +634 -0
- package/dist/chunk-JRLTGLN4.js +1 -0
- package/dist/chunk-JTRJJLKL.js +1 -0
- package/dist/chunk-JUAMLEHX.js +379 -0
- package/dist/chunk-JXJB2X6M.js +131 -0
- package/dist/chunk-KMI5ML5I.js +262 -0
- package/dist/chunk-KTAC4MGE.js +4 -0
- package/dist/chunk-LAECSR4H.js +178 -0
- package/dist/chunk-LK4BCSGX.js +1 -0
- package/dist/chunk-LOE5YNN3.js +470 -0
- package/dist/chunk-LRJNDEIA.js +302 -0
- package/dist/chunk-LSS54GLJ.js +116 -0
- package/dist/chunk-LZLHELAG.js +137 -0
- package/dist/chunk-M2LUXGKZ.js +1 -0
- package/dist/chunk-MOUJOP55.js +425 -0
- package/dist/chunk-N32RGOA5.js +159 -0
- package/dist/chunk-N5ZNE2YE.js +1 -0
- package/dist/chunk-NBNCBML6.js +1 -0
- package/dist/chunk-NKB7BZYK.js +167 -0
- package/dist/chunk-NQYBUO3K.js +1795 -0
- package/dist/chunk-NY2U3PBI.js +59 -0
- package/dist/chunk-O4AEHWII.js +464 -0
- package/dist/chunk-O7AE6DAF.js +146 -0
- package/dist/chunk-OETCUXQP.js +134 -0
- package/dist/chunk-OOPPQH27.js +179 -0
- package/dist/chunk-OPXFZ4Y5.js +204 -0
- package/dist/chunk-P23EQNRQ.js +338 -0
- package/dist/chunk-PJXUJYS6.js +248 -0
- package/dist/chunk-PNWUWOBF.js +1 -0
- package/dist/chunk-PUPM2YGF.js +376 -0
- package/dist/chunk-PZXZJBMP.js +620 -0
- package/dist/chunk-Q72LJOVE.js +169 -0
- package/dist/chunk-QCDXZBC5.js +247 -0
- package/dist/chunk-QDYDBCFP.js +197 -0
- package/dist/chunk-QJJ6MADV.js +274 -0
- package/dist/chunk-QKXCPTFS.js +349 -0
- package/dist/chunk-QWCMU5B2.js +860 -0
- package/dist/chunk-RIBDXGUG.js +108 -0
- package/dist/chunk-RJQOOHT2.js +214 -0
- package/dist/chunk-RNGLIHFA.js +191 -0
- package/dist/chunk-RQ3HASCC.js +6 -0
- package/dist/chunk-RSOJT5YW.js +124 -0
- package/dist/chunk-RTIKSY6A.js +1 -0
- package/dist/chunk-RUZHTGKB.js +237 -0
- package/dist/chunk-RW6ZL2FQ.js +284 -0
- package/dist/chunk-S7F6XLTK.js +540 -0
- package/dist/chunk-SBMNWWBV.js +155 -0
- package/dist/chunk-SDTQPRRB.js +1 -0
- package/dist/chunk-SYNOJOQO.js +377 -0
- package/dist/chunk-T753XT36.js +1 -0
- package/dist/chunk-TKQYWZ5V.js +196 -0
- package/dist/chunk-TKVFVF5D.js +95 -0
- package/dist/chunk-TOXAGRDQ.js +144 -0
- package/dist/chunk-TUTI5HOZ.js +112 -0
- package/dist/chunk-U7TFM2JA.js +140 -0
- package/dist/chunk-UC2WGUP3.js +627 -0
- package/dist/chunk-UWFLYMAR.js +1126 -0
- package/dist/chunk-UWSZB7BJ.js +1 -0
- package/dist/chunk-UYG5L34B.js +87 -0
- package/dist/chunk-VBOXAEPI.js +336 -0
- package/dist/chunk-VCDBXW5H.js +188 -0
- package/dist/chunk-VCXPHHDS.js +1 -0
- package/dist/chunk-VEAYTRPX.js +414 -0
- package/dist/chunk-VFCUIDRT.js +1 -0
- package/dist/chunk-VQHPQEUH.js +273 -0
- package/dist/chunk-VXDZFJGP.js +874 -0
- package/dist/chunk-WS56BL3D.js +1 -0
- package/dist/chunk-WSW6ZFAQ.js +98 -0
- package/dist/chunk-XNUVB5OL.js +375 -0
- package/dist/chunk-XOSCY2CW.js +242 -0
- package/dist/chunk-XXXEFXY5.js +99 -0
- package/dist/chunk-Y5CSSPV4.js +1 -0
- package/dist/chunk-Y6DU4J47.js +1 -0
- package/dist/chunk-YC6WORFL.js +535 -0
- package/dist/chunk-YDGVJETP.js +60 -0
- package/dist/chunk-YF4NSSMT.js +366 -0
- package/dist/chunk-YG3ZJHGU.js +223 -0
- package/dist/chunk-YIJ55TBC.js +1 -0
- package/dist/chunk-YKZ4RZZF.js +1 -0
- package/dist/chunk-YLLCXTMC.js +37 -0
- package/dist/chunk-YTA74UZR.js +1 -0
- package/dist/chunk-YTFKPRUK.js +845 -0
- package/dist/chunk-Z7H7VSFZ.js +323 -0
- package/dist/chunk-ZK2H4O5P.js +1 -0
- package/dist/chunk-ZVFE5QGU.js +524 -0
- package/dist/coachmark/index.js +4 -0
- package/dist/code-block/index.js +4 -0
- package/dist/color-field/index.js +7 -0
- package/dist/commerce/index.js +20 -0
- package/dist/connectivity/index.js +2 -0
- package/dist/context-menu/index.js +5 -0
- package/dist/copy-button/index.js +3 -0
- package/dist/data-grid/index.js +24 -0
- package/dist/date-range-picker/index.js +13 -0
- package/dist/description-list/index.js +2 -0
- package/dist/dialog/index.js +3 -0
- package/dist/empty-state/index.js +3 -0
- package/dist/error-summary/index.js +3 -0
- package/dist/fab/index.js +2 -0
- package/dist/file-upload/index.js +9 -0
- package/dist/filter-bar/index.js +8 -0
- package/dist/flow-run/index.js +14 -0
- package/dist/form/index.js +3 -0
- package/dist/gallery/index.js +2 -0
- package/dist/header/index.js +5 -0
- package/dist/hold-to-confirm/index.js +4 -0
- package/dist/index.d.ts +22883 -0
- package/dist/index.js +158 -0
- package/dist/infinite-list/index.js +2 -0
- package/dist/inline-edit/index.js +5 -0
- package/dist/input/index.js +5 -0
- package/dist/interactions/index.js +2 -0
- package/dist/invoice/index.js +10 -0
- package/dist/kbd/index.js +1 -0
- package/dist/keyboard/index.js +3 -0
- package/dist/list-row/index.js +7 -0
- package/dist/log-viewer/index.js +2 -0
- package/dist/map-view/index.js +3 -0
- package/dist/markdown/index.js +6 -0
- package/dist/metric/index.js +4 -0
- package/dist/money/index.js +3 -0
- package/dist/nav-drawer/index.js +2 -0
- package/dist/navigation/index.js +2 -0
- package/dist/notification-center/index.js +16 -0
- package/dist/number-roll/index.js +3 -0
- package/dist/one-handed/index.js +5 -0
- package/dist/os-surfaces/index.js +3 -0
- package/dist/otp/index.js +7 -0
- package/dist/page-header/index.js +1 -0
- package/dist/payment-method/index.js +12 -0
- package/dist/payment-status/index.js +16 -0
- package/dist/permission/index.js +2 -0
- package/dist/picker/index.js +7 -0
- package/dist/progress/index.js +5 -0
- package/dist/pull-to-refresh/index.js +4 -0
- package/dist/radio-group/index.js +6 -0
- package/dist/rating/index.js +2 -0
- package/dist/receivables/index.js +20 -0
- package/dist/refund/index.js +16 -0
- package/dist/save-status/index.js +3 -0
- package/dist/scroll-area/index.js +1 -0
- package/dist/search-bar/index.js +5 -0
- package/dist/segmented-control/index.js +5 -0
- package/dist/sheet/index.js +4 -0
- package/dist/skeleton/index.js +2 -0
- package/dist/slider/index.js +5 -0
- package/dist/spinner/index.js +2 -0
- package/dist/split-button/index.js +7 -0
- package/dist/spotlight/index.js +2 -0
- package/dist/stage-timeline/index.js +4 -0
- package/dist/status-dot/index.js +2 -0
- package/dist/stepper/index.js +6 -0
- package/dist/streaming-text/index.js +3 -0
- package/dist/survey/index.js +19 -0
- package/dist/switch/index.js +5 -0
- package/dist/system-state/index.js +4 -0
- package/dist/tab-bar/index.js +2 -0
- package/dist/tabs/index.js +2 -0
- package/dist/textarea/index.js +6 -0
- package/dist/thinking/index.js +2 -0
- package/dist/timeline/index.js +4 -0
- package/dist/toast/index.js +2 -0
- package/dist/toolbar/index.js +2 -0
- package/dist/tour/index.js +11 -0
- package/dist/truncation-notice/index.js +2 -0
- package/dist/update-notice/index.js +11 -0
- package/dist/video-player/index.js +2 -0
- package/dist/viewfinder/index.js +5 -0
- package/dist/widget/index.js +4 -0
- package/dist/wizard/index.js +6 -0
- package/package.json +76 -3
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
import { ContextMenuOverlay, LONG_PRESS_DELAY_MS } from './chunk-PZXZJBMP.js';
|
|
2
|
+
import { haptics } from './chunk-NY2U3PBI.js';
|
|
3
|
+
import { Icon } from './chunk-TKVFVF5D.js';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { Animated, Easing, PanResponder, View, Pressable, StyleSheet, Text } from 'react-native';
|
|
6
|
+
import { createStyleSheet, useLoamTheme, useStyles } from '@btndprojects/loam-theme-native';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var SWIPE_ACTION_WIDTH = 64;
|
|
10
|
+
var SWIPE_ACTION_GAP = 8;
|
|
11
|
+
var SWIPE_ACTION_PAD = 6;
|
|
12
|
+
var SWIPE_ARM_THRESHOLD = 214;
|
|
13
|
+
var SWIPE_FLICK_VELOCITY = -1600;
|
|
14
|
+
var SWIPE_AXIS_LOCK_PX = 6;
|
|
15
|
+
var SWIPE_OVERSCROLL_DAMP = 0.3;
|
|
16
|
+
var SWIPE_PROJECTION_S = 0.1;
|
|
17
|
+
var SWIPE_COLLAPSE_MARGIN = -10;
|
|
18
|
+
var SWIPE_COLLAPSE_REMOVE_MS = 340;
|
|
19
|
+
var SWIPE_SPRING_SNAP = {
|
|
20
|
+
stiffness: 220,
|
|
21
|
+
damping: 26,
|
|
22
|
+
mass: 1,
|
|
23
|
+
restDisplacementThreshold: 0.25,
|
|
24
|
+
restSpeedThreshold: 0.25
|
|
25
|
+
};
|
|
26
|
+
var SWIPE_SPRING_COMMIT = {
|
|
27
|
+
stiffness: 240,
|
|
28
|
+
damping: 22,
|
|
29
|
+
mass: 1,
|
|
30
|
+
restDisplacementThreshold: 0.25,
|
|
31
|
+
restSpeedThreshold: 0.25
|
|
32
|
+
};
|
|
33
|
+
var FALLBACK_ROW_WIDTH = 300;
|
|
34
|
+
function swipeSideSign(side) {
|
|
35
|
+
return side === "leading" ? 1 : -1;
|
|
36
|
+
}
|
|
37
|
+
function activeSwipeSide(offset) {
|
|
38
|
+
if (offset > 0) return "leading";
|
|
39
|
+
if (offset < 0) return "trailing";
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function swipeActionCollapses(action) {
|
|
43
|
+
return action.collapses ?? action.destructive === true;
|
|
44
|
+
}
|
|
45
|
+
function swipeCommitAction(actions) {
|
|
46
|
+
const primary = actions[actions.length - 1];
|
|
47
|
+
if (!primary) return null;
|
|
48
|
+
return primary.fullSwipe ?? primary.destructive === true ? primary : null;
|
|
49
|
+
}
|
|
50
|
+
function normalizeSwipeActions(prop) {
|
|
51
|
+
if (prop == null) return { leading: [], trailing: [] };
|
|
52
|
+
if (Array.isArray(prop)) {
|
|
53
|
+
return { leading: [], trailing: orderSwipeActions(prop) };
|
|
54
|
+
}
|
|
55
|
+
const sides = prop;
|
|
56
|
+
return {
|
|
57
|
+
leading: orderSwipeActions(sides.leading ?? []),
|
|
58
|
+
trailing: orderSwipeActions(sides.trailing ?? [])
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function railOrder(actions, side) {
|
|
62
|
+
return side === "leading" ? [...actions].reverse() : actions;
|
|
63
|
+
}
|
|
64
|
+
function swipeOpenOffset(count, side = "trailing") {
|
|
65
|
+
if (count <= 0) return 0;
|
|
66
|
+
const magnitude = SWIPE_ACTION_WIDTH * count + SWIPE_ACTION_GAP * (count - 1) + SWIPE_ACTION_PAD;
|
|
67
|
+
return swipeSideSign(side) * magnitude;
|
|
68
|
+
}
|
|
69
|
+
function swipeProgress(offset, openOffset) {
|
|
70
|
+
if (openOffset === 0) return 0;
|
|
71
|
+
return Math.max(0, Math.min(1, offset / openOffset));
|
|
72
|
+
}
|
|
73
|
+
function swipeActionOpacity(p) {
|
|
74
|
+
return Math.min(1, p * 1.4);
|
|
75
|
+
}
|
|
76
|
+
function swipeActionScale(p) {
|
|
77
|
+
return 0.72 + 0.28 * Math.min(1, p * 1.3);
|
|
78
|
+
}
|
|
79
|
+
function isSwipeArmed(offset, side = "trailing") {
|
|
80
|
+
return offset * swipeSideSign(side) > SWIPE_ARM_THRESHOLD;
|
|
81
|
+
}
|
|
82
|
+
function clampSwipeOffset(x, hasLeading = false, hasTrailing = true) {
|
|
83
|
+
if (x > 0) return hasLeading ? x : Math.min(0, x * SWIPE_OVERSCROLL_DAMP);
|
|
84
|
+
if (x < 0) return hasTrailing ? x : Math.max(0, x * SWIPE_OVERSCROLL_DAMP);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
function lockSwipeAxis(dx, dy) {
|
|
88
|
+
if (Math.abs(dx) + Math.abs(dy) <= SWIPE_AXIS_LOCK_PX) return null;
|
|
89
|
+
return Math.abs(dx) > Math.abs(dy) ? "x" : "y";
|
|
90
|
+
}
|
|
91
|
+
function armedActionWidth(offset, rowWidth) {
|
|
92
|
+
return Math.min(Math.abs(offset) - SWIPE_ACTION_PAD, rowWidth - SWIPE_ACTION_PAD * 2);
|
|
93
|
+
}
|
|
94
|
+
function resolveSwipeRelease(offset, velocity, openOffset, canCommit, side = "trailing") {
|
|
95
|
+
const sign = swipeSideSign(side);
|
|
96
|
+
const flicked = velocity * sign > -SWIPE_FLICK_VELOCITY;
|
|
97
|
+
if (canCommit && (isSwipeArmed(offset, side) || flicked)) {
|
|
98
|
+
return { kind: "commit" };
|
|
99
|
+
}
|
|
100
|
+
const projected = (offset + velocity * SWIPE_PROJECTION_S) * sign;
|
|
101
|
+
return { kind: "snap", to: projected > openOffset * sign / 2 ? openOffset : 0 };
|
|
102
|
+
}
|
|
103
|
+
function orderSwipeActions(actions) {
|
|
104
|
+
const safe = actions.filter((a) => a.destructive !== true);
|
|
105
|
+
const destructive = actions.filter((a) => a.destructive === true);
|
|
106
|
+
return destructive.length === 0 ? actions : [...safe, ...destructive];
|
|
107
|
+
}
|
|
108
|
+
function swipeAccessibilityActions(actions, menuLabel) {
|
|
109
|
+
return [
|
|
110
|
+
{ name: "longpress", label: menuLabel },
|
|
111
|
+
...actions.map((a) => ({ name: a.key, label: a.label }))
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function resolveSwipeAccessibilityAction(actions, name) {
|
|
115
|
+
if (name === "longpress") return "menu";
|
|
116
|
+
return actions.find((a) => a.key === name) ?? null;
|
|
117
|
+
}
|
|
118
|
+
function resolveRowMotion(motion, osReduced) {
|
|
119
|
+
if (motion === "none") return "none";
|
|
120
|
+
if (motion === "reduced") return "reduced";
|
|
121
|
+
return osReduced ? "reduced" : "full";
|
|
122
|
+
}
|
|
123
|
+
function line(value, style) {
|
|
124
|
+
if (value == null || value === false) return null;
|
|
125
|
+
return typeof value === "string" || typeof value === "number" ? /* @__PURE__ */ jsx(Text, { style, numberOfLines: 1, children: value }) : value;
|
|
126
|
+
}
|
|
127
|
+
function SelectionIndicator({ selected }) {
|
|
128
|
+
const theme = useLoamTheme();
|
|
129
|
+
const { styles } = useStyles(sheet, { selectedBox: selected });
|
|
130
|
+
return /* @__PURE__ */ jsx(
|
|
131
|
+
View,
|
|
132
|
+
{
|
|
133
|
+
accessible: false,
|
|
134
|
+
importantForAccessibility: "no-hide-descendants",
|
|
135
|
+
style: styles.selectBox,
|
|
136
|
+
children: selected ? /* @__PURE__ */ jsx(
|
|
137
|
+
Icon,
|
|
138
|
+
{
|
|
139
|
+
name: "check",
|
|
140
|
+
size: theme.icon.sm,
|
|
141
|
+
color: theme.color.accentInk,
|
|
142
|
+
strokeWidth: theme.icon.strokeBold
|
|
143
|
+
}
|
|
144
|
+
) : null
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function RowBody({
|
|
149
|
+
title,
|
|
150
|
+
subtitle,
|
|
151
|
+
leading,
|
|
152
|
+
trailing,
|
|
153
|
+
onPress,
|
|
154
|
+
divider = false,
|
|
155
|
+
disabled = false,
|
|
156
|
+
accessibilityLabel,
|
|
157
|
+
density = "comfortable",
|
|
158
|
+
selectable = false,
|
|
159
|
+
selected = false,
|
|
160
|
+
onToggleSelected,
|
|
161
|
+
style,
|
|
162
|
+
interactive = false,
|
|
163
|
+
onLongPress,
|
|
164
|
+
accessibilityActions,
|
|
165
|
+
onAccessibilityAction,
|
|
166
|
+
accessibilityHint,
|
|
167
|
+
dragHandle
|
|
168
|
+
}) {
|
|
169
|
+
const { styles } = useStyles(sheet, { divider, disabled, density, selected });
|
|
170
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
171
|
+
selectable && selected ? /* @__PURE__ */ jsx(View, { accessible: false, style: styles.selectBar }) : null,
|
|
172
|
+
selectable ? /* @__PURE__ */ jsx(View, { style: styles.slot, children: /* @__PURE__ */ jsx(SelectionIndicator, { selected }) }) : null,
|
|
173
|
+
leading != null ? /* @__PURE__ */ jsx(View, { style: styles.slot, children: leading }) : null,
|
|
174
|
+
/* @__PURE__ */ jsxs(View, { style: styles.main, children: [
|
|
175
|
+
line(title, styles.title),
|
|
176
|
+
line(subtitle, styles.subtitle)
|
|
177
|
+
] }),
|
|
178
|
+
trailing != null ? /* @__PURE__ */ jsx(View, { style: styles.slot, children: trailing }) : null,
|
|
179
|
+
dragHandle != null ? dragHandle : null
|
|
180
|
+
] });
|
|
181
|
+
const primaryPress = selectable ? onToggleSelected : onPress;
|
|
182
|
+
if (primaryPress || interactive) {
|
|
183
|
+
const label = accessibilityLabel ?? (typeof title === "string" ? title : void 0);
|
|
184
|
+
return /* @__PURE__ */ jsx(
|
|
185
|
+
Pressable,
|
|
186
|
+
{
|
|
187
|
+
accessibilityRole: selectable ? "checkbox" : "button",
|
|
188
|
+
accessibilityLabel: label,
|
|
189
|
+
accessibilityHint,
|
|
190
|
+
accessibilityState: selectable ? { disabled, checked: selected } : { disabled },
|
|
191
|
+
"aria-checked": selectable ? selected : void 0,
|
|
192
|
+
accessibilityActions,
|
|
193
|
+
onAccessibilityAction,
|
|
194
|
+
disabled,
|
|
195
|
+
onPress: disabled ? void 0 : primaryPress,
|
|
196
|
+
onLongPress: disabled ? void 0 : onLongPress,
|
|
197
|
+
delayLongPress: LONG_PRESS_DELAY_MS,
|
|
198
|
+
style: ({ pressed }) => [styles.base, style, pressed && !disabled ? styles.pressed : null],
|
|
199
|
+
children: content
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.base, style], children: content });
|
|
204
|
+
}
|
|
205
|
+
function ListRow(props) {
|
|
206
|
+
const {
|
|
207
|
+
swipeActions,
|
|
208
|
+
swipeReleaseLabel,
|
|
209
|
+
swipeMenuLabel,
|
|
210
|
+
swipeHint,
|
|
211
|
+
selectable = false,
|
|
212
|
+
selected,
|
|
213
|
+
defaultSelected = false,
|
|
214
|
+
onSelectedChange,
|
|
215
|
+
draggable = false,
|
|
216
|
+
onDragStart,
|
|
217
|
+
onDragMove,
|
|
218
|
+
onDragEnd,
|
|
219
|
+
onMoveUp,
|
|
220
|
+
onMoveDown,
|
|
221
|
+
dragHandleLabel,
|
|
222
|
+
motion = "full",
|
|
223
|
+
...rest
|
|
224
|
+
} = props;
|
|
225
|
+
const isControlled = selected !== void 0;
|
|
226
|
+
const [internalSelected, setInternalSelected] = React.useState(defaultSelected);
|
|
227
|
+
const isSelected = isControlled ? selected : internalSelected;
|
|
228
|
+
const toggleSelected = React.useCallback(() => {
|
|
229
|
+
const next = !isSelected;
|
|
230
|
+
if (!isControlled) setInternalSelected(next);
|
|
231
|
+
haptics.selection();
|
|
232
|
+
onSelectedChange?.(next);
|
|
233
|
+
}, [isControlled, isSelected, onSelectedChange]);
|
|
234
|
+
const row = {
|
|
235
|
+
...rest,
|
|
236
|
+
selectable,
|
|
237
|
+
selected: isSelected,
|
|
238
|
+
onToggleSelected: selectable ? toggleSelected : void 0
|
|
239
|
+
};
|
|
240
|
+
const sides = React.useMemo(() => normalizeSwipeActions(swipeActions), [swipeActions]);
|
|
241
|
+
if (draggable) {
|
|
242
|
+
return /* @__PURE__ */ jsx(
|
|
243
|
+
DraggableRow,
|
|
244
|
+
{
|
|
245
|
+
row,
|
|
246
|
+
onDragStart,
|
|
247
|
+
onDragMove,
|
|
248
|
+
onDragEnd,
|
|
249
|
+
onMoveUp,
|
|
250
|
+
onMoveDown,
|
|
251
|
+
handleLabel: dragHandleLabel ?? "Drag to reorder",
|
|
252
|
+
motion
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (sides.leading.length === 0 && sides.trailing.length === 0) return /* @__PURE__ */ jsx(RowBody, { ...row });
|
|
257
|
+
return /* @__PURE__ */ jsx(
|
|
258
|
+
SwipeRow,
|
|
259
|
+
{
|
|
260
|
+
row,
|
|
261
|
+
sides,
|
|
262
|
+
releaseLabel: swipeReleaseLabel ?? "Release",
|
|
263
|
+
menuLabel: swipeMenuLabel ?? "Row actions",
|
|
264
|
+
hint: swipeHint ?? "Long press for actions",
|
|
265
|
+
motion
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
function SwipeRow({
|
|
270
|
+
row,
|
|
271
|
+
sides,
|
|
272
|
+
releaseLabel,
|
|
273
|
+
menuLabel,
|
|
274
|
+
hint,
|
|
275
|
+
motion
|
|
276
|
+
}) {
|
|
277
|
+
const theme = useLoamTheme();
|
|
278
|
+
const { styles } = useStyles(sheet, { divider: false, disabled: false });
|
|
279
|
+
const reducedMotion = resolveRowMotion(motion, theme.reduceMotion) !== "full";
|
|
280
|
+
const hasLeading = sides.leading.length > 0;
|
|
281
|
+
const hasTrailing = sides.trailing.length > 0;
|
|
282
|
+
const rails = React.useMemo(
|
|
283
|
+
() => ({
|
|
284
|
+
leading: {
|
|
285
|
+
actions: sides.leading,
|
|
286
|
+
openOffset: swipeOpenOffset(sides.leading.length, "leading"),
|
|
287
|
+
commit: swipeCommitAction(sides.leading)
|
|
288
|
+
},
|
|
289
|
+
trailing: {
|
|
290
|
+
actions: sides.trailing,
|
|
291
|
+
openOffset: swipeOpenOffset(sides.trailing.length, "trailing"),
|
|
292
|
+
commit: swipeCommitAction(sides.trailing)
|
|
293
|
+
}
|
|
294
|
+
}),
|
|
295
|
+
[sides]
|
|
296
|
+
);
|
|
297
|
+
const allActions = React.useMemo(() => [...sides.leading, ...sides.trailing], [sides]);
|
|
298
|
+
const dir = theme.rtl ? -1 : 1;
|
|
299
|
+
const x = React.useRef(new Animated.Value(0)).current;
|
|
300
|
+
const arm = React.useRef(new Animated.Value(0)).current;
|
|
301
|
+
const armWidth = React.useRef(new Animated.Value(SWIPE_ACTION_WIDTH)).current;
|
|
302
|
+
const collapseH = React.useRef(new Animated.Value(0)).current;
|
|
303
|
+
const collapseOpacity = React.useRef(new Animated.Value(1)).current;
|
|
304
|
+
const collapseMargin = React.useRef(new Animated.Value(0)).current;
|
|
305
|
+
const cur = React.useRef(0);
|
|
306
|
+
const rowWidth = React.useRef(FALLBACK_ROW_WIDTH);
|
|
307
|
+
const rowHeight = React.useRef(0);
|
|
308
|
+
const armedRef = React.useRef(false);
|
|
309
|
+
const sideRef = React.useRef(null);
|
|
310
|
+
const axis = React.useRef(null);
|
|
311
|
+
const lastX = React.useRef(0);
|
|
312
|
+
const lastT = React.useRef(0);
|
|
313
|
+
const vel = React.useRef(0);
|
|
314
|
+
const running = React.useRef(null);
|
|
315
|
+
const removeTimer = React.useRef(null);
|
|
316
|
+
const [armed, setArmed] = React.useState(false);
|
|
317
|
+
const [openSide, setOpenSide] = React.useState(null);
|
|
318
|
+
const [collapsing, setCollapsing] = React.useState(false);
|
|
319
|
+
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
320
|
+
const [anchor, setAnchor] = React.useState(null);
|
|
321
|
+
const rootRef = React.useRef(null);
|
|
322
|
+
React.useEffect(
|
|
323
|
+
() => () => {
|
|
324
|
+
if (removeTimer.current) clearTimeout(removeTimer.current);
|
|
325
|
+
},
|
|
326
|
+
[]
|
|
327
|
+
);
|
|
328
|
+
const set = React.useCallback(
|
|
329
|
+
(next) => {
|
|
330
|
+
const v = clampSwipeOffset(next, hasLeading, hasTrailing);
|
|
331
|
+
cur.current = v;
|
|
332
|
+
x.setValue(v);
|
|
333
|
+
const side = activeSwipeSide(v);
|
|
334
|
+
if (side !== sideRef.current) {
|
|
335
|
+
sideRef.current = side;
|
|
336
|
+
setOpenSide(side);
|
|
337
|
+
}
|
|
338
|
+
const nowArmed = side !== null && rails[side].commit !== null && isSwipeArmed(v, side);
|
|
339
|
+
if (nowArmed !== armedRef.current) {
|
|
340
|
+
armedRef.current = nowArmed;
|
|
341
|
+
setArmed(nowArmed);
|
|
342
|
+
arm.setValue(nowArmed ? 1 : 0);
|
|
343
|
+
if (nowArmed) haptics.impact("medium");
|
|
344
|
+
}
|
|
345
|
+
armWidth.setValue(nowArmed ? armedActionWidth(v, rowWidth.current) : SWIPE_ACTION_WIDTH);
|
|
346
|
+
},
|
|
347
|
+
[x, arm, armWidth, rails, hasLeading, hasTrailing]
|
|
348
|
+
);
|
|
349
|
+
const springTo = React.useCallback(
|
|
350
|
+
(to, velocity, config, done) => {
|
|
351
|
+
running.current?.stop();
|
|
352
|
+
if (reducedMotion) {
|
|
353
|
+
running.current = Animated.timing(x, {
|
|
354
|
+
toValue: to,
|
|
355
|
+
duration: theme.motion.collapse.duration,
|
|
356
|
+
easing: Easing.bezier(...theme.motion.collapse.easing),
|
|
357
|
+
useNativeDriver: true
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
running.current = Animated.spring(x, {
|
|
361
|
+
toValue: to,
|
|
362
|
+
velocity,
|
|
363
|
+
...config,
|
|
364
|
+
useNativeDriver: true
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const toSide = activeSwipeSide(to);
|
|
368
|
+
const widthTarget = toSide !== null && rails[toSide].commit !== null && isSwipeArmed(to, toSide) ? armedActionWidth(to, rowWidth.current) : SWIPE_ACTION_WIDTH;
|
|
369
|
+
Animated.timing(armWidth, {
|
|
370
|
+
toValue: widthTarget,
|
|
371
|
+
duration: theme.motion.expand.duration,
|
|
372
|
+
easing: Easing.bezier(...theme.motion.expand.easing),
|
|
373
|
+
useNativeDriver: false
|
|
374
|
+
}).start();
|
|
375
|
+
running.current.start(({ finished }) => {
|
|
376
|
+
if (!finished) return;
|
|
377
|
+
set(to);
|
|
378
|
+
done?.();
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
[x, armWidth, set, rails, reducedMotion, theme.motion.collapse, theme.motion.expand]
|
|
382
|
+
);
|
|
383
|
+
const collapse = React.useCallback(
|
|
384
|
+
(done) => {
|
|
385
|
+
collapseH.setValue(rowHeight.current);
|
|
386
|
+
setCollapsing(true);
|
|
387
|
+
const height = theme.motion.spring;
|
|
388
|
+
const fade = theme.motion.collapse;
|
|
389
|
+
Animated.parallel([
|
|
390
|
+
Animated.timing(collapseH, {
|
|
391
|
+
toValue: 0,
|
|
392
|
+
duration: theme.duration.base,
|
|
393
|
+
easing: Easing.bezier(...height.easing),
|
|
394
|
+
useNativeDriver: false
|
|
395
|
+
}),
|
|
396
|
+
Animated.timing(collapseOpacity, {
|
|
397
|
+
toValue: 0,
|
|
398
|
+
duration: theme.duration.base,
|
|
399
|
+
easing: Easing.bezier(...fade.easing),
|
|
400
|
+
useNativeDriver: false
|
|
401
|
+
}),
|
|
402
|
+
Animated.timing(collapseMargin, {
|
|
403
|
+
toValue: SWIPE_COLLAPSE_MARGIN,
|
|
404
|
+
duration: theme.duration.base,
|
|
405
|
+
easing: Easing.bezier(...height.easing),
|
|
406
|
+
useNativeDriver: false
|
|
407
|
+
})
|
|
408
|
+
]).start();
|
|
409
|
+
removeTimer.current = setTimeout(
|
|
410
|
+
() => done?.(),
|
|
411
|
+
theme.reduceMotion ? 1 : SWIPE_COLLAPSE_REMOVE_MS
|
|
412
|
+
);
|
|
413
|
+
},
|
|
414
|
+
[
|
|
415
|
+
collapseH,
|
|
416
|
+
collapseOpacity,
|
|
417
|
+
collapseMargin,
|
|
418
|
+
theme.duration.base,
|
|
419
|
+
theme.motion.spring,
|
|
420
|
+
theme.motion.collapse,
|
|
421
|
+
theme.reduceMotion
|
|
422
|
+
]
|
|
423
|
+
);
|
|
424
|
+
const fire = React.useCallback(
|
|
425
|
+
(action) => {
|
|
426
|
+
if (swipeActionCollapses(action)) {
|
|
427
|
+
haptics.impact("heavy");
|
|
428
|
+
collapse(action.onAction);
|
|
429
|
+
} else {
|
|
430
|
+
haptics.impact("light");
|
|
431
|
+
springTo(0, 0, SWIPE_SPRING_SNAP);
|
|
432
|
+
action.onAction();
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
[springTo, collapse]
|
|
436
|
+
);
|
|
437
|
+
const end = React.useCallback(() => {
|
|
438
|
+
if (axis.current !== "x") return;
|
|
439
|
+
axis.current = null;
|
|
440
|
+
const side = activeSwipeSide(cur.current);
|
|
441
|
+
if (side === null) return;
|
|
442
|
+
const rail = rails[side];
|
|
443
|
+
const decision = resolveSwipeRelease(
|
|
444
|
+
cur.current,
|
|
445
|
+
vel.current,
|
|
446
|
+
rail.openOffset,
|
|
447
|
+
rail.commit !== null,
|
|
448
|
+
side
|
|
449
|
+
);
|
|
450
|
+
if (decision.kind === "snap") {
|
|
451
|
+
springTo(decision.to, vel.current, SWIPE_SPRING_SNAP);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const target = rail.commit;
|
|
455
|
+
if (!target) return;
|
|
456
|
+
haptics.impact("heavy");
|
|
457
|
+
if (!swipeActionCollapses(target)) {
|
|
458
|
+
springTo(0, vel.current, SWIPE_SPRING_COMMIT);
|
|
459
|
+
target.onAction();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
springTo(
|
|
463
|
+
swipeSideSign(side) * rowWidth.current,
|
|
464
|
+
vel.current,
|
|
465
|
+
SWIPE_SPRING_COMMIT,
|
|
466
|
+
() => collapse(target.onAction)
|
|
467
|
+
);
|
|
468
|
+
}, [rails, springTo, collapse]);
|
|
469
|
+
const setRef = React.useRef(set);
|
|
470
|
+
setRef.current = set;
|
|
471
|
+
const endRef = React.useRef(end);
|
|
472
|
+
endRef.current = end;
|
|
473
|
+
const dirRef = React.useRef(dir);
|
|
474
|
+
dirRef.current = dir;
|
|
475
|
+
const startOffset = React.useRef(0);
|
|
476
|
+
const pan = React.useRef(
|
|
477
|
+
PanResponder.create({
|
|
478
|
+
// Never claim on touch-down: a tap must still reach the row, and the
|
|
479
|
+
// enclosing scroll view must keep an unambiguous vertical drag.
|
|
480
|
+
onStartShouldSetPanResponder: () => false,
|
|
481
|
+
// The axis lock. RN re-runs this negotiation on every move until someone
|
|
482
|
+
// claims the gesture, which is exactly the reference's `axisLock === null`
|
|
483
|
+
// window — returning `false` for a vertical drag leaves it to the list,
|
|
484
|
+
// which is what makes the row usable inside a scroll view at all.
|
|
485
|
+
onMoveShouldSetPanResponder: (_e, g) => lockSwipeAxis(g.dx, g.dy) === "x",
|
|
486
|
+
onPanResponderGrant: (_e, g) => {
|
|
487
|
+
running.current?.stop();
|
|
488
|
+
axis.current = "x";
|
|
489
|
+
startOffset.current = cur.current;
|
|
490
|
+
lastX.current = g.moveX;
|
|
491
|
+
lastT.current = Date.now();
|
|
492
|
+
vel.current = 0;
|
|
493
|
+
},
|
|
494
|
+
onPanResponderMove: (_e, g) => {
|
|
495
|
+
setRef.current(startOffset.current + g.dx * dirRef.current);
|
|
496
|
+
const now = Date.now();
|
|
497
|
+
vel.current = (g.moveX - lastX.current) / Math.max(1, now - lastT.current) * 1e3 * dirRef.current;
|
|
498
|
+
lastX.current = g.moveX;
|
|
499
|
+
lastT.current = now;
|
|
500
|
+
},
|
|
501
|
+
onPanResponderRelease: () => endRef.current(),
|
|
502
|
+
onPanResponderTerminate: () => endRef.current()
|
|
503
|
+
})
|
|
504
|
+
).current;
|
|
505
|
+
const onLayout = (e) => {
|
|
506
|
+
rowWidth.current = e.nativeEvent.layout.width || FALLBACK_ROW_WIDTH;
|
|
507
|
+
rowHeight.current = e.nativeEvent.layout.height || rowHeight.current;
|
|
508
|
+
};
|
|
509
|
+
const openMenu = () => {
|
|
510
|
+
const node = rootRef.current;
|
|
511
|
+
if (node && typeof node.measureInWindow === "function") {
|
|
512
|
+
node.measureInWindow((mx, my, width, height) => setAnchor({ x: mx, y: my, width, height }));
|
|
513
|
+
}
|
|
514
|
+
haptics.impact("heavy");
|
|
515
|
+
setMenuOpen(true);
|
|
516
|
+
};
|
|
517
|
+
const a11yActions = swipeAccessibilityActions(allActions, menuLabel);
|
|
518
|
+
const onAccessibilityAction = (e) => {
|
|
519
|
+
const target = resolveSwipeAccessibilityAction(allActions, e.nativeEvent.actionName);
|
|
520
|
+
if (target === "menu") openMenu();
|
|
521
|
+
else if (target) fire(target);
|
|
522
|
+
};
|
|
523
|
+
const menuItems = allActions.map((a) => ({
|
|
524
|
+
key: a.key,
|
|
525
|
+
label: a.label,
|
|
526
|
+
icon: a.icon ?? (a.destructive === true ? "trash" : "folder"),
|
|
527
|
+
destructive: a.destructive,
|
|
528
|
+
onSelect: () => fire(a)
|
|
529
|
+
}));
|
|
530
|
+
const renderRail = (side) => {
|
|
531
|
+
const rail = rails[side];
|
|
532
|
+
return /* @__PURE__ */ jsx(
|
|
533
|
+
View,
|
|
534
|
+
{
|
|
535
|
+
style: [
|
|
536
|
+
StyleSheet.absoluteFill,
|
|
537
|
+
styles.rail,
|
|
538
|
+
side === "leading" ? styles.railLeading : styles.railTrailing,
|
|
539
|
+
// A closed rail sits under the foreground at opacity 0. Taking it out
|
|
540
|
+
// of hit-testing means a tap can never reach the wrong side's buttons,
|
|
541
|
+
// and costs nothing: `openSide` only re-renders when the sign changes.
|
|
542
|
+
// In `style`, not the prop — the prop is deprecated as of RN 0.74.
|
|
543
|
+
{ pointerEvents: openSide === side ? "auto" : "none" }
|
|
544
|
+
],
|
|
545
|
+
children: railOrder(rail.actions, side).map((action) => /* @__PURE__ */ jsx(
|
|
546
|
+
SwipeActionButton,
|
|
547
|
+
{
|
|
548
|
+
action,
|
|
549
|
+
side,
|
|
550
|
+
x,
|
|
551
|
+
arm,
|
|
552
|
+
armWidth,
|
|
553
|
+
openOffset: rail.openOffset,
|
|
554
|
+
morphs: action === rail.commit,
|
|
555
|
+
armed: armed && openSide === side,
|
|
556
|
+
releaseLabel: action.releaseLabel ?? releaseLabel,
|
|
557
|
+
reduced: reducedMotion,
|
|
558
|
+
onPress: () => {
|
|
559
|
+
if (activeSwipeSide(cur.current) !== side || Math.abs(cur.current) < 2) return;
|
|
560
|
+
fire(action);
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
action.key
|
|
564
|
+
))
|
|
565
|
+
},
|
|
566
|
+
side
|
|
567
|
+
);
|
|
568
|
+
};
|
|
569
|
+
return /* @__PURE__ */ jsxs(
|
|
570
|
+
Animated.View,
|
|
571
|
+
{
|
|
572
|
+
ref: rootRef,
|
|
573
|
+
onLayout,
|
|
574
|
+
style: [
|
|
575
|
+
styles.swipeRoot,
|
|
576
|
+
collapsing ? {
|
|
577
|
+
height: collapseH,
|
|
578
|
+
opacity: collapseOpacity,
|
|
579
|
+
marginTop: collapseMargin
|
|
580
|
+
} : null
|
|
581
|
+
],
|
|
582
|
+
children: [
|
|
583
|
+
hasLeading ? renderRail("leading") : null,
|
|
584
|
+
hasTrailing ? renderRail("trailing") : null,
|
|
585
|
+
/* @__PURE__ */ jsx(
|
|
586
|
+
Animated.View,
|
|
587
|
+
{
|
|
588
|
+
style: [styles.foreground, { transform: [{ translateX: Animated.multiply(x, dir) }] }],
|
|
589
|
+
...pan.panHandlers,
|
|
590
|
+
children: /* @__PURE__ */ jsx(
|
|
591
|
+
RowBody,
|
|
592
|
+
{
|
|
593
|
+
...row,
|
|
594
|
+
interactive: true,
|
|
595
|
+
onLongPress: openMenu,
|
|
596
|
+
accessibilityActions: a11yActions,
|
|
597
|
+
onAccessibilityAction,
|
|
598
|
+
accessibilityHint: hint
|
|
599
|
+
}
|
|
600
|
+
)
|
|
601
|
+
}
|
|
602
|
+
),
|
|
603
|
+
/* @__PURE__ */ jsx(
|
|
604
|
+
ContextMenuOverlay,
|
|
605
|
+
{
|
|
606
|
+
open: menuOpen,
|
|
607
|
+
onClose: () => setMenuOpen(false),
|
|
608
|
+
items: menuItems,
|
|
609
|
+
anchor,
|
|
610
|
+
accessibilityLabel: menuLabel
|
|
611
|
+
}
|
|
612
|
+
)
|
|
613
|
+
]
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
function SwipeActionButton({
|
|
618
|
+
action,
|
|
619
|
+
side,
|
|
620
|
+
x,
|
|
621
|
+
arm,
|
|
622
|
+
armWidth,
|
|
623
|
+
openOffset,
|
|
624
|
+
morphs,
|
|
625
|
+
armed,
|
|
626
|
+
releaseLabel,
|
|
627
|
+
reduced,
|
|
628
|
+
onPress
|
|
629
|
+
}) {
|
|
630
|
+
const theme = useLoamTheme();
|
|
631
|
+
const destructive = action.destructive === true;
|
|
632
|
+
const { styles } = useStyles(sheet, { destructive });
|
|
633
|
+
const p = side === "leading" ? x.interpolate({
|
|
634
|
+
inputRange: [0, openOffset],
|
|
635
|
+
outputRange: [0, 1],
|
|
636
|
+
extrapolate: "clamp"
|
|
637
|
+
}) : x.interpolate({
|
|
638
|
+
inputRange: [openOffset, 0],
|
|
639
|
+
outputRange: [1, 0],
|
|
640
|
+
extrapolate: "clamp"
|
|
641
|
+
});
|
|
642
|
+
const revealOpacity = p.interpolate({
|
|
643
|
+
inputRange: [0, 1 / 1.4, 1],
|
|
644
|
+
outputRange: [0, 1, 1]
|
|
645
|
+
});
|
|
646
|
+
const revealScale = p.interpolate({
|
|
647
|
+
inputRange: [0, 1 / 1.3, 1],
|
|
648
|
+
outputRange: [0.72, 1, 1]
|
|
649
|
+
});
|
|
650
|
+
const notArmed = Animated.subtract(1, arm);
|
|
651
|
+
const opacity = morphs ? revealOpacity : Animated.multiply(revealOpacity, notArmed);
|
|
652
|
+
const scale = reduced ? 1 : morphs ? revealScale : Animated.add(Animated.multiply(revealScale, notArmed), Animated.multiply(arm, 0.4));
|
|
653
|
+
const label = morphs && armed ? releaseLabel : action.label;
|
|
654
|
+
const icon = action.icon ?? (destructive ? "trash" : "folder");
|
|
655
|
+
const ink = destructive ? theme.color.dangerInk : theme.color.infoInk;
|
|
656
|
+
return (
|
|
657
|
+
// Outer slot: the JS-driven morph width lives here, alone, so the inner
|
|
658
|
+
// view's `transform`/`opacity` can stay on the native driver.
|
|
659
|
+
/* @__PURE__ */ jsx(Animated.View, { style: [styles.actionSlot, morphs ? { width: armWidth } : null], children: /* @__PURE__ */ jsx(Animated.View, { style: [styles.actionFill, { opacity, transform: [{ scale }] }], children: /* @__PURE__ */ jsxs(
|
|
660
|
+
Pressable,
|
|
661
|
+
{
|
|
662
|
+
accessibilityRole: "button",
|
|
663
|
+
accessibilityLabel: action.label,
|
|
664
|
+
onPress,
|
|
665
|
+
style: [styles.action, morphs ? styles.actionMorphing : null],
|
|
666
|
+
children: [
|
|
667
|
+
/* @__PURE__ */ jsx(Icon, { name: icon, size: theme.icon.md, color: ink, strokeWidth: theme.icon.stroke }),
|
|
668
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.actionLabel, { color: ink }], numberOfLines: 1, children: label })
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
) }) })
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
function dragAccessibilityActions(canMoveUp, canMoveDown) {
|
|
675
|
+
const actions = [];
|
|
676
|
+
if (canMoveUp) actions.push({ name: "moveUp", label: "Move up" });
|
|
677
|
+
if (canMoveDown) actions.push({ name: "moveDown", label: "Move down" });
|
|
678
|
+
return actions;
|
|
679
|
+
}
|
|
680
|
+
function DraggableRow({
|
|
681
|
+
row,
|
|
682
|
+
onDragStart,
|
|
683
|
+
onDragMove,
|
|
684
|
+
onDragEnd,
|
|
685
|
+
onMoveUp,
|
|
686
|
+
onMoveDown,
|
|
687
|
+
handleLabel,
|
|
688
|
+
motion
|
|
689
|
+
}) {
|
|
690
|
+
const theme = useLoamTheme();
|
|
691
|
+
const { styles } = useStyles(sheet, { divider: false, disabled: false });
|
|
692
|
+
const reducedMotion = resolveRowMotion(motion, theme.reduceMotion) !== "full";
|
|
693
|
+
const noMotion = motion === "none";
|
|
694
|
+
const y = React.useRef(new Animated.Value(0)).current;
|
|
695
|
+
const lift = React.useRef(new Animated.Value(0)).current;
|
|
696
|
+
const [dragging, setDragging] = React.useState(false);
|
|
697
|
+
const begin = React.useCallback(() => {
|
|
698
|
+
setDragging(true);
|
|
699
|
+
haptics.impact("medium");
|
|
700
|
+
onDragStart?.();
|
|
701
|
+
if (reducedMotion) {
|
|
702
|
+
lift.setValue(0);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
Animated.timing(lift, {
|
|
706
|
+
toValue: 1,
|
|
707
|
+
duration: theme.motion.enter.duration,
|
|
708
|
+
easing: Easing.bezier(...theme.motion.enter.easing),
|
|
709
|
+
useNativeDriver: true
|
|
710
|
+
}).start();
|
|
711
|
+
}, [lift, onDragStart, reducedMotion, theme.motion.enter]);
|
|
712
|
+
const settle = React.useCallback(() => {
|
|
713
|
+
haptics.selection();
|
|
714
|
+
onDragEnd?.();
|
|
715
|
+
if (noMotion) {
|
|
716
|
+
y.setValue(0);
|
|
717
|
+
lift.setValue(0);
|
|
718
|
+
setDragging(false);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
lift.setValue(0);
|
|
722
|
+
const settleY = reducedMotion ? Animated.timing(y, {
|
|
723
|
+
toValue: 0,
|
|
724
|
+
duration: theme.motion.exit.duration,
|
|
725
|
+
easing: Easing.bezier(...theme.motion.exit.easing),
|
|
726
|
+
useNativeDriver: true
|
|
727
|
+
}) : Animated.spring(y, { toValue: 0, useNativeDriver: true, ...theme.spring.press });
|
|
728
|
+
settleY.start(({ finished }) => {
|
|
729
|
+
if (finished) setDragging(false);
|
|
730
|
+
});
|
|
731
|
+
}, [y, lift, onDragEnd, noMotion, reducedMotion, theme.spring.press, theme.motion.exit]);
|
|
732
|
+
const beginRef = React.useRef(begin);
|
|
733
|
+
beginRef.current = begin;
|
|
734
|
+
const settleRef = React.useRef(settle);
|
|
735
|
+
settleRef.current = settle;
|
|
736
|
+
const moveRef = React.useRef(onDragMove);
|
|
737
|
+
moveRef.current = onDragMove;
|
|
738
|
+
const pan = React.useRef(
|
|
739
|
+
PanResponder.create({
|
|
740
|
+
// The handle is a dedicated grip, so it claims the gesture on touch-down —
|
|
741
|
+
// there is nothing else it could mean.
|
|
742
|
+
onStartShouldSetPanResponder: () => true,
|
|
743
|
+
onMoveShouldSetPanResponder: () => true,
|
|
744
|
+
onPanResponderGrant: () => beginRef.current(),
|
|
745
|
+
onPanResponderMove: (_e, g) => {
|
|
746
|
+
y.setValue(g.dy);
|
|
747
|
+
moveRef.current?.(g.dy);
|
|
748
|
+
},
|
|
749
|
+
onPanResponderRelease: () => settleRef.current(),
|
|
750
|
+
onPanResponderTerminate: () => settleRef.current()
|
|
751
|
+
})
|
|
752
|
+
).current;
|
|
753
|
+
const dragActions = dragAccessibilityActions(onMoveUp != null, onMoveDown != null);
|
|
754
|
+
const onRowAction = (e) => {
|
|
755
|
+
if (e.nativeEvent.actionName === "moveUp") onMoveUp?.();
|
|
756
|
+
else if (e.nativeEvent.actionName === "moveDown") onMoveDown?.();
|
|
757
|
+
};
|
|
758
|
+
const handle = /* @__PURE__ */ jsx(
|
|
759
|
+
View,
|
|
760
|
+
{
|
|
761
|
+
accessible: false,
|
|
762
|
+
importantForAccessibility: "no-hide-descendants",
|
|
763
|
+
style: styles.slot,
|
|
764
|
+
...pan.panHandlers,
|
|
765
|
+
children: /* @__PURE__ */ jsx(View, { style: styles.dragHandle, children: /* @__PURE__ */ jsx(
|
|
766
|
+
Icon,
|
|
767
|
+
{
|
|
768
|
+
name: "grip",
|
|
769
|
+
size: theme.icon.md,
|
|
770
|
+
color: dragging ? theme.color.accent : theme.color.text3,
|
|
771
|
+
strokeWidth: theme.icon.stroke
|
|
772
|
+
}
|
|
773
|
+
) })
|
|
774
|
+
}
|
|
775
|
+
);
|
|
776
|
+
const scale = reducedMotion ? 1 : lift.interpolate({ inputRange: [0, 1], outputRange: [1, 1.02] });
|
|
777
|
+
return /* @__PURE__ */ jsx(
|
|
778
|
+
Animated.View,
|
|
779
|
+
{
|
|
780
|
+
style: [
|
|
781
|
+
styles.dragRoot,
|
|
782
|
+
dragging ? styles.dragActive : null,
|
|
783
|
+
{ transform: [{ translateY: y }, { scale }] }
|
|
784
|
+
],
|
|
785
|
+
children: /* @__PURE__ */ jsx(
|
|
786
|
+
RowBody,
|
|
787
|
+
{
|
|
788
|
+
...row,
|
|
789
|
+
interactive: true,
|
|
790
|
+
dragHandle: handle,
|
|
791
|
+
accessibilityActions: dragActions.length > 0 ? dragActions : void 0,
|
|
792
|
+
onAccessibilityAction: dragActions.length > 0 ? onRowAction : void 0,
|
|
793
|
+
accessibilityHint: `${handleLabel}. Use the move up and move down actions to reorder.`
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
}
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
function List({ children, role }) {
|
|
800
|
+
const { styles } = useStyles(sheet, { divider: false, disabled: false });
|
|
801
|
+
const items = React.Children.toArray(children).filter(Boolean);
|
|
802
|
+
return /* @__PURE__ */ jsx(View, { role, accessibilityRole: role, style: styles.list, children: items.map((child, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
803
|
+
child,
|
|
804
|
+
i < items.length - 1 ? (
|
|
805
|
+
// Hidden, not just unlabelled. The dividers are interleaved between
|
|
806
|
+
// the children, so under `role="list"` they would be owned children
|
|
807
|
+
// of the list alongside the items — and a `list` may own only
|
|
808
|
+
// `listitem`s. Hiding them keeps `aria-required-children` satisfied
|
|
809
|
+
// while `role="list"` satisfies `aria-required-parent`, which is the
|
|
810
|
+
// trade the board and notification-center notes both warn about.
|
|
811
|
+
/* @__PURE__ */ jsx(
|
|
812
|
+
View,
|
|
813
|
+
{
|
|
814
|
+
"aria-hidden": true,
|
|
815
|
+
accessibilityElementsHidden: true,
|
|
816
|
+
importantForAccessibility: "no-hide-descendants",
|
|
817
|
+
style: styles.listDivider
|
|
818
|
+
}
|
|
819
|
+
)
|
|
820
|
+
) : null
|
|
821
|
+
] }, i)) });
|
|
822
|
+
}
|
|
823
|
+
var sheet = createStyleSheet((theme) => ({
|
|
824
|
+
base: {
|
|
825
|
+
flexDirection: "row",
|
|
826
|
+
alignItems: "center",
|
|
827
|
+
gap: theme.space.s3,
|
|
828
|
+
paddingHorizontal: theme.space.s4,
|
|
829
|
+
backgroundColor: "transparent",
|
|
830
|
+
variants: {
|
|
831
|
+
// Density drives the height floor + vertical padding from the shared
|
|
832
|
+
// ramp: `comfortable` is the canonical list-row height (56 at the default
|
|
833
|
+
// density, the 1.x value — so nothing regresses); `compact` drops to the
|
|
834
|
+
// touch control tier (44), which still clears `theme.tapMin`.
|
|
835
|
+
density: {
|
|
836
|
+
comfortable: { minHeight: theme.layout.listRowH, paddingVertical: theme.space.s3 },
|
|
837
|
+
compact: { minHeight: theme.ctl.lg, paddingVertical: theme.space.s2 }
|
|
838
|
+
},
|
|
839
|
+
// A selected row is drawn on an opaque `surface` — not the translucent
|
|
840
|
+
// `selected` wash — so its `text2` subtitle keeps a full AA margin (5.65:1
|
|
841
|
+
// in light) instead of the 4.28:1 the wash would leave it with over the
|
|
842
|
+
// page background. The accent signal is the start-edge bar + filled check.
|
|
843
|
+
selected: {
|
|
844
|
+
true: { backgroundColor: theme.color.surface },
|
|
845
|
+
false: {}
|
|
846
|
+
},
|
|
847
|
+
divider: {
|
|
848
|
+
true: { borderBottomWidth: theme.borderWidth.thin, borderBottomColor: theme.color.border },
|
|
849
|
+
false: {}
|
|
850
|
+
},
|
|
851
|
+
disabled: {
|
|
852
|
+
true: { opacity: theme.state.disabledOpacity },
|
|
853
|
+
false: {}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
pressed: { backgroundColor: theme.color.surface2 },
|
|
858
|
+
slot: { flexShrink: 0 },
|
|
859
|
+
main: { flex: 1, minWidth: 0, gap: theme.space.s1 },
|
|
860
|
+
// Row title = the "Headline" role (the one emphasised body-size style).
|
|
861
|
+
title: {
|
|
862
|
+
fontFamily: theme.font.sans,
|
|
863
|
+
fontWeight: theme.type.headline.fontWeight,
|
|
864
|
+
fontSize: theme.type.headline.fontSize,
|
|
865
|
+
lineHeight: theme.type.headline.lineHeight,
|
|
866
|
+
letterSpacing: theme.type.headline.letterSpacing,
|
|
867
|
+
color: theme.color.text
|
|
868
|
+
},
|
|
869
|
+
// Row subtitle = the "Subheadline" role (supporting text under the title).
|
|
870
|
+
subtitle: {
|
|
871
|
+
fontFamily: theme.font.sans,
|
|
872
|
+
fontSize: theme.type.subheadline.fontSize,
|
|
873
|
+
lineHeight: theme.type.subheadline.lineHeight,
|
|
874
|
+
letterSpacing: theme.type.subheadline.letterSpacing,
|
|
875
|
+
color: theme.color.text2
|
|
876
|
+
},
|
|
877
|
+
list: {
|
|
878
|
+
backgroundColor: theme.color.surface,
|
|
879
|
+
borderWidth: theme.borderWidth.thin,
|
|
880
|
+
borderColor: theme.color.border,
|
|
881
|
+
borderRadius: theme.radius.xl,
|
|
882
|
+
overflow: "hidden"
|
|
883
|
+
},
|
|
884
|
+
listDivider: { height: theme.space[1], backgroundColor: theme.color.border },
|
|
885
|
+
/* ---- selection affordance ---- */
|
|
886
|
+
/**
|
|
887
|
+
* The leading selection circle. `w2` is the static ring weight (part of the
|
|
888
|
+
* drawing, so it must not re-scale). On: `accent` fill + accent ring, with an
|
|
889
|
+
* `accentInk` check painted at the use site. Off: transparent over a
|
|
890
|
+
* `borderStrong` ring, which stays visible on the surface.
|
|
891
|
+
*/
|
|
892
|
+
selectBox: {
|
|
893
|
+
width: theme.icon.lg,
|
|
894
|
+
height: theme.icon.lg,
|
|
895
|
+
borderRadius: theme.radius.full,
|
|
896
|
+
borderWidth: theme.borderWidth.thick,
|
|
897
|
+
alignItems: "center",
|
|
898
|
+
justifyContent: "center",
|
|
899
|
+
variants: {
|
|
900
|
+
// Checked: a filled accent box, border matching the fill — so unlike the
|
|
901
|
+
// accent *edges* elsewhere (see `focusRingColor()`) there is no separate
|
|
902
|
+
// edge to hold to 3:1. The checkmark inside rides `accentInk`, which is
|
|
903
|
+
// measured against this fill, and the box's own boundary is fill-vs-row.
|
|
904
|
+
selectedBox: {
|
|
905
|
+
true: { backgroundColor: theme.color.accent, borderColor: theme.color.accent },
|
|
906
|
+
false: { backgroundColor: "transparent", borderColor: theme.color.borderStrong }
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
/**
|
|
911
|
+
* The start-edge accent bar marking a selected row. Absolutely positioned so
|
|
912
|
+
* it adds no layout shift between selected/unselected, and `accent` clears the
|
|
913
|
+
* 3:1 non-text-contrast bar against the surface in both modes.
|
|
914
|
+
*/
|
|
915
|
+
selectBar: {
|
|
916
|
+
position: "absolute",
|
|
917
|
+
top: 0,
|
|
918
|
+
bottom: 0,
|
|
919
|
+
start: 0,
|
|
920
|
+
width: theme.space.s1,
|
|
921
|
+
backgroundColor: theme.color.accent,
|
|
922
|
+
pointerEvents: "none"
|
|
923
|
+
},
|
|
924
|
+
/* ---- drag layer ---- */
|
|
925
|
+
/** The draggable root at rest — transparent, so a grouped List's surface shows. */
|
|
926
|
+
dragRoot: { backgroundColor: "transparent" },
|
|
927
|
+
/**
|
|
928
|
+
* The lifted row: an opaque `surface` card so it detaches from the list, with
|
|
929
|
+
* `shadow.lg` for elevation. Under high contrast the shadow is zero, but the
|
|
930
|
+
* `scale` lift and the accent grip still signal the drag.
|
|
931
|
+
*/
|
|
932
|
+
dragActive: {
|
|
933
|
+
backgroundColor: theme.color.surface,
|
|
934
|
+
borderRadius: theme.radius.md,
|
|
935
|
+
// A row being dragged is lifted out of the flow, which is what `raised` is
|
|
936
|
+
// for — and what `sortable` / `board` use on the web for the same gesture.
|
|
937
|
+
// A bare `zIndex: 2` only worked because nothing else in the list claimed a
|
|
938
|
+
// layer; it says "above my neighbour" where the intent is "above the list".
|
|
939
|
+
zIndex: theme.z.raised,
|
|
940
|
+
// The lift is the whole "this row is moving" signal, and elevation is zeroed
|
|
941
|
+
// under high contrast — the accent edge says it there instead. It is
|
|
942
|
+
// `transparent` until then, so a resting row is untouched.
|
|
943
|
+
borderWidth: theme.borderWidth.base,
|
|
944
|
+
borderColor: theme.edge.accent,
|
|
945
|
+
...theme.shadow.lg
|
|
946
|
+
},
|
|
947
|
+
/** The grip's hit area — padded to `tapMin`, the glyph itself never grows. */
|
|
948
|
+
dragHandle: {
|
|
949
|
+
minWidth: theme.tapMin,
|
|
950
|
+
minHeight: theme.tapMin,
|
|
951
|
+
alignItems: "center",
|
|
952
|
+
justifyContent: "center"
|
|
953
|
+
},
|
|
954
|
+
/* ---- swipe layer ---- */
|
|
955
|
+
/** Clips the rail while the row is closed and while it collapses. */
|
|
956
|
+
swipeRoot: {
|
|
957
|
+
position: "relative",
|
|
958
|
+
overflow: "hidden"
|
|
959
|
+
},
|
|
960
|
+
/** Shared geometry of both action rails behind the row. */
|
|
961
|
+
rail: {
|
|
962
|
+
flexDirection: "row",
|
|
963
|
+
alignItems: "stretch",
|
|
964
|
+
gap: SWIPE_ACTION_GAP,
|
|
965
|
+
// The reference's 74px row around 62px buttons is exactly `2 * PAD`.
|
|
966
|
+
paddingVertical: SWIPE_ACTION_PAD
|
|
967
|
+
},
|
|
968
|
+
/**
|
|
969
|
+
* Logical `flex-end` + `paddingEnd` / `flex-start` + `paddingStart` is what
|
|
970
|
+
* pins each rail to the correct *physical* edge under RTL with no extra code —
|
|
971
|
+
* both flip with the writing direction, so only the row's own `translateX`
|
|
972
|
+
* needs the `dir` multiplier.
|
|
973
|
+
*/
|
|
974
|
+
railTrailing: { justifyContent: "flex-end", paddingEnd: SWIPE_ACTION_PAD },
|
|
975
|
+
railLeading: { justifyContent: "flex-start", paddingStart: SWIPE_ACTION_PAD },
|
|
976
|
+
/**
|
|
977
|
+
* The draggable row. It only takes the `surface` fill it needs to mask the
|
|
978
|
+
* rail: the reference's row is a standalone card (`radius.lg` + hairline +
|
|
979
|
+
* `shadow.sm`), but a Loam `ListRow` composes inside `List`, which owns that
|
|
980
|
+
* chrome — adding it here would double the border inside a grouped list.
|
|
981
|
+
*/
|
|
982
|
+
foreground: {
|
|
983
|
+
backgroundColor: theme.color.surface
|
|
984
|
+
},
|
|
985
|
+
/** Holds the animated morph width; never transformed itself. */
|
|
986
|
+
actionSlot: { flexGrow: 0, flexShrink: 0, alignSelf: "stretch" },
|
|
987
|
+
/** Carries the native-driven reveal (`opacity` + `scale`). */
|
|
988
|
+
actionFill: { flex: 1 },
|
|
989
|
+
/** The morphing destructive action fills whatever width the slot animates to. */
|
|
990
|
+
actionMorphing: { width: "100%" },
|
|
991
|
+
action: {
|
|
992
|
+
width: SWIPE_ACTION_WIDTH,
|
|
993
|
+
flex: 1,
|
|
994
|
+
minHeight: theme.tapMin,
|
|
995
|
+
alignItems: "center",
|
|
996
|
+
justifyContent: "center",
|
|
997
|
+
// Reference `gap:5px` between glyph and label → `space.s1` (4).
|
|
998
|
+
gap: theme.space.s1,
|
|
999
|
+
borderRadius: theme.radius.md,
|
|
1000
|
+
overflow: "hidden",
|
|
1001
|
+
variants: {
|
|
1002
|
+
destructive: {
|
|
1003
|
+
true: { backgroundColor: theme.color.danger },
|
|
1004
|
+
false: { backgroundColor: theme.color.info }
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
actionLabel: {
|
|
1009
|
+
fontFamily: theme.font.sans,
|
|
1010
|
+
// Reference `font-weight:700;font-size:11px` → the `caption2` role at 700.
|
|
1011
|
+
fontWeight: theme.fontWeight.bold,
|
|
1012
|
+
fontSize: theme.type.caption2.fontSize,
|
|
1013
|
+
lineHeight: theme.type.caption2.lineHeight,
|
|
1014
|
+
letterSpacing: theme.type.caption2.letterSpacing
|
|
1015
|
+
}
|
|
1016
|
+
}));
|
|
1017
|
+
|
|
1018
|
+
export { List, ListRow, SWIPE_ACTION_GAP, SWIPE_ACTION_PAD, SWIPE_ACTION_WIDTH, SWIPE_ARM_THRESHOLD, SWIPE_AXIS_LOCK_PX, SWIPE_COLLAPSE_MARGIN, SWIPE_COLLAPSE_REMOVE_MS, SWIPE_FLICK_VELOCITY, SWIPE_OVERSCROLL_DAMP, SWIPE_PROJECTION_S, SWIPE_SPRING_COMMIT, SWIPE_SPRING_SNAP, activeSwipeSide, armedActionWidth, clampSwipeOffset, dragAccessibilityActions, isSwipeArmed, lockSwipeAxis, normalizeSwipeActions, orderSwipeActions, railOrder, resolveSwipeAccessibilityAction, resolveSwipeRelease, swipeAccessibilityActions, swipeActionCollapses, swipeActionOpacity, swipeActionScale, swipeCommitAction, swipeOpenOffset, swipeProgress, swipeSideSign };
|