@orderly.network/ui-order-entry 3.0.0-beta.3 → 3.0.0-beta.4

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
@@ -2042,6 +2042,7 @@ var OrderTypeSelect = (props) => {
2042
2042
  [OrderType.TRAILING_STOP]: t("orderEntry.orderType.trailingStop")
2043
2043
  };
2044
2044
  }, [t]);
2045
+ const mobileOptions = useMemo(() => allOptions, [allOptions]);
2045
2046
  if (!isMobile) {
2046
2047
  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";
2047
2048
  const selectedButtonClassName = cn(
@@ -2140,7 +2141,6 @@ var OrderTypeSelect = (props) => {
2140
2141
  }
2141
2142
  );
2142
2143
  }
2143
- const mobileOptions = useMemo(() => allOptions, [allOptions]);
2144
2144
  const handleMobileValueChange = (value) => {
2145
2145
  if (marketOrderDisabled && value === OrderType.MARKET && marketOrderDisabledTooltip) {
2146
2146
  modal.alert({
@@ -4860,10 +4860,19 @@ var usePnlInputContext = () => {
4860
4860
  };
4861
4861
 
4862
4862
  // src/components/pnlInput/useBuilder.script.ts
4863
+ function normalizeSlPnlForStore(raw) {
4864
+ const v = `${raw}`.replace(/,/g, "").trim();
4865
+ if (v === "" || v === "-") return v;
4866
+ const withoutTrailingDot = v.replace(/\.$/, "");
4867
+ const num = Number(withoutTrailingDot);
4868
+ if (!isNaN(num) && num === 0) return "0";
4869
+ if (v.startsWith("-")) return v;
4870
+ return `-${v}`;
4871
+ }
4863
4872
  var usePNLInputBuilder = (props) => {
4864
4873
  const { type, values, quote_dp } = props;
4865
4874
  const { t } = useTranslation();
4866
- const [focus, setFocus] = useState(true);
4875
+ const [, setFocus] = useState(true);
4867
4876
  const { mode, setMode, tipsEle } = usePnlInputContext();
4868
4877
  const [tipVisible, setTipVisible] = useState(false);
4869
4878
  const [isFocused, setIsFocused] = useState(false);
@@ -4873,10 +4882,14 @@ var usePNLInputBuilder = (props) => {
4873
4882
  return `${type.toLowerCase()}_offset`;
4874
4883
  case "Offset%" /* PERCENTAGE */:
4875
4884
  return `${type.toLowerCase()}_offset_percentage`;
4885
+ case "OffsetFromMark" /* OFFSET_FROM_MARK */:
4886
+ return `${type.toLowerCase()}_offset_from_mark`;
4887
+ case "PercentageFromMark" /* PERCENTAGE_FROM_MARK */:
4888
+ return `${type.toLowerCase()}_offset_percentage_from_mark`;
4876
4889
  default:
4877
4890
  return `${type.toLowerCase()}_pnl`;
4878
4891
  }
4879
- }, [mode]);
4892
+ }, [mode, type]);
4880
4893
  const [innerValue, setInnerValue] = useState(
4881
4894
  values[mode]
4882
4895
  );
@@ -4899,23 +4912,39 @@ var usePNLInputBuilder = (props) => {
4899
4912
  testId: `${"Offset" /* OFFSET */}_mneu_item`
4900
4913
  },
4901
4914
  {
4902
- label: `${t("tpsl.offset")}%`,
4915
+ label: t("tpsl.offsetPercent"),
4903
4916
  value: "Offset%" /* PERCENTAGE */,
4904
4917
  testId: `${"Offset%" /* PERCENTAGE */}_menu_item`
4918
+ },
4919
+ {
4920
+ // @ts-ignore
4921
+ label: t("tpsl.offsetMark"),
4922
+ value: "OffsetFromMark" /* OFFSET_FROM_MARK */,
4923
+ testId: `${"OffsetFromMark" /* OFFSET_FROM_MARK */}_menu_item`
4924
+ },
4925
+ {
4926
+ // @ts-ignore
4927
+ label: t("tpsl.offsetPercentMark"),
4928
+ value: "PercentageFromMark" /* PERCENTAGE_FROM_MARK */,
4929
+ testId: `${"PercentageFromMark" /* PERCENTAGE_FROM_MARK */}_menu_item`
4905
4930
  }
4906
4931
  ];
4907
4932
  }, [t]);
