@orderly.network/ui-leverage 2.8.1 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var i18n = require('@orderly.network/i18n');
4
4
  var ui = require('@orderly.network/ui');
5
- var be = require('react');
5
+ var React = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var hooks = require('@orderly.network/hooks');
8
8
  var perp = require('@orderly.network/perp');
@@ -11,19 +11,745 @@ var utils = require('@orderly.network/utils');
11
11
 
12
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
 
14
- var be__default = /*#__PURE__*/_interopDefault(be);
14
+ var React__default = /*#__PURE__*/_interopDefault(React);
15
15
 
16
- var K=e=>{let{Icon:t,onClick:r,disabled:o}=e;return jsxRuntime.jsx(t,{onClick:o?void 0:r,className:ui.cn("oui-m-2 oui-text-white oui-transition-all",o?"oui-cursor-not-allowed oui-opacity-20":"oui-cursor-pointer oui-opacity-100")})},O=e=>{let t=be__default.default.useMemo(()=>[ui.inputFormatter.numberFormatter,ui.inputFormatter.dpFormatter(0)],[]),r=be.useId();return jsxRuntime.jsxs("label",{htmlFor:r,className:ui.cn("oui-w-full","oui-rounded","oui-bg-base-6","oui-flex","oui-items-center","oui-justify-between","oui-outline","oui-outline-offset-0","oui-outline-1","oui-outline-transparent","focus-within:oui-outline-primary-light","oui-input-root"),children:[jsxRuntime.jsx(K,{Icon:ui.ReduceIcon,onClick:e.onLeverageReduce,disabled:e.isReduceDisabled}),jsxRuntime.jsxs(ui.Flex,{itemAlign:"center",justify:"center",className:"oui-mr-4",children:[jsxRuntime.jsx(ui.Input,{value:e.value,id:r,autoComplete:"off",classNames:{input:ui.cn("oui-text-right oui-text-[24px]"),root:ui.cn("oui-w-12","oui-px-0","oui-outline","oui-outline-offset-0","oui-outline-1","oui-outline-transparent","focus-within:oui-outline-primary-none")},formatters:t,onChange:e.onInputChange}),jsxRuntime.jsx("div",{className:ui.cn("oui-ml-1 oui-mt-1 oui-select-none","oui-text-base oui-text-base-contrast-36"),children:"x"})]}),jsxRuntime.jsx(K,{Icon:ui.PlusIcon,onClick:e.onLeverageIncrease,disabled:e.isIncreaseDisabled})]})},q=e=>{let{currentLeverage:t}=e,{t:r}=i18n.useTranslation();return jsxRuntime.jsxs(ui.Flex,{itemAlign:"start",direction:"column",mb:0,children:[jsxRuntime.jsx(E,{currentLeverage:t}),jsxRuntime.jsx(O,{...e}),jsxRuntime.jsx(G,{...e}),jsxRuntime.jsx(W,{...e}),jsxRuntime.jsx(A,{...e})]})},A=e=>{let{t}=i18n.useTranslation();return jsxRuntime.jsxs(ui.Flex,{direction:"row",gap:2,width:"100%",mt:0,pt:5,children:[jsxRuntime.jsx(ui.Button,{variant:"contained",color:"gray",fullWidth:true,onClick:e.onCancel,"data-testid":"oui-testid-leverage-cancel-btn",size:e.isMobile?"md":"lg",children:t("common.cancel")}),jsxRuntime.jsx(ui.Button,{fullWidth:true,loading:e.isLoading,onClick:e.onSave,"data-testid":"oui-testid-leverage-save-btn",disabled:e.disabled,size:e.isMobile?"md":"lg",children:t("common.save")})]})},E=e=>{let{t}=i18n.useTranslation(),{currentLeverage:r}=e;return jsxRuntime.jsx(ui.Flex,{justify:"center",width:"100%",mb:2,children:jsxRuntime.jsxs(ui.Flex,{gap:1,children:[`${t("common.current")}:`,jsxRuntime.jsx(ui.Text.numeral,{unit:"x",size:"sm",intensity:80,dp:0,children:r??"--"})]})})},G=e=>{let{value:t,onLeverageChange:r}=e;return jsxRuntime.jsx(ui.Flex,{itemAlign:"center",justify:"between",width:"100%",mt:4,className:"oui-text-base-contrast-80",children:e.toggles.map(o=>jsxRuntime.jsx(ui.Flex,{itemAlign:"center",justify:"center",className:ui.cn("oui-box-border oui-cursor-pointer oui-rounded-md oui-border oui-border-solid oui-bg-clip-padding oui-px-3 oui-py-2.5 oui-transition-all",t===o?"oui-border-primary oui-bg-base-6":"oui-border-line-12"),onClick:()=>r?.(o),children:jsxRuntime.jsxs(ui.Flex,{itemAlign:"center",justify:"center",className:ui.cn("oui-h-3 oui-w-9 oui-select-none"),children:[o,"x"]})},o))})},Me=(e,t,r,o)=>{let s=100/(r-1)*(e-1);return t===0?Math.min(s+2,100):t===o-1?Math.max(s-3,0):s},W=e=>{let{leverageLevers:t,maxLeverage:r=0,className:o,value:u,showSliderTip:a,marks:n}=e,s=t.length>0?Math.max(...t):r;return jsxRuntime.jsxs(ui.Box,{pt:4,pb:7,width:"100%",className:o,children:[jsxRuntime.jsx(ui.Slider,{step:1,max:r,min:1,marks:n,value:[u],onValueChange:l=>{e.onLeverageChange(l[0]),e.setShowSliderTip(true);},color:"primary",onValueCommit:l=>{e.onValueCommit?.(l),e.setShowSliderTip(false);},showTip:a,tipFormatter:l=>`${l}x`}),jsxRuntime.jsx("div",{className:"oui-relative oui-w-full oui-pt-3",children:t?.map((l,C)=>{let m=Me(l,C,s,t.length);return jsxRuntime.jsx("button",{onClick:()=>{e.onLeverageChange(l),e.onValueCommit?.([l]);},className:ui.cn("oui-absolute oui-pb-3 oui-text-2xs oui-transform oui--translate-x-1/2",Number(e.value)>=Number(l)?"oui-text-primary-light":"oui-text-base-contrast-54"),style:{left:`${m}%`},"data-testid":`oui-testid-leverage-${l}-btn`,children:`${l}x`},l)})})]})};var z=e=>{let[t,r]=be.useState(false),{t:o}=i18n.useTranslation(),{curLeverage:u,maxLeverage:a,isLoading:n,leverageLevers:s,update:l}=hooks.useLeverage(),C=be.useMemo(()=>s.map(g=>({label:`${g}x`,value:g})),[s]),[m,L]=be.useState(u??0),P=100/((C?.length||0)-1),p=g=>{L(g);},R=()=>{L(g=>g+1);},S=()=>{L(g=>g-1);},F=be.useCallback(g=>{let T=Number.parseInt(g.target.value),H=Number.isNaN(T)?"":T;L(H);},[a]),d=async()=>{try{l({leverage:m}).then(()=>{e?.close?.(),ui.toast.success(o("leverage.updated"));},g=>{ui.toast.error(g.message);});}catch{}},f=m<=1,k=m>=a,x=!m||m<1||m>a,w=be.useMemo(()=>[5,10,20,50,100].filter(g=>g<=a),[a]);return {leverageLevers:s,currentLeverage:u,value:m,marks:C,onLeverageChange:p,onLeverageIncrease:R,onLeverageReduce:S,onInputChange:F,isReduceDisabled:f,isIncreaseDisabled:k,disabled:x,step:P,onCancel:e?.close,onSave:d,isLoading:n,showSliderTip:t,setShowSliderTip:r,maxLeverage:a,toggles:w}};var B=e=>{let t=z({close:e.close});return jsxRuntime.jsx(q,{...t})};var re=e=>{let{curLeverage:t=1,symbol:r,side:o}=e,[u,a]=be.useState(false),[n,s]=be.useState(t),{t:l}=i18n.useTranslation(),{isMobile:C}=ui.useScreen(),{maxLeverage:m,update:L,isLoading:P}=hooks.useSymbolLeverage(r),p=m,{position:R,maxPositionNotional:S,maxPositionLeverage:F,overMaxPositionLeverage:d,overRequiredMargin:f}=je({symbol:r,leverage:n,maxLeverage:p}),k=be.useMemo(()=>_e(p),[p]),x=be.useMemo(()=>Ue(p),[p]),w=be.useMemo(()=>x.map(v=>({label:`${v}x`,value:v}))||[],[x]),g=be.useMemo(()=>100/((w?.length||0)-1),[w]),T=v=>{s(v);},H=()=>{s(v=>v+1);},le=()=>{s(v=>v-1);},ue=be.useCallback(v=>{let U=Number.parseInt(v.target.value),Le=Number.isNaN(U)?"":U;s(Le);},[p]),ce=async()=>{try{L?.({leverage:n,symbol:r}).then(v=>{v.success?(e?.close?.(),ui.toast.success(l("leverage.updated"))):ui.toast.error(v.message);},v=>{ui.toast.error(v.message);});}catch{}},me=async()=>{ui.modal.confirm({title:l("leverage.confirm"),content:jsxRuntime.jsx(ui.Text,{intensity:54,children:l("leverage.confirm.content")}),onOk:ce,onCancel:()=>Promise.resolve()});},ge=n<=1,ve=n>=p,de=o?o===types.OrderSide.BUY:R?.position_qty&&R.position_qty>0,pe=!n||n<1||n>p||f||d;return {leverageLevers:x,currentLeverage:n,value:n,marks:w,onLeverageChange:T,onLeverageIncrease:H,onLeverageReduce:le,onInputChange:ue,isReduceDisabled:ge,isIncreaseDisabled:ve,disabled:pe,step:g,onCancel:e?.close,onSave:me,isLoading:P,showSliderTip:u,setShowSliderTip:a,maxLeverage:p,toggles:k,symbol:r,maxPositionNotional:S,maxPositionLeverage:F,overMaxPositionLeverage:d,overRequiredMargin:f,isBuy:de,isMobile:C}},_e=e=>{if(e===10)return [1,3,5,8,10];if(e===50)return [1,10,20,35,50];let t=1,r=5,o=(e-t)/(r-1),u=[];for(let a=0;a<r;a++)u.push(Math.floor(t+o*a));return u},$e=e=>{let t=[];if(e%5===0){let r=e/5;for(let o=0;o<6;o++){let u=r*o;t.push(u===0?1:u);}}else {t.push(1);let r=e*.25,o=e*.5,u=e*.75,a=Math.round(r),n=Math.round(o),s=Math.round(u);a>1&&a!==n&&t.push(a),n>1&&t.push(n),s>n&&s<e&&t.push(s),e>1&&t.push(e);}return t},Ue=e=>{switch(e){case 10:return [1,2,3,4,5,6,7,8,9,10];case 20:return [1,5,10,15,20];case 50:return [1,10,20,30,40,50];case 100:return [1,20,40,60,80,100]}let t=[];if(e<10)for(let r=1;r<=e;r++)t.push(r);else t.push(...$e(e));return t};function je(e){let{symbol:t,leverage:r,maxLeverage:o}=e,u=hooks.useSymbolsInfo(),{data:a}=hooks.useAccountInfo(),{data:n}=hooks.useMarkPricesStream(),{totalCollateral:s}=hooks.usePortfolio(),[l,C]=hooks.useLocalStorage("unPnlPriceBasis","markPrice"),[m]=hooks.usePositionStream("all",{calcMode:l}),L=be.useMemo(()=>{if(t&&m?.rows?.length)return m.rows.find(d=>d.symbol===t)},[m,t]),P=be.useMemo(()=>{let d=a?.imr_factor?.[t],f=L?.notional;if(d&&f){let k=perp.positions.maxPositionLeverage({IMRFactor:d,notional:f});return Math.min(k,o)}return o},[L,o,t]),p=be.useMemo(()=>{let d=a?.imr_factor?.[t];if(r&&d)return perp.positions.maxPositionNotional({leverage:r,IMRFactor:d})},[r,t]),R=be.useMemo(()=>r>P,[r,P]),S=be.useMemo(()=>{if(!a||!n||!u)return utils.zero;let d=r?m?.rows.map(x=>x.symbol===t?{...x,leverage:r}:x):m?.rows,f=perp.account.totalInitialMarginWithQty({positions:d,markPrices:n,IMR_Factors:a.imr_factor,maxLeverage:a.max_leverage,symbolInfo:u});return perp.account.freeCollateral({totalCollateral:s,totalInitialMarginWithOrders:f})},[m,u,a,n,s,r,t]),F=be.useMemo(()=>S.eq(0)||S.isNegative(),[S]);return {position:L,freeCollateral:S,maxPositionNotional:p,maxPositionLeverage:P,overMaxPositionLeverage:R,overRequiredMargin:F}}var ne=e=>{let{t}=i18n.useTranslation();return jsxRuntime.jsxs("div",{className:"oui-flex oui-flex-col oui-gap-3 lg:oui-gap-4",children:[jsxRuntime.jsxs("div",{className:"oui-flex oui-items-center oui-gap-2",children:[jsxRuntime.jsx(ui.TokenIcon,{symbol:e.symbol,className:"oui-size-5"}),jsxRuntime.jsx(ui.Text.formatted,{rule:"symbol",formatString:"base-type",size:e.isMobile?"xs":"base",weight:"semibold",intensity:98,children:e.symbol}),jsxRuntime.jsxs("div",{className:ui.cn(["oui-ml-auto oui-flex oui-items-center oui-gap-1"]),children:[jsxRuntime.jsx(ui.Badge,{color:e.isBuy?"success":"danger",size:"xs",children:e.isBuy?t("common.long"):t("common.short")}),jsxRuntime.jsx(Ze,{leverage:e.currentLeverage})]})]}),jsxRuntime.jsx(ui.Divider,{}),jsxRuntime.jsxs(ui.Flex,{itemAlign:"start",direction:"column",mb:0,children:[jsxRuntime.jsx(E,{currentLeverage:e.currentLeverage}),jsxRuntime.jsx(O,{...e}),jsxRuntime.jsx(G,{...e}),jsxRuntime.jsx(W,{...e}),jsxRuntime.jsx(ui.Divider,{className:"oui-mb-3 oui-w-full"}),jsxRuntime.jsxs("div",{className:"oui-flex oui-flex-col oui-gap-1 oui-pb-4 oui-text-xs oui-font-normal oui-text-base-contrast-54",children:[jsxRuntime.jsx("div",{children:jsxRuntime.jsx(i18n.Trans,{i18nKey:"leverage.maxAvailableLeverage.tips",values:{leverage:e.maxPositionLeverage},components:[jsxRuntime.jsx(ui.Text.numeral,{dp:0,suffix:"x",as:"span",className:"oui-text-base-contrast"},"0")]})}),jsxRuntime.jsx("div",{children:t("leverage.actualPositionLeverage.tips")})]}),jsxRuntime.jsxs("div",{className:ui.cn(["-oui-mb-2",e.overRequiredMargin||e.overMaxPositionLeverage?"oui-block oui-text-xs oui-font-normal":"oui-hidden"]),children:[e.overRequiredMargin&&jsxRuntime.jsx("div",{children:jsxRuntime.jsx(ui.Text,{color:"warning",children:t("leverage.overRequiredMargin.tips")})}),e.overMaxPositionLeverage&&jsxRuntime.jsx("div",{children:jsxRuntime.jsx(ui.Text,{color:"warning",children:jsxRuntime.jsx(i18n.Trans,{i18nKey:"leverage.overMaxPositionLeverage.tips",values:{leverage:e.maxPositionLeverage},components:[jsxRuntime.jsx(ui.Text.numeral,{dp:0,suffix:"X",as:"span"},"0")]})})})]}),jsxRuntime.jsx(A,{...e})]})]})},Ze=({leverage:e})=>jsxRuntime.jsxs("div",{className:ui.cn("oui-flex oui-h-[18px] oui-items-center oui-gap-1","oui-cursor-pointer oui-rounded oui-bg-line-6 oui-px-2","oui-text-2xs oui-font-semibold oui-text-base-contrast-36"),children:[jsxRuntime.jsx(ui.Text,{children:"Cross"}),jsxRuntime.jsx(ui.Text.numeral,{dp:0,size:"2xs",unit:"X",children:e})]});var V=e=>{let t=re(e);return jsxRuntime.jsx(ne,{...t})};var ot="SymbolLeverageSheetId",at="SymbolLeverageDialogId";ui.registerSimpleSheet(ot,V,{title:()=>i18n.i18n.t("leverage.adjustedLeverage"),classNames:{}});ui.registerSimpleDialog(at,V,{title:()=>i18n.i18n.t("leverage.adjustedLeverage"),classNames:{content:"oui-w-[420px]"}});var st="LeverageWidgetWithDialog",lt="LeverageWidgetWithSheet";ui.registerSimpleDialog(st,B,{title:()=>i18n.i18n.t("leverage.maxAccountLeverage"),size:"md"});ui.registerSimpleSheet(lt,B,{title:()=>i18n.i18n.t("leverage.maxAccountLeverage")});
16
+ // src/index.ts
17
+ var IconButton = (props) => {
18
+ const { Icon, onClick, disabled } = props;
19
+ return /* @__PURE__ */ jsxRuntime.jsx(
20
+ Icon,
21
+ {
22
+ onClick: disabled ? void 0 : onClick,
23
+ className: ui.cn(
24
+ "oui-m-2 oui-text-white oui-transition-all",
25
+ disabled ? "oui-cursor-not-allowed oui-opacity-20" : "oui-cursor-pointer oui-opacity-100"
26
+ )
27
+ }
28
+ );
29
+ };
30
+ var LeverageInput = (props) => {
31
+ const formatters = React__default.default.useMemo(
32
+ () => [ui.inputFormatter.numberFormatter, ui.inputFormatter.dpFormatter(0)],
33
+ []
34
+ );
35
+ const id = React.useId();
36
+ return /* @__PURE__ */ jsxRuntime.jsxs(
37
+ "label",
38
+ {
39
+ htmlFor: id,
40
+ className: ui.cn(
41
+ "oui-w-full",
42
+ "oui-rounded",
43
+ "oui-bg-base-6",
44
+ "oui-flex",
45
+ "oui-items-center",
46
+ "oui-justify-between",
47
+ "oui-outline",
48
+ "oui-outline-offset-0",
49
+ "oui-outline-1",
50
+ "oui-outline-transparent",
51
+ "focus-within:oui-outline-primary-light",
52
+ "oui-input-root"
53
+ ),
54
+ children: [
55
+ /* @__PURE__ */ jsxRuntime.jsx(
56
+ IconButton,
57
+ {
58
+ Icon: ui.ReduceIcon,
59
+ onClick: props.onLeverageReduce,
60
+ disabled: props.isReduceDisabled
61
+ }
62
+ ),
63
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "center", justify: "center", className: "oui-mr-4", children: [
64
+ /* @__PURE__ */ jsxRuntime.jsx(
65
+ ui.Input,
66
+ {
67
+ value: props.value,
68
+ id,
69
+ autoComplete: "off",
70
+ classNames: {
71
+ input: ui.cn("oui-text-right oui-text-[24px]"),
72
+ root: ui.cn(
73
+ "oui-w-12",
74
+ "oui-px-0",
75
+ "oui-outline",
76
+ "oui-outline-offset-0",
77
+ "oui-outline-1",
78
+ "oui-outline-transparent",
79
+ "focus-within:oui-outline-primary-none"
80
+ )
81
+ },
82
+ formatters,
83
+ onChange: props.onInputChange
84
+ }
85
+ ),
86
+ /* @__PURE__ */ jsxRuntime.jsx(
87
+ "div",
88
+ {
89
+ className: ui.cn(
90
+ "oui-ml-1 oui-mt-1 oui-select-none",
91
+ "oui-text-base oui-text-base-contrast-36"
92
+ ),
93
+ children: "x"
94
+ }
95
+ )
96
+ ] }),
97
+ /* @__PURE__ */ jsxRuntime.jsx(
98
+ IconButton,
99
+ {
100
+ Icon: ui.PlusIcon,
101
+ onClick: props.onLeverageIncrease,
102
+ disabled: props.isIncreaseDisabled
103
+ }
104
+ )
105
+ ]
106
+ }
107
+ );
108
+ };
109
+ var Leverage = (props) => {
110
+ const { currentLeverage } = props;
111
+ const { t } = i18n.useTranslation();
112
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "start", direction: "column", mb: 0, children: [
113
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageHeader, { currentLeverage }),
114
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageInput, { ...props }),
115
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageSelector, { ...props }),
116
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageSlider, { ...props }),
117
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageFooter, { ...props })
118
+ ] });
119
+ };
120
+ var LeverageFooter = (props) => {
121
+ const { t } = i18n.useTranslation();
122
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "row", gap: 2, width: "100%", mt: 0, pt: 5, children: [
123
+ /* @__PURE__ */ jsxRuntime.jsx(
124
+ ui.Button,
125
+ {
126
+ variant: "contained",
127
+ color: "gray",
128
+ fullWidth: true,
129
+ onClick: props.onCancel,
130
+ "data-testid": "oui-testid-leverage-cancel-btn",
131
+ size: props.isMobile ? "md" : "lg",
132
+ children: t("common.cancel")
133
+ }
134
+ ),
135
+ /* @__PURE__ */ jsxRuntime.jsx(
136
+ ui.Button,
137
+ {
138
+ fullWidth: true,
139
+ loading: props.isLoading,
140
+ onClick: props.onSave,
141
+ "data-testid": "oui-testid-leverage-save-btn",
142
+ disabled: props.disabled,
143
+ size: props.isMobile ? "md" : "lg",
144
+ children: t("common.save")
145
+ }
146
+ )
147
+ ] });
148
+ };
149
+ var LeverageHeader = (props) => {
150
+ const { t } = i18n.useTranslation();
151
+ const { currentLeverage } = props;
152
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "center", width: "100%", mb: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 1, children: [
153
+ `${t("common.current")}:`,
154
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text.numeral, { unit: "x", size: "sm", intensity: 80, dp: 0, children: currentLeverage ?? "--" })
155
+ ] }) });
156
+ };
157
+ var LeverageSelector = (props) => {
158
+ const { value, onLeverageChange } = props;
159
+ return /* @__PURE__ */ jsxRuntime.jsx(
160
+ ui.Flex,
161
+ {
162
+ itemAlign: "center",
163
+ justify: "between",
164
+ width: "100%",
165
+ mt: 4,
166
+ className: "oui-text-base-contrast-80",
167
+ children: props.toggles.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
168
+ ui.Flex,
169
+ {
170
+ itemAlign: "center",
171
+ justify: "center",
172
+ className: ui.cn(
173
+ `oui-box-border oui-cursor-pointer oui-rounded-md oui-border oui-border-solid oui-bg-clip-padding oui-px-3 oui-py-2.5 oui-transition-all`,
174
+ value === option ? "oui-border-primary oui-bg-base-6" : "oui-border-line-12"
175
+ ),
176
+ onClick: () => onLeverageChange?.(option),
177
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
178
+ ui.Flex,
179
+ {
180
+ itemAlign: "center",
181
+ justify: "center",
182
+ className: ui.cn(`oui-h-3 oui-w-9 oui-select-none`),
183
+ children: [
184
+ option,
185
+ "x"
186
+ ]
187
+ }
188
+ )
189
+ },
190
+ option
191
+ ))
192
+ }
193
+ );
194
+ };
195
+ var getMarkPosition = (item, index, max, total) => {
196
+ const min = 1;
197
+ const maxSteps = max - min;
198
+ const percentPerStep = 100 / maxSteps;
199
+ const position = percentPerStep * (item - min);
200
+ if (index === 0)
201
+ return Math.min(position + 2, 100);
202
+ if (index === total - 1)
203
+ return Math.max(position - 3, 0);
204
+ return position;
205
+ };
206
+ var LeverageSlider = (props) => {
207
+ const {
208
+ leverageLevers,
209
+ maxLeverage = 0,
210
+ className,
211
+ value,
212
+ showSliderTip,
213
+ marks
214
+ } = props;
215
+ const sliderMax = leverageLevers.length > 0 ? Math.max(...leverageLevers) : maxLeverage;
216
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { pt: 4, pb: 7, width: "100%", className, children: [
217
+ /* @__PURE__ */ jsxRuntime.jsx(
218
+ ui.Slider,
219
+ {
220
+ step: 1,
221
+ max: maxLeverage,
222
+ min: 1,
223
+ marks,
224
+ value: [value],
225
+ onValueChange: (e) => {
226
+ props.onLeverageChange(e[0]);
227
+ props.setShowSliderTip(true);
228
+ },
229
+ color: "primary",
230
+ onValueCommit: (e) => {
231
+ props.onValueCommit?.(e);
232
+ props.setShowSliderTip(false);
233
+ },
234
+ showTip: showSliderTip,
235
+ tipFormatter: (value2) => {
236
+ return `${value2}x`;
237
+ }
238
+ }
239
+ ),
240
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oui-relative oui-w-full oui-pt-3", children: leverageLevers?.map((item, index) => {
241
+ const position = getMarkPosition(
242
+ item,
243
+ index,
244
+ sliderMax,
245
+ leverageLevers.length
246
+ );
247
+ return /* @__PURE__ */ jsxRuntime.jsx(
248
+ "button",
249
+ {
250
+ onClick: () => {
251
+ props.onLeverageChange(item);
252
+ props.onValueCommit?.([item]);
253
+ },
254
+ className: ui.cn(
255
+ "oui-absolute oui-pb-3 oui-text-2xs oui-transform oui--translate-x-1/2",
256
+ Number(props.value) >= Number(item) ? "oui-text-primary-light" : "oui-text-base-contrast-54"
257
+ ),
258
+ style: {
259
+ left: `${position}%`
260
+ },
261
+ "data-testid": `oui-testid-leverage-${item}-btn`,
262
+ children: `${item}x`
263
+ },
264
+ item
265
+ );
266
+ }) })
267
+ ] });
268
+ };
269
+ var useLeverageScript = (options) => {
270
+ const [showSliderTip, setShowSliderTip] = React.useState(false);
271
+ const { t } = i18n.useTranslation();
272
+ const { curLeverage, maxLeverage, isLoading, leverageLevers, update } = hooks.useLeverage();
273
+ const marks = React.useMemo(() => {
274
+ return leverageLevers.map((e) => ({
275
+ label: `${e}x`,
276
+ value: e
277
+ }));
278
+ }, [leverageLevers]);
279
+ const [leverage, setLeverage] = React.useState(curLeverage ?? 0);
280
+ const step = 100 / ((marks?.length || 0) - 1);
281
+ const onLeverageChange = (leverage2) => {
282
+ setLeverage(leverage2);
283
+ };
284
+ const onLeverageIncrease = () => {
285
+ setLeverage((prev) => prev + 1);
286
+ };
287
+ const onLeverageReduce = () => {
288
+ setLeverage((prev) => prev - 1);
289
+ };
290
+ const onInputChange = React.useCallback(
291
+ (e) => {
292
+ const parsed = Number.parseInt(e.target.value);
293
+ const value = Number.isNaN(parsed) ? "" : parsed;
294
+ setLeverage(value);
295
+ },
296
+ [maxLeverage]
297
+ );
298
+ const onSave = async () => {
299
+ try {
300
+ update({ leverage }).then(
301
+ () => {
302
+ options?.close?.();
303
+ ui.toast.success(t("leverage.updated"));
304
+ },
305
+ (err) => {
306
+ ui.toast.error(err.message);
307
+ }
308
+ );
309
+ } catch (err) {
310
+ }
311
+ };
312
+ const isReduceDisabled = leverage <= 1;
313
+ const isIncreaseDisabled = leverage >= maxLeverage;
314
+ const disabled = !leverage || leverage < 1 || leverage > maxLeverage;
315
+ const toggles = React.useMemo(() => {
316
+ return [5, 10, 20, 50, 100].filter((e) => e <= maxLeverage);
317
+ }, [maxLeverage]);
318
+ return {
319
+ leverageLevers,
320
+ currentLeverage: curLeverage,
321
+ value: leverage,
322
+ marks,
323
+ onLeverageChange,
324
+ onLeverageIncrease,
325
+ onLeverageReduce,
326
+ onInputChange,
327
+ isReduceDisabled,
328
+ isIncreaseDisabled,
329
+ disabled,
330
+ step,
331
+ onCancel: options?.close,
332
+ onSave,
333
+ isLoading,
334
+ showSliderTip,
335
+ setShowSliderTip,
336
+ maxLeverage,
337
+ toggles
338
+ };
339
+ };
340
+ var LeverageEditor = (props) => {
341
+ const state = useLeverageScript({ close: props.close });
342
+ return /* @__PURE__ */ jsxRuntime.jsx(Leverage, { ...state });
343
+ };
344
+ var useSymbolLeverageScript = (options) => {
345
+ const { curLeverage = 1, symbol, side } = options;
346
+ const [showSliderTip, setShowSliderTip] = React.useState(false);
347
+ const [leverage, setLeverage] = React.useState(curLeverage);
348
+ const { t } = i18n.useTranslation();
349
+ const { isMobile } = ui.useScreen();
350
+ const {
351
+ maxLeverage: originalMaxLeverage,
352
+ update,
353
+ isLoading
354
+ } = hooks.useSymbolLeverage(symbol);
355
+ const maxLeverage = originalMaxLeverage;
356
+ const {
357
+ position,
358
+ maxPositionNotional,
359
+ maxPositionLeverage,
360
+ overMaxPositionLeverage,
361
+ overRequiredMargin
362
+ } = useCalc({ symbol, leverage, maxLeverage });
363
+ const formattedLeverageLevers = React.useMemo(() => {
364
+ return generateLeverageLeversForSelector(maxLeverage);
365
+ }, [maxLeverage]);
366
+ const leverageLevers = React.useMemo(() => {
367
+ return generateLeverageLevers(maxLeverage);
368
+ }, [maxLeverage]);
369
+ const marks = React.useMemo(() => {
370
+ return leverageLevers.map((e) => ({
371
+ label: `${e}x`,
372
+ value: e
373
+ })) || [];
374
+ }, [leverageLevers]);
375
+ const step = React.useMemo(() => {
376
+ return 100 / ((marks?.length || 0) - 1);
377
+ }, [marks]);
378
+ const onLeverageChange = (leverage2) => {
379
+ setLeverage(leverage2);
380
+ };
381
+ const onLeverageIncrease = () => {
382
+ setLeverage((prev) => prev + 1);
383
+ };
384
+ const onLeverageReduce = () => {
385
+ setLeverage((prev) => prev - 1);
386
+ };
387
+ const onInputChange = React.useCallback(
388
+ (e) => {
389
+ const parsed = Number.parseInt(e.target.value);
390
+ const value = Number.isNaN(parsed) ? "" : parsed;
391
+ setLeverage(value);
392
+ },
393
+ [maxLeverage]
394
+ );
395
+ const onConfirmSave = async () => {
396
+ try {
397
+ update?.({ leverage, symbol }).then(
398
+ (res) => {
399
+ if (res.success) {
400
+ options?.close?.();
401
+ ui.toast.success(t("leverage.updated"));
402
+ } else {
403
+ ui.toast.error(res.message);
404
+ }
405
+ },
406
+ (err) => {
407
+ ui.toast.error(err.message);
408
+ }
409
+ );
410
+ } catch (err) {
411
+ }
412
+ };
413
+ const onSave = async () => {
414
+ ui.modal.confirm({
415
+ title: t("leverage.confirm"),
416
+ content: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { intensity: 54, children: t("leverage.confirm.content") }),
417
+ onOk: onConfirmSave,
418
+ onCancel: () => {
419
+ return Promise.resolve();
420
+ }
421
+ });
422
+ };
423
+ const isReduceDisabled = leverage <= 1;
424
+ const isIncreaseDisabled = leverage >= maxLeverage;
425
+ const isBuy = side ? side === types.OrderSide.BUY : position?.position_qty && position.position_qty > 0;
426
+ const disabled = !leverage || leverage < 1 || leverage > maxLeverage || overRequiredMargin || overMaxPositionLeverage;
427
+ return {
428
+ leverageLevers,
429
+ currentLeverage: leverage,
430
+ value: leverage,
431
+ marks,
432
+ onLeverageChange,
433
+ onLeverageIncrease,
434
+ onLeverageReduce,
435
+ onInputChange,
436
+ isReduceDisabled,
437
+ isIncreaseDisabled,
438
+ disabled,
439
+ step,
440
+ onCancel: options?.close,
441
+ onSave,
442
+ isLoading,
443
+ showSliderTip,
444
+ setShowSliderTip,
445
+ maxLeverage,
446
+ toggles: formattedLeverageLevers,
447
+ symbol,
448
+ maxPositionNotional,
449
+ maxPositionLeverage,
450
+ overMaxPositionLeverage,
451
+ overRequiredMargin,
452
+ isBuy,
453
+ isMobile
454
+ };
455
+ };
456
+ var generateLeverageLeversForSelector = (max) => {
457
+ if (max === 10) {
458
+ return [1, 3, 5, 8, 10];
459
+ } else if (max === 50) {
460
+ return [1, 10, 20, 35, 50];
461
+ }
462
+ const min = 1;
463
+ const parts = 5;
464
+ const step = (max - min) / (parts - 1);
465
+ const result = [];
466
+ for (let i = 0; i < parts; i++) {
467
+ result.push(Math.floor(min + step * i));
468
+ }
469
+ return result;
470
+ };
471
+ var generateEvenlyDistributedMarks = (max) => {
472
+ const result = [];
473
+ if (max % 5 === 0) {
474
+ const step = max / 5;
475
+ for (let i = 0; i < 6; i++) {
476
+ const value = step * i;
477
+ result.push(value === 0 ? 1 : value);
478
+ }
479
+ } else {
480
+ result.push(1);
481
+ const quarter = max * 0.25;
482
+ const half = max * 0.5;
483
+ const threeQuarter = max * 0.75;
484
+ const quarterRounded = Math.round(quarter);
485
+ const halfRounded = Math.round(half);
486
+ const threeQuarterRounded = Math.round(threeQuarter);
487
+ if (quarterRounded > 1 && quarterRounded !== halfRounded) {
488
+ result.push(quarterRounded);
489
+ }
490
+ if (halfRounded > 1) {
491
+ result.push(halfRounded);
492
+ }
493
+ if (threeQuarterRounded > halfRounded && threeQuarterRounded < max) {
494
+ result.push(threeQuarterRounded);
495
+ }
496
+ if (max > 1) {
497
+ result.push(max);
498
+ }
499
+ }
500
+ return result;
501
+ };
502
+ var generateLeverageLevers = (max) => {
503
+ switch (max) {
504
+ case 10:
505
+ return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
506
+ case 20:
507
+ return [1, 5, 10, 15, 20];
508
+ case 50:
509
+ return [1, 10, 20, 30, 40, 50];
510
+ case 100:
511
+ return [1, 20, 40, 60, 80, 100];
512
+ }
513
+ const result = [];
514
+ if (max < 10) {
515
+ for (let i = 1; i <= max; i++) {
516
+ result.push(i);
517
+ }
518
+ } else {
519
+ result.push(...generateEvenlyDistributedMarks(max));
520
+ }
521
+ return result;
522
+ };
523
+ function useCalc(inputs) {
524
+ const { symbol, leverage, maxLeverage } = inputs;
525
+ const symbolsInfo = hooks.useSymbolsInfo();
526
+ const { data: accountInfo } = hooks.useAccountInfo();
527
+ const { data: markPrices } = hooks.useMarkPricesStream();
528
+ const { totalCollateral } = hooks.usePortfolio();
529
+ const [unPnlPriceBasis, setUnPnlPriceBasic] = hooks.useLocalStorage(
530
+ "unPnlPriceBasis",
531
+ "markPrice"
532
+ );
533
+ const [positions] = hooks.usePositionStream("all", {
534
+ calcMode: unPnlPriceBasis
535
+ });
536
+ const position = React.useMemo(() => {
537
+ if (symbol && positions?.rows?.length) {
538
+ return positions.rows.find((item) => item.symbol === symbol);
539
+ }
540
+ }, [positions, symbol]);
541
+ const maxPositionLeverage = React.useMemo(() => {
542
+ const IMRFactor = accountInfo?.imr_factor?.[symbol];
543
+ const notional = position?.notional;
544
+ if (IMRFactor && notional) {
545
+ const maxPositionLeverage2 = perp.positions.maxPositionLeverage({
546
+ IMRFactor,
547
+ notional
548
+ });
549
+ return Math.min(maxPositionLeverage2, maxLeverage);
550
+ }
551
+ return maxLeverage;
552
+ }, [position, maxLeverage, symbol]);
553
+ const maxPositionNotional = React.useMemo(() => {
554
+ const IMRFactor = accountInfo?.imr_factor?.[symbol];
555
+ if (leverage && IMRFactor) {
556
+ return perp.positions.maxPositionNotional({
557
+ leverage,
558
+ IMRFactor
559
+ });
560
+ }
561
+ }, [leverage, symbol]);
562
+ const overMaxPositionLeverage = React.useMemo(() => {
563
+ return leverage > maxPositionLeverage;
564
+ }, [leverage, maxPositionLeverage]);
565
+ const freeCollateral = React.useMemo(() => {
566
+ if (!accountInfo || !markPrices || !symbolsInfo) {
567
+ return utils.zero;
568
+ }
569
+ const positionList = leverage ? positions?.rows.map((item) => {
570
+ if (item.symbol === symbol) {
571
+ return {
572
+ ...item,
573
+ leverage
574
+ };
575
+ }
576
+ return item;
577
+ }) : positions?.rows;
578
+ const totalInitialMarginWithOrders = perp.account.totalInitialMarginWithQty({
579
+ positions: positionList,
580
+ markPrices,
581
+ IMR_Factors: accountInfo.imr_factor,
582
+ // not used
583
+ maxLeverage: accountInfo.max_leverage,
584
+ symbolInfo: symbolsInfo
585
+ });
586
+ const freeCollateral2 = perp.account.freeCollateral({
587
+ totalCollateral,
588
+ totalInitialMarginWithOrders
589
+ });
590
+ return freeCollateral2;
591
+ }, [
592
+ positions,
593
+ symbolsInfo,
594
+ accountInfo,
595
+ markPrices,
596
+ totalCollateral,
597
+ leverage,
598
+ symbol
599
+ ]);
600
+ const overRequiredMargin = React.useMemo(() => {
601
+ return freeCollateral.eq(0) || freeCollateral.isNegative();
602
+ }, [freeCollateral]);
603
+ return {
604
+ position,
605
+ freeCollateral,
606
+ maxPositionNotional,
607
+ maxPositionLeverage,
608
+ overMaxPositionLeverage,
609
+ overRequiredMargin
610
+ };
611
+ }
612
+ var SymbolLeverage = (props) => {
613
+ const { t } = i18n.useTranslation();
614
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-flex oui-flex-col oui-gap-3 lg:oui-gap-4", children: [
615
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-flex oui-items-center oui-gap-2", children: [
616
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TokenIcon, { symbol: props.symbol, className: "oui-size-5" }),
617
+ /* @__PURE__ */ jsxRuntime.jsx(
618
+ ui.Text.formatted,
619
+ {
620
+ rule: "symbol",
621
+ formatString: "base-type",
622
+ size: props.isMobile ? "xs" : "base",
623
+ weight: "semibold",
624
+ intensity: 98,
625
+ children: props.symbol
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsxRuntime.jsxs(
629
+ "div",
630
+ {
631
+ className: ui.cn(["oui-ml-auto oui-flex oui-items-center oui-gap-1"]),
632
+ children: [
633
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: props.isBuy ? "success" : "danger", size: "xs", children: props.isBuy ? t("common.long") : t("common.short") }),
634
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageBadge, { leverage: props.currentLeverage })
635
+ ]
636
+ }
637
+ )
638
+ ] }),
639
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, {}),
640
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { itemAlign: "start", direction: "column", mb: 0, children: [
641
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageHeader, { currentLeverage: props.currentLeverage }),
642
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageInput, { ...props }),
643
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageSelector, { ...props }),
644
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageSlider, { ...props }),
645
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Divider, { className: "oui-mb-3 oui-w-full" }),
646
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oui-flex oui-flex-col oui-gap-1 oui-pb-4 oui-text-xs oui-font-normal oui-text-base-contrast-54", children: [
647
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
648
+ i18n.Trans,
649
+ {
650
+ i18nKey: "leverage.maxAvailableLeverage.tips",
651
+ values: { leverage: props.maxPositionLeverage },
652
+ components: [
653
+ // @ts-ignore
654
+ /* @__PURE__ */ jsxRuntime.jsx(
655
+ ui.Text.numeral,
656
+ {
657
+ dp: 0,
658
+ suffix: "x",
659
+ as: "span",
660
+ className: "oui-text-base-contrast"
661
+ },
662
+ "0"
663
+ )
664
+ ]
665
+ }
666
+ ) }),
667
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: t("leverage.actualPositionLeverage.tips") })
668
+ ] }),
669
+ /* @__PURE__ */ jsxRuntime.jsxs(
670
+ "div",
671
+ {
672
+ className: ui.cn([
673
+ "-oui-mb-2",
674
+ props.overRequiredMargin || props.overMaxPositionLeverage ? "oui-block oui-text-xs oui-font-normal" : "oui-hidden"
675
+ ]),
676
+ children: [
677
+ props.overRequiredMargin && /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { color: "warning", children: t("leverage.overRequiredMargin.tips") }) }),
678
+ props.overMaxPositionLeverage && /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { color: "warning", children: /* @__PURE__ */ jsxRuntime.jsx(
679
+ i18n.Trans,
680
+ {
681
+ i18nKey: "leverage.overMaxPositionLeverage.tips",
682
+ values: { leverage: props.maxPositionLeverage },
683
+ components: [
684
+ // @ts-ignore
685
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text.numeral, { dp: 0, suffix: "X", as: "span" }, "0")
686
+ ]
687
+ }
688
+ ) }) })
689
+ ]
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsxRuntime.jsx(LeverageFooter, { ...props })
693
+ ] })
694
+ ] });
695
+ };
696
+ var LeverageBadge = ({ leverage }) => {
697
+ return /* @__PURE__ */ jsxRuntime.jsxs(
698
+ "div",
699
+ {
700
+ className: ui.cn(
701
+ "oui-flex oui-h-[18px] oui-items-center oui-gap-1",
702
+ "oui-cursor-pointer oui-rounded oui-bg-line-6 oui-px-2",
703
+ "oui-text-2xs oui-font-semibold oui-text-base-contrast-36"
704
+ ),
705
+ children: [
706
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Cross" }),
707
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text.numeral, { dp: 0, size: "2xs", unit: "X", children: leverage })
708
+ ]
709
+ }
710
+ );
711
+ };
712
+ var SymbolLeverageWidget = (props) => {
713
+ const state = useSymbolLeverageScript(props);
714
+ return /* @__PURE__ */ jsxRuntime.jsx(SymbolLeverage, { ...state });
715
+ };
17
716
 
