@orderly.network/ui-orders
Version:
1,570 lines (1,563 loc) • 205 kB
JavaScript
'use strict';
var React2 = require('react');
var hooks = require('@orderly.network/hooks');
var i18n = require('@orderly.network/i18n');
var types = require('@orderly.network/types');
var utils = require('@orderly.network/utils');
var jsxRuntime = require('react/jsx-runtime');
var ui = require('@orderly.network/ui');
var reactApp = require('@orderly.network/react-app');
var uiTpsl = require('@orderly.network/ui-tpsl');
var dateFns = require('date-fns');
var uiShare = require('@orderly.network/ui-share');
var uiConnector = require('@orderly.network/ui-connector');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React2__default = /*#__PURE__*/_interopDefault(React2);
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
exports.useOrdersScript = void 0;
var init_orders_script = __esm({
"src/components/orders.script.ts"() {
exports.useOrdersScript = (props) => {
const { current, pnlNotionalDecimalPrecision, sharePnLConfig } = props;
const orderListRef = React2.useRef(null);
React2.useImperativeHandle(props.ref, () => ({
download: () => {
orderListRef.current?.download?.();
}
}));
return {
current,
pnlNotionalDecimalPrecision,
orderListRef,
sharePnLConfig
};
};
}
});
function parseBadgesFor(record) {
const orderType = record.type;
const algoType = record.algo_type;
if (typeof orderType !== "undefined") {
const list = [];
if (!!record.parent_algo_type) {
if (algoType === types.AlgoOrderType.STOP_LOSS) {
const types$1 = orderType === types.OrderType.CLOSE_POSITION ? [i18n.i18n.t("common.position"), i18n.i18n.t("tpsl.sl")] : [i18n.i18n.t("tpsl.sl")];
list.push(...types$1);
}
if (algoType === types.AlgoOrderType.TAKE_PROFIT) {
const types$1 = orderType === types.OrderType.CLOSE_POSITION ? [i18n.i18n.t("common.position"), i18n.i18n.t("tpsl.tp")] : [i18n.i18n.t("tpsl.tp")];
list.push(...types$1);
}
return list;
}
const type = typeof orderType === "string" ? orderType.replace("_ORDER", "") : "";
if ([types.OrderType.ASK, types.OrderType.BID].includes(orderType)) {
return [i18n.i18n.t("orderEntry.orderType.limit")];
}
if (record.algo_order_id === void 0 || record.algo_order_id && algoType === "BRACKET") {
const typeMap = {
[types.OrderType.LIMIT]: i18n.i18n.t("orderEntry.orderType.limit"),
[types.OrderType.MARKET]: i18n.i18n.t("orderEntry.orderType.market"),
[types.OrderType.POST_ONLY]: i18n.i18n.t("orderEntry.orderType.postOnly"),
[types.OrderType.IOC]: i18n.i18n.t("orderEntry.orderType.ioc"),
[types.OrderType.FOK]: i18n.i18n.t("orderEntry.orderType.fok")
};
return [
typeMap[type] || upperCaseFirstLetter(type)
];
}
if (algoType === types.AlgoOrderRootType.TRAILING_STOP) {
return [i18n.i18n.t("orderEntry.orderType.trailingStop")];
}
if (type) {
const typeMap = {
[types.OrderType.LIMIT]: i18n.i18n.t("orderEntry.orderType.stopLimit"),
[types.OrderType.MARKET]: i18n.i18n.t("orderEntry.orderType.stopMarket")
};
return [typeMap[type] || type];
}
}
if (typeof algoType !== "undefined") {
const list = [];
if (algoType === types.AlgoOrderRootType.POSITIONAL_TP_SL) {
list.push(i18n.i18n.t("common.position"));
}
const tpOrder = record?.child_orders?.find(
(order) => order.algo_type === types.AlgoOrderType.TAKE_PROFIT && !!order.trigger_price
);
const slOrder = record?.child_orders?.find(
(order) => order.algo_type === types.AlgoOrderType.STOP_LOSS && !!order.trigger_price
);
if (tpOrder || slOrder) {
list.push(
tpOrder && slOrder ? i18n.i18n.t("common.tpsl") : tpOrder ? i18n.i18n.t("tpsl.tp") : i18n.i18n.t("tpsl.sl")
);
}
return list;
}
return void 0;
}
function grayCell(record) {
return record.status === types.OrderStatus.CANCELLED || record.algo_status === types.OrderStatus.CANCELLED;
}
function getOrderStatus(record) {
return record.status || record.algo_status;
}
function isGrayCell(status2) {
return status2 === types.OrderStatus.CANCELLED;
}
function findBracketTPSLOrder(order) {
if (order.algo_type !== types.AlgoOrderRootType.BRACKET) {
return {
tpOrder: void 0,
slOrder: void 0
};
}
const innerOrder = order.child_orders?.[0];
if (!innerOrder)
return {
tpOrder: void 0,
slOrder: void 0
};
const tpOrder = innerOrder?.child_orders?.find(
(item) => item.algo_type === types.AlgoOrderType.TAKE_PROFIT
);
const slOrder = innerOrder?.child_orders?.find(
(item) => item.algo_type === types.AlgoOrderType.STOP_LOSS
);
return {
tpOrder,
slOrder
};
}
function calcBracketRoiAndPnL(order) {
const defaultCallback = {
pnl: {
tpPnL: void 0,
slPnL: void 0
}
// roi: {
// tpRoi: undefined,
// slRoi: undefined,
// },
};
const { tpOrder, slOrder } = findBracketTPSLOrder(order);
if (!tpOrder && !slOrder)
return defaultCallback;
if (typeof order.price === void 0 || !order.price)
return defaultCallback;
const quantity2 = order.side === types.OrderSide.BUY ? order.quantity : order.quantity * -1;
const tpPnL = tpOrder?.trigger_price && hooks.utils.priceToPnl({
qty: quantity2,
price: tpOrder?.trigger_price,
entryPrice: order.price,
// @ts-ignore
orderSide: order.side,
// @ts-ignore
orderType: tpOrder.algo_type
});
const slPnL = slOrder?.trigger_price && hooks.utils.priceToPnl({
qty: quantity2,
// trigger price
price: slOrder?.trigger_price,
//
entryPrice: order.price,
// @ts-ignore
orderSide: order.side,
// @ts-ignore
orderType: slOrder.algo_type
});
return {
pnl: {
tpPnL,
slPnL
}
// roi: {
// tpRoi,
// slRoi,
// },
};
}
function areDatesEqual(date1, date2) {
if (!date1 || !date2)
return false;
return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
}
function isTrailingStopOrder(order) {
return order.algo_type === types.OrderType.TRAILING_STOP;
}
function getNotional(order, dp = 2) {
if (order.price && order.quantity) {
return new utils.Decimal(order.price).mul(order.quantity).toFixed(dp, utils.Decimal.ROUND_DOWN);
}
return 0;
}
function convertApiOrderTypeToOrderEntryType(order) {
if (order.algo_type === types.OrderType.TRAILING_STOP) {
return order.algo_type;
}
const isAlgoOrder = order.algo_order_id !== void 0;
if (isAlgoOrder && order.algo_type !== types.AlgoOrderRootType.BRACKET) {
return `STOP_${order.type}`;
}
return order.type;
}
var upperCaseFirstLetter;
var init_util = __esm({
"src/utils/util.ts"() {
upperCaseFirstLetter = (str) => {
if (str === void 0)
return str;
if (str.length === 0)
return str;
if (str.length === 1)
return str.charAt(0).toUpperCase();
return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
};
}
});
var SymbolContext, useSymbolContext;
var init_symbolContext = __esm({
"src/components/provider/symbolContext.tsx"() {
SymbolContext = React2.createContext(
{}
);
useSymbolContext = () => {
return React2.useContext(SymbolContext);
};
}
});
var SymbolProvider;
var init_symbolProvider = __esm({
"src/components/provider/symbolProvider.tsx"() {
init_symbolContext();
SymbolProvider = (props) => {
const { symbol, children } = props;
const symbolInfo = hooks.useSymbolsInfo()[symbol];
const memoizedValue = React2.useMemo(() => {
return {
symbol,
base_dp: symbolInfo("base_dp"),
quote_dp: symbolInfo("quote_dp"),
base_tick: symbolInfo("base_tick"),
quote_tick: symbolInfo("quote_tick"),
base: symbolInfo("base"),
quote: symbolInfo("quote"),
origin: symbolInfo(),
quote_max: symbolInfo("quote_max"),
quote_min: symbolInfo("quote_min")
};
}, [symbol, symbolInfo]);
return /* @__PURE__ */ jsxRuntime.jsx(SymbolContext.Provider, { value: memoizedValue, children });
};
}
});
var ShareButton;
var init_shareButton_ui = __esm({
"src/components/shareButton/shareButton.ui.tsx"() {
ShareButton = (props) => {
if (props.sharePnLConfig == null) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(
"button",
{
type: "button",
onClick: (e) => {
e.stopPropagation();
props.showModal();
},
children: /* @__PURE__ */ jsxRuntime.jsx(ui.ShareIcon, { color: "white", opacity: 0.54, size: props.iconSize ?? 16 })
}
);
};
}
});
var useShareButtonScript;
var init_shareButton_script = __esm({
"src/components/shareButton/shareButton.script.tsx"() {
useShareButtonScript = (props) => {
const { sharePnLConfig, order, iconSize } = props;
const { t } = i18n.useTranslation();
const { getFirstRefCode } = hooks.useReferralInfo();
const refCode = React2.useMemo(() => {
return getFirstRefCode()?.code;
}, [getFirstRefCode]);
const leverage = hooks.useLeverageBySymbol(order.symbol);
const showModal = () => {
ui.modal.show(props.modalId, {
pnl: {
entity: {
symbol: order.symbol,
pnl: order.realized_pnl,
side: order.side == "BUY" ? t("share.pnl.share.long") : t("share.pnl.share.short"),
openPrice: order.average_executed_price,
openTime: order.updated_time,
quantity: order.quantity
},
refCode,
leverage,
...sharePnLConfig
}
});
};
return {
iconSize,
sharePnLConfig,
showModal
};
};
}
});
var ShareButtonWidget;
var init_shareButton_widget = __esm({
"src/components/shareButton/shareButton.widget.tsx"() {
init_shareButton_script();
init_shareButton_ui();
ShareButtonWidget = (props) => {
const state = useShareButtonScript(props);
return /* @__PURE__ */ jsxRuntime.jsx(ShareButton, { ...state });
};
}
});
// src/components/shareButton/index.ts
var init_shareButton = __esm({
"src/components/shareButton/index.ts"() {
init_shareButton_ui();
init_shareButton_widget();
}
});
var AvgPrice;
var init_avgPrice = __esm({
"src/components/orderList/desktop/avgPrice.tsx"() {
AvgPrice = React2.memo((props) => {
const symbolsInfo = hooks.useSymbolsInfo();
const info = symbolsInfo[props.symbol];
if (!props.value) {
return "--";
}
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Text.numeral,
{
padding: false,
dp: info("quote_dp", 2),
rm: utils.Decimal.ROUND_UP,
children: props.value
}
);
});
}
});
var BracketOrderPrice, EditBracketOrder, Price;
var init_bracketOrderPrice = __esm({
"src/components/orderList/desktop/bracketOrderPrice.tsx"() {
init_util();
init_symbolContext();
BracketOrderPrice = (props) => {
const { order } = props;
const { quote_dp, base_dp } = useSymbolContext();
const { t } = i18n.useTranslation();
const { sl_trigger_price, tp_trigger_price } = React2.useMemo(() => {
if (!("algo_type" in order) || !Array.isArray(order.child_orders)) {
return {};
}
return hooks.utils.findTPSLFromOrder(props.order.child_orders[0]);
}, [props.order]);
const { pnl } = calcBracketRoiAndPnL(order);
if (!tp_trigger_price && !sl_trigger_price) {
return "--";
}
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Tooltip,
{
content: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", itemAlign: "start", gap: 1, children: [
typeof pnl.tpPnL !== "undefined" && /* @__PURE__ */ jsxRuntime.jsx(
ui.Text.numeral,
{
prefix: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { intensity: 80, children: [
`${t("tpsl.tpPnl")}:`,
" \xA0"
] }),
suffix: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { intensity: 20, children: " USDC" }),
dp: quote_dp,
color: "buy",
showIdentifier: true,
children: pnl.tpPnL
}
),
typeof pnl.slPnL !== "undefined" && /* @__PURE__ */ jsxRuntime.jsx(
ui.Text.numeral,
{
prefix: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { intensity: 80, children: [
`${t("tpsl.slPnl")}:`,
" \xA0"
] }),
suffix: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { intensity: 20, children: " USDC" }),
dp: quote_dp,
color: "sell",
children: pnl.slPnL
}
)
] }),
className: "oui-bg-base-6",
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", justify: "start", gap: 2, children: [
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", justify: "start", itemAlign: "start", children: [
/* @__PURE__ */ jsxRuntime.jsx(Price, { type: "TP", value: tp_trigger_price, quote_dp }),
/* @__PURE__ */ jsxRuntime.jsx(Price, { type: "SL", value: sl_trigger_price, quote_dp })
] }),
/* @__PURE__ */ jsxRuntime.jsx(EditBracketOrder, { order })
] })
}
);
};
EditBracketOrder = (props) => {
const { order } = props;
const onEdit = () => {
ui.modal.show("EditBracketOrderDialogId", {
order
});
};
return /* @__PURE__ */ jsxRuntime.jsx(
ui.EditIcon,
{
size: 16,
className: "oui-text-base-contrast oui-cursor-pointer",
onClick: () => {
onEdit();
}
}
);
};
Price = (props) => {
const { type, value, quote_dp } = props;
const { t } = i18n.useTranslation();
return value ? /* @__PURE__ */ jsxRuntime.jsx(
ui.Text.numeral,
{
className: ui.cn(
"oui-gap-0 oui-decoration-white/20 oui-border-b oui-border-dashed oui-border-base-contrast-12",
type === "TP" ? "oui-text-trade-profit" : "oui-text-trade-loss"
),
rule: "price",
dp: quote_dp,
prefix: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oui-text-base-contrast-54", children: [
type === "TP" ? `${t("tpsl.tp")} -` : `${t("tpsl.sl")} -`,
"\xA0"
] }),
children: value
},
"tp"
) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
};
}
});
var OrderListContext, useOrderListContext;
var init_orderListContext = __esm({
"src/components/orderList/orderListContext.tsx"() {
OrderListContext = React2.createContext(
{}
);
useOrderListContext = () => {
return React2.useContext(OrderListContext);
};
}
});
var CancelButton;
var init_cancelBtn = __esm({
"src/components/orderList/desktop/cancelBtn.tsx"() {
init_orderListContext();
CancelButton = (props) => {
const { order } = props;
const { t } = i18n.useTranslation();
const { onCancelOrder } = useOrderListContext();
const [isLoading, setIsLoading] = React2.useState(false);
return /* @__PURE__ */ jsxRuntime.jsx(
ui.ThrottledButton,
{
size: "sm",
variant: "outlined",
color: "secondary",
onClick: (event) => {
if (!onCancelOrder)
return;
event.preventDefault();
event.stopPropagation();
setIsLoading(true);
onCancelOrder(order).then(
(res) => res,
(error) => {
ui.toast.error(error.message);
}
).finally(() => {
setIsLoading(false);
});
},
loading: isLoading,
children: t("common.cancel")
}
);
};
}
});
// src/type.ts
var init_type = __esm({
"src/type.ts"() {
}
});
var ConfirmContent;
var init_confirmContent = __esm({
"src/components/orderList/desktop/editOrder/confirmContent.tsx"() {
init_type();
ConfirmContent = React2.memo((props) => {
const { type, base, value, cancelPopover, isSubmitting, onConfirm } = props;
const { t } = i18n.useTranslation();
const renderLabel = () => {
const common = {
values: { base, value: utils.commify(value) },
components: [/* @__PURE__ */ jsxRuntime.jsx("span", { className: "oui-text-warning-darken" }, "0")]
};
switch (type) {
case 0 /* quantity */:
return (
// @ts-ignore
/* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "order.edit.confirm.quantity", ...common })
);
case 1 /* price */:
return (
// @ts-ignore
/* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "order.edit.confirm.price", ...common })
);
case 2 /* triggerPrice */:
return (
// @ts-ignore
/* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "order.edit.confirm.triggerPrice", ...common })
);
case 3 /* callbackValue */:
return (
// @ts-ignore
/* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "order.edit.confirm.callbackValue", ...common })
);
case 4 /* callbackRate */:
return (
// @ts-ignore
/* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "order.edit.confirm.callbackRate", ...common })
);
}
};
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-relative oui-pt-5", children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "desktop:oui-text-sm oui-text-2xs oui-text-base-contrast-54", children: renderLabel() }),
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-mt-5 oui-grid oui-grid-cols-2 oui-gap-2", children: [
/* @__PURE__ */ jsxRuntime.jsx(
ui.Button,
{
color: "secondary",
size: "md",
onClick: cancelPopover,
disabled: isSubmitting,
children: t("common.cancel")
}
),
/* @__PURE__ */ jsxRuntime.jsx(ui.ThrottledButton, { size: "md", loading: isSubmitting, onClick: onConfirm, children: t("common.confirm") })
] }),
/* @__PURE__ */ jsxRuntime.jsx(
"button",
{
className: "oui-absolute oui-right-0 oui-top-0 oui-text-base-contrast-54",
onClick: cancelPopover,
children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, { size: 16, color: "white", opacity: 1 })
}
)
] });
});
ConfirmContent.displayName = "ConfirmContent";
}
});
function usePopoverState(props) {
const { originValue, value, setValue } = props;
const [open, setOpen] = React2.useState(false);
const [editing, setEditing] = React2.useState(false);
const containerRef = React2.useRef(null);
const closePopover = React2.useCallback(() => {
setOpen(false);
setEditing(false);
}, []);
const cancelPopover = React2.useCallback(() => {
closePopover();
setValue(originValue);
}, [originValue]);
const onClick = React2.useCallback(
async (event) => {
event.stopPropagation();
event.preventDefault();
if (value === originValue) {
setEditing(false);
return;
}
setOpen(true);
},
[value, originValue]
);
React2.useEffect(() => {
const handleClickOutside = (event) => {
if (containerRef.current && !containerRef.current.contains(event.target) && !open) {
cancelPopover();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [open, cancelPopover]);
return {
value,
setValue,
open,
setOpen,
editing,
setEditing,
containerRef,
closePopover,
cancelPopover,
onClick
};
}
var init_usePopoverState = __esm({
"src/components/orderList/desktop/hooks/usePopoverState.ts"() {
}
});
function useValidateField(props) {
const { order, field, originValue, value, fieldValues } = props;
const changeFields = fieldValues || { [field]: value };
const orderType = React2.useMemo(
() => convertApiOrderTypeToOrderEntryType(order),
[order]
);
const { errors, validate, clearErrors } = hooks.useOrderEntity({
symbol: order.symbol,
order_type: orderType,
side: order.side,
...changeFields
});
const { getErrorMsg } = reactApp.useOrderEntryFormErrorMsg(errors);
const error = React2.useMemo(
() => field ? getErrorMsg(field) : void 0,
[getErrorMsg, field]
);
React2.useEffect(() => {
if (value === originValue) {
clearErrors();
return;
}
validate().then((order2) => {
}).catch((errors2) => {
});
}, [value, originValue]);
return { error, errors, getErrorMsg };
}
var init_useValidateField = __esm({
"src/components/orderList/desktop/hooks/useValidateField.ts"() {
init_util();
}
});
var EditableCellInput;
var init_editableCellInput = __esm({
"src/components/orderList/desktop/components/editableCellInput.tsx"() {
EditableCellInput = (props) => {
const { onClick } = props;
const inputRef = React2.useRef(null);
const handleKeyDown = React2.useCallback(
(event) => {
if (event.key === "Enter") {
onClick(event);
}
},
[onClick]
);
React2.useEffect(() => {
const input = inputRef.current;
if (input) {
const length = input.value.length;
input.setSelectionRange(length, length);
}
}, [props.value]);
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Input.tooltip,
{
ref: inputRef,
tooltip: props.error,
autoComplete: "off",
autoFocus: true,
type: "text",
size: "sm",
placeholder: props.readonly ? "" : props.placeholder || "",
id: props.id,
name: props.name,
color: props.error ? "danger" : void 0,
value: props.readonly ? "" : props.value || "",
onValueChange: props.onChange,
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
},
onFocus: props.onFocus,
onBlur: props.onBlur,
onKeyDown: props.handleKeyDown || handleKeyDown,
formatters: props.overrideFormatters || [
...props.formatters ?? types.EMPTY_LIST,
ui.inputFormatter.numberFormatter,
ui.inputFormatter.decimalPointFormatter
],
classNames: {
root: "oui-bg-base-700 oui-px-2 oui-py-1 oui-rounded",
input: "oui-pr-2"
},
readOnly: props.readonly,
suffix: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gapX: 1, children: [
props.suffix,
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick, disabled: !!props.error, children: /* @__PURE__ */ jsxRuntime.jsx(
ui.CheckIcon,
{
size: 18,
color: "white",
opacity: 1,
className: ui.cn(
"oui-opacity-50",
props.error ? "oui-cursor-not-allowed" : "oui-cursor-pointer hover:oui-opacity-100"
)
}
) })
] })
}
);
};
}
});
var PreviewCell;
var init_previewCell = __esm({
"src/components/orderList/desktop/components/previewCell.tsx"() {
init_util();
PreviewCell = React2.memo((props) => {
const { status: status2, value, disabled } = props;
return /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
className: ui.cn(
"oui-flex oui-items-center oui-justify-start",
"oui-relative oui-max-w-[110px] oui-gap-1 oui-font-semibold",
isGrayCell(status2) && "oui-text-base-contrast-20",
props.className
),
onClick: (e) => {
if (!disabled) {
e.stopPropagation();
e.preventDefault();
props.setEditing(true);
}
},
children: /* @__PURE__ */ jsxRuntime.jsx(
ui.Flex,
{
r: "base",
className: ui.cn(
"oui-h-[28px] oui-min-w-[70px]",
!disabled && "oui-border oui-border-line-12 oui-bg-base-7 oui-px-2"
),
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "2xs", children: [
utils.commifyOptional(value),
props.suffix
] })
}
)
}
);
});
}
});
var ActivedPriceCell;
var init_activedPriceCell = __esm({
"src/components/orderList/desktop/components/activedPriceCell.tsx"() {
init_type();
init_util();
init_symbolContext();
init_orderListContext();
init_confirmContent();
init_usePopoverState();
init_useValidateField();
init_editableCellInput();
init_previewCell();
ActivedPriceCell = (props) => {
const { order } = props;
const { t } = i18n.useTranslation();
const originValue = order.activated_price?.toString() ?? "";
const [value, setValue] = React2.useState(originValue);
const [submitting, setSubmitting] = React2.useState(false);
const disabled = props.disabled || order.is_activated || order.is_triggered;
const { editAlgoOrder } = useOrderListContext();
const { base, quote_dp } = useSymbolContext();
const {
open,
setOpen,
editing,
setEditing,
containerRef,
closePopover,
cancelPopover,
onClick
} = usePopoverState({
originValue,
value,
setValue
});
const { error } = useValidateField({
order,
field: "activated_price",
originValue,
value
});
const onConfirm = React2.useCallback(() => {
setSubmitting(true);
const data = {
algo_order_id: order.algo_order_id,
activated_price: value
};
editAlgoOrder(order.algo_order_id.toString(), data).then((result) => {
closePopover();
setValue(value);
}).catch((err) => {
ui.toast.error(err.message);
cancelPopover();
}).finally(() => setSubmitting(false));
}, [order.algo_order_id, value, cancelPopover]);
if (!order.activated_price && order.type === types.OrderType.MARKET) {
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("common.marketPrice") });
}
const renderContent = () => {
if (!editing || disabled) {
return /* @__PURE__ */ jsxRuntime.jsx(
PreviewCell,
{
value,
status: getOrderStatus(order),
setEditing,
disabled
}
);
}
return /* @__PURE__ */ jsxRuntime.jsx(
EditableCellInput,
{
value,
onChange: setValue,
onClick,
error: value ? error : t("orderEntry.triggerPrice.error.required"),
formatters: [ui.inputFormatter.dpFormatter(quote_dp)]
}
);
};
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Popover,
{
open,
onOpenChange: setOpen,
content: /* @__PURE__ */ jsxRuntime.jsx(
ConfirmContent,
{
type: 2 /* triggerPrice */,
base,
value,
cancelPopover,
isSubmitting: submitting,
onConfirm
}
),
children: /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
},
ref: containerRef,
children: renderContent()
}
)
}
);
};
}
});
var PriceCell;
var init_priceCell = __esm({
"src/components/orderList/desktop/components/priceCell.tsx"() {
init_type();
init_util();
init_symbolContext();
init_orderListContext();
init_confirmContent();
init_usePopoverState();
init_useValidateField();
init_editableCellInput();
init_previewCell();
PriceCell = (props) => {
const { order } = props;
const { t } = i18n.useTranslation();
const originValue = React2.useMemo(() => {
if (order.type === types.OrderType.MARKET && !order.price) {
return "Market";
}
return order.price?.toString() ?? "Market";
}, [order.price, order.type]);
const [value, setValue] = React2.useState(originValue);
const isAlgoOrder = order?.algo_order_id !== void 0;
const [submitting, setSubmitting] = React2.useState(false);
const { editOrder, editAlgoOrder } = useOrderListContext();
const { base, quote_dp } = useSymbolContext();
const {
open,
setOpen,
editing,
setEditing,
containerRef,
closePopover,
cancelPopover,
onClick
} = usePopoverState({
originValue,
value,
setValue
});
const { error, getErrorMsg } = useValidateField({
order,
field: "order_price",
originValue,
value,
fieldValues: {
order_price: value,
// add order quantity to validate total
order_quantity: order.quantity?.toString(),
// need to pass trigger_price to validate order_price, because order_price is depend on trigger_price when type is stop limit
trigger_price: order.trigger_price?.toString()
}
});
const onConfirm = () => {
setSubmitting(true);
let order_id = order.order_id;
let data = {
symbol: order.symbol,
order_type: order.type,
side: order.side,
order_quantity: order.quantity,
order_price: value
};
if (order.reduce_only !== void 0) {
data.reduce_only = order.reduce_only;
}
if (order.order_tag) {
data.order_tag = order.order_tag;
}
if (order.client_order_id) {
data.client_order_id = order.client_order_id;
}
if (isAlgoOrder) {
order_id = order.algo_order_id;
data = {
...data,
order_id,
price: value,
algo_order_id: order_id
};
}
if (order.tag !== void 0) {
data["order_tag"] = order.tag;
}
let future;
if (order.algo_order_id !== void 0) {
future = editAlgoOrder(order.algo_order_id.toString(), data);
} else {
future = editOrder(order.order_id.toString(), data);
}
future.then((result) => {
closePopover();
setValue(value);
}).catch((err) => {
ui.toast.error(err.message);
cancelPopover();
}).finally(() => setSubmitting(false));
};
const isAlgoMarketOrder = order.algo_order_id && order.type == "MARKET";
if (isAlgoMarketOrder || value === "Market") {
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("common.marketPrice") });
}
const renderContent = () => {
if (!editing || props.disabled) {
return /* @__PURE__ */ jsxRuntime.jsx(
PreviewCell,
{
value,
status: getOrderStatus(order),
setEditing,
disabled: props.disabled
}
);
}
return /* @__PURE__ */ jsxRuntime.jsx(
EditableCellInput,
{
value,
onChange: setValue,
onClick,
error: error || getErrorMsg("total"),
formatters: [ui.inputFormatter.dpFormatter(quote_dp)]
}
);
};
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Popover,
{
open,
onOpenChange: setOpen,
content: /* @__PURE__ */ jsxRuntime.jsx(
ConfirmContent,
{
type: 1 /* price */,
base,
value,
cancelPopover,
isSubmitting: submitting,
onConfirm
}
),
children: /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
},
ref: containerRef,
children: renderContent()
}
)
}
);
};
}
});
function calcTPSLPnL(props) {
const { order, position, quote_dp } = props;
if (!position)
return {
sl_trigger_price: void 0,
tp_trigger_price: void 0,
slPnL: void 0,
tpPnL: void 0
};
const isTPSLOrder = "algo_type" in order && Array.isArray(order.child_orders);
const { sl_trigger_price, tp_trigger_price } = isTPSLOrder ? hooks.findTPSLFromOrder(order) : {
sl_trigger_price: void 0,
tp_trigger_price: void 0
};
const { sl_order_price, tp_order_price } = isTPSLOrder ? hooks.findTPSLOrderPriceFromOrder(order) : {
sl_order_price: void 0,
tp_order_price: void 0
};
let quantity2 = order.quantity;
if (quantity2 === 0) {
if (order.child_orders?.[0].type === "CLOSE_POSITION") {
quantity2 = position.position_qty;
}
}
const avgOpenPrice = position.average_open_price;
const tpPnL = typeof quantity2 === "number" && typeof tp_trigger_price === "number" && typeof avgOpenPrice === "number" ? hooks.utils.priceToPnl(
{
qty: quantity2,
price: tp_trigger_price,
entryPrice: position.average_open_price,
orderSide: order.side,
orderType: types.AlgoOrderType.TAKE_PROFIT
},
{ symbol: { quote_dp } }
) : void 0;
const slPnL = typeof quantity2 === "number" && typeof sl_trigger_price === "number" && typeof avgOpenPrice === "number" ? hooks.utils.priceToPnl(
{
qty: quantity2,
price: sl_trigger_price,
entryPrice: position.average_open_price,
orderSide: order.side,
orderType: types.AlgoOrderType.STOP_LOSS
},
{ symbol: { quote_dp } }
) : void 0;
return {
sl_trigger_price,
tp_trigger_price,
sl_order_price,
tp_order_price,
slPnL,
tpPnL
};
}
var TPSLOrderRowContext, useTPSLOrderRowContext, TPSLOrderRowProvider;
var init_tpslOrderRowContext = __esm({
"src/components/orderList/tpslOrderRowContext.tsx"() {
init_symbolContext();
TPSLOrderRowContext = React2.createContext(
{}
);
useTPSLOrderRowContext = () => {
return React2.useContext(TPSLOrderRowContext);
};
TPSLOrderRowProvider = (props) => {
const { order, children } = props;
const { quote_dp } = useSymbolContext();
const [position, setPosition] = React2.useState();
const [doDeleteOrder] = hooks.useMutation("/v1/algo/order", "DELETE");
const [doUpdateOrder] = hooks.useMutation("/v1/algo/order", "PUT");
const config = hooks.useSWRConfig();
const { state } = hooks.useAccount();
const positionKey = React2.useMemo(() => {
return hooks.unstable_serialize(() => ["/v1/positions", state.accountId]);
}, [state.accountId]);
const onCancelOrder = hooks.useMemoizedFn(async (order2) => {
return doDeleteOrder(null, {
order_id: order2.algo_order_id,
symbol: order2.symbol
});
});
const onUpdateOrder = hooks.useMemoizedFn(
async (order2, params) => {
return doUpdateOrder({
order_id: order2.algo_order_id,
child_orders: order2.child_orders.map((order3) => ({
order_id: order3.algo_order_id,
quantity: params.order_quantity
}))
});
}
);
const getRelatedPosition = hooks.useMemoizedFn(
(symbol) => {
const positions = config.cache.get(positionKey);
return positions?.data?.rows?.find(
(p) => p.symbol === symbol
);
}
);
const {
sl_trigger_price,
tp_trigger_price,
tpPnL,
slPnL,
sl_order_price,
tp_order_price
} = calcTPSLPnL({
order,
position,
quote_dp
});
React2.useEffect(() => {
if ("algo_type" in order || (order?.reduce_only ?? false)) {
const position2 = getRelatedPosition(order.symbol);
if (position2) {
setPosition(position2);
}
}
}, [order.symbol]);
const memoizedValue = React2.useMemo(() => {
return {
order,
sl_trigger_price,
tp_trigger_price,
sl_order_price,
tp_order_price,
tpPnL,
slPnL,
position,
onCancelOrder,
onUpdateOrder,
getRelatedPosition
};
}, [
order,
sl_trigger_price,
tp_trigger_price,
sl_order_price,
tp_order_price,
tpPnL,
slPnL,
position,
onCancelOrder,
onUpdateOrder,
getRelatedPosition
]);
return /* @__PURE__ */ jsxRuntime.jsx(TPSLOrderRowContext.Provider, { value: memoizedValue, children });
};
}
});
var InnerInput;
var init_innerInput = __esm({
"src/components/orderList/desktop/editOrder/innerInput.tsx"() {
InnerInput = (props) => {
const {
inputRef,
dp,
value,
setValue,
setEditing,
error,
handleKeyDown,
onClick,
onClose,
onFocus,
onBlur,
hintInfo
} = props;
React2.useEffect(() => {
const input = inputRef.current;
if (input) {
const length = input.value.length;
input.setSelectionRange(length, length);
}
setEditing(true);
}, []);
const open = (hintInfo?.length || 0) > 0;
return /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { content: hintInfo, open, children: /* @__PURE__ */ jsxRuntime.jsx(
ui.Input,
{
ref: inputRef,
type: "text",
size: "sm",
formatters: [
ui.inputFormatter.numberFormatter,
ui.inputFormatter.dpFormatter(dp),
ui.inputFormatter.currencyFormatter
],
value,
onValueChange: (e) => setValue(e),
helpText: error,
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
},
autoComplete: "off",
onFocus,
onBlur,
onKeyDown: handleKeyDown,
autoFocus: true,
color: open ? "danger" : void 0,
classNames: {
root: "oui-bg-base-700 oui-px-2 oui-py-1 oui-rounded",
input: "oui-pr-2"
},
suffix: /* @__PURE__ */ jsxRuntime.jsx("button", { onClick, children: /* @__PURE__ */ jsxRuntime.jsx(
ui.CheckIcon,
{
size: 18,
color: "white",
opacity: 1,
className: "oui-cursor-pointer oui-opacity-50 hover:oui-opacity-100"
}
) })
}
) });
};
}
});
var QuantityCell, PreviewCell2, EditState, Buttons;
var init_quantityCell = __esm({
"src/components/orderList/desktop/components/quantityCell.tsx"() {
init_type();
init_util();
init_symbolContext();
init_orderListContext();
init_tpslOrderRowContext();
init_confirmContent();
init_innerInput();
QuantityCell = (props) => {
const { order } = props;
const { reduce_only } = order;
const originValue = order.quantity.toString();
const [value, setValue] = React2.useState(originValue);
const [editing, setEditing] = React2.useState(false);
const { t } = i18n.useTranslation();
const [open, setOpen] = React2.useState(false);
const [error, setError] = React2.useState();
const { editOrder, editAlgoOrder, checkMinNotional } = useOrderListContext();
const { onUpdateOrder: onUpdateTPSLOrder, position } = useTPSLOrderRowContext();
const { base_dp, base, base_tick } = useSymbolContext();
const setQuantity = async (qty, maxQty) => {
setValue(qty);
const positionQty = Math.abs(position?.position_qty || 0);
if (position && reduce_only && Number(qty) > positionQty) {
setError(
t("orders.quantity.lessThanPosition", {
quantity: positionQty
})
);
} else {
const quantity2 = Number(qty);
if (maxQty && quantity2 > maxQty) {
setError(
t("orders.quantity.lessThan", {
quantity: utils.commifyOptional(maxQty, {
fix: base_dp
})
})
);
} else {
setError(void 0);
}
}
return Promise.resolve();
};
React2.useEffect(() => {
setQuantity(order.quantity.toString());
}, [props.order.quantity]);
const closePopover = () => {
setOpen(false);
setEditing(false);
};
const cancelPopover = () => {
setOpen(false);
setQuantity(order.quantity.toString());
setEditing(false);
};
const [isSubmitting, setIsSubmitting] = React2.useState(false);
const inputRef = React2.useRef(null);
const clickHandler = () => {
if (!!error) {
return;
}
if (Number(value) === Number(order.quantity)) {
setEditing(false);
return;
}
const price2 = order.algo_order_id !== void 0 ? order.trigger_price : order.price;
if (price2 !== null && order.reduce_only !== true) {
const notionalText = checkMinNotional(order.symbol, price2, value);
if (notionalText) {
ui.toast.error(notionalText);
setIsSubmitting(false);
cancelPopover();
return;
}
}
setOpen(true);
};
const onClick = (event) => {
event?.stopPropagation();
event?.preventDefault();
clickHandler();
};
const handleKeyDown = (event) => {
if (event.key === "Enter") {
event?.stopPropagation();
event?.preventDefault();
clickHandler();
}
};
const onConfirm = React2.useCallback(() => {
setIsSubmitting(true);
let params = {
symbol: order.symbol,
order_type: order.type,
side: order.side,
order_price: order.price,
order_quantity: value,
// reduce_only: Boolean(order.reduce_only),
algo_order_id: order.algo_order_id
};
if (typeof params.algo_order_id !== "undefined" && params.order_type === "MARKET") {
const { order_price, ...rest } = params;
params = rest;
}
if (typeof order.reduce_only !== "undefined") {
params.reduce_only = order.reduce_only;
}
if (order.order_tag) {
params.order_tag = order.order_tag;
}
if (order.client_order_id) {
params.client_order_id = order.client_order_id;
}
if (order?.visible_quantity === 0) {
params["visible_quantity"] = 0;
}
if (order?.tag !== void 0) {
params["order_tag"] = order.tag;
}
let future;
if ("algo_type" in order && order.algo_type === types.AlgoOrderRootType.TP_SL) {
future = onUpdateTPSLOrder(order, params);
} else {
if (order.algo_order_id !== void 0) {
future = editAlgoOrder(order.algo_order_id.toString(), params);
} else {
future = editOrder(order.order_id.toString(), params);
}
}
future.then(
(result) => {
closePopover();
setQuantity(value.toString());
},
(err) => {
ui.toast.error(err.message);
setQuantity(order.quantity.toString());
cancelPopover();
}
).finally(() => setIsSubmitting(false));
}, [value]);
const componentRef = React2.useRef(null);
const quantitySliderRef = React2.useRef(null);
const handleClickOutside = (event) => {
if (componentRef.current && quantitySliderRef.current && !componentRef.current.contains(event.target) && !quantitySliderRef.current.contains(event.target) && !open) {
cancelPopover();
}
};
React2.useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [open, order.quantity]);
const renderContent = () => {
if (!editing || props.disabled) {
return /* @__PURE__ */ jsxRuntime.jsx(
PreviewCell2,
{
order,
value,
setEditing,
disable: props.disabled
}
);
}
return /* @__PURE__ */ jsxRuntime.jsx(
EditState,
{
inputRef,
quantitySliderRef,
base_dp,
base_tick,
quantity: value,
setQuantity,
editing,
setEditing,
handleKeyDown,
onClick,
onClose: cancelPopover,
symbol: order.symbol,
reduce_only,
positionQty: position?.position_qty,
error,
confirmOpen: open,
side: order.side,
order,
setError
}
);
};
return /* @__PURE__ */ jsxRuntime.jsx(
ui.Popover,
{
open,
onOpenChange: setOpen,
content: /* @__PURE__ */ jsxRuntime.jsx(
ConfirmContent,
{
type: 0 /* quantity */,
base,
value,
cancelPopover,
isSubmitting,
onConfirm
}
),
contentProps: {
onOpenAutoFocus: (e) => {
}
},
children: /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
},
ref: componentRef,
children: renderContent()
}
)
}
);
};
PreviewCell2 = (props) => {
const { order, value } = props;
const executed = order.total_executed_quantity;
return /* @__PURE__ */ jsxRuntime.jsxs(
ui.Flex,
{
direction: "row",
justify: "start",
gap: 1,
className: ui.cn(
"oui-relative oui-max-w-[110px]",
order.side === types.OrderSide.BUY && "oui-text-trade-profit",
order.side === types.OrderSide.SELL && "oui-text-trade-loss",
grayCell(order) && "oui-text-base-contrast-20"
),
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
props.setEditing(true);
},
children: [
"algo_type" in order && order.algo_type === types.AlgoOrderRootType.TP_SL ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx("span", { children: executed }),
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "/" })
] }),
/* @__PURE__ */ jsxRuntime.jsx(
ui.Flex,
{
r: "base",
className: ui.cn(
"oui-h-[28px] oui-min-w-[70px]",
!props.disable && "oui-border oui-border-line-12 oui-bg-base-7 oui-px-2"
),
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "2xs", children: value })
}
)
]
}
);
};
EditState = (props) => {
const {
inputRef,
quantitySliderRef,
base_dp,
base_tick,
quantity: quantity2,
setQuantity,
setEditing,
handleKeyDown,
onClick,
onClose,
error,
symbol,
reduce_only,
positionQty,
confirmOpen,
order
} = props;
const maxBuyQty = hooks.useMaxQty(symbol, order.side, order.reduce_only);
const maxQty = React2.useMemo(() => {