4908
4933
  const modeLabelMap = useMemo(() => {
4909
4934
  return {
4910
4935
  ["PnL" /* PnL */]: t("tpsl.pnl"),
4911
- ["Offset" /* OFFSET */]: t("tpsl.offset"),
4912
- ["Offset%" /* PERCENTAGE */]: `${t("tpsl.offset")}%`
4936
+ ["Offset" /* OFFSET */]: t("tpsl.offsetHolder"),
4937
+ ["Offset%" /* PERCENTAGE */]: `${t("tpsl.offsetHolder")}`,
4938
+ // Extend locale keys; not yet in LocaleMessages typings
4939
+ ["OffsetFromMark" /* OFFSET_FROM_MARK */]: t("tpsl.offsetHolder"),
4940
+ ["PercentageFromMark" /* PERCENTAGE_FROM_MARK */]: t("tpsl.offsetHolder")
4913
4941
  };
4914
4942
  }, [t]);
4915
4943
  const percentageSuffix = useRef("");
4916
4944
  const onValueChange = (value) => {
4917
4945
  setInnerValue(value);
4918
- props.onChange(key, value);
4946
+ const outgoing = key === "sl_pnl" ? normalizeSlPnlForStore(value) : value;
4947
+ props.onChange(key, outgoing);
4919
4948
  };
