@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.d.mts CHANGED
@@ -166,12 +166,17 @@ declare const MobileLiquidation: FC<LiquidationState & {
166
166
  };
167
167
  }>;
168
168
 
169
+ declare enum CloseType {
170
+ ALL = "ALL",
171
+ PROFIT = "PROFIT",
172
+ LOSS = "LOSS"
173
+ }
169
174
  interface UseCloseAllPositionsScriptOptions {
170
175
  symbol?: string;
171
176
  onSuccess?: () => void;
172
177
  }
173
178
  declare const useCloseAllPositionsScript: (options?: UseCloseAllPositionsScriptOptions) => {
174
- onCloseAll: () => void;
179
+ confirmAndCloseAll: (closeType: CloseType) => Promise<boolean>;
175
180
  hasOpenPositions: boolean;
176
181
  isClosing: boolean;
177
182
  openPositionsCount: number;
@@ -287,4 +292,4 @@ declare const OrderInfoCard: FC<OrderInfoCardProps>;
287
292
 
288
293
  declare const MarketCloseConfirmID = "MarketCloseConfirmID";
289
294
 
290
- export { CloseAllPositions, type CloseAllPositionsProps, type CloseAllPositionsState, CloseAllPositionsWidget, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, type OrderInfoCardProps, PositionHistory, PositionHistoryWidget, type PositionsProps, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, type ReversePositionProps, type ReversePositionState, ReversePositionWidget, type ReversePositionWidgetProps, type SortType, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
295
+ export { CloseAllPositions, type CloseAllPositionsProps, type CloseAllPositionsState, CloseAllPositionsWidget, CloseType, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, type OrderInfoCardProps, PositionHistory, PositionHistoryWidget, type PositionsProps, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, type ReversePositionProps, type ReversePositionState, ReversePositionWidget, type ReversePositionWidgetProps, type SortType, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
package/dist/index.d.ts CHANGED
@@ -166,12 +166,17 @@ declare const MobileLiquidation: FC<LiquidationState & {
166
166
  };
167
167
  }>;
168
168
 
169
+ declare enum CloseType {
170
+ ALL = "ALL",
171
+ PROFIT = "PROFIT",
172
+ LOSS = "LOSS"
173
+ }
169
174
  interface UseCloseAllPositionsScriptOptions {
170
175
  symbol?: string;
171
176
  onSuccess?: () => void;
172
177
  }
173
178
  declare const useCloseAllPositionsScript: (options?: UseCloseAllPositionsScriptOptions) => {
174
- onCloseAll: () => void;
179
+ confirmAndCloseAll: (closeType: CloseType) => Promise<boolean>;
175
180
  hasOpenPositions: boolean;
176
181
  isClosing: boolean;
177
182
  openPositionsCount: number;
@@ -287,4 +292,4 @@ declare const OrderInfoCard: FC<OrderInfoCardProps>;
287
292
 
288
293
  declare const MarketCloseConfirmID = "MarketCloseConfirmID";
289
294
 
290
- export { CloseAllPositions, type CloseAllPositionsProps, type CloseAllPositionsState, CloseAllPositionsWidget, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, type OrderInfoCardProps, PositionHistory, PositionHistoryWidget, type PositionsProps, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, type ReversePositionProps, type ReversePositionState, ReversePositionWidget, type ReversePositionWidgetProps, type SortType, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
295
+ export { CloseAllPositions, type CloseAllPositionsProps, type CloseAllPositionsState, CloseAllPositionsWidget, CloseType, CombinePositionsWidget, FundingFeeButton, FundingFeeHistoryUI, Liquidation, LiquidationWidget, MarketCloseConfirmID, MobileLiquidation, MobileLiquidationWidget, MobilePositionHistory, MobilePositionHistoryWidget, MobilePositionsWidget, OrderInfoCard, type OrderInfoCardProps, PositionHistory, PositionHistoryWidget, type PositionsProps, PositionsTabName, PositionsWidget, ReversePosition, ReversePositionDialogId, type ReversePositionProps, type ReversePositionState, ReversePositionWidget, type ReversePositionWidgetProps, type SortType, sortList, useCloseAllPositionsScript, useLiquidationScript, usePositionHistoryScript, usePositionsRowContext, useReversePositionEnabled, useReversePositionScript, useSort, useTabSort };
package/dist/index.js CHANGED
@@ -2769,14 +2769,13 @@ var SelIcon = () => {
2769
2769
  viewBox: "0 0 16 16",
2770
2770
  fill: "currentColor",
2771
2771
  xmlns: "http://www.w3.org/2000/svg",
2772
- className: "oui-fill-white",
2772
+ className: "oui-fill-base-contrast",
2773
2773
  children: [
2774
2774
  /* @__PURE__ */ jsxRuntime.jsx(
2775
2775
  "path",
2776
2776
  {
2777
- 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",
2778
- fill: "#fff",
2779
- fillOpacity: ".36"
2777
+ className: "oui-fill-base-contrast-36",
2778
+ 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"
2780
2779
  }
2781
2780
  ),
2782
2781
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "3.333" })
@@ -2791,14 +2790,14 @@ var UnselIcon = () => {
2791
2790
  width: "16",
2792
2791
  height: "16",
2793
2792
  viewBox: "0 0 16 16",
2794
- fill: "none",
2793
+ fill: "currentColor",
2795
2794
  xmlns: "http://www.w3.org/2000/svg",
2795
+ className: "oui-fill-base-contrast-54",
2796
2796
  children: /* @__PURE__ */ jsxRuntime.jsx(
2797
2797
  "path",
2798
2798
  {
2799
2799
  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",
2800
- fill: "#fff",
2801
- fillOpacity: ".54"
2800
+ fill: "currentColor"
2802
2801
  }
2803
2802
  )
2804
2803
  }
@@ -4997,15 +4996,14 @@ var ArrowIcon = (props) => {
4997
4996
  width: "16",
4998
4997
  height: "16",
4999
4998
  viewBox: "0 0 16 16",
5000
- fill: "none",
4999
+ fill: "currentColor",
5001
5000
  xmlns: "http://www.w3.org/2000/svg",
5002
- className: props.className,
5001
+ className: ui.cn(props.className, "oui-fill-base-contrast-54"),
5003
5002
  children: /* @__PURE__ */ jsxRuntime.jsx(
5004
5003
  "path",
5005
5004
  {
5006
5005
  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",
5007
- fill: "#fff",
5008
- fillOpacity: ".54"
5006
+ fill: "currentColor"
5009
5007
  }
5010
5008
  )
5011
5009
  }
@@ -5493,27 +5491,24 @@ var MobileLiquidationWidget = (props) => {
5493
5491
  const state = useLiquidationScript(rest);
5494
5492
  return /* @__PURE__ */ jsxRuntime.jsx(MobileLiquidation, { classNames, ...state });
5495
5493
  };
5496
- var CloseAllPositions = (props) => {
5497
- const { onCloseAll, hasOpenPositions, isClosing, className, style, symbol } = props;
5498
- const { t } = i18n.useTranslation();
5499
- if (symbol !== void 0) {
5500
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
5494
+ var MAX_BATCH_ORDER_SIZE2 = 10;
5495
+ var BATCH_RATE_LIMIT_MS = 1100;
5496
+ var CloseType = /* @__PURE__ */ ((CloseType2) => {
5497
+ CloseType2["ALL"] = "ALL";
5498
+ CloseType2["PROFIT"] = "PROFIT";
5499
+ CloseType2["LOSS"] = "LOSS";
5500
+ return CloseType2;
5501
+ })(CloseType || {});
5502
+ function getPositionsByCloseType(positions2, closeType) {
5503
+ if (closeType === "ALL" /* ALL */) return positions2;
5504
+ if (closeType === "PROFIT" /* PROFIT */) {
5505
+ return positions2.filter((p) => (p.unrealized_pnl ?? 0) > 0);
5501
5506
  }
5502
- return /* @__PURE__ */ jsxRuntime.jsx(
5503
- ui.Button,
5504
- {
5505
- onClick: onCloseAll,
5506
- disabled: !hasOpenPositions || isClosing,
5507
- loading: isClosing,
5508
- variant: "outlined",
5509
- color: "secondary",
5510
- size: "xs",
5511
- className: ui.cn("disabled:oui-bg-transport", className),
5512
- style,
5513
- children: t("positions.closeAll")
5514
- }
5515
- );
5516
- };
5507
+ if (closeType === "LOSS" /* LOSS */) {
5508
+ return positions2.filter((p) => (p.unrealized_pnl ?? 0) < 0);
5509
+ }
5510
+ return positions2;
5511
+ }
5517
5512
  var useCloseAllPositionsScript = (options) => {
5518
5513
  const { symbol, onSuccess } = options || {};
5519
5514
  const { t } = i18n.useTranslation();
@@ -5528,64 +5523,216 @@ var useCloseAllPositionsScript = (options) => {
5528
5523
  const hasOpenPositions = React2.useMemo(() => {
5529
5524
  return openPositions.length > 0;
5530
5525
  }, [openPositions]);
5531
- const [doCreateOrder, { isMutating }] = hooks.useSubAccountMutation(
5532
- "/v1/order",
5526
+ const [doBatchCreateOrder, { isMutating }] = hooks.useSubAccountMutation(
5527
+ "/v1/batch-order",
5533
5528
  "POST"
5534
5529
  );
5535
- const closeAllPositions = React2.useCallback(async () => {
5536
- if (!hasOpenPositions) return;
5537
- try {
5538
- const closePromises = openPositions.map(
5539
- (position) => {
5540
- const side = position.position_qty > 0 ? types.OrderSide.SELL : types.OrderSide.BUY;
5541
- const quantity = Math.abs(position.position_qty);
5542
- return doCreateOrder({
5543
- symbol: position.symbol,
5544
- order_type: types.OrderType.MARKET,
5545
- side,
5546
- order_quantity: quantity,
5547
- reduce_only: true
5548
- });
5530
+ const buildOrdersFromPositions = React2.useCallback(
5531
+ (positions2) => positions2.map((position) => {
5532
+ const side = position.position_qty > 0 ? types.OrderSide.SELL : types.OrderSide.BUY;
5533
+ const quantity = Math.abs(position.position_qty);
5534
+ return {
5535
+ symbol: position.symbol,
5536
+ order_type: types.OrderType.MARKET,
5537
+ side,
5538
+ order_quantity: quantity,
5539
+ reduce_only: true
5540
+ };
5541
+ }),
5542
+ []
5543
+ );
5544
+ const closePositions = React2.useCallback(
5545
+ async (positionsToClose) => {
5546
+ if (positionsToClose.length === 0) return false;
5547
+ try {
5548
+ const orders = buildOrdersFromPositions(positionsToClose);
5549
+ for (let i = 0; i < orders.length; i += MAX_BATCH_ORDER_SIZE2) {
5550
+ const batch = orders.slice(i, i + MAX_BATCH_ORDER_SIZE2);
5551
+ const result = await doBatchCreateOrder({ orders: batch });
5552
+ if (result?.error) {
5553
+ throw result.error;
5554
+ }
5555
+ if (i + MAX_BATCH_ORDER_SIZE2 < orders.length) {
5556
+ await new Promise(
5557
+ (resolve) => setTimeout(resolve, BATCH_RATE_LIMIT_MS)
5558
+ );
5559
+ }
5549
5560
  }
5550
- );
5551
- await Promise.all(closePromises);
5552
- ui.toast.success(t("positions.closeAll.success"));
5553
- onSuccess?.();
5554
- return true;
5555
- } catch (error) {
5556
- if (error?.message !== void 0) {
5557
- ui.toast.error(error.message);
5561
+ ui.toast.success(t("positions.closeAll.success"));
5562
+ onSuccess?.();
5563
+ return true;
5564
+ } catch (error) {
5565
+ if (error?.message !== void 0) {
5566
+ ui.toast.error(error.message);
5567
+ }
5568
+ return false;
5558
5569
  }
5559
- return false;
5560
- }
5561
- }, [openPositions, hasOpenPositions, doCreateOrder, t, onSuccess]);
5562
- const onCloseAll = React2.useCallback(() => {
5563
- ui.modal.confirm({
5564
- title: t("positions.closeAll"),
5565
- content: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", children: t("positions.closeAll.description") }),
5566
- onCancel: async () => {
5567
- },
5568
- onOk: async () => {
5569
- try {
5570
- tracking(types.TrackerEventName.confirmCloseAllPositions, {
5571
- positions_count: openPositions.length
5572
- });
5573
- const result = await closeAllPositions();
5574
- return Promise.resolve(result);
5575
- } catch (error) {
5576
- return Promise.resolve(false);
5570
+ },
5571
+ [doBatchCreateOrder, buildOrdersFromPositions, t, onSuccess]
5572
+ );
5573
+ const confirmAndCloseAll = React2.useCallback(
5574
+ async (closeType) => {
5575
+ const positionsToClose = getPositionsByCloseType(
5576
+ openPositions,
5577
+ closeType
5578
+ );
5579
+ if (positionsToClose.length === 0) {
5580
+ if (closeType === "PROFIT" /* PROFIT */ || closeType === "LOSS" /* LOSS */) {
5581
+ const msg = closeType === "PROFIT" /* PROFIT */ ? t("positions.closeAll.noPositions.profit") : t("positions.closeAll.noPositions.loss");
5582
+ ui.toast.error(msg);
5583
+ onSuccess?.();
5584
+ return true;
5577
5585
  }
5586
+ return false;
5578
5587
  }
5579
- });
5580
- }, [t, tracking, openPositions, closeAllPositions]);
5588
+ tracking(types.TrackerEventName.confirmCloseAllPositions, {
5589
+ positions_count: positionsToClose.length
5590
+ });
5591
+ return closePositions(positionsToClose);
5592
+ },
5593
+ [openPositions, tracking, closePositions, t, onSuccess]
5594
+ );
5581
5595
  return {
5582
- onCloseAll,
5596
+ confirmAndCloseAll,
5583
5597
  hasOpenPositions,
5584
5598
  isClosing: isMutating,
5585
5599
  openPositionsCount: openPositions.length,
5586
5600
  symbol
5587
5601
  };
5588
5602
  };
5603
+ var CloseAllPositions = (props) => {
5604
+ const {
5605
+ confirmAndCloseAll,
5606
+ hasOpenPositions,
5607
+ isClosing,
5608
+ className,
5609
+ style,
5610
+ symbol
5611
+ } = props;
5612
+ const { t } = i18n.useTranslation();
5613
+ const { isMobile } = ui.useScreen();
5614
+ const [open, setOpen] = React2.useState(false);
5615
+ const [selectedCloseType, setSelectedCloseType] = React2.useState(
5616
+ "ALL" /* ALL */
5617
+ );
5618
+ const radioRow = (label, value) => {
5619
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5620
+ ui.Flex,
5621
+ {
5622
+ justify: "start",
5623
+ itemAlign: "center",
5624
+ gap: 2,
5625
+ className: "oui-w-full oui-cursor-pointer",
5626
+ onClick: () => setSelectedCloseType(value),
5627
+ children: [
5628
+ /* @__PURE__ */ jsxRuntime.jsx(
5629
+ ui.Checkbox,
5630
+ {
5631
+ variant: "radio",
5632
+ color: "white",
5633
+ checked: selectedCloseType === value
5634
+ }
5635
+ ),
5636
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: isMobile ? "xs" : "2xs", weight: "semibold", className: "", children: label })
5637
+ ]
5638
+ }
5639
+ );
5640
+ };
5641
+ if (symbol !== void 0) {
5642
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
5643
+ }
5644
+ const handleConfirm = async () => {
5645
+ const ok = await confirmAndCloseAll(selectedCloseType);
5646
+ if (ok) setOpen(false);
5647
+ };
5648
+ const bodyContent = /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", gap: 2, className: "oui-w-full", children: [
5649
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: isMobile ? "xs" : "2xs", intensity: 54, children: t("positions.closeAll.popover.desc") }),
5650
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", gap: 1, className: "oui-w-full", children: [
5651
+ radioRow(t("positions.closeAll.optionAll"), "ALL" /* ALL */),
5652
+ radioRow(t("positions.closeAll.optionProfit"), "PROFIT" /* PROFIT */),
5653
+ radioRow(t("positions.closeAll.optionLoss"), "LOSS" /* LOSS */)
5654
+ ] }),
5655
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, className: "oui-justify-end oui-mt-1 oui-w-full", children: [
5656
+ /* @__PURE__ */ jsxRuntime.jsx(
5657
+ ui.Button,
5658
+ {
5659
+ variant: "outlined",
5660
+ color: "secondary",
5661
+ size: isMobile ? "lg" : "md",
5662
+ onClick: () => setOpen(false),
5663
+ fullWidth: true,
5664
+ children: t("common.cancel")
5665
+ }
5666
+ ),
5667
+ /* @__PURE__ */ jsxRuntime.jsx(
5668
+ ui.Button,
5669
+ {
5670
+ variant: "contained",
5671
+ color: "primary",
5672
+ size: isMobile ? "lg" : "md",
5673
+ loading: isClosing,
5674
+ onClick: handleConfirm,
5675
+ fullWidth: true,
5676
+ children: t("common.confirm")
5677
+ }
5678
+ )
5679
+ ] })
5680
+ ] });
5681
+ const triggerButton = /* @__PURE__ */ jsxRuntime.jsx(
5682
+ ui.Button,
5683
+ {
5684
+ disabled: !hasOpenPositions || isClosing,
5685
+ loading: isClosing,
5686
+ variant: "outlined",
5687
+ color: "secondary",
5688
+ size: "xs",
5689
+ className: ui.cn("disabled:oui-bg-transport", className),
5690
+ style,
5691
+ onClick: isMobile ? () => setOpen(true) : void 0,
5692
+ children: t("positions.closeAll")
5693
+ }
5694
+ );
5695
+ if (isMobile) {
5696
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5697
+ triggerButton,
5698
+ /* @__PURE__ */ jsxRuntime.jsx(
5699
+ ui.SimpleSheet,
5700
+ {
5701
+ open,
5702
+ onOpenChange: setOpen,
5703
+ title: t("positions.closeAll"),
5704
+ children: bodyContent
5705
+ }
5706
+ )
5707
+ ] });
5708
+ }
5709
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.PopoverRoot, { open, onOpenChange: setOpen, children: [
5710
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PopoverTrigger, { asChild: true, children: triggerButton }),
5711
+ /* @__PURE__ */ jsxRuntime.jsx(
5712
+ ui.PopoverContent,
5713
+ {
5714
+ align: "end",
5715
+ sideOffset: 8,
5716
+ className: "oui-w-[280px] oui-p-4",
5717
+ onOpenAutoFocus: (e) => e.preventDefault(),
5718
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", gap: 2, className: "oui-w-full", children: [
5719
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", justify: "between", className: "oui-w-full", children: [
5720
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "sm", weight: "semibold", children: t("positions.closeAll") }),
5721
+ /* @__PURE__ */ jsxRuntime.jsx(
5722
+ "button",
5723
+ {
5724
+ onClick: () => setOpen(false),
5725
+ className: "oui-text-base-contrast-54 hover:oui-text-base-contrast transition-colors oui-cursor-pointer",
5726
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, { size: 16, color: "inherit", opacity: 1 })
5727
+ }
5728
+ )
5729
+ ] }),
5730
+ bodyContent
5731
+ ] })
5732
+ }
5733
+ )
5734
+ ] });
5735
+ };
5589
5736
  var CloseAllPositionsWidget = (props) => {
5590
5737
  const { className, style, symbol, onSuccess } = props;
5591
5738
  const state = useCloseAllPositionsScript({ symbol, onSuccess });
@@ -5601,6 +5748,7 @@ ui.registerSimpleDialog(MarketCloseConfirmID, MarketCloseConfirm, {
5601
5748
 
5602
5749
  exports.CloseAllPositions = CloseAllPositions;
5603
5750
  exports.CloseAllPositionsWidget = CloseAllPositionsWidget;
5751
+ exports.CloseType = CloseType;
5604
5752
  exports.CombinePositionsWidget = CombinePositionsWidget;
5605
5753
  exports.FundingFeeButton = FundingFeeButton;
5606
5754
  exports.FundingFeeHistoryUI = FundingFeeHistoryUI;