@orderly.network/ui-positions 2.10.1 → 2.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { registerSimpleDialog, useModal, SimpleDialog, Flex, Text, Badge, Divider, CloseIcon, Button, ThrottledButton, useScreen, ArrowDownShortIcon, ArrowUpShortIcon, Grid, Statistic, ExclamationFillIcon, modal, Tooltip, DataTable, cn, ListView, SimpleSheet, usePagination, DataFilter, toast, formatAddress, Box, HoverCard, ArrowLeftRightIcon, capitalizeFirstLetter, ShareIcon, EditIcon, Input, inputFormatter, Select, PopoverRoot, PopoverTrigger, PopoverContent, Tips, Slider } from '@orderly.network/ui';
1
+ import { registerSimpleDialog, useModal, SimpleDialog, Flex, Text, Badge, Divider, CloseIcon, Button, ThrottledButton, useScreen, ArrowDownShortIcon, ArrowUpShortIcon, Grid, Statistic, ExclamationFillIcon, modal, Tooltip, DataTable, cn, ListView, SimpleSheet, usePagination, DataFilter, toast, PopoverRoot, PopoverTrigger, PopoverContent, Checkbox, formatAddress, Box, HoverCard, ArrowLeftRightIcon, capitalizeFirstLetter, ShareIcon, EditIcon, Input, inputFormatter, Select, Tips, Slider } from '@orderly.network/ui';
2
2
  import React2, { createContext, useMemo, useState, useCallback, useContext, useEffect, useRef } from 'react';
3
3
  import { i18n, useTranslation } from '@orderly.network/i18n';
4
4
  import { OrderSide, OrderType, EMPTY_LIST, AccountStatusEnum, TrackerEventName, OrderStatus, AlgoOrderRootType, PositionType, AlgoOrderType } from '@orderly.network/types';
@@ -2763,14 +2763,13 @@ var SelIcon = () => {
2763
2763
  viewBox: "0 0 16 16",
2764
2764
  fill: "currentColor",
2765
2765
  xmlns: "http://www.w3.org/2000/svg",
2766
- className: "oui-fill-white",
2766
+ className: "oui-fill-base-contrast",
2767
2767
  children: [
2768
2768
  /* @__PURE__ */ jsx(
2769
2769
  "path",
2770
2770
  {
2771
- d: "M8.01 1.333a6.667 6.667 0 1 0 0 13.333 6.667 6.667 0 0 0 0-13.333m0 1.333a5.334 5.334 0 1 1-.001 10.667 5.334 5.334 0 0 1 0-10.667",
2772
- fill: "#fff",
2773
- fillOpacity: ".36"
2771
+ className: "oui-fill-base-contrast-36",
2772
+ d: "M8.01 1.333a6.667 6.667 0 1 0 0 13.333 6.667 6.667 0 0 0 0-13.333m0 1.333a5.334 5.334 0 1 1-.001 10.667 5.334 5.334 0 0 1 0-10.667"
2774
2773
  }
2775
2774
  ),
2776
2775
  /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "3.333" })
@@ -2785,14 +2784,14 @@ var UnselIcon = () => {
2785
2784
  width: "16",
2786
2785
  height: "16",
2787
2786
  viewBox: "0 0 16 16",
2788
- fill: "none",
2787
+ fill: "currentColor",
2789
2788
  xmlns: "http://www.w3.org/2000/svg",
2789
+ className: "oui-fill-base-contrast-54",
2790
2790
  children: /* @__PURE__ */ jsx(
2791
2791
  "path",
2792
2792
  {
2793
2793
  d: "M8.01 1.333a6.667 6.667 0 1 0 0 13.333 6.667 6.667 0 0 0 0-13.333m0 1.333a5.334 5.334 0 1 1-.001 10.667 5.334 5.334 0 0 1 0-10.667",
2794
- fill: "#fff",
2795
- fillOpacity: ".54"
2794
+ fill: "currentColor"
2796
2795
  }
2797
2796
  )
2798
2797
  }
@@ -4991,15 +4990,14 @@ var ArrowIcon = (props) => {
4991
4990
  width: "16",
4992
4991
  height: "16",
4993
4992
  viewBox: "0 0 16 16",
4994
- fill: "none",
4993
+ fill: "currentColor",
4995
4994
  xmlns: "http://www.w3.org/2000/svg",
4996
- className: props.className,
4995
+ className: cn(props.className, "oui-fill-base-contrast-54"),
4997
4996
  children: /* @__PURE__ */ jsx(
4998
4997
  "path",
4999
4998
  {
5000
4999
  d: "M3.884 6.02a.67.67 0 0 0-.436.27.68.68 0 0 0 .187.933L7.63 9.88a.68.68 0 0 0 .749 0l3.994-2.657a.68.68 0 0 0 .187-.934.68.68 0 0 0-.936-.186L8.003 8.51l-3.62-2.407a.65.65 0 0 0-.499-.084",
5001
- fill: "#fff",
5002
- fillOpacity: ".54"
5000
+ fill: "currentColor"
5003
5001
  }
5004
5002
  )
5005
5003
  }
