@orderly.network/ui-order-entry 2.10.2 → 2.11.0-alpha.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/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import React2, { forwardRef, useState, useImperativeHandle, useEffect, memo, createContext, useMemo, useRef, useCallback, useContext, useId } from 'react';
2
- import { useLocalStorage, utils, usePositionStream, useEventEmitter, useDebouncedCallback, useFeeState, useRwaSymbolsInfoStore, useOrderlyContext, useMemoizedFn, ERROR_MSG_CODES, useOrderEntry, useMarginRatio, useComputedLTV, useTpslPriceChecker, useHoldingStream, useAppStore, useIndexPricesStream, useGetEstLiqPrice, useTrack, useQuery, useSymbolLeverage, useBoolean } from '@orderly.network/hooks';
1
+ import React3, { forwardRef, useState, useImperativeHandle, useEffect, memo, createContext, useMemo, useRef, useCallback, useContext, useId } from 'react';
2
+ import { useLocalStorage, utils, usePositionStream, useMarkets, MarketsType, useMarginModes, useMarginModeBySymbol, useEventEmitter, useDebouncedCallback, useFeeState, useRwaSymbolsInfoStore, useOrderlyContext, useMemoizedFn, ERROR_MSG_CODES, useOrderEntry, useMarginRatio, useComputedLTV, useTpslPriceChecker, useHoldingStream, useAppStore, useIndexPricesStream, useGetEstLiqPrice, useTrack, useQuery, useFeatureFlag, FlagKeys, useBoolean } from '@orderly.network/hooks';
3
3
  import { useTranslation, i18n } from '@orderly.network/i18n';
4
4
  import { useOrderEntryFormErrorMsg, useCanTrade } from '@orderly.network/react-app';
5
- import { EMPTY_LIST, DistributionType, TrailingCallbackType, OrderSide, OrderType, PositionType, BBOOrderType, ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, OrderLevel, TrackerEventName } from '@orderly.network/types';
6
- import { ExclamationFillIcon, modal, Text, Tooltip, TooltipTrigger, Flex, Input, cn, inputFormatter, Box, registerSimpleDialog, SimpleDialog, Select, Grid, Checkbox, Slider, textVariants, SettingFillIcon, Badge, Divider, Button, TokenIcon, DataTable, SimpleDropdownMenu, CaretDownIcon, Switch, useScreen, toast, ThrottledButton, SimpleSheet, InfoCircleIcon, AddCircleIcon, PopoverRoot, PopoverTrigger, PopoverContent, DotStatus, EditIcon as EditIcon$1 } from '@orderly.network/ui';
5
+ import { EMPTY_LIST, DistributionType, TrailingCallbackType, OrderSide, OrderType, MarginMode, PositionType, BBOOrderType, ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, OrderLevel, TrackerEventName } from '@orderly.network/types';
6
+ import { ExclamationFillIcon, modal, Text, Tooltip, TooltipTrigger, Flex, Input, cn, inputFormatter, Box, registerSimpleDialog, SimpleDialog, registerSimpleSheet, Select, Grid, Checkbox, Slider, textVariants, SettingFillIcon, useModal, Badge, Divider, Button, TokenIcon, DataTable, useScreen, toast, IconButton, CloseIcon, CloseCircleFillIcon, ChevronRightIcon, SimpleDropdownMenu, CaretDownIcon, Switch, ThrottledButton, SimpleSheet, InfoCircleIcon, AddCircleIcon, PopoverRoot, PopoverTrigger, PopoverContent, DotStatus, EditIcon as EditIcon$1 } from '@orderly.network/ui';
7
7
  import { TPSLPositionTypeWidget, TPSLAdvancedWidget } from '@orderly.network/ui-tpsl';
8
- import { Decimal, zero, todpIfNeed, getBBOType, removeTrailingZeros } from '@orderly.network/utils';
8
+ import { Decimal, zero, formatSymbol, todpIfNeed, getBBOType, removeTrailingZeros } from '@orderly.network/utils';
9
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
10
  import { AuthGuard } from '@orderly.network/ui-connector';
11
11
  import { account } from '@orderly.network/perp';
@@ -902,7 +902,8 @@ var LTVRiskTooltipUI = (props) => {
902
902
  isThresholdLoading,
903
903
  holdingData = [],
904
904
  currentLtv,
905
- onConvert
905
+ onConvert,
906
+ marginMode
906
907
  } = props;
