lynx-console 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -182,6 +182,7 @@ You can also integrate it with a back press handler so that the console closes w
182
182
  | `theme` | `"light" \| "dark"` | `"light"` | Console UI theme |
183
183
  | `safeAreaInsetBottom` | `string` | `"50px"` | Bottom safe area inset |
184
184
  | `customTabs` | `CustomTab[]` | `undefined` | Additional custom tabs to display in the console |
185
+ | `initialPosition` | `{ top?: number; left?: number; right?: number; bottom?: number }` | `{ right: 16, bottom: 84 }` | Initial position (px) of the floating button. Each side is independent, so you can anchor it to any corner (e.g. `{ top: 50, left: 16 }`). When both `top` and `bottom` (or both `left` and `right`) are set, `top`/`left` win. Once the user drags the button, the saved position takes precedence. |
185
186
 
186
187
  ### `CustomTab`
187
188
 
package/dist/index.cjs CHANGED
@@ -2,6 +2,16 @@ import "./index.css";
2
2
  let _lynx_js_react = require("@lynx-js/react");
3
3
  let javascript_stringify = require("javascript-stringify");
4
4
 
5
+ //#region src/hooks/useKeyboardHeight.ts
6
+ function useKeyboardHeight() {
7
+ const [keyboardHeight, setKeyboardHeight] = (0, _lynx_js_react.useState)(0);
8
+ (0, _lynx_js_react.useLynxGlobalEventListener)("keyboardstatuschanged", (status, height) => {
9
+ setKeyboardHeight(status === "on" ? height : 0);
10
+ });
11
+ return keyboardHeight;
12
+ }
13
+
14
+ //#endregion
5
15
  //#region src/styles/theme.ts