@@ -5487,27 +5485,24 @@ var MobileLiquidationWidget = (props) => {
5487
5485
  const state = useLiquidationScript(rest);
5488
5486
  return /* @__PURE__ */ jsx(MobileLiquidation, { classNames, ...state });
5489
5487
  };
5490
- var CloseAllPositions = (props) => {
5491
- const { onCloseAll, hasOpenPositions, isClosing, className, style, symbol } = props;
5492
- const { t } = useTranslation();
5493
- if (symbol !== void 0) {
5494
- return /* @__PURE__ */ jsx(Fragment, {});
5488
+ var MAX_BATCH_ORDER_SIZE2 = 10;
5489
+ var BATCH_RATE_LIMIT_MS = 1100;
5490
+ var CloseType = /* @__PURE__ */ ((CloseType2) => {
5491
+ CloseType2["ALL"] = "ALL";
5492
+ CloseType2["PROFIT"] = "PROFIT";
5493
+ CloseType2["LOSS"] = "LOSS";
5494
+ return CloseType2;
5495
+ })(CloseType || {});
5496
+ function getPositionsByCloseType(positions2, closeType) {
5497
+ if (closeType === "ALL" /* ALL */) return positions2;
5498
+ if (closeType === "PROFIT" /* PROFIT */) {
5499
+ return positions2.filter((p) => (p.unrealized_pnl ?? 0) > 0);
5495
5500
  }
5496
- return /* @__PURE__ */ jsx(
5497
- Button,
5498
- {
5499
- onClick: onCloseAll,
5500
- disabled: !hasOpenPositions || isClosing,
5501
- loading: isClosing,
5502
- variant: "outlined",
5503
- color: "secondary",
5504
- size: "xs",
5505
- className: cn("disabled:oui-bg-transport", className),
5506
- style,
5507
- children: t("positions.closeAll")
5508
- }
5509
- );
5510
- };
5501
+ if (closeType === "LOSS" /* LOSS */) {
5502
+ return positions2.filter((p) => (p.unrealized_pnl ?? 0) < 0);
5503
+ }
5504
+ return positions2;
5505
+ }
5511
5506
  var useCloseAllPositionsScript = (options) => {
5512
5507
  const { symbol, onSuccess } = options || {};
5513
5508
  const { t } = useTranslation();
@@ -5522,64 +5517,216 @@ var useCloseAllPositionsScript = (options) => {
5522
5517
  const hasOpenPositions = useMemo(() => {
5523
5518
  return openPositions.length > 0;
5524
5519
  }, [openPositions]);
5525
- const [doCreateOrder, { isMutating }] = useSubAccountMutation(
5526
- "/v1/order",
5520
+ const [doBatchCreateOrder, { isMutating }] = useSubAccountMutation(
5521
+ "/v1/batch-order",
5527
5522
  "POST"
5528
5523
  );
5529
- const closeAllPositions = useCallback(async () => {
5530
- if (!hasOpenPositions) return;
5531
- try {
5532
- const closePromises = openPositions.map(
5533
- (position) => {
5534
- const side = position.position_qty > 0 ? OrderSide.SELL : OrderSide.BUY;
5535
- const quantity = Math.abs(position.position_qty);
5536
- return doCreateOrder({
5537
- symbol: position.symbol,
5538
- order_type: OrderType.MARKET,
5539
- side,
5540
- order_quantity: quantity,
5541
- reduce_only: true
5542
- });
5524
+ const buildOrdersFromPositions = useCallback(
5525
+ (positions2) => positions2.map((position) => {
5526
+ const side = position.position_qty > 0 ? OrderSide.SELL : OrderSide.BUY;
5527
+ const quantity = Math.abs(position.position_qty);
5528
+ return {
5529
+ symbol: position.symbol,
5530
+ order_type: OrderType.MARKET,
5531
+ side,
5532
+ order_quantity: quantity,
5533
+ reduce_only: true
5534
+ };
5535
+ }),
5536
+ []
5537
+ );
5538
+ const closePositions = useCallback(
5539
+ async (positionsToClose) => {
5540
+ if (positionsToClose.length === 0) return false;
5541
+ try {
5542
+ const orders = buildOrdersFromPositions(positionsToClose);
5543
+ for (let i = 0; i < orders.length; i += MAX_BATCH_ORDER_SIZE2) {
5544
+ const batch = orders.slice(i, i + MAX_BATCH_ORDER_SIZE2);
5545
+ const result = await doBatchCreateOrder({ orders: batch });
5546
+ if (result?.error) {
5547
+ throw result.error;
5548
+ }
5549
+ if (i + MAX_BATCH_ORDER_SIZE2 < orders.length) {
5550
+ await new Promise(
5551
+ (resolve) => setTimeout(resolve, BATCH_RATE_LIMIT_MS)
5552
+ );
5553
+ }
5543
5554
  }
5544
- );
5545
- await Promise.all(closePromises);
5546
- toast.success(t("positions.closeAll.success"));
5547
- onSuccess?.();
5548
- return true;
5549
- } catch (error) {
5550
- if (error?.message !== void 0) {
5551
- toast.error(error.message);
5555
+ toast.success(t("positions.closeAll.success"));
5556
+ onSuccess?.();
5557
+ return true;
5558
+ } catch (error) {
5559
+ if (error?.message !== void 0) {
5560
+ toast.error(error.message);
5561
+ }
5562
+ return false;
5552
5563
  }
5553
- return false;
5554
- }
5555
- }, [openPositions, hasOpenPositions, doCreateOrder, t, onSuccess]);
5556
- const onCloseAll = useCallback(() => {
5557
- modal.confirm({
5558
- title: t("positions.closeAll"),
5559
- content: /* @__PURE__ */ jsx(Text, { size: "sm", children: t("positions.closeAll.description") }),
5560
- onCancel: async () => {
5561
- },
5562
- onOk: async () => {
5563
- try {
5564
- tracking(TrackerEventName.confirmCloseAllPositions, {
5565
- positions_count: openPositions.length
5566
- });
5567
- const result = await closeAllPositions();
5568
- return Promise.resolve(result);
5569
- } catch (error) {
5570
- return Promise.resolve(false);
5564
+ },
5565
+ [doBatchCreateOrder, buildOrdersFromPositions, t, onSuccess]
5566
+ );
5567
+ const confirmAndCloseAll = useCallback(
5568
+ async (closeType) => {
5569
+ const positionsToClose = getPositionsByCloseType(
5570
+ openPositions,
5571
+ closeType
5572
+ );
5573
+ if (positionsToClose.length === 0) {
5574
+ if (closeType === "PROFIT" /* PROFIT */ || closeType === "LOSS" /* LOSS */) {
5575
+ const msg = closeType === "PROFIT" /* PROFIT */ ? t("positions.closeAll.noPositions.profit") : t("positions.closeAll.noPositions.loss");
5576
+ toast.error(msg);
5577
+ onSuccess?.();
5578
+ return true;
5571
5579
  }
5580
+ return false;
5572
5581
  }
5573
- });
5574
- }, [t, tracking, openPositions, closeAllPositions]);
5582
+ tracking(TrackerEventName.confirmCloseAllPositions, {
5583
+ positions_count: positionsToClose.length
5584
+ });
5585
+ return closePositions(positionsToClose);
5586
+ },
5587
+ [openPositions, tracking, closePositions, t, onSuccess]
5588
+ );
5575
5589
  return {
5576
- onCloseAll,
5590
+ confirmAndCloseAll,
5577
5591
  hasOpenPositions,
5578
5592
  isClosing: isMutating,
5579
5593
  openPositionsCount: openPositions.length,
5580
5594
  symbol
5581
5595
  };
5582
5596
  };
5597
+ var CloseAllPositions = (props) => {
5598
+ const {
5599
+ confirmAndCloseAll,
5600
+ hasOpenPositions,
5601
+ isClosing,
5602
+ className,
5603
+ style,
5604
+ symbol
5605
+ } = props;
5606
+ const { t } = useTranslation();
5607
+ const { isMobile } = useScreen();
5608
+ const [open, setOpen] = useState(false);
5609
+ const [selectedCloseType, setSelectedCloseType] = useState(
5610
+ "ALL" /* ALL */
5611
+ );
5612
+ const radioRow = (label, value) => {
5613
+ return /* @__PURE__ */ jsxs(
5614
+ Flex,
5615
+ {
5616
+ justify: "start",
5617
+ itemAlign: "center",
5618
+ gap: 2,
5619
+ className: "oui-w-full oui-cursor-pointer",
5620
+ onClick: () => setSelectedCloseType(value),
5621
+ children: [
5622
+ /* @__PURE__ */ jsx(
5623
+ Checkbox,
5624
+ {
5625
+ variant: "radio",
5626
+ color: "white",
5627
+ checked: selectedCloseType === value
5628
+ }
5629
+ ),
5630
+ /* @__PURE__ */ jsx(Text, { size: isMobile ? "xs" : "2xs", weight: "semibold", className: "", children: label })
5631
+ ]
5632
+ }
5633
+ );
5634
+ };
5635
+ if (symbol !== void 0) {
5636
+ return /* @__PURE__ */ jsx(Fragment, {});
5637
+ }
5638
+ const handleConfirm = async () => {
5639
+ const ok = await confirmAndCloseAll(selectedCloseType);
5640
+ if (ok) setOpen(false);
5641
+ };
5642
+ const bodyContent = /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, className: "oui-w-full", children: [
5643
+ /* @__PURE__ */ jsx(Text, { size: isMobile ? "xs" : "2xs", intensity: 54, children: t("positions.closeAll.popover.desc") }),
5644
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, className: "oui-w-full", children: [
5645
+ radioRow(t("positions.closeAll.optionAll"), "ALL" /* ALL */),
5646
+ radioRow(t("positions.closeAll.optionProfit"), "PROFIT" /* PROFIT */),
5647
+ radioRow(t("positions.closeAll.optionLoss"), "LOSS" /* LOSS */)
5648
+ ] }),
5649
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, className: "oui-justify-end oui-mt-1 oui-w-full", children: [
5650
+ /* @__PURE__ */ jsx(
5651
+ Button,
5652
+ {
5653
+ variant: "outlined",
5654
+ color: "secondary",
5655
+ size: isMobile ? "lg" : "md",
5656
+ onClick: () => setOpen(false),
5657
+ fullWidth: true,
5658
+ children: t("common.cancel")
5659
+ }
5660
+ ),
5661
+ /* @__PURE__ */ jsx(
5662
+ Button,
5663
+ {
5664
+ variant: "contained",
5665
+ color: "primary",
5666
+ size: isMobile ? "lg" : "md",
5667
+ loading: isClosing,
5668
+ onClick: handleConfirm,
5669
+ fullWidth: true,
5670
+ children: t("common.confirm")
5671
+ }
5672
+ )
5673
+ ] })
5674
+ ] });
5675
+ const triggerButton = /* @__PURE__ */ jsx(
5676
+ Button,
5677
+ {
5678
+ disabled: !hasOpenPositions || isClosing,
5679
+ loading: isClosing,
5680
+ variant: "outlined",
5681
+ color: "secondary",
5682
+ size: "xs",
5683
+ className: cn("disabled:oui-bg-transport", className),
5684
+ style,
5685
+ onClick: isMobile ? () => setOpen(true) : void 0,
5686
+ children: t("positions.closeAll")
5687
+ }
5688
+ );
5689
+ if (isMobile) {
5690
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5691
+ triggerButton,
5692
+ /* @__PURE__ */ jsx(
5693
+ SimpleSheet,
5694
+ {
5695
+ open,
5696
+ onOpenChange: setOpen,
5697
+ title: t("positions.closeAll"),
5698
+ children: bodyContent
5699
+ }
5700
+ )
5701
+ ] });
5702
+ }
5703
+ return /* @__PURE__ */ jsxs(PopoverRoot, { open, onOpenChange: setOpen, children: [
5704
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: triggerButton }),
5705
+ /* @__PURE__ */ jsx(
5706
+ PopoverContent,
5707
+ {
5708
+ align: "end",
5709
+ sideOffset: 8,
5710
+ className: "oui-w-[280px] oui-p-4",
5711
+ onOpenAutoFocus: (e) => e.preventDefault(),
5712
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, className: "oui-w-full", children: [
5713
+ /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", justify: "between", className: "oui-w-full", children: [
5714
+ /* @__PURE__ */ jsx(Text, { size: "sm", weight: "semibold", children: t("positions.closeAll") }),
5715
+ /* @__PURE__ */ jsx(
5716
+ "button",
5717
+ {
5718
+ onClick: () => setOpen(false),
5719
+ className: "oui-text-base-contrast-54 hover:oui-text-base-contrast transition-colors oui-cursor-pointer",
5720
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 16, color: "inherit", opacity: 1 })
5721
+ }
5722
+ )
5723
+ ] }),
5724
+ bodyContent
5725
+ ] })
5726
+ }
5727
+ )
5728
+ ] });
5729
+ };
5583
5730
  var CloseAllPositionsWidget = (props) => {
5584
5731
  const { className, style, symbol, onSuccess } = props;
5585
5732
  const state = useCloseAllPositionsScript({ symbol, onSuccess });
@@ -5593,6 +5740,6 @@ registerSimpleDialog(MarketCloseConfirmID, MarketCloseConfirm, {
5593
5740
  closable: false
5594
5741
  });
5595
5742
 
5596
- export { CloseAllPositions, CloseAllPositionsWidget, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, PositionHistory, PositionHistoryWidget, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, ReversePositionWidget, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
5743
+ export { CloseAllPositions, CloseAllPositionsWidget, CloseType, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, PositionHistory, PositionHistoryWidget, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, ReversePositionWidget, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
5597
5744
  //# sourceMappingURL=index.mjs.map
5598
5745
  //# sourceMappingURL=index.mjs.map