Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .changeset/spicy-brooms-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
"@orderly.network/ui-order-entry": patch
"@orderly.network/ui-tradingview": patch
"@orderly.network/trading": patch
"@orderly.network/ai-docs": patch
"@orderly.network/storybook": patch
"@orderly.network/affiliate": patch
"@orderly.network/react-app": patch
"@orderly.network/chart": patch
"@orderly.network/devkit": patch
"@orderly.network/core": patch
"@orderly.network/default-evm-adapter": patch
"@orderly.network/default-solana-adapter": patch
"@orderly.network/hooks": patch
"@orderly.network/i18n": patch
"@orderly.network/layout-core": patch
"@orderly.network/layout-grid": patch
"@orderly.network/layout-split": patch
"@orderly.network/markets": patch
"@orderly.network/net": patch
"@orderly.network/perp": patch
"@orderly.network/plugin-core": patch
"@orderly.network/portfolio": patch
"@orderly.network/sdk-docs": patch
"storybook-theme-tool": patch
"@orderly.network/trading-leaderboard": patch
"@orderly.network/trading-next": patch
"@orderly.network/trading-rewards": patch
"tsconfig": patch
"@orderly.network/types": patch
"@orderly.network/ui": patch
"@orderly.network/ui-chain-selector": patch
"@orderly.network/ui-connector": patch
"@orderly.network/ui-leverage": patch
"@orderly.network/ui-notification": patch
"@orderly.network/ui-orders": patch
"@orderly.network/ui-positions": patch
"@orderly.network/ui-scaffold": patch
"@orderly.network/ui-share": patch
"@orderly.network/ui-tpsl": patch
"@orderly.network/ui-transfer": patch
"@orderly.network/utils": patch
"@orderly.network/vaults": patch
"@orderly.network/wallet-connector": patch
"@orderly.network/wallet-connector-privy": patch
"@orderly.network/web3-provider-ethers": patch
---

Add interceptor sockets so plugins can extend the trading screen without forking the SDK. Each is additive and defaults to passthrough, so behavior is unchanged when no plugin is installed.

- `Trading.OrderEntry.AdvancedSelect`: add a custom entry to the order-type Advanced dropdown. A value that isn't a real OrderType is routed via `onExtraSelect`/`selectedExtraId` instead of `order_type`.
- `Trading.OrderEntry.Body`: replace the order-entry form body with a custom panel while the type selector stays in place.
- `Trading.OrderEntry.BuySellSwitch`: new `selectedCustomTypeId` prop lets a plugin hide the Buy/Sell switch for its own type only.
- `Trading.OrderEntry.MobileTypeSelect`: add a custom entry to the mobile order-type dropdown (same routing model as `AdvancedSelect`; preserves the `marketOrderDisabled` modal).
- `Trading.Chart.Overlay`: render over the chart once the TradingView widget is ready (receives the live widget and current symbol).
- `Trading.DataList.Desktop.Tabs`: add custom tabs to the desktop data-list strip.
- `Trading.DataList.Mobile.Tabs`: add custom tabs to the mobile data-list strip.
- `Trading.Layout.Mobile`: mobile counterpart to `Trading.Layout.Desktop`. Lets a plugin mount a context provider (e.g. a grid-bot or basis-trade provider) above the mobile trading layout, so feature UI rendered by other interceptors (`OrderEntry.MobileTypeSelect`, `OrderEntry.Body`, `DataList.Mobile.Tabs`, `Chart.Overlay`) can read that context.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import { TabPanel, injectable } from "@orderly.network/ui";

/** Interceptor target for the desktop data-list tabs; plugins append custom tabs after the built-in ones. */
export const DataListDesktopTabsTarget = "Trading.DataList.Desktop.Tabs";

export interface DataListDesktopTabItem {
/** Unique tab id (also its `value` in the Tabs context). */
id: string;
title: React.ReactNode;
content: React.ReactNode;
}

export interface DataListDesktopTabsProps {
items: DataListDesktopTabItem[];
}