18
- exports.Leverage = q;
19
- exports.LeverageEditor = B;
20
- exports.LeverageHeader = E;
21
- exports.LeverageSlider = W;
22
- exports.LeverageWidgetWithDialogId = st;
23
- exports.LeverageWidgetWithSheetId = lt;
24
- exports.SymbolLeverageDialogId = at;
25
- exports.SymbolLeverageSheetId = ot;
26
- exports.SymbolLeverageWidget = V;
27
- exports.useLeverageScript = z;
717
+ // src/symbolLeverage/index.ts
718
+ var SymbolLeverageSheetId = "SymbolLeverageSheetId";
719
+ var SymbolLeverageDialogId = "SymbolLeverageDialogId";
720
+ ui.registerSimpleSheet(SymbolLeverageSheetId, SymbolLeverageWidget, {
721
+ title: () => i18n.i18n.t("leverage.adjustedLeverage"),
722
+ classNames: {
723
+ // content: "oui-p-5",
724
+ }
725
+ });
726
+ ui.registerSimpleDialog(SymbolLeverageDialogId, SymbolLeverageWidget, {
727
+ title: () => i18n.i18n.t("leverage.adjustedLeverage"),
728
+ classNames: {
729
+ content: "oui-w-[420px]"
730
+ }
731
+ });
732
+
733
+ // src/index.ts
734
+ var LeverageWidgetWithDialogId = "LeverageWidgetWithDialog";
735
+ var LeverageWidgetWithSheetId = "LeverageWidgetWithSheet";
736
+ ui.registerSimpleDialog(LeverageWidgetWithDialogId, LeverageEditor, {
737
+ title: () => i18n.i18n.t("leverage.maxAccountLeverage"),
738
+ size: "md"
739
+ });
740
+ ui.registerSimpleSheet(LeverageWidgetWithSheetId, LeverageEditor, {
741
+ title: () => i18n.i18n.t("leverage.maxAccountLeverage")
742
+ });
743
+
744
+ exports.Leverage = Leverage;
745
+ exports.LeverageEditor = LeverageEditor;
746
+ exports.LeverageHeader = LeverageHeader;
747
+ exports.LeverageSlider = LeverageSlider;
748
+ exports.LeverageWidgetWithDialogId = LeverageWidgetWithDialogId;
749
+ exports.LeverageWidgetWithSheetId = LeverageWidgetWithSheetId;
750
+ exports.SymbolLeverageDialogId = SymbolLeverageDialogId;
751
+ exports.SymbolLeverageSheetId = SymbolLeverageSheetId;
752
+ exports.SymbolLeverageWidget = SymbolLeverageWidget;
753
+ exports.useLeverageScript = useLeverageScript;
28
754
  //# sourceMappingURL=out.js.map
29
755
  //# sourceMappingURL=index.js.map