6
16
  const colorMap = {
7
17
  light: {
@@ -114,6 +124,7 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
114
124
  const [dragStartHeight, setDragStartHeight] = (0, _lynx_js_react.useState)(savedHeight ?? DEFAULT_HEIGHT);
115
125
  const [isOpening, setIsOpening] = (0, _lynx_js_react.useState)(true);
116
126
  const [isClosing, setIsClosing] = (0, _lynx_js_react.useState)(false);
127
+ const keyboardHeight = useKeyboardHeight();
117
128
  const handleClose = () => {
118
129
  setIsClosing(true);
119
130
  setTimeout(() => {
@@ -157,9 +168,9 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
157
168
  <view className="bs-overlay" bindtap={handleClose}>
158
169
  <view className="bs-content" catchtap={() => {}} style={{
159
170
  background: colors.bg.layerFloating,
160
- height: `${isDragging ? tempHeight : sheetHeight}px`,
171
+ height: `${keyboardHeight > 0 ? Math.min(MAX_HEIGHT, (isDragging ? tempHeight : sheetHeight) + keyboardHeight) : isDragging ? tempHeight : sheetHeight}px`,
161
172
  transform: isOpening || isClosing ? "translateY(100%)" : "translateY(0)",
162
- transition: isDragging ? "none" : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`
173
+ transition: isDragging ? "none" : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1), height ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`
163
174
  }}>
164
175
  {}
165
176
  <view className="bs-handleContainer" bindtouchstart={handleTouchStart} bindtouchmove={handleTouchMove} bindtouchend={handleTouchEnd}>
@@ -173,7 +184,7 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
173
184
  {title}
174
185
  </text>}
175
186
  </view>
176
- <view className="bs-body" style={{ paddingBottom: safeAreaInsetBottom }}>
187
+ <view className="bs-body" style={{ paddingBottom: keyboardHeight > 0 ? `${keyboardHeight}px` : safeAreaInsetBottom }}>
177
188
  {children}
178
189
  </view>
179
190
  {footer && <view className="bs-footer">{footer}</view>}
@@ -1357,22 +1368,42 @@ const LONG_PRESS_DURATION = 400;
1357
1368
  const MOVE_THRESHOLD = 5;
1358
1369
  const DEFAULT_RIGHT = 16;
1359
1370
  const DEFAULT_BOTTOM = 84;
1360
- let savedRight = DEFAULT_RIGHT;
1361
- let savedBottom = DEFAULT_BOTTOM;
1362
- function useLongPressDrag(onTap) {
1363
- const [right, setRight] = (0, _lynx_js_react.useState)(savedRight);
1364
- const [bottom, setBottom] = (0, _lynx_js_react.useState)(savedBottom);
1371
+ function resolveAnchors(initial) {
1372
+ const vertical = initial?.top !== void 0 ? "top" : "bottom";
1373
+ const horizontal = initial?.left !== void 0 ? "left" : "right";
1374
+ const y = vertical === "top" ? initial?.top ?? 0 : initial?.bottom ?? DEFAULT_BOTTOM;
1375
+ return {
1376
+ vertical,
1377
+ horizontal,
1378
+ x: horizontal === "left" ? initial?.left ?? 0 : initial?.right ?? DEFAULT_RIGHT,
1379
+ y
1380
+ };
1381
+ }
1382
+ let saved = null;
1383
+ function useLongPressDrag(onTap, options) {
1384
+ const anchors = resolveAnchors(options?.initialPosition);
1385
+ const snapshot = saved;
1386
+ let initX = anchors.x;
1387
+ let initY = anchors.y;
1388
+ if (snapshot !== null && snapshot.vertical === anchors.vertical && snapshot.horizontal === anchors.horizontal) {
1389
+ initX = snapshot.x;
1390
+ initY = snapshot.y;
1391
+ }
1392
+ const [x, setX] = (0, _lynx_js_react.useState)(initX);
1393
+ const [y, setY] = (0, _lynx_js_react.useState)(initY);
1365
1394
  const [phase, setPhase] = (0, _lynx_js_react.useState)("idle");
1366
- const [tempRight, setTempRight] = (0, _lynx_js_react.useState)(savedRight);
1367
- const [tempBottom, setTempBottom] = (0, _lynx_js_react.useState)(savedBottom);
1395
+ const [tempX, setTempX] = (0, _lynx_js_react.useState)(initX);
1396
+ const [tempY, setTempY] = (0, _lynx_js_react.useState)(initY);
1368
1397
  const timerRef = (0, _lynx_js_react.useRef)(null);
1369
1398
  const draggingRef = (0, _lynx_js_react.useRef)(false);
1370
1399
  const startRef = (0, _lynx_js_react.useRef)({
1371
1400
  x: 0,
1372
1401
  y: 0,
1373
- r: 0,
1374
- b: 0
1402
+ ax: 0,
1403
+ ay: 0
1375
1404
  });
1405
+ const xSign = anchors.horizontal === "right" ? -1 : 1;
1406
+ const ySign = anchors.vertical === "bottom" ? -1 : 1;
1376
1407
  const clearTimer = () => {
1377
1408
  if (timerRef.current) {
1378
1409
  clearTimeout(timerRef.current);
@@ -1383,15 +1414,15 @@ function useLongPressDrag(onTap) {
1383
1414
  startRef.current = {
1384
1415
  x: e.detail.x,
1385
1416
  y: e.detail.y,
1386
- r: right,
1387
- b: bottom
1417
+ ax: x,
1418
+ ay: y
1388
1419
  };
1389
1420
  draggingRef.current = false;
1390
1421
  timerRef.current = setTimeout(() => {
1391
1422
  draggingRef.current = true;
1392
1423
  setPhase("dragging");
1393
- setTempRight(right);
1394
- setTempBottom(bottom);
1424
+ setTempX(x);
1425
+ setTempY(y);
1395
1426
  }, LONG_PRESS_DURATION);
1396
1427
  };
1397
1428
  const handleTouchMove = (e) => {
@@ -1399,26 +1430,34 @@ function useLongPressDrag(onTap) {
1399
1430
  const dy = e.detail.y - startRef.current.y;
1400
1431
  if (!draggingRef.current && (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)) clearTimer();
1401
1432
  if (!draggingRef.current) return;
1402
- setTempRight(startRef.current.r - dx);
1403
- setTempBottom(startRef.current.b - dy);
1433
+ setTempX(startRef.current.ax + xSign * dx);
1434
+ setTempY(startRef.current.ay + ySign * dy);
1404
1435
  };
1405
1436
  const handleTouchEnd = () => {
1406
1437
  clearTimer();
1407
1438
  if (draggingRef.current) {
1408
- setRight(tempRight);
1409
- setBottom(tempBottom);
1410
- savedRight = tempRight;
1411
- savedBottom = tempBottom;
1439
+ setX(tempX);
1440
+ setY(tempY);
1441
+ saved = {
1442
+ vertical: anchors.vertical,
1443
+ horizontal: anchors.horizontal,
1444
+ x: tempX,
1445
+ y: tempY
1446
+ };
1412
1447
  setPhase("releasing");
1413
1448
  draggingRef.current = false;
1414
1449
  setTimeout(() => setPhase("idle"), 300);
1415
1450
  } else onTap();
1416
1451
  };
1417
1452
  const isDragging = phase === "dragging";
1453
+ const currentX = isDragging ? tempX : x;
1454
+ const currentY = isDragging ? tempY : y;
1418
1455
  return {
1419
1456
  phase,
1420
- right: isDragging ? tempRight : right,
1421
- bottom: isDragging ? tempBottom : bottom,
1457
+ positionStyle: {
1458
+ [anchors.horizontal]: `${currentX}px`,
1459
+ [anchors.vertical]: `${currentY}px`
1460
+ },
1422
1461
  clearTimer,
1423
1462
  handlers: {
1424
1463
  catchtouchstart: handleTouchStart,
@@ -1446,9 +1485,9 @@ const SHINE_STYLES = {
1446
1485
  transition: "opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)"
1447
1486
  }
1448
1487
  };
1449
- const FloatingButton = ({ bindtap, children }) => {
1488
+ const FloatingButton = ({ bindtap, children, initialPosition }) => {
1450
1489
  const colors = useThemeColors();
1451
- const { phase, right, bottom, clearTimer, handlers } = useLongPressDrag(bindtap);
1490
+ const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(bindtap, { initialPosition });
1452
1491
  const handleReload = () => {
1453
1492
  try {
1454
1493
  lynx.reload({}, () => {
@@ -1460,8 +1499,7 @@ const FloatingButton = ({ bindtap, children }) => {
1460
1499
  };
1461
1500
  const isDragging = phase === "dragging";
1462
1501
  return <view className={"fb-wrapper"} consume-slide-event={[[-180, 180]]} style={{
1463
- right: `${right}px`,
1464
- bottom: `${bottom}px`,
1502
+ ...positionStyle,
1465
1503
  transform: isDragging ? "scale(1.05)" : "scale(1)",
1466
1504
  transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`
1467
1505
  }} {...handlers}>
@@ -1479,7 +1517,7 @@ const FloatingButton = ({ bindtap, children }) => {
1479
1517
 
1480
1518
  //#endregion
1481
1519
  //#region src/index.tsx
1482
- const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaInsetBottom = "50px", customTabs }, ref) => {
1520
+ const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaInsetBottom = "50px", customTabs, initialPosition }, ref) => {
1483
1521
  const [isOpen, setIsOpen] = (0, _lynx_js_react.useState)(false);
1484
1522
  const [shouldClose, setShouldClose] = (0, _lynx_js_react.useState)(false);
1485
1523
  const { performances } = usePerformance();
@@ -1517,7 +1555,7 @@ const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaI
1517
1555
  backgroundColor: colors.bg.layerDefault,
1518
1556
  color: colors.fg.neutral
1519
1557
  }}>
1520
- <FloatingButton bindtap={handleOpenBottomSheet}>
1558
+ <FloatingButton bindtap={handleOpenBottomSheet} initialPosition={initialPosition}>
1521
1559
  <text className="fb-title t4" style={{
1522
1560
  fontWeight: "400",
1523
1561
  color: colors.palette.staticWhite
package/dist/index.d.cts CHANGED
@@ -88,6 +88,12 @@ interface LynxConsoleProps {
88
88
  theme?: "light" | "dark";
89
89
  safeAreaInsetBottom?: string;
90
90
  customTabs?: CustomTab[];
91
+ initialPosition?: {
92
+ top?: number;
93
+ left?: number;
94
+ right?: number;
95
+ bottom?: number;
96
+ };
91
97
  }
92
98
  declare const LynxConsole: react.ForwardRefExoticComponent<LynxConsoleProps & react.RefAttributes<LynxConsoleHandle>>;
93
99
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;AAAA;AAAA,cAcT,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;EACb,eAAA;IACE,GAAA;IACA,IAAA;IACA,KAAA;IACA,MAAA;EAAA;AAAA;AAAA,cAeE,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
package/dist/index.d.mts CHANGED
@@ -88,6 +88,12 @@ interface LynxConsoleProps {
88
88
  theme?: "light" | "dark";
89
89
  safeAreaInsetBottom?: string;
90
90
  customTabs?: CustomTab[];
91
+ initialPosition?: {
92
+ top?: number;
93
+ left?: number;
94
+ right?: number;
95
+ bottom?: number;
96
+ };
91
97
  }
92
98
  declare const LynxConsole: react.ForwardRefExoticComponent<LynxConsoleProps & react.RefAttributes<LynxConsoleHandle>>;
93
99
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;AAAA;AAAA,cAcT,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;EACb,eAAA;IACE,GAAA;IACA,IAAA;IACA,KAAA;IACA,MAAA;EAAA;AAAA;AAAA,cAeE,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
package/dist/index.mjs CHANGED
@@ -1,7 +1,17 @@
1
1
  import "./index.css";
2
- import { createContext, forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "@lynx-js/react";
2
+ import { createContext, forwardRef, useContext, useEffect, useImperativeHandle, useLynxGlobalEventListener, useMemo, useRef, useState } from "@lynx-js/react";
3
3
  import { stringify } from "javascript-stringify";
4
4
 
5
+ //#region src/hooks/useKeyboardHeight.ts
6
+ function useKeyboardHeight() {
7
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
8
+ useLynxGlobalEventListener("keyboardstatuschanged", (status, height) => {
9
+ setKeyboardHeight(status === "on" ? height : 0);
10
+ });
11
+ return keyboardHeight;
12
+ }
13
+
14
+ //#endregion
5
15
  //#region src/styles/theme.ts
6
16
  const colorMap = {
7
17
  light: {
@@ -114,6 +124,7 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
114
124
  const [dragStartHeight, setDragStartHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);
115
125
  const [isOpening, setIsOpening] = useState(true);
116
126
  const [isClosing, setIsClosing] = useState(false);
127
+ const keyboardHeight = useKeyboardHeight();
117
128
  const handleClose = () => {
118
129
  setIsClosing(true);
119
130
  setTimeout(() => {
@@ -157,9 +168,9 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
157
168
  <view className="bs-overlay" bindtap={handleClose}>
158
169
  <view className="bs-content" catchtap={() => {}} style={{
159
170
  background: colors.bg.layerFloating,
160
- height: `${isDragging ? tempHeight : sheetHeight}px`,
171
+ height: `${keyboardHeight > 0 ? Math.min(MAX_HEIGHT, (isDragging ? tempHeight : sheetHeight) + keyboardHeight) : isDragging ? tempHeight : sheetHeight}px`,
161
172
  transform: isOpening || isClosing ? "translateY(100%)" : "translateY(0)",
162
- transition: isDragging ? "none" : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`
173
+ transition: isDragging ? "none" : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1), height ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`
163
174
  }}>
164
175
  {}
165
176
  <view className="bs-handleContainer" bindtouchstart={handleTouchStart} bindtouchmove={handleTouchMove} bindtouchend={handleTouchEnd}>
@@ -173,7 +184,7 @@ function BottomSheet({ children, title, footer, onClose, isOpen, shouldClose = f
173
184
  {title}
174
185
  </text>}
175
186
  </view>
176
- <view className="bs-body" style={{ paddingBottom: safeAreaInsetBottom }}>
187
+ <view className="bs-body" style={{ paddingBottom: keyboardHeight > 0 ? `${keyboardHeight}px` : safeAreaInsetBottom }}>
177
188
  {children}
178
189
  </view>
179
190
  {footer && <view className="bs-footer">{footer}</view>}
@@ -1357,22 +1368,42 @@ const LONG_PRESS_DURATION = 400;
1357
1368
  const MOVE_THRESHOLD = 5;
1358
1369
  const DEFAULT_RIGHT = 16;
1359
1370
  const DEFAULT_BOTTOM = 84;
1360
- let savedRight = DEFAULT_RIGHT;
1361
- let savedBottom = DEFAULT_BOTTOM;
1362
- function useLongPressDrag(onTap) {
1363
- const [right, setRight] = useState(savedRight);
1364
- const [bottom, setBottom] = useState(savedBottom);
1371
+ function resolveAnchors(initial) {
1372
+ const vertical = initial?.top !== void 0 ? "top" : "bottom";
1373
+ const horizontal = initial?.left !== void 0 ? "left" : "right";
1374
+ const y = vertical === "top" ? initial?.top ?? 0 : initial?.bottom ?? DEFAULT_BOTTOM;
1375
+ return {
1376
+ vertical,
1377
+ horizontal,
1378
+ x: horizontal === "left" ? initial?.left ?? 0 : initial?.right ?? DEFAULT_RIGHT,
1379
+ y
1380
+ };
1381
+ }
1382
+ let saved = null;
1383
+ function useLongPressDrag(onTap, options) {
1384
+ const anchors = resolveAnchors(options?.initialPosition);
1385
+ const snapshot = saved;
1386
+ let initX = anchors.x;
1387
+ let initY = anchors.y;
1388
+ if (snapshot !== null && snapshot.vertical === anchors.vertical && snapshot.horizontal === anchors.horizontal) {
1389
+ initX = snapshot.x;
1390
+ initY = snapshot.y;
1391
+ }
1392
+ const [x, setX] = useState(initX);
1393
+ const [y, setY] = useState(initY);
1365
1394
  const [phase, setPhase] = useState("idle");
1366
- const [tempRight, setTempRight] = useState(savedRight);
1367
- const [tempBottom, setTempBottom] = useState(savedBottom);
1395
+ const [tempX, setTempX] = useState(initX);
1396
+ const [tempY, setTempY] = useState(initY);
1368
1397
  const timerRef = useRef(null);
1369
1398
  const draggingRef = useRef(false);
1370
1399
  const startRef = useRef({
1371
1400
  x: 0,
1372
1401
  y: 0,
1373
- r: 0,
1374
- b: 0
1402
+ ax: 0,
1403
+ ay: 0
1375
1404
  });
1405
+ const xSign = anchors.horizontal === "right" ? -1 : 1;
1406
+ const ySign = anchors.vertical === "bottom" ? -1 : 1;
1376
1407
  const clearTimer = () => {
1377
1408
  if (timerRef.current) {
1378
1409
  clearTimeout(timerRef.current);
@@ -1383,15 +1414,15 @@ function useLongPressDrag(onTap) {
1383
1414
  startRef.current = {
1384
1415
  x: e.detail.x,
1385
1416
  y: e.detail.y,
1386
- r: right,
1387
- b: bottom
1417
+ ax: x,
1418
+ ay: y
1388
1419
  };
1389
1420
  draggingRef.current = false;
1390
1421
  timerRef.current = setTimeout(() => {
1391
1422
  draggingRef.current = true;
1392
1423
  setPhase("dragging");
1393
- setTempRight(right);
1394
- setTempBottom(bottom);
1424
+ setTempX(x);
1425
+ setTempY(y);
1395
1426
  }, LONG_PRESS_DURATION);
1396
1427
  };
1397
1428
  const handleTouchMove = (e) => {
@@ -1399,26 +1430,34 @@ function useLongPressDrag(onTap) {
1399
1430
  const dy = e.detail.y - startRef.current.y;
1400
1431
  if (!draggingRef.current && (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)) clearTimer();
1401
1432
  if (!draggingRef.current) return;
1402
- setTempRight(startRef.current.r - dx);
1403
- setTempBottom(startRef.current.b - dy);
1433
+ setTempX(startRef.current.ax + xSign * dx);
1434
+ setTempY(startRef.current.ay + ySign * dy);
1404
1435
  };
1405
1436
  const handleTouchEnd = () => {
1406
1437
  clearTimer();
1407
1438
  if (draggingRef.current) {
1408
- setRight(tempRight);
1409
- setBottom(tempBottom);
1410
- savedRight = tempRight;
1411
- savedBottom = tempBottom;
1439
+ setX(tempX);
1440
+ setY(tempY);
1441
+ saved = {
1442
+ vertical: anchors.vertical,
1443
+ horizontal: anchors.horizontal,
1444
+ x: tempX,
1445
+ y: tempY
1446
+ };
1412
1447
  setPhase("releasing");
1413
1448
  draggingRef.current = false;
1414
1449
  setTimeout(() => setPhase("idle"), 300);
1415
1450
  } else onTap();
1416
1451
  };
1417
1452
  const isDragging = phase === "dragging";
1453
+ const currentX = isDragging ? tempX : x;
1454
+ const currentY = isDragging ? tempY : y;
1418
1455
  return {
1419
1456
  phase,
1420
- right: isDragging ? tempRight : right,
1421
- bottom: isDragging ? tempBottom : bottom,
1457
+ positionStyle: {
1458
+ [anchors.horizontal]: `${currentX}px`,
1459
+ [anchors.vertical]: `${currentY}px`
1460
+ },
1422
1461
  clearTimer,
1423
1462
  handlers: {
1424
1463
  catchtouchstart: handleTouchStart,
@@ -1446,9 +1485,9 @@ const SHINE_STYLES = {
1446
1485
  transition: "opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)"
1447
1486
  }
1448
1487
  };
1449
- const FloatingButton = ({ bindtap, children }) => {
1488
+ const FloatingButton = ({ bindtap, children, initialPosition }) => {
1450
1489
  const colors = useThemeColors();
1451
- const { phase, right, bottom, clearTimer, handlers } = useLongPressDrag(bindtap);
1490
+ const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(bindtap, { initialPosition });
1452
1491
  const handleReload = () => {
1453
1492
  try {
1454
1493
  lynx.reload({}, () => {
@@ -1460,8 +1499,7 @@ const FloatingButton = ({ bindtap, children }) => {
1460
1499
  };
1461
1500
  const isDragging = phase === "dragging";
1462
1501
  return <view className={"fb-wrapper"} consume-slide-event={[[-180, 180]]} style={{
1463
- right: `${right}px`,
1464
- bottom: `${bottom}px`,
1502
+ ...positionStyle,
1465
1503
  transform: isDragging ? "scale(1.05)" : "scale(1)",
1466
1504
  transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`
1467
1505
  }} {...handlers}>
@@ -1479,7 +1517,7 @@ const FloatingButton = ({ bindtap, children }) => {
1479
1517
 
1480
1518
  //#endregion
1481
1519
  //#region src/index.tsx
1482
- const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px", customTabs }, ref) => {
1520
+ const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px", customTabs, initialPosition }, ref) => {
1483
1521
  const [isOpen, setIsOpen] = useState(false);
1484
1522
  const [shouldClose, setShouldClose] = useState(false);
1485
1523
  const { performances } = usePerformance();
@@ -1517,7 +1555,7 @@ const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px",
1517
1555
  backgroundColor: colors.bg.layerDefault,
1518
1556
  color: colors.fg.neutral
1519
1557
  }}>
1520
- <FloatingButton bindtap={handleOpenBottomSheet}>
1558
+ <FloatingButton bindtap={handleOpenBottomSheet} initialPosition={initialPosition}>
1521
1559
  <text className="fb-title t4" style={{
1522
1560
  fontWeight: "400",
1523
1561
  color: colors.palette.staticWhite