// Renders a TabPanel per item. TabPanel self-registers into the parent Tabs,
// so appended tabs show up as triggers next to the built-in ones.
const DataListDesktopTabs: React.FC<DataListDesktopTabsProps> = ({ items }) => (
<>
{items.map((tab) => (
<TabPanel key={tab.id} value={tab.id} title={tab.title}>
{tab.content}
</TabPanel>
))}
</>
);

export const InjectableDataListDesktopTabs =
injectable<DataListDesktopTabsProps>(
DataListDesktopTabs,
DataListDesktopTabsTarget,
);
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
PositionHistoryWidget,
PositionsWidget,
} from "@orderly.network/ui-positions";
import { InjectableDataListDesktopTabs } from "./dataList.injectable";
import { DataListState, DataListTabType } from "./dataList.script";

const LazySettingWidget = React.lazy(() =>
Expand Down Expand Up @@ -250,6 +251,10 @@ export const DataList: React.FC<DataListState> = (props) => {
</TabPanel>
);
})}
{/* Plugin-appendable tabs. Empty by default; a plugin intercepts
`Trading.DataList.Desktop.Tabs` and appends `{ id, title, content }`
entries, which self-register into the Tabs context above. */}
<InjectableDataListDesktopTabs items={[]} />
</Tabs>
);
};
6 changes: 6 additions & 0 deletions packages/trading/src/components/desktop/dataList/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export { DataList } from "./dataList.ui";
export { DataListWidget } from "./dataList.widget";
export {
DataListDesktopTabsTarget,
InjectableDataListDesktopTabs,
type DataListDesktopTabItem,
type DataListDesktopTabsProps,
} from "./dataList.injectable";
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";
import { TabPanel, injectable } from "@orderly.network/ui";

/** Interceptor target for the mobile data-list tabs; plugins append custom tabs after the built-in ones. */
export const DataListMobileTabsTarget = "Trading.DataList.Mobile.Tabs";

export interface DataListMobileTabItem {
/** Unique tab id (also its `value` in the Tabs context). */
id: string;
title: React.ReactNode;
content: React.ReactNode;
}

export interface DataListMobileTabsProps {
items: DataListMobileTabItem[];
}

// Renders a TabPanel per item. TabPanel self-registers into the parent Tabs,
// so appended tabs show up as triggers next to the built-in ones.
const DataListMobileTabs: React.FC<DataListMobileTabsProps> = ({ items }) => (
<>
{items.map((tab) => (
<TabPanel key={tab.id} value={tab.id} title={tab.title}>
{tab.content}
</TabPanel>
))}
</>
);

export const InjectableDataListMobileTabs = injectable<DataListMobileTabsProps>(
DataListMobileTabs,
DataListMobileTabsTarget,
);
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
MobilePositionsWidget,
} from "@orderly.network/ui-positions";
import { formatSymbol } from "@orderly.network/utils";
import { InjectableDataListMobileTabs } from "./dataList.injectable";
import {
type DataListState,
DataListTabSubType,
Expand Down Expand Up @@ -263,6 +264,7 @@ export const DataList: React.FC<DataListState & { className?: string }> = (
</TabPanel>
);
})}
<InjectableDataListMobileTabs items={[]} />
</Tabs>
);
};
6 changes: 6 additions & 0 deletions packages/trading/src/components/mobile/dataList/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export { DataList } from "./dataList.ui";
export { DataListWidget } from "./dataList.widget";
export { useDataListScript } from "./dataList.script";
export {
DataListMobileTabsTarget,
InjectableDataListMobileTabs,
type DataListMobileTabItem,
type DataListMobileTabsProps,
} from "./dataList.injectable";
6 changes: 6 additions & 0 deletions packages/trading/src/pages/trading/trading.injectable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { injectable } from "@orderly.network/ui";
import { DesktopLayout } from "./trading.ui.desktop";
import { MobileLayout } from "./trading.ui.mobile";

export const InjectableDesktopLayout = injectable(
DesktopLayout,
"Trading.Layout.Desktop",
);

export const InjectableMobileLayout = injectable(
MobileLayout,
"Trading.Layout.Mobile",
);
4 changes: 3 additions & 1 deletion packages/trading/src/pages/trading/trading.ui.mobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ const MaybeEqual: React.FC = () => {
);
};