907
908
  return /* @__PURE__ */ jsxs(
908
909
  Flex,
@@ -910,6 +911,7 @@ var LTVRiskTooltipUI = (props) => {
910
911
  gap: 1,
911
912
  className: "oui-orderEntry-ltvRiskTooltip oui-w-72 oui-max-w-72",
912
913
  direction: "column",
914
+ itemAlign: "start",
913
915
  children: [
914
916
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justify: "between", itemAlign: "center", children: [
915
917
  /* @__PURE__ */ jsx(Text, { intensity: 36, size: "xs", children: t("common.assets") }),
@@ -988,7 +990,7 @@ var useConvertThreshold = () => {
988
990
  error
989
991
  };
990
992
  };
991
- var useLTVTooltipScript = () => {
993
+ var useLTVTooltipScript = (marginMode) => {
992
994
  const { data: holdingList = [], isLoading: isHoldingLoading } = useHoldingStream();
993
995
  const {
994
996
  ltv_threshold,
@@ -1029,15 +1031,18 @@ var useLTVTooltipScript = () => {
1029
1031
  negative_usdc_threshold,
1030
1032
  isThresholdLoading,
1031
1033
  currentLtv,
1032
- onConvert
1034
+ onConvert,
1035
+ marginMode
1033
1036
  };
1034
1037
  };
1035
- var LTVRiskTooltipWidget = () => {
1036
- const state = useLTVTooltipScript();
1038
+ var LTVRiskTooltipWidget = ({
1039
+ marginMode
1040
+ }) => {
1041
+ const state = useLTVTooltipScript(marginMode);
1037
1042
  return /* @__PURE__ */ jsx(LTVRiskTooltipUI, { ...state });
1038
1043
  };
1039
1044
  var Available = (props) => {
1040
- const { canTrade, currentLtv, quote, freeCollateral } = props;
1045
+ const { canTrade, currentLtv, quote, freeCollateral, marginMode } = props;
1041
1046
  const { t } = useTranslation();
1042
1047
  const { isMobile } = useScreen();
1043
1048
  const showLTV = useMemo(() => {
@@ -1050,13 +1055,13 @@ var Available = (props) => {
1050
1055
  justify: "between",
1051
1056
  className: "oui-orderEntry-available",
1052
1057
  children: [
1053
- /* @__PURE__ */ jsx(Text, { className: "oui-available-label", size: "2xs", children: t("common.available") }),
1058
+ marginMode === MarginMode.ISOLATED ? /* @__PURE__ */ jsx(Tooltip, { content: t("transfer.LTV.isolatedModeUsdcOnly"), children: /* @__PURE__ */ jsx(Text, { className: "oui-available-label oui-cursor-pointer", size: "2xs", children: t("common.available") }) }) : /* @__PURE__ */ jsx(Text, { className: "oui-available-label", size: "2xs", children: t("common.available") }),
1054
1059
  /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", justify: "center", gap: 1, children: [
1055
1060
  showLTV && /* @__PURE__ */ jsx(
1056
1061
  Tooltip,
1057
1062
  {
1058
1063
  className: "oui-available-ltvRisk-tooltip oui-bg-base-6 oui-p-2",
1059
- content: /* @__PURE__ */ jsx(LTVRiskTooltipWidget, {}),
1064
+ content: /* @__PURE__ */ jsx(LTVRiskTooltipWidget, { marginMode }),
1060
1065
  children: /* @__PURE__ */ jsx(
1061
1066
  InfoCircleIcon,
1062
1067
  {
@@ -1148,9 +1153,15 @@ var OrderConfirmDialog = (props) => {
1148
1153
  const { symbolInfo, order, onConfirm, onCancel } = props;
1149
1154
  const { quote, quote_dp, base_dp } = symbolInfo;
1150
1155
  const { side, order_type, order_type_ext, level, symbol } = order;
1156
+ const orderMarginMode = order.margin_mode;
1151
1157
  const { t } = useTranslation();
1152
1158
  const [{ rows: positions }] = usePositionStream(symbol);
1153
- const position = positions?.[0];
1159
+ const position = useMemo(
1160
+ () => orderMarginMode != null ? positions?.find(
1161
+ (row) => row.symbol === symbol && row.margin_mode === orderMarginMode
1162
+ ) : positions?.[0],
1163
+ [positions, symbol, orderMarginMode]
1164
+ );
1154
1165
  const positionQty = position?.position_qty;
1155
1166
  const [_, setNeedConfirm] = useLocalStorage("orderly_order_confirm", true);
1156
1167
  const renderPositionType = () => {
@@ -1844,7 +1855,8 @@ registerSimpleDialog(scaledOrderConfirmDialogId, ScaledOrderConfirmWidget, {
1844
1855
  });
1845
1856
  var OrderTypeSelect = (props) => {
1846
1857
  const { t } = useTranslation();
1847
- const options2 = useMemo(() => {
1858
+ const { isMobile } = useScreen();
1859
+ const allOptions = useMemo(() => {
1848
1860
  return [
1849
1861
  { label: t("orderEntry.orderType.limitOrder"), value: OrderType.LIMIT },
1850
1862
  { label: t("orderEntry.orderType.marketOrder"), value: OrderType.MARKET },
@@ -1866,6 +1878,23 @@ var OrderTypeSelect = (props) => {
1866
1878
  }
1867
1879
  ];
1868
1880
  }, [t]);
1881
+ const advancedOptions = useMemo(() => {
1882
+ return [
1883
+ {
1884
+ label: t("orderEntry.orderType.stopLimit"),
1885
+ value: OrderType.STOP_LIMIT
1886
+ },
1887
+ {
1888
+ label: t("orderEntry.orderType.stopMarket"),
1889
+ value: OrderType.STOP_MARKET
1890
+ },
1891
+ { label: t("orderEntry.orderType.scaledOrder"), value: OrderType.SCALED },
1892
+ {
1893
+ label: t("orderEntry.orderType.trailingStop"),
1894
+ value: OrderType.TRAILING_STOP
1895
+ }
1896
+ ];
1897
+ }, [t]);
1869
1898
  const displayLabelMap = useMemo(() => {
1870
1899
  return {
1871
1900
  [OrderType.LIMIT]: t("orderEntry.orderType.limit"),
@@ -1876,13 +1905,94 @@ var OrderTypeSelect = (props) => {
1876
1905
  [OrderType.TRAILING_STOP]: t("orderEntry.orderType.trailingStop")
1877
1906
  };
1878
1907
  }, [t]);
1908
+ if (!isMobile) {
1909
+ const baseButtonClassName = "oui-flex oui-flex-1 oui-items-center oui-justify-center oui-gap-x-1 oui-rounded oui-px-3 oui-py-0.5 oui-text-xs oui-font-semibold oui-h-8";
1910
+ const selectedButtonClassName = cn(
1911
+ baseButtonClassName,
1912
+ "oui-bg-base-5 oui-text-base-contrast"
1913
+ );
1914
+ const unselectedButtonClassName = cn(
1915
+ baseButtonClassName,
1916
+ "oui-bg-base-7 oui-text-base-contrast-36"
1917
+ );
1918
+ const handleChange = (type) => {
1919
+ props.onChange(type);
1920
+ };
1921
+ return /* @__PURE__ */ jsxs(
1922
+ "div",
1923
+ {
1924
+ className: "oui-flex oui-w-full oui-gap-1",
1925
+ "data-testid": "oui-testid-orderEntry-orderType-desktop",
1926
+ children: [
1927
+ /* @__PURE__ */ jsx(
1928
+ "button",
1929
+ {
1930
+ type: "button",
1931
+ className: props.type === OrderType.LIMIT ? selectedButtonClassName : unselectedButtonClassName,
1932
+ "aria-pressed": props.type === OrderType.LIMIT,
1933
+ onClick: () => handleChange(OrderType.LIMIT),
1934
+ disabled: !props.canTrade,
1935
+ "data-testid": "oui-testid-orderEntry-orderType-limit",
1936
+ children: /* @__PURE__ */ jsx(Text, { size: "xs", children: t("orderEntry.orderType.limit") })
1937
+ }
1938
+ ),
1939
+ /* @__PURE__ */ jsx(
1940
+ "button",
1941
+ {
1942
+ type: "button",
1943
+ className: props.type === OrderType.MARKET ? selectedButtonClassName : unselectedButtonClassName,
1944
+ "aria-pressed": props.type === OrderType.MARKET,
1945
+ onClick: () => handleChange(OrderType.MARKET),
1946
+ disabled: !props.canTrade,
1947
+ "data-testid": "oui-testid-orderEntry-orderType-market",
1948
+ children: /* @__PURE__ */ jsx(Text, { size: "xs", children: t("orderEntry.orderType.market") })
1949
+ }
1950
+ ),
1951
+ /* @__PURE__ */ jsx(
1952
+ "div",
1953
+ {
1954
+ className: "oui-flex-1",
1955
+ "data-testid": "oui-testid-orderEntry-orderType-advanced",
1956
+ children: /* @__PURE__ */ jsx(
1957
+ Select.options,
1958
+ {
1959
+ testid: "oui-testid-orderEntry-orderType-advanced-select",
1960
+ currentValue: props.type,
1961
+ value: props.type,
1962
+ options: advancedOptions,
1963
+ onValueChange: props.onChange,
1964
+ placeholder: t("trading.layout.advanced"),
1965
+ disabled: !props.canTrade,
1966
+ contentProps: {
1967
+ className: "oui-bg-base-8"
1968
+ },
1969
+ classNames: {
1970
+ trigger: "oui-bg-base-7 oui-border-none oui-h-8 oui-rounded-md"
1971
+ },
1972
+ valueFormatter: (value, option) => {
1973
+ const isAdvanced = value === OrderType.STOP_LIMIT || value === OrderType.STOP_MARKET || value === OrderType.SCALED || value === OrderType.TRAILING_STOP;
1974
+ if (!isAdvanced) {
1975
+ return /* @__PURE__ */ jsx(Text, { size: "xs", className: "oui-text-base-contrast-80", children: option.placeholder });
1976
+ }
1977
+ const label = displayLabelMap[value];
1978
+ return /* @__PURE__ */ jsx(Text, { size: "xs", className: "oui-text-base-contrast-80", children: label });
1979
+ },
1980
+ size: "md"
1981
+ }
1982
+ )
1983
+ }
1984
+ )
1985
+ ]
1986
+ }
1987
+ );
1988
+ }
1879
1989
  return /* @__PURE__ */ jsx(
1880
1990
  Select.options,
1881
1991
  {
1882
1992
  testid: "oui-testid-orderEntry-orderType-button",
1883
1993
  currentValue: props.type,
1884
1994
  value: props.type,
1885
- options: options2,
1995
+ options: allOptions,
1886
1996
  onValueChange: props.onChange,
1887
1997
  contentProps: {
1888
1998
  className: cn(
@@ -1893,57 +2003,823 @@ var OrderTypeSelect = (props) => {
1893
2003
  classNames: {
1894
2004
  trigger: cn(
1895
2005
  "oui-orderEntry-orderTypeSelect-btn",
1896
- "oui-bg-base-6 oui-border-line"
2006
+ "oui-bg-base-7 oui-border-line-12 oui-h-8 oui-rounded-md"
1897
2007
  )
1898
2008
  },
1899
2009
  valueFormatter: (value, option) => {
1900
- const item = options2.find((o) => o.value === value);
2010
+ const item = allOptions.find((o) => o.value === value);
1901
2011
  if (!item) {
1902
2012
  return /* @__PURE__ */ jsx(Text, { size: "xs", children: option.placeholder });
1903
2013
  }
1904
2014
  const label = displayLabelMap[value];
1905
- return /* @__PURE__ */ jsx(
1906
- Text,
2015
+ return /* @__PURE__ */ jsx(Text, { size: "xs", className: "oui-text-base-contrast-80", children: label });
2016
+ },
2017
+ size: "md"
2018
+ }
2019
+ );
2020
+ };
2021
+ var MarginModeSwitch = (props) => {
2022
+ const { t } = useTranslation();
2023
+ const handleSelect = (mode) => {
2024
+ props.onSelect(mode);
2025
+ };
2026
+ const titleClassName = props.isMobile ? "oui-text-lg oui-leading-[26px]" : "oui-text-base oui-leading-6";
2027
+ const headerPadding = props.isMobile ? "oui-pt-3" : "oui-px-5 oui-pt-3";
2028
+ const contentPadding = props.isMobile ? "oui-py-4" : "oui-p-5";
2029
+ return /* @__PURE__ */ jsxs(
2030
+ Flex,
2031
+ {
2032
+ direction: "column",
2033
+ className: cn(
2034
+ "oui-w-full",
2035
+ props.isMobile ? "oui-rounded-t-xl oui-bg-base-8" : "oui-rounded-xl oui-bg-base-8"
2036
+ ),
2037
+ "data-testid": "oui-testid-marginModeSwitch",
2038
+ children: [
2039
+ /* @__PURE__ */ jsxs("div", { className: cn("oui-w-full", headerPadding), children: [
2040
+ /* @__PURE__ */ jsxs(
2041
+ Flex,
2042
+ {
2043
+ itemAlign: "center",
2044
+ justify: "between",
2045
+ className: cn(props.isMobile && "oui-px-4"),
2046
+ children: [
2047
+ props.isMobile ? /* @__PURE__ */ jsx(
2048
+ "button",
2049
+ {
2050
+ type: "button",
2051
+ className: "oui-size-[18px] oui-opacity-0",
2052
+ "aria-hidden": "true",
2053
+ tabIndex: -1
2054
+ }
2055
+ ) : null,
2056
+ /* @__PURE__ */ jsx(
2057
+ Text,
2058
+ {
2059
+ className: cn(
2060
+ "oui-font-semibold oui-tracking-[0.03em]",
2061
+ titleClassName
2062
+ ),
2063
+ intensity: 98,
2064
+ children: t("marginMode.switchMarginMode")
2065
+ }
2066
+ ),
2067
+ /* @__PURE__ */ jsx(
2068
+ IconButton,
2069
+ {
2070
+ color: "light",
2071
+ className: "oui-size-[18px]",
2072
+ onClick: props.close,
2073
+ "aria-label": "Close",
2074
+ "data-testid": "oui-testid-marginModeSwitch-close",
2075
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 18, color: "white", opacity: 0.98 })
2076
+ }
2077
+ )
2078
+ ]
2079
+ }
2080
+ ),
2081
+ /* @__PURE__ */ jsx(Divider, { className: "oui-mt-[9px] oui-w-full" })
2082
+ ] }),
2083
+ /* @__PURE__ */ jsxs(
2084
+ "div",
1907
2085
  {
1908
- size: "xs",
1909
- color: props.canTrade ? props.side === OrderSide.BUY ? "buy" : "sell" : void 0,
1910
- children: label
2086
+ className: cn(
2087
+ "oui-w-full",
2088
+ contentPadding,
2089
+ props.isMobile && "oui-px-4"
2090
+ ),
2091
+ children: [
2092
+ /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", gap: 2, children: [
2093
+ /* @__PURE__ */ jsx(TokenIcon, { symbol: props.symbol, className: "oui-size-5" }),
2094
+ /* @__PURE__ */ jsx(
2095
+ Text.formatted,
2096
+ {
2097
+ className: "oui-tracking-[0.03em]",
2098
+ rule: "symbol",
2099
+ formatString: "base-type",
2100
+ size: "base",
2101
+ weight: "semibold",
2102
+ intensity: 98,
2103
+ children: props.symbol
2104
+ }
2105
+ )
2106
+ ] }),
2107
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, className: "oui-mt-3 oui-w-full", children: [
2108
+ /* @__PURE__ */ jsx(
2109
+ OptionCard,
2110
+ {
2111
+ mode: MarginMode.CROSS,
2112
+ selected: props.selectedMarginMode === MarginMode.CROSS,
2113
+ isCurrent: props.currentMarginMode === MarginMode.CROSS,
2114
+ onClick: () => handleSelect(MarginMode.CROSS)
2115
+ }
2116
+ ),
2117
+ /* @__PURE__ */ jsx(
2118
+ OptionCard,
2119
+ {
2120
+ mode: MarginMode.ISOLATED,
2121
+ selected: props.selectedMarginMode === MarginMode.ISOLATED,
2122
+ isCurrent: props.currentMarginMode === MarginMode.ISOLATED,
2123
+ onClick: () => handleSelect(MarginMode.ISOLATED)
2124
+ }
2125
+ )
2126
+ ] }),
2127
+ /* @__PURE__ */ jsx(Flex, { justify: "center", className: "oui-mt-3 oui-w-full", children: /* @__PURE__ */ jsxs(
2128
+ "button",
2129
+ {
2130
+ type: "button",
2131
+ className: cn(
2132
+ "oui-flex oui-items-center oui-gap-1",
2133
+ "oui-group",
2134
+ "oui-text-xs oui-leading-[15px] oui-font-semibold oui-text-base-contrast-54 oui-tracking-[0.03em]",
2135
+ props.onOpenSettings ? "oui-cursor-pointer hover:oui-text-base-contrast-80 oui-transition-colors" : "oui-cursor-default"
2136
+ ),
2137
+ onClick: props.onOpenSettings,
2138
+ disabled: !props.onOpenSettings,
2139
+ "data-testid": "oui-testid-marginModeSwitch-settings",
2140
+ children: [
2141
+ /* @__PURE__ */ jsx("span", { children: t("marginMode.marginModeSettings") }),
2142
+ /* @__PURE__ */ jsx(
2143
+ ChevronRightIcon,
2144
+ {
2145
+ size: 18,
2146
+ color: "white",
2147
+ opacity: 1,
2148
+ className: cn(
2149
+ "oui-text-base-contrast-54 oui-transition-colors",
2150
+ props.onOpenSettings && "group-hover:oui-text-base-contrast-80"
2151
+ )
2152
+ }
2153
+ )
2154
+ ]
2155
+ }
2156
+ ) }),
2157
+ props.isMobile ? /* @__PURE__ */ jsx("div", { className: "oui-mt-4 oui-h-[34px] oui-w-full" }) : null
2158
+ ]
1911
2159
  }
2160
+ )
2161
+ ]
2162
+ }
2163
+ );
2164
+ };
2165
+ var OptionCard = (props) => {
2166
+ const { t } = useTranslation();
2167
+ const title = props.mode === MarginMode.CROSS ? t("marginMode.crossMargin") : t("marginMode.isolatedMargin");
2168
+ const desc = props.mode === MarginMode.CROSS ? t("marginMode.crossMarginDescription") : t("marginMode.isolatedMarginDescription");
2169
+ return /* @__PURE__ */ jsxs(
2170
+ "button",
2171
+ {
2172
+ type: "button",
2173
+ className: cn(
2174
+ "oui-relative oui-w-full oui-rounded-md oui-p-2",
2175
+ "oui-bg-base-6",
2176
+ "oui-text-left",
2177
+ props.selected ? "oui-border oui-border-[#38e2fe]" : "oui-border oui-border-transparent hover:oui-border-line-12"
2178
+ ),
2179
+ onClick: props.onClick,
2180
+ "data-testid": `oui-testid-marginModeSwitch-option-${props.mode}`,
2181
+ children: [
2182
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, itemAlign: "start", className: "oui-w-full", children: [
2183
+ /* @__PURE__ */ jsx(
2184
+ Text,
2185
+ {
2186
+ className: "oui-text-sm oui-font-semibold oui-leading-5 oui-tracking-[0.03em]",
2187
+ intensity: 98,
2188
+ children: title
2189
+ }
2190
+ ),
2191
+ /* @__PURE__ */ jsx(Text, { className: "oui-text-2xs oui-leading-[15px] oui-text-base-contrast-36 oui-font-semibold oui-tracking-[0.03em]", children: desc })
2192
+ ] }),
2193
+ props.isCurrent ? /* @__PURE__ */ jsx(
2194
+ "div",
2195
+ {
2196
+ className: cn(
2197
+ "oui-absolute -oui-right-px -oui-top-px",
2198
+ "oui-rounded-bl-md oui-rounded-tr-md",
2199
+ "oui-bg-[#38e2fe] oui-px-1 oui-py-0.5"
2200
+ ),
2201
+ children: /* @__PURE__ */ jsx(Text, { className: "oui-text-2xs oui-leading-none oui-font-semibold oui-text-black", children: t("marginMode.current") })
2202
+ }
2203
+ ) : null
2204
+ ]
2205
+ }
2206
+ );
2207
+ };
2208
+ var useMarginModeSwitchScript = (options2) => {
2209
+ const { symbol, close } = options2;
2210
+ const { isMobile } = useScreen();
2211
+ const { t } = useTranslation();
2212
+ const { marginMode: currentMarginMode, update } = useMarginModeBySymbol(symbol);
2213
+ const [selectedMarginMode, setSelectedMarginMode] = useState(currentMarginMode);
2214
+ useEffect(() => {
2215
+ setSelectedMarginMode(currentMarginMode);
2216
+ }, [currentMarginMode]);
2217
+ const applyMarginMode = useCallback(
2218
+ async (mode) => {
2219
+ const result = await update(mode);
2220
+ setSelectedMarginMode(mode);
2221
+ return result;
2222
+ },
2223
+ [update]
2224
+ );
2225
+ const onSelect = useCallback(
2226
+ (mode) => {
2227
+ if (mode === currentMarginMode) {
2228
+ close?.();
2229
+ return;
2230
+ }
2231
+ close?.();
2232
+ applyMarginMode(mode).then(() => {
2233
+ toast.success(t("marginMode.updatedSuccessfully"));
2234
+ }).catch((error) => {
2235
+ toast.error(
2236
+ error instanceof Error ? error.message : "Failed to update margin mode"
1912
2237
  );
1913
- },
1914
- size: "md"
2238
+ });
2239
+ },
2240
+ [applyMarginMode, close, currentMarginMode, t]
2241
+ );
2242
+ return {
2243
+ symbol,
2244
+ isMobile,
2245
+ currentMarginMode,
2246
+ selectedMarginMode,
2247
+ setSelectedMarginMode,
2248
+ applyMarginMode,
2249
+ close,
2250
+ onSelect
2251
+ };
2252
+ };
2253
+ var useMarginModeSettingsScript = (options2) => {
2254
+ const { isMobile } = useScreen();
2255
+ const [markets] = useMarkets(MarketsType.ALL);
2256
+ const items = useMemo(() => {
2257
+ if (!markets || markets.length === 0) {
2258
+ return [];
2259
+ }
2260
+ return markets.map((market) => ({
2261
+ key: market.symbol,
2262
+ // Original symbol: "PERP_BTC_USDC"
2263
+ symbol: formatSymbol(market.symbol, "base-type")
2264
+ // Formatted: "BTC-PERP"
2265
+ }));
2266
+ }, [markets]);
2267
+ const [searchKeyword, setSearchKeyword] = useState("");
2268
+ const [selectedKeys, setSelectedKeys] = useState(
2269
+ () => /* @__PURE__ */ new Set()
2270
+ );
2271
+ const [isOperationLoading, setIsOperationLoading] = useState(false);
2272
+ const {
2273
+ marginModes,
2274
+ isLoading: isMarginModesLoading,
2275
+ updateMarginMode,
2276
+ isMutating: isSettingMarginMode
2277
+ } = useMarginModes();
2278
+ const [isDataLoading, setIsDataLoading] = useState(true);
2279
+ useEffect(() => {
2280
+ if (markets.length > 0) {
2281
+ setIsDataLoading(false);
2282
+ }
2283
+ }, [markets]);
2284
+ const itemMarginModes = useMemo(() => {
2285
+ const result = {};
2286
+ for (const item of items) {
2287
+ const marginMode = marginModes[item.key];
2288
+ result[item.key] = marginMode ?? MarginMode.CROSS;
2289
+ }
2290
+ return result;
2291
+ }, [items, marginModes]);
2292
+ const filteredItems = useMemo(() => {
2293
+ const keyword = searchKeyword.trim().toLowerCase();
2294
+ if (!keyword) return items;
2295
+ return items.filter((item) => item.symbol.toLowerCase().includes(keyword));
2296
+ }, [items, searchKeyword]);
2297
+ const visibleSelectedCount = useMemo(() => {
2298
+ return filteredItems.filter((item) => selectedKeys.has(item.key)).length;
2299
+ }, [filteredItems, selectedKeys]);
2300
+ const isSelectAll = useMemo(() => {
2301
+ return filteredItems.length > 0 && visibleSelectedCount === filteredItems.length;
2302
+ }, [filteredItems.length, visibleSelectedCount]);
2303
+ const isIndeterminate = useMemo(() => {
2304
+ return visibleSelectedCount > 0 && visibleSelectedCount < filteredItems.length;
2305
+ }, [filteredItems.length, visibleSelectedCount]);
2306
+ const selectedMarginModeStats = useMemo(() => {
2307
+ let crossCount = 0;
2308
+ let isolatedCount = 0;
2309
+ selectedKeys.forEach((key) => {
2310
+ const mode = itemMarginModes[key] ?? MarginMode.CROSS;
2311
+ if (mode === MarginMode.CROSS) {
2312
+ crossCount++;
2313
+ } else {
2314
+ isolatedCount++;
2315
+ }
2316
+ });
2317
+ return {
2318
+ crossCount,
2319
+ isolatedCount,
2320
+ isCrossButtonDisabled: crossCount > 0 && isolatedCount === 0,
2321
+ isIsolatedButtonDisabled: isolatedCount > 0 && crossCount === 0
2322
+ };
2323
+ }, [selectedKeys, itemMarginModes]);
2324
+ const onSearchChange = useCallback((keyword) => {
2325
+ setSearchKeyword(keyword);
2326
+ }, []);
2327
+ const onToggleItem = useCallback((key) => {
2328
+ setSelectedKeys((prev) => {
2329
+ const next = new Set(prev);
2330
+ if (next.has(key)) {
2331
+ next.delete(key);
2332
+ } else {
2333
+ next.add(key);
2334
+ }
2335
+ return next;
2336
+ });
2337
+ }, []);
2338
+ const onToggleSelectAll = useCallback(() => {
2339
+ setSelectedKeys((prev) => {
2340
+ const next = new Set(prev);
2341
+ if (isSelectAll) {
2342
+ for (const item of filteredItems) {
2343
+ next.delete(item.key);
2344
+ }
2345
+ return next;
2346
+ }
2347
+ for (const item of filteredItems) {
2348
+ next.add(item.key);
2349
+ }
2350
+ return next;
2351
+ });
2352
+ }, [filteredItems, isSelectAll]);
2353
+ const onSetMarginMode = useCallback(
2354
+ async (mode) => {
2355
+ if (selectedKeys.size === 0) return;
2356
+ setIsOperationLoading(true);
2357
+ try {
2358
+ const payload = {
2359
+ symbol_list: Array.from(selectedKeys),
2360
+ default_margin_mode: mode
2361
+ };
2362
+ await updateMarginMode(payload);
2363
+ toast.success("Updated successfully");
2364
+ } catch (error) {
2365
+ toast.error(
2366
+ error instanceof Error ? error.message : "Failed to update margin mode"
2367
+ );
2368
+ } finally {
2369
+ setIsOperationLoading(false);
2370
+ }
2371
+ },
2372
+ [selectedKeys, updateMarginMode]
2373
+ );
2374
+ const isLoading = isDataLoading || isMarginModesLoading || isOperationLoading || isSettingMarginMode;
2375
+ return {
2376
+ isMobile,
2377
+ close: options2.close,
2378
+ items,
2379
+ filteredItems,
2380
+ searchKeyword,
2381
+ selectedKeys,
2382
+ itemMarginModes,
2383
+ isSelectAll,
2384
+ isIndeterminate,
2385
+ isLoading,
2386
+ isCrossButtonDisabled: selectedMarginModeStats.isCrossButtonDisabled,
2387
+ isIsolatedButtonDisabled: selectedMarginModeStats.isIsolatedButtonDisabled,
2388
+ onSearchChange,
2389
+ onToggleItem,
2390
+ onToggleSelectAll,
2391
+ onSetMarginMode
2392
+ };
2393
+ };
2394
+ var MarginModeSettings = (props) => {
2395
+ const { t } = useTranslation();
2396
+ const headerPadding = props.isMobile ? "oui-px-4 oui-pt-3" : "oui-px-5 oui-pt-3";
2397
+ const contentPadding = props.isMobile ? "oui-px-4" : "oui-px-5";
2398
+ const selectedCount = props.selectedKeys.size;
2399
+ const totalCountTextClassName = selectedCount > 0 ? "oui-text-primary-light" : "oui-text-base-contrast-36";
2400
+ const handleClearSearch = useCallback(() => {
2401
+ props.onSearchChange("");
2402
+ }, [props.onSearchChange]);
2403
+ const handleSetCross = useCallback(() => {
2404
+ props.onSetMarginMode(MarginMode.CROSS);
2405
+ }, [props.onSetMarginMode]);
2406
+ const handleSetIsolated = useCallback(() => {
2407
+ props.onSetMarginMode(MarginMode.ISOLATED);
2408
+ }, [props.onSetMarginMode]);
2409
+ return /* @__PURE__ */ jsxs(
2410
+ Flex,
2411
+ {
2412
+ direction: "column",
2413
+ className: cn(
2414
+ "oui-size-full",
2415
+ "oui-rounded-xl oui-bg-base-8",
2416
+ "oui-overflow-hidden"
2417
+ ),
2418
+ "data-testid": "oui-testid-marginModeSettings",
2419
+ children: [
2420
+ /* @__PURE__ */ jsxs("div", { className: cn("oui-w-full", headerPadding), children: [
2421
+ /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", justify: "between", children: [
2422
+ props.isMobile ? /* @__PURE__ */ jsx(
2423
+ "button",
2424
+ {
2425
+ type: "button",
2426
+ className: "oui-size-[18px] oui-opacity-0",
2427
+ "aria-hidden": "true",
2428
+ tabIndex: -1
2429
+ }
2430
+ ) : null,
2431
+ /* @__PURE__ */ jsx(
2432
+ Text,
2433
+ {
2434
+ className: cn(
2435
+ "oui-font-semibold oui-tracking-[0.48px]",
2436
+ props.isMobile ? "oui-text-center oui-text-lg oui-leading-[26px]" : "oui-text-base oui-leading-6"
2437
+ ),
2438
+ intensity: 98,
2439
+ children: t("marginMode.perpetualFutures")
2440
+ }
2441
+ ),
2442
+ /* @__PURE__ */ jsx(
2443
+ IconButton,
2444
+ {
2445
+ color: "light",
2446
+ className: "oui-size-[18px]",
2447
+ onClick: props.close,
2448
+ "aria-label": t("common.close"),
2449
+ "data-testid": "oui-testid-marginModeSettings-close",
2450
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 18, color: "white", opacity: 0.98 })
2451
+ }
2452
+ )
2453
+ ] }),
2454
+ /* @__PURE__ */ jsx(Divider, { className: "oui-mt-[9px] oui-w-full" })
2455
+ ] }),
2456
+ /* @__PURE__ */ jsx(
2457
+ "div",
2458
+ {
2459
+ className: cn(
2460
+ "oui-relative oui-z-10 oui-w-full oui-bg-base-8",
2461
+ contentPadding,
2462
+ "oui-py-3"
2463
+ ),
2464
+ children: /* @__PURE__ */ jsx(
2465
+ Input,
2466
+ {
2467
+ value: props.searchKeyword,
2468
+ onValueChange: props.onSearchChange,
2469
+ placeholder: t("marginMode.searchPlaceholder"),
2470
+ size: "md",
2471
+ fullWidth: true,
2472
+ classNames: {
2473
+ root: cn(
2474
+ "oui-outline-line",
2475
+ props.searchKeyword.trim() ? "oui-outline-primary-light" : ""
2476
+ )
2477
+ },
2478
+ prefix: /* @__PURE__ */ jsx("div", { className: "oui-pl-3 oui-pr-1", "aria-hidden": "true", children: /* @__PURE__ */ jsx(SearchGlyph, { className: "oui-text-base-contrast-54" }) }),
2479
+ suffix: props.searchKeyword ? /* @__PURE__ */ jsx("div", { className: "oui-pr-2", children: /* @__PURE__ */ jsx(
2480
+ CloseCircleFillIcon,
2481
+ {
2482
+ size: 14,
2483
+ className: "oui-cursor-pointer oui-text-base-contrast-36",
2484
+ onClick: handleClearSearch
2485
+ }
2486
+ ) }) : null,
2487
+ autoComplete: "off"
2488
+ }
2489
+ )
2490
+ }
2491
+ ),
2492
+ /* @__PURE__ */ jsxs(
2493
+ Flex,
2494
+ {
2495
+ direction: "column",
2496
+ className: cn(
2497
+ "oui-w-full",
2498
+ props.isMobile ? "oui-flex-1 oui-min-h-0" : ""
2499
+ ),
2500
+ children: [
2501
+ /* @__PURE__ */ jsx(
2502
+ Flex,
2503
+ {
2504
+ direction: "column",
2505
+ gap: 3,
2506
+ className: cn(
2507
+ "oui-w-full oui-overflow-y-auto oui-custom-scrollbar",
2508
+ props.isMobile ? "oui-flex-1 oui-min-h-0 oui-px-4 oui-pt-0 oui-pb-3" : "oui-h-[308px] oui-px-5 oui-pt-0 oui-pb-3"
2509
+ ),
2510
+ style: {
2511
+ scrollbarWidth: "thin",
2512
+ scrollbarColor: "rgba(255, 255, 255, 0.2) transparent"
2513
+ },
2514
+ children: props.filteredItems.map((item) => /* @__PURE__ */ jsx(
2515
+ SymbolRow,
2516
+ {
2517
+ item,
2518
+ checked: props.selectedKeys.has(item.key),
2519
+ marginMode: props.itemMarginModes[item.key] ?? MarginMode.CROSS,
2520
+ onToggle: props.onToggleItem
2521
+ },
2522
+ item.key
2523
+ ))
2524
+ }
2525
+ ),
2526
+ /* @__PURE__ */ jsx(Divider, { className: "oui-w-full" }),
2527
+ /* @__PURE__ */ jsxs(
2528
+ Flex,
2529
+ {
2530
+ itemAlign: "center",
2531
+ justify: "between",
2532
+ className: cn(
2533
+ "oui-w-full",
2534
+ props.isMobile ? "oui-px-4 oui-py-3" : "oui-px-5 oui-py-3"
2535
+ ),
2536
+ children: [
2537
+ /* @__PURE__ */ jsx(Flex, { itemAlign: "center", gap: 2, children: /* @__PURE__ */ jsxs(
2538
+ "label",
2539
+ {
2540
+ className: cn(
2541
+ "oui-flex oui-items-center oui-gap-2 oui-cursor-pointer oui-select-none"
2542
+ ),
2543
+ children: [
2544
+ /* @__PURE__ */ jsx(
2545
+ Checkbox,
2546
+ {
2547
+ color: "white",
2548
+ checked: props.isSelectAll,
2549
+ onCheckedChange: () => {
2550
+ props.onToggleSelectAll();
2551
+ },
2552
+ "aria-label": t("marginMode.selectAll")
2553
+ }
2554
+ ),
2555
+ /* @__PURE__ */ jsx(Text, { className: "oui-text-sm oui-font-semibold oui-text-base-contrast-80", children: t("marginMode.selectAll") })
2556
+ ]
2557
+ }
2558
+ ) }),
2559
+ /* @__PURE__ */ jsxs(Text, { className: "oui-text-sm oui-text-base-contrast-54", children: [
2560
+ t("common.total"),
2561
+ ":",
2562
+ " ",
2563
+ /* @__PURE__ */ jsx("span", { className: cn("oui-font-semibold", totalCountTextClassName), children: selectedCount })
2564
+ ] })
2565
+ ]
2566
+ }
2567
+ ),
2568
+ /* @__PURE__ */ jsxs(
2569
+ Flex,
2570
+ {
2571
+ itemAlign: "center",
2572
+ justify: "end",
2573
+ gap: 3,
2574
+ className: cn(
2575
+ "oui-w-full",
2576
+ props.isMobile ? "oui-px-4 oui-pt-3 oui-pb-[calc(20px+env(safe-area-inset-bottom))]" : "oui-px-5 oui-pt-3 oui-pb-5"
2577
+ ),
2578
+ children: [
2579
+ /* @__PURE__ */ jsx(Text, { className: "oui-text-sm oui-leading-8 oui-text-base-contrast-80", children: t("marginMode.setAs") }),
2580
+ /* @__PURE__ */ jsx(
2581
+ Button,
2582
+ {
2583
+ size: "md",
2584
+ className: cn(
2585
+ "oui-bg-base-3 hover:oui-bg-base-3/80 active:oui-bg-base-3/70",
2586
+ selectedCount > 0 && !props.isLoading ? "oui-text-base-contrast-80" : "oui-text-base-contrast-98"
2587
+ ),
2588
+ disabled: selectedCount === 0 || props.isLoading || (props.isCrossButtonDisabled ?? false),
2589
+ onClick: handleSetCross,
2590
+ "aria-label": t("marginMode.cross"),
2591
+ "data-testid": "oui-testid-marginModeSettings-set-cross",
2592
+ children: t("marginMode.cross")
2593
+ }
2594
+ ),
2595
+ /* @__PURE__ */ jsx(
2596
+ Button,
2597
+ {
2598
+ size: "md",
2599
+ className: cn(
2600
+ "oui-bg-base-3 hover:oui-bg-base-3/80 active:oui-bg-base-3/70",
2601
+ selectedCount > 0 && !props.isLoading ? "oui-text-base-contrast-80" : "oui-text-base-contrast-98"
2602
+ ),
2603
+ disabled: selectedCount === 0 || props.isLoading || (props.isIsolatedButtonDisabled ?? false),
2604
+ onClick: handleSetIsolated,
2605
+ "aria-label": t("marginMode.isolated"),
2606
+ "data-testid": "oui-testid-marginModeSettings-set-isolated",
2607
+ children: t("marginMode.isolated")
2608
+ }
2609
+ )
2610
+ ]
2611
+ }
2612
+ )
2613
+ ]
2614
+ }
2615
+ )
2616
+ ]
1915
2617
  }
1916
2618
  );
1917
2619
  };
2620
+ var SymbolRow = (props) => {
2621
+ const { t } = useTranslation();
2622
+ const handleCheckedChange = useCallback(() => {
2623
+ props.onToggle(props.item.key);
2624
+ }, [props]);
2625
+ return /* @__PURE__ */ jsx(Flex, { itemAlign: "center", className: "oui-w-full", children: /* @__PURE__ */ jsxs(
2626
+ "label",
2627
+ {
2628
+ className: cn(
2629
+ "oui-flex oui-items-center oui-gap-2 oui-flex-1 oui-cursor-pointer oui-select-none oui-w-full"
2630
+ ),
2631
+ "data-testid": `oui-testid-marginModeSettings-item-${props.item.key}`,
2632
+ children: [
2633
+ /* @__PURE__ */ jsx(
2634
+ Checkbox,
2635
+ {
2636
+ color: "white",
2637
+ checked: props.checked,
2638
+ onCheckedChange: handleCheckedChange,
2639
+ "aria-label": props.item.symbol
2640
+ }
2641
+ ),
2642
+ /* @__PURE__ */ jsx(Text, { className: "oui-text-sm oui-font-semibold oui-text-base-contrast-80", children: props.item.symbol }),
2643
+ /* @__PURE__ */ jsx(
2644
+ "span",
2645
+ {
2646
+ className: cn(
2647
+ "oui-inline-flex oui-items-center",
2648
+ "oui-rounded oui-bg-base-6 oui-px-2 oui-py-0",
2649
+ "oui-h-[18px] oui-text-xs oui-leading-[18px]",
2650
+ "oui-text-base-contrast-36"
2651
+ ),
2652
+ children: props.marginMode === MarginMode.ISOLATED ? t("marginMode.isolated") : t("marginMode.cross")
2653
+ }
2654
+ )
2655
+ ]
2656
+ }
2657
+ ) });
2658
+ };
2659
+ var SearchGlyph = (props) => {
2660
+ return /* @__PURE__ */ jsx(
2661
+ "svg",
2662
+ {
2663
+ width: "14",
2664
+ height: "14",
2665
+ viewBox: "0 0 14 14",
2666
+ fill: "none",
2667
+ xmlns: "http://www.w3.org/2000/svg",
2668
+ className: props.className,
2669
+ children: /* @__PURE__ */ jsx(
2670
+ "path",
2671
+ {
2672
+ d: "M6.417 1.167a5.25 5.25 0 1 0 3.138 9.46l2.139 2.14a.583.583 0 1 0 .825-.826l-2.14-2.139a5.25 5.25 0 0 0-3.962-8.635Zm0 1.167a4.083 4.083 0 1 1 0 8.166 4.083 4.083 0 0 1 0-8.166Z",
2673
+ fill: "currentColor",
2674
+ fillOpacity: "0.8"
2675
+ }
2676
+ )
2677
+ }
2678
+ );
2679
+ };
2680
+ var MarginModeSettingsWidget = (props) => {
2681
+ const state = useMarginModeSettingsScript(props);
2682
+ return /* @__PURE__ */ jsx(MarginModeSettings, { ...state, onSetMarginMode: state.onSetMarginMode });
2683
+ };
2684
+ var MarginModeSettingsSheetId = "MarginModeSettingsSheetId";
2685
+ var MarginModeSettingsDialogId = "MarginModeSettingsDialogId";
2686
+ registerSimpleSheet(MarginModeSettingsSheetId, MarginModeSettingsWidget, {
2687
+ title: void 0,
2688
+ closable: false,
2689
+ classNames: {
2690
+ content: "oui-bg-transparent !oui-px-0 !oui-py-0 oui-top-[100px] oui-bottom-0",
2691
+ // SheetBody must be full height, otherwise child `h-full`/flex layout can't allocate space
2692
+ // and footer may be pushed out of viewport.
2693
+ body: "oui-p-0 oui-h-full",
2694
+ overlay: "!oui-bg-black/10"
2695
+ }
2696
+ });
2697
+ registerSimpleDialog(MarginModeSettingsDialogId, MarginModeSettingsWidget, {
2698
+ title: void 0,
2699
+ closable: false,
2700
+ classNames: {
2701
+ content: "oui-w-[360px] oui-bg-transparent !oui-px-0",
2702
+ body: "oui-p-0",
2703
+ overlay: "!oui-bg-black/10"
2704
+ }
2705
+ });
2706
+ var MarginModeSwitchWidget = (props) => {
2707
+ const state = useMarginModeSwitchScript(props);
2708
+ const { id: currentModalId } = useModal();
2709
+ const onOpenSettings = () => {
2710
+ const modalId = currentModalId === MarginModeSwitchSheetId ? MarginModeSettingsSheetId : MarginModeSettingsDialogId;
2711
+ modal.show(modalId, {});
2712
+ };
2713
+ return /* @__PURE__ */ jsx(
2714
+ MarginModeSwitch,
2715
+ {
2716
+ ...state,
2717
+ onOpenSettings: props.onOpenSettings ?? onOpenSettings
2718
+ }
2719
+ );
2720
+ };
2721
+ var MarginModeSwitchSheetId = "MarginModeSwitchSheetId";
2722
+ var MarginModeSwitchDialogId = "MarginModeSwitchDialogId";
2723
+ registerSimpleSheet(MarginModeSwitchSheetId, MarginModeSwitchWidget, {
2724
+ // Use custom header in widget UI for both web and mweb.
2725
+ title: void 0,
2726
+ closable: false,
2727
+ classNames: {
2728
+ content: "oui-bg-transparent !oui-px-0",
2729
+ body: "oui-p-0"
2730
+ }
2731
+ });
2732
+ registerSimpleDialog(MarginModeSwitchDialogId, MarginModeSwitchWidget, {
2733
+ // Use custom header in widget UI for both web and mweb.
2734
+ title: void 0,
2735
+ closable: false,
2736
+ classNames: {
2737
+ content: "oui-w-[360px] oui-bg-transparent !oui-px-0",
2738
+ body: "oui-p-0"
2739
+ }
2740
+ });
1918
2741
  var LeverageBadge = (props) => {
1919
- const { symbol, side, symbolLeverage } = props;
2742
+ const { symbol, side, symbolLeverage, disabled } = props;
1920
2743
  const { isMobile } = useScreen();
1921
- const { maxLeverage } = useSymbolLeverage(symbol);
1922
- const curLeverage = symbolLeverage || maxLeverage;
1923
- const showModal = () => {
2744
+ const { t } = useTranslation();
2745
+ const { enabled } = useFeatureFlag(FlagKeys.IsolatedMargin);
2746
+ const marginMode = props.marginMode;
2747
+ const isDisabled = !!disabled;
2748
+ const curLeverage = symbolLeverage ?? 1;
2749
+ const showLeverageModal = () => {
2750
+ if (isDisabled) return;
1924
2751
  const modalId = isMobile ? SymbolLeverageSheetId : SymbolLeverageDialogId;
1925
2752
  modal.show(modalId, {
1926
2753
  symbol,
1927
2754
  side,
1928
- curLeverage
2755
+ curLeverage,
2756
+ marginMode
2757
+ });
2758
+ };
2759
+ const showMarginModeModal = () => {
2760
+ if (isDisabled || !enabled) {
2761
+ return;
2762
+ }
2763
+ const modalId = isMobile ? MarginModeSwitchSheetId : MarginModeSwitchDialogId;
2764
+ modal.show(modalId, {
2765
+ symbol
1929
2766
  });
1930
2767
  };
1931
2768
  return /* @__PURE__ */ jsxs(
1932
- Flex,
2769
+ "div",
1933
2770
  {
1934
- justify: "center",
1935
- itemAlign: "center",
1936
- gapX: 1,
1937
2771
  className: cn(
2772
+ "oui-flex oui-w-full oui-items-center oui-rounded-md oui-border oui-border-line-12 oui-bg-base-6",
1938
2773
  "oui-orderEntry-leverage-btn",
1939
2774
  "oui-h-8",
1940
- "oui-rounded oui-border oui-border-line oui-bg-base-6",
1941
- "oui-cursor-pointer oui-select-none oui-text-xs oui-font-semibold oui-text-base-contrast-54"
2775
+ "oui-select-none"
1942
2776
  ),
1943
- onClick: showModal,
2777
+ "data-testid": "oui-testid-orderEntry-margin-leverage",
1944
2778
  children: [
1945
- /* @__PURE__ */ jsx(Text, { children: "Cross" }),
1946
- /* @__PURE__ */ jsx(Text.numeral, { dp: 0, rm: Decimal.ROUND_DOWN, unit: "X", children: curLeverage })
2779
+ /* @__PURE__ */ jsx(
2780
+ "button",
2781
+ {
2782
+ type: "button",
2783
+ className: cn(
2784
+ "oui-flex oui-flex-1 oui-items-center oui-justify-center oui-gap-x-1",
2785
+ "oui-px-3 oui-py-1.5",
2786
+ "oui-text-xs oui-font-semibold oui-text-base-contrast-54",
2787
+ isDisabled ? "oui-cursor-not-allowed" : "oui-cursor-pointer"
2788
+ ),
2789
+ "data-testid": "oui-testid-orderEntry-margin-mode",
2790
+ "aria-label": t("marginMode.switchMarginMode"),
2791
+ disabled: isDisabled,
2792
+ onClick: showMarginModeModal,
2793
+ children: /* @__PURE__ */ jsx(Text, { children: marginMode === void 0 ? "--" : marginMode === MarginMode.ISOLATED ? t("marginMode.isolated") : t("marginMode.cross") })
2794
+ }
2795
+ ),
2796
+ /* @__PURE__ */ jsx("div", { className: "oui-h-5 oui-w-px oui-bg-line", "aria-hidden": "true" }),
2797
+ /* @__PURE__ */ jsx(
2798
+ "button",
2799
+ {
2800
+ type: "button",
2801
+ className: cn(
2802
+ "oui-flex oui-flex-1 oui-items-center oui-justify-center oui-gap-x-1",
2803
+ "oui-px-3 oui-py-1.5",
2804
+ "oui-text-xs oui-font-semibold oui-text-base-contrast-54",
2805
+ isDisabled ? "oui-cursor-not-allowed" : "oui-cursor-pointer"
2806
+ ),
2807
+ "aria-label": "Adjust leverage",
2808
+ disabled: isDisabled,
2809
+ onClick: showLeverageModal,
2810
+ "data-testid": "oui-testid-orderEntry-leverage",
2811
+ children: /* @__PURE__ */ jsx(
2812
+ Text.numeral,
2813
+ {
2814
+ dp: 0,
2815
+ rm: Decimal.ROUND_DOWN,
2816
+ unit: "x",
2817
+ unitClassName: "oui-ml-0",
2818
+ children: curLeverage
2819
+ }
2820
+ )
2821
+ }
2822
+ )
1947
2823
  ]
1948
2824
  }
1949
2825
  );
@@ -1952,6 +2828,27 @@ function OrderEntryHeader(props) {
1952
2828
  const { canTrade, side, order_type, setOrderValue } = props;
1953
2829
  const { t } = useTranslation();
1954
2830
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2831
+ /* @__PURE__ */ jsx("div", { className: "oui-w-full", children: /* @__PURE__ */ jsx(
2832
+ LeverageBadge,
2833
+ {
2834
+ symbol: props.symbol,
2835
+ side: props.side,
2836
+ symbolLeverage: props.symbolLeverage,
2837
+ marginMode: props.marginMode,
2838
+ disabled: !props.canTrade
2839
+ }
2840
+ ) }),
2841
+ /* @__PURE__ */ jsx("div", { className: "oui-w-full", children: /* @__PURE__ */ jsx(
2842
+ OrderTypeSelect,
2843
+ {
2844
+ type: order_type,
2845
+ side,
2846
+ canTrade,
2847
+ onChange: (type) => {
2848
+ setOrderValue("order_type", type);
2849
+ }
2850
+ }
2851
+ ) }),
1955
2852
  /* @__PURE__ */ jsxs(
1956
2853
  "div",
1957
2854
  {
@@ -1997,37 +2894,6 @@ function OrderEntryHeader(props) {
1997
2894
  )
1998
2895
  ]
1999
2896
  }
2000
- ),
2001
- /* @__PURE__ */ jsxs(
2002
- "div",
2003
- {
2004
- className: cn(
2005
- "oui-orderEntry-header-controls",
2006
- "oui-grid oui-gap-x-2 lg:oui-flex lg:oui-gap-x-[6px]",
2007
- "oui-grid-cols-2"
2008
- ),
2009
- children: [
2010
- /* @__PURE__ */ jsx("div", { className: "oui-w-full oui-orderEntry-orderTypeSelect", children: /* @__PURE__ */ jsx(
2011
- OrderTypeSelect,
2012
- {
2013
- type: order_type,
2014
- side,
2015
- canTrade,
2016
- onChange: (type) => {
2017
- setOrderValue("order_type", type);
2018
- }
2019
- }
2020
- ) }),
2021
- /* @__PURE__ */ jsx("div", { className: "oui-w-full oui-orderEntry-leverage", children: /* @__PURE__ */ jsx(
2022
- LeverageBadge,
2023
- {
2024
- symbol: props.symbol,
2025
- side: props.side,
2026
- symbolLeverage: props.symbolLeverage
2027
- }
2028
- ) })
2029
- ]
2030
- }
2031
2897
  )
2032
2898
  ] });
2033
2899
  }
@@ -3944,7 +4810,7 @@ var PnlInputProvider = (props) => {
3944
4810
  return /* @__PURE__ */ jsx(PnlInputContext.Provider, { value: memoizedValue, children });
3945
4811
  };
3946
4812
  var OrderTPSL = (props) => {
3947
- const tpslFormRef = React2.useRef(null);
4813
+ const tpslFormRef = React3.useRef(null);
3948
4814
  const { t } = useTranslation();
3949
4815
  const { isMobile } = useScreen();
3950
4816
  useEffect(() => {
@@ -4045,7 +4911,7 @@ var TPSLPriceWarning = (props) => {
4045
4911
  }
4046
4912
  );
4047
4913
  };
4048
- var TPSLInputForm = React2.forwardRef((props, ref) => {
4914
+ var TPSLInputForm = React3.forwardRef((props, ref) => {
4049
4915
  const { getErrorMsg } = useOrderEntryFormErrorMsg(props.errors);
4050
4916
  const { t } = useTranslation();
4051
4917
  return /* @__PURE__ */ jsxs(
@@ -4569,7 +5435,8 @@ var OrderEntry = (props) => {
4569
5435
  side,
4570
5436
  order_type: formattedOrder.order_type,
4571
5437
  setOrderValue,
4572
- symbolLeverage: props.symbolLeverage
5438
+ symbolLeverage: props.symbolLeverage,
5439
+ marginMode: props.marginMode
4573
5440
  }
4574
5441
  ),
4575
5442
  /* @__PURE__ */ jsx(
@@ -4578,7 +5445,8 @@ var OrderEntry = (props) => {
4578
5445
  currentLtv,
4579
5446
  canTrade: props.canTrade,
4580
5447
  quote: symbolInfo?.quote,
4581
- freeCollateral
5448
+ freeCollateral,
5449
+ marginMode: props.marginMode
4582
5450
  }
4583
5451
  ),
4584
5452
  /* @__PURE__ */ jsx(
@@ -4787,7 +5655,7 @@ var OrderEntry = (props) => {
4787
5655
  )
4788
5656
  },
4789
5657
  contentProps: { side: "right", closeable: false },
4790
- children: /* @__PURE__ */ jsx(
5658
+ children: showTPSLAdvanced && /* @__PURE__ */ jsx(
4791
5659
  TPSLAdvancedWidget,
4792
5660
  {
4793
5661
  setOrderValue,
@@ -4966,18 +5834,21 @@ var useOrderEntryScript = (inputs) => {
4966
5834
  defaultSoundValue ?? null
4967
5835
  );
4968
5836
  const canTrade = useCanTrade();
5837
+ const { marginMode } = useMarginModeBySymbol(symbol);
4969
5838
  const {
4970
5839
  formattedOrder,
4971
5840
  setValue,
4972
5841
  setValues: setOrderValues,
4973
5842
  symbolInfo,
5843
+ symbolLeverage,
4974
5844
  ...state
4975
5845
  } = useOrderEntry(symbol, {
4976
5846
  initialOrder: {
4977
5847
  symbol,
4978
5848
  order_type: localOrderType,
4979
5849
  position_type: PositionType.PARTIAL,
4980
- side: localOrderSide
5850
+ side: localOrderSide,
5851
+ margin_mode: marginMode
4981
5852
  }
4982
5853
  });
4983
5854
  const [tpslSwitch, setTpslSwitch] = useLocalStorage(
@@ -5013,13 +5884,13 @@ var useOrderEntryScript = (inputs) => {
5013
5884
  setOrderValues({
5014
5885
  tp_trigger_price: "",
5015
5886
  sl_trigger_price: "",
5016
- position_type: PositionType.FULL
5887
+ position_type: PositionType.PARTIAL
5017
5888
  });
5018
5889
  };
5019
5890
  const enableTP_SL = () => {
5020
5891
  setOrderValues({
5021
5892
  order_type_ext: void 0,
5022
- position_type: PositionType.FULL
5893
+ position_type: PositionType.PARTIAL
5023
5894
  });
5024
5895
  };
5025
5896
  const setOrderValue = useMemoizedFn(
@@ -5052,13 +5923,18 @@ var useOrderEntryScript = (inputs) => {
5052
5923
  return;
5053
5924
  }
5054
5925
  if (key === "order_type" && value === OrderType.SCALED) {
5055
- setOrderValues({
5926
+ const data = {
5056
5927
  distribution_type: DistributionType.FLAT,
5057
5928
  [key]: value
5058
- });
5929
+ };
5930
+ setOrderValues(data);
5059
5931
  return;
5060
5932
  }
5061
- setValue(key, value, options2);
5933
+ setValue(
5934
+ key,
5935
+ value,
5936
+ options2
5937
+ );
5062
5938
  }
5063
5939
  );
5064
5940
  const onTPSLSwitchChanged = (state2) => {
@@ -5203,6 +6079,9 @@ var useOrderEntryScript = (inputs) => {
5203
6079
  isUserActive
5204
6080
  });
5205
6081
  }, [ee, symbol, state.estLiqPrice]);
6082
+ useEffect(() => {
6083
+ setOrderValue("margin_mode", marginMode);
6084
+ }, [marginMode]);
5206
6085
  return {
5207
6086
  ...state,
5208
6087
  slPriceError: slPriceError ?? void 0,
@@ -5212,7 +6091,11 @@ var useOrderEntryScript = (inputs) => {
5212
6091
  formattedOrder,
5213
6092
  setOrderValue,
5214
6093
  setOrderValues,
6094
+ // account-level leverage (for other consumers)
5215
6095
  currentLeverage,
6096
+ // symbol-level leverage & margin mode for this order entry
6097
+ symbolLeverage,
6098
+ marginMode,
5216
6099
  // cancelTP_SL,
5217
6100
  // enableTP_SL,
5218
6101
  tpslSwitch,