4920
4949
  const onFocus = () => {
4921
4950
  setTipVisible(true);
@@ -4924,20 +4953,19 @@ var usePNLInputBuilder = (props) => {
4924
4953
  const onBlur = () => {
4925
4954
  setTipVisible(false);
4926
4955
  setIsFocused(false);
4927
- props.onChange(key, innerValue);
4956
+ const outgoing = key === "sl_pnl" ? normalizeSlPnlForStore(innerValue) : innerValue;
4957
+ props.onChange(key, outgoing);
4928
4958
  };
4929
4959
  const formatter = (options2) => {
4930
4960
  const { dp = 2 } = options2;
4931
4961
  return {
4932
4962
  onRenderBefore: (value, options3) => {
4933
4963
  value = `${value}`;
4934
- if (focus) {
4935
- if (type === "SL" && mode === "PnL" /* PnL */) {
4936
- value = value.startsWith("-") ? value : "-" + value;
4937
- }
4964
+ if (type === "SL" && mode === "PnL" /* PnL */) {
4965
+ value = value.startsWith("-") ? value : "-" + value;
4938
4966
  }
4939
4967
  if (value === "" || value === "-") return "";
4940
- if (mode === "Offset%" /* PERCENTAGE */) {
4968
+ if (mode === "Offset%" /* PERCENTAGE */ || mode === "PercentageFromMark" /* PERCENTAGE_FROM_MARK */) {
4941
4969
  let normalized = value.replace(
4942
4970
  new RegExp(percentageSuffix.current.replace(".", "\\.") + "$"),
4943
4971
  ""
@@ -4947,7 +4975,7 @@ var usePNLInputBuilder = (props) => {
4947
4975
  return value;
4948
4976
  }
4949
4977
  return `${new Decimal(normalized).mul(100).todp(2, 4).toString()}${percentageSuffix.current}`;
4950
- } else if (mode === "Offset" /* OFFSET */) {
4978
+ } else if (mode === "Offset" /* OFFSET */ || mode === "OffsetFromMark" /* OFFSET_FROM_MARK */) {
4951
4979
  value = todpIfNeed(value, dp);
4952
4980
  } else ;
4953
4981
  return `${value}`;
@@ -4956,7 +4984,7 @@ var usePNLInputBuilder = (props) => {
4956
4984
  if (/^\-?0{2,}$/.test(value)) {
4957
4985
  return "0";
4958
4986
  }
4959
- if (mode === "Offset%" /* PERCENTAGE */) {
4987
+ if (mode === "Offset%" /* PERCENTAGE */ || mode === "PercentageFromMark" /* PERCENTAGE_FROM_MARK */) {
4960
4988
  if (value !== "") {
4961
4989
  value = todpIfNeed(value, 2);
4962
4990
  const endStr = value.match(/\.0{0,2}$/);
@@ -4971,7 +4999,7 @@ var usePNLInputBuilder = (props) => {
4971
4999
  value = new Decimal(value).div(100).toString();
4972
5000
  value = `${value}${percentageSuffix.current}`;
4973
5001
  }
4974
- } else if (mode === "PnL" /* PnL */ && type === "SL" && focus) {
5002
+ } else if (mode === "PnL" /* PnL */ && type === "SL") {
4975
5003
  value = value.startsWith("-") ? value : "-" + value;
4976
5004
  } else {
4977
5005
  value = todpIfNeed(value, dp);
@@ -5015,21 +5043,28 @@ var PNLInput = (props) => {
5015
5043
  setFocus
5016
5044
  } = props;
5017
5045
  const [prefix, setPrefix] = useState(mode);
5046
+ const isPercentageMode = mode === "Offset%" /* PERCENTAGE */ || mode === "PercentageFromMark" /* PERCENTAGE_FROM_MARK */;
5018
5047
  const [placeholder, setPlaceholder] = useState(
5019
- mode === "Offset%" /* PERCENTAGE */ ? "%" : quote
5048
+ isPercentageMode ? "%" : quote
5020
5049
  );
5021
5050
  useEffect(() => {
5022
5051
  setPrefix(mode);
5023
- setPlaceholder(mode === "Offset%" /* PERCENTAGE */ ? "%" : quote);
5024
- }, [mode]);
5052
+ setPlaceholder(isPercentageMode ? "%" : quote);
5053
+ }, [mode, isPercentageMode, quote]);
5025
5054
  useEffect(() => {
5026
5055
  setPrefix(!!value ? "" : mode);
5027
5056
  }, [value]);
5028
- const id = useMemo(() => `${type.toLowerCase()}_${mode.toLowerCase()}`, []);
5057
+ const id = useMemo(
5058
+ () => `${type.toLowerCase()}_${mode.toLowerCase()}`,
5059
+ [type, mode]
5060
+ );
5061
+ const prefixLabel = prefix === "" ? "" : String(
5062
+ modeLabelMap[prefix] ?? prefix ?? ""
5063
+ );
5029
5064
  return /* @__PURE__ */ jsx(
5030
5065
  Input.tooltip,
5031
5066
  {
5032
- prefix: modeLabelMap[prefix] || prefix,
5067
+ prefix: prefixLabel,
5033
5068
  size: "md",
5034
5069
  placeholder,
5035
5070
  id,
@@ -5066,11 +5101,11 @@ var PNLInput = (props) => {
5066
5101
  },
5067
5102
  onBlur: () => {
5068
5103
  setPrefix(!!value ? "" : mode);
5069
- setPlaceholder(mode === "Offset%" /* PERCENTAGE */ ? "%" : quote);
5104
+ setPlaceholder(isPercentageMode ? "%" : quote);
5070
5105
  onBlur();
5071
5106
  },
5072
5107
  suffix: /* @__PURE__ */ jsxs(Fragment, { children: [
5073
- mode === "Offset%" /* PERCENTAGE */ && !!value && /* @__PURE__ */ jsx(Text, { size: "2xs", color: "inherit", className: "oui-ml-[2px]", children: "%" }),
5108
+ isPercentageMode && !!value && /* @__PURE__ */ jsx(Text, { size: "2xs", color: "inherit", className: "oui-ml-[2px]", children: "%" }),
5074
5109
  /* @__PURE__ */ jsx(
5075
5110
  PNLMenus,
5076
5111
  {
@@ -5490,6 +5525,8 @@ var TPSLInputRow = (props) => {
5490
5525
  PnL: props.values.PnL,
5491
5526
  Offset: props.values.Offset,
5492
5527
  "Offset%": props.values["Offset%"],
5528
+ OffsetFromMark: props.values.OffsetFromMark,
5529
+ PercentageFromMark: props.values.PercentageFromMark,
5493
5530
  ROI: props.values.ROI
5494
5531
  }
5495
5532
  }
@@ -5709,6 +5746,8 @@ var OrderEntry = (props) => {
5709
5746
  tp_pnl: order.tp_pnl,
5710
5747
  tp_offset: order.tp_offset,
5711
5748
  tp_offset_percentage: order.tp_offset_percentage,
5749
+ tp_offset_from_mark: order.tp_offset_from_mark,
5750
+ tp_offset_percentage_from_mark: order.tp_offset_percentage_from_mark,
5712
5751
  tp_ROI: order.tp_ROI,
5713
5752
  tp_trigger_price: order.tp_trigger_price,
5714
5753
  tp_order_price: order.tp_order_price,
@@ -5718,6 +5757,8 @@ var OrderEntry = (props) => {
5718
5757
  sl_pnl: order.sl_pnl,
5719
5758
  sl_offset: order.sl_offset,
5720
5759
  sl_offset_percentage: order.sl_offset_percentage,
5760
+ sl_offset_from_mark: order.sl_offset_from_mark,
5761
+ sl_offset_percentage_from_mark: order.sl_offset_percentage_from_mark,
5721
5762
  sl_ROI: order.sl_ROI
5722
5763
  });
5723
5764
  setShowTPSLAdvanced(false);
@@ -5734,6 +5775,14 @@ var OrderEntry = (props) => {
5734
5775
  sl_order_type: OrderType.MARKET,
5735
5776
  tp_pnl: void 0,
5736
5777
  sl_pnl: void 0,
5778
+ tp_offset: void 0,
5779
+ tp_offset_percentage: void 0,
5780
+ tp_offset_from_mark: void 0,
5781
+ tp_offset_percentage_from_mark: void 0,
5782
+ sl_offset: void 0,
5783
+ sl_offset_percentage: void 0,
5784
+ sl_offset_from_mark: void 0,
5785
+ sl_offset_percentage_from_mark: void 0,
5737
5786
  position_type: PositionType.FULL
5738
5787
  });
5739
5788
  };
@@ -5902,6 +5951,8 @@ var OrderEntry = (props) => {
5902
5951
  PnL: formattedOrder.tp_pnl ?? "",
5903
5952
  Offset: formattedOrder.tp_offset ?? "",
5904
5953
  "Offset%": formattedOrder.tp_offset_percentage ?? "",
5954
+ OffsetFromMark: formattedOrder.tp_offset_from_mark ?? "",
5955
+ PercentageFromMark: formattedOrder.tp_offset_percentage_from_mark ?? "",
5905
5956
  ROI: formattedOrder.tp_ROI ?? ""
5906
5957
  },
5907
5958
  sl: {
@@ -5909,6 +5960,8 @@ var OrderEntry = (props) => {
5909
5960
  PnL: formattedOrder.sl_pnl ?? "",
5910
5961
  Offset: formattedOrder.sl_offset ?? "",
5911
5962
  "Offset%": formattedOrder.sl_offset_percentage ?? "",
5963
+ OffsetFromMark: formattedOrder.sl_offset_from_mark ?? "",
5964
+ PercentageFromMark: formattedOrder.sl_offset_percentage_from_mark ?? "",
5912
5965
  ROI: formattedOrder.sl_ROI ?? ""
5913
5966
  }
5914
5967
  },
@@ -6425,10 +6478,24 @@ var useOrderEntryScript = (inputs) => {
6425
6478
  const { priceInputContainerRef, priceInputContainerWidth } = usePriceInputContainer({
6426
6479
  order_type_ext: formattedOrder.order_type_ext
6427
6480
  });
6481
+ const effectiveEstLiqPriceForSlCheck = useMemo(() => {
6482
+ const estLiqPrice = state.estLiqPrice;
6483
+ if (estLiqPrice == null || formattedOrder.side == null || state.markPrice == null) {
6484
+ return null;
6485
+ }
6486
+ if (formattedOrder.side === OrderSide.BUY && estLiqPrice > state.markPrice || formattedOrder.side === OrderSide.SELL && estLiqPrice < state.markPrice) {
6487
+ return null;
6488
+ }
6489
+ if (!Number.isFinite(estLiqPrice) || estLiqPrice <= 0) {
6490
+ return null;
6491
+ }
6492
+ return estLiqPrice;
6493
+ }, [state.estLiqPrice, state.markPrice, formattedOrder.side]);
6428
6494
  const slPriceError = useTpslPriceChecker({
6429
6495
  slPrice: formattedOrder.sl_trigger_price,
6430
- liqPrice: state.estLiqPrice,
6496
+ liqPrice: effectiveEstLiqPriceForSlCheck,
6431
6497
  side: formattedOrder.side,
6498
+ markPrice: state.markPrice,
6432
6499
  currentPosition: state.currentPosition,
6433
6500
  orderQuantity: Number(formattedOrder.order_quantity)
6434
6501
  });
@@ -6443,12 +6510,7 @@ var useOrderEntryScript = (inputs) => {
6443
6510
  }
6444
6511
  }, [tpslSwitch]);
6445
6512
  useEffect(() => {
6446
- let estLiqPrice = state.estLiqPrice;
6447
- if (estLiqPrice == null || formattedOrder.side == null || state.markPrice == null) {
6448
- estLiqPrice = null;
6449
- } else if (formattedOrder.side === OrderSide.BUY && estLiqPrice > state.markPrice || formattedOrder.side === OrderSide.SELL && estLiqPrice < state.markPrice) {
6450
- estLiqPrice = null;
6451
- }
6513
+ const estLiqPrice = effectiveEstLiqPriceForSlCheck;
6452
6514
  const lastActive = lastUserActiveTimeRef.current;
6453
6515
  const now = Date.now();
6454
6516
  const isUserActive = now - lastActive <= ORDER_ENTRY_EST_LIQ_ACTIVE_WINDOW_MS;
@@ -6457,7 +6519,7 @@ var useOrderEntryScript = (inputs) => {
6457
6519
  estLiqPrice,
6458
6520
  isUserActive
6459
6521
  });
6460
- }, [ee, symbol, state.estLiqPrice, state.markPrice, formattedOrder.side]);
6522
+ }, [ee, symbol, effectiveEstLiqPriceForSlCheck]);
6461
6523
  useEffect(() => {
6462
6524
  setOrderValue("margin_mode", marginMode);
6463
6525
  }, [marginMode]);