export const MobileLayout: React.FC<TradingState> = (props) => {
export type MobileLayoutProps = TradingState;

export const MobileLayout: React.FC<MobileLayoutProps> = (props) => {
const { t } = useTranslation();

const { isRwa, open, closeTimeInterval } = useGetRwaSymbolInfo(props.symbol);
Expand Down
8 changes: 5 additions & 3 deletions packages/trading/src/pages/trading/trading.ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { FC, useEffect } from "react";
import { useSymbolsInfo } from "@orderly.network/hooks";
import { useTranslation } from "@orderly.network/i18n";
import { toast, useScreen } from "@orderly.network/ui";
import { InjectableDesktopLayout } from "./trading.injectable";
import {
InjectableDesktopLayout,
InjectableMobileLayout,
} from "./trading.injectable";
import type { TradingState } from "./trading.script";
import { MobileLayout } from "./trading.ui.mobile";

export const Trading: FC<TradingState> = (props) => {
const { isMobile } = useScreen();
Expand All @@ -29,7 +31,7 @@ export const Trading: FC<TradingState> = (props) => {
// }, [symbol, symbolsInfo, t]);

if (isMobile) {
return <MobileLayout {...props} />;
return <InjectableMobileLayout {...props} />;
}

return (
Expand Down
6 changes: 6 additions & 0 deletions packages/trading/src/types/interceptorTargets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@
*/
/// <reference types="@orderly.network/plugin-core" />
import type { SymbolInfoBarFullProps } from "@orderly.network/markets";
import type { DataListDesktopTabsProps } from "../components/desktop/dataList/dataList.injectable";
import type { Props as OrderBookDesktopAsksProps } from "../components/desktop/orderBook/asks.desktop";
import type { Props as OrderBookDesktopBidsProps } from "../components/desktop/orderBook/bids.desktop";
import type { AccountState } from "../components/mobile/bottomNavBar/account/account.script";
import type { DataListMobileTabsProps } from "../components/mobile/dataList/dataList.injectable";
import type { DesktopLayoutProps } from "../pages/trading/trading.ui.desktop";
import type { MobileLayoutProps } from "../pages/trading/trading.ui.mobile";

declare module "@orderly.network/plugin-core" {
interface InterceptorTargetPropsMap {
"Account.MobileAccountMenu": AccountState;
"OrderBook.Desktop.Asks": OrderBookDesktopAsksProps;
"OrderBook.Desktop.Bids": OrderBookDesktopBidsProps;
"Trading.DataList.Desktop.Tabs": DataListDesktopTabsProps;
"Trading.DataList.Mobile.Tabs": DataListMobileTabsProps;
"Trading.Layout.Desktop": DesktopLayoutProps;
"Trading.Layout.Mobile": MobileLayoutProps;
"Trading.SymbolInfoBar.Desktop": SymbolInfoBarFullProps;
}
}
6 changes: 6 additions & 0 deletions packages/ui-order-entry/src/components/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ type OrderEntryHeaderProps = {
marketOrderDisabled?: boolean;
/** Tooltip when hovering over the disabled Market button. */
marketOrderDisabledTooltip?: string;
/** Active custom order-type id (null for a real OrderType). */
selectedExtraId?: string | null;
onExtraSelect?: (id: string | null) => void;
};

export function OrderEntryHeader(props: OrderEntryHeaderProps) {
Expand All @@ -47,13 +50,16 @@ export function OrderEntryHeader(props: OrderEntryHeaderProps) {
}}
marketOrderDisabled={props.marketOrderDisabled}
marketOrderDisabledTooltip={props.marketOrderDisabledTooltip}
selectedExtraId={props.selectedExtraId}
onExtraSelect={props.onExtraSelect}
/>
<OrderEntryBuySellSwitchInjectabled
side={side}
canTrade={canTrade}
onSideChange={(nextSide: OrderSide) => {
props.setOrderValue("side", nextSide);
}}
selectedCustomTypeId={props.selectedExtraId}
/>
</>
);
Expand Down
Loading