diff --git a/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx b/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx index 5074642baa..3c45a3a833 100644 --- a/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx +++ b/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx @@ -107,13 +107,20 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { const onUserInputDispatch = useCallback( (typedValue: string) => { - const value = convertUsdToTokenValue(typedValue, isUsdValuesMode) + // Always pass through empty string to allow clearing + if (typedValue === '') { + setTypedValue('') + onUserInput(field, '') + return + } - setTypedValue(value) + const value = convertUsdToTokenValue(typedValue, isUsdValuesMode) + setTypedValue(typedValue) onUserInput(field, value) }, - [onUserInput, field, viewAmount, convertUsdToTokenValue, isUsdValuesMode], + [onUserInput, field, convertUsdToTokenValue, isUsdValuesMode], ) + const handleMaxInput = useCallback(() => { if (!maxBalance) { return @@ -125,16 +132,20 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { onUserInputDispatch(value.toExact()) setMaxSellTokensAnalytics() } - }, [maxBalance, onUserInputDispatch, convertUsdToTokenValue, isUsdValuesMode, maxBalanceUsdAmount]) + }, [maxBalance, onUserInputDispatch, isUsdValuesMode, maxBalanceUsdAmount]) useEffect(() => { - const areValuesSame = parseFloat(viewAmount) === parseFloat(typedValue) + // Compare the actual string values to preserve trailing decimals + if (viewAmount === typedValue) return + + // Don't override empty input + if (viewAmount === '' && typedValue === '') return - // Don't override typedValue when, for example: viewAmount = 5 and typedValue = 5. - if (areValuesSame) return + // Don't override when typing a decimal + if (typedValue.endsWith('.')) return - // Don't override typedValue, when viewAmount from props and typedValue are zero (0 or 0. or 0.000) - if (!viewAmount && (!typedValue || parseFloat(typedValue) === 0)) return + // Don't override when the values are numerically equal (e.g., "5." and "5") + if (parseFloat(viewAmount || '0') === parseFloat(typedValue || '0')) return setTypedValue(viewAmount) // We don't need triggering from typedValue changes @@ -151,7 +162,7 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { div { display: flex; - flex-flow: row wrap; align-items: center; color: inherit; } diff --git a/apps/cowswap-frontend/src/legacy/components/NumericalInput/index.tsx b/apps/cowswap-frontend/src/legacy/components/NumericalInput/index.tsx index 9cd1042f43..0b30181b1f 100644 --- a/apps/cowswap-frontend/src/legacy/components/NumericalInput/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/NumericalInput/index.tsx @@ -1,29 +1,40 @@ import React from 'react' -import { escapeRegExp } from '@cowprotocol/common-utils' import { UI } from '@cowprotocol/ui' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { autofocus } from 'common/utils/autofocus' -const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>` +const textStyle = css<{ error?: boolean; fontSize?: string }>` color: ${({ error }) => (error ? `var(${UI.COLOR_DANGER})` : 'inherit')}; + font-size: ${({ fontSize }) => fontSize ?? '28px'}; + font-weight: 500; +` + +const PrependSymbol = styled.span<{ error?: boolean; fontSize?: string }>` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + user-select: none; + ${textStyle} +` + +const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>` + ${textStyle} width: 0; position: relative; - font-weight: 500; outline: none; border: none; flex: 1 1 auto; background-color: var(${UI.COLOR_PAPER}); - font-size: ${({ fontSize }) => fontSize ?? '28px'}; - text-align: ${({ align }) => align && align}; + text-align: ${({ align }) => align || 'right'}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0px; appearance: textfield; - text-align: right; ::-webkit-search-decoration { -webkit-appearance: none; @@ -43,7 +54,8 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s } ` -const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group +// Allow decimal point at any position, including at the end +const inputRegex = /^(\d*\.?\d*)?$/ export const Input = React.memo(function InnerInput({ value, @@ -51,7 +63,6 @@ export const Input = React.memo(function InnerInput({ onUserInput, placeholder, prependSymbol, - type, onFocus, ...rest }: { @@ -63,51 +74,84 @@ export const Input = React.memo(function InnerInput({ align?: 'right' | 'left' prependSymbol?: string | undefined } & Omit, 'ref' | 'onChange' | 'as'>) { + // Keep the input strictly as a string + const stringValue = typeof value === 'string' ? value : String(value) + const enforcer = (nextUserInput: string) => { - if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) { - onUserInput(nextUserInput) + // Always allow empty input + if (nextUserInput === '') { + onUserInput('') + return + } + + // Convert commas to dots + const sanitizedValue = nextUserInput.replace(/,/g, '.') + + // Allow the value if it matches our number format + if (inputRegex.test(sanitizedValue)) { + onUserInput(sanitizedValue) + } + } + + const handlePaste = (event: React.ClipboardEvent) => { + const pastedText = event.clipboardData.getData('text') + + // Clean up pasted content - only allow numbers and single decimal + const cleanedText = pastedText + .replace(/,/g, '.') // Convert commas to dots + .replace(/[^\d.]/g, '') // Remove non-numeric/dot chars + .replace(/(\..*)\./g, '$1') // Keep only the first decimal point + .replace(/\.$/, '') // Remove trailing decimal point + + if (inputRegex.test(cleanedText)) { + event.preventDefault() + enforcer(cleanedText) } } return ( - { - autofocus(event) - onFocus?.(event) - }} - onChange={(event) => { - if (prependSymbol) { - const value = event.target.value - - // cut off prepended symbol - const formattedValue = value.toString().includes(prependSymbol) - ? value.toString().slice(1, value.toString().length + 1) - : value - - // replace commas with periods, because uniswap exclusively uses period as the decimal separator - enforcer(formattedValue.replace(/,/g, '.')) - } else { - enforcer(event.target.value.replace(/,/g, '.')) - } - }} - // universal input options - inputMode="decimal" - autoComplete="off" - autoCorrect="off" - // text-specific options - type={type || 'text'} - pattern="^[0-9]*[.,]?[0-9]*$" - placeholder={placeholder || '0.0'} - minLength={1} - maxLength={32} - spellCheck="false" - /> + <> + {prependSymbol && ( + + {prependSymbol} + + )} + { + autofocus(event) + onFocus?.(event) + }} + onChange={(event) => { + const rawValue = event.target.value + + if (prependSymbol) { + // Remove prepended symbol if it appears in rawValue + const formattedValue = rawValue.includes(prependSymbol) ? rawValue.slice(prependSymbol.length) : rawValue + enforcer(formattedValue) + } else { + enforcer(rawValue) + } + }} + onPaste={handlePaste} + // Use text inputMode so decimals can be typed + inputMode="decimal" + autoComplete="off" + autoCorrect="off" + // Keep type="text" to preserve trailing decimals + type="text" + // Remove pattern to prevent browser validation interference + pattern="" + placeholder={placeholder || '0'} + // minLength to 0 so empty strings are always valid + minLength={0} + maxLength={79} + spellCheck="false" + /> + ) }) export default Input - -// const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`) // match escaped "." characters via in a non-capturing group diff --git a/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/index.tsx b/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/index.tsx index e68bdaab89..249fd8fdb4 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/index.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/index.tsx @@ -74,21 +74,52 @@ export function RateInput() { // Handle rate input const handleUserInput = useCallback( (typedValue: string) => { + // Always pass through empty string to allow clearing + if (typedValue === '') { + setTypedTrailingZeros('') + updateLimitRateState({ typedValue: '' }) + updateRate({ + activeRate: null, + isTypedValue: true, + isRateFromUrl: false, + isAlternativeOrderRate: false, + }) + return + } + + // Keep the trailing decimal point or zeros const trailing = typedValue.slice(displayedRate.length) + const hasTrailingDecimal = typedValue.endsWith('.') const onlyTrailingZeroAdded = typedValue.includes('.') && /^0+$/.test(trailing) /** - * Since we convert USD to token value, we need to handle trailing zeros separately, otherwise we will lose them + * Since we convert USD to token value, we need to handle trailing zeros and decimal points separately. + * If we don't, they will be lost during the conversion between USD and token values. */ - if (onlyTrailingZeroAdded) { + if (hasTrailingDecimal || onlyTrailingZeroAdded) { setTypedTrailingZeros(trailing) + + // For trailing decimal, we also need to update the base value + if (hasTrailingDecimal && !onlyTrailingZeroAdded) { + const baseValue = typedValue.slice(0, -1) // Remove the trailing decimal for conversion + const value = convertUsdToTokenValue(baseValue, isUsdRateMode) + updateLimitRateState({ typedValue: value }) + updateRate({ + activeRate: toFraction(value, isInverted), + isTypedValue: true, + isRateFromUrl: false, + isAlternativeOrderRate: false, + }) + } return } setTypedTrailingZeros('') + // Convert to token value if in USD mode const value = convertUsdToTokenValue(typedValue, isUsdRateMode) + // Update the rate state with the new value updateLimitRateState({ typedValue: value }) updateRate({ activeRate: toFraction(value, isInverted), diff --git a/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/styled.ts b/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/styled.ts index c56abdaaa8..773ab8b8e5 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/styled.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/containers/RateInput/styled.ts @@ -82,7 +82,7 @@ export const Body = styled.div` justify-content: space-between; width: 100%; max-width: 100%; - gap: 8px; + gap: 0; padding: 12px 0 4px; color: inherit; ` @@ -109,6 +109,7 @@ export const CurrencyToggleGroup = styled.div` align-items: center; background: transparent; overflow: hidden; + margin: 0 0 0 8px; ` export const ActiveCurrency = styled.button<{ $active?: boolean }>` diff --git a/apps/cowswap-frontend/src/modules/limitOrders/containers/SettingsWidget/index.tsx b/apps/cowswap-frontend/src/modules/limitOrders/containers/SettingsWidget/index.tsx index f4222a4efc..a2495b34ff 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/containers/SettingsWidget/index.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/containers/SettingsWidget/index.tsx @@ -1,11 +1,8 @@ import { useAtomValue, useSetAtom } from 'jotai' -import UsdIcon from '@cowprotocol/assets/images/icon-USD.svg' - import { Menu, MenuItem, MenuPopover, MenuItems } from '@reach/menu-button' -import SVG from 'react-inlinesvg' -import { ButtonsContainer, SettingsButton, SettingsIcon, UsdButton } from 'modules/trade/pure/Settings' +import { ButtonsContainer, SettingsButton, SettingsIcon } from 'modules/trade/pure/Settings' import { Settings } from '../../pure/Settings' import { limitOrdersSettingsAtom, updateLimitOrdersSettingsAtom } from '../../state/limitOrdersSettingsAtom' @@ -13,13 +10,9 @@ import { limitOrdersSettingsAtom, updateLimitOrdersSettingsAtom } from '../../st export function SettingsWidget() { const settingsState = useAtomValue(limitOrdersSettingsAtom) const updateSettingsState = useSetAtom(updateLimitOrdersSettingsAtom) - const isUsdValuesMode = settingsState.isUsdValuesMode return ( - updateSettingsState({ isUsdValuesMode: !isUsdValuesMode })} active={isUsdValuesMode}> - - diff --git a/apps/cowswap-frontend/src/modules/limitOrders/pure/RateInput/HeadingText.tsx b/apps/cowswap-frontend/src/modules/limitOrders/pure/RateInput/HeadingText.tsx index 88ed3dee08..52b75d2e91 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/pure/RateInput/HeadingText.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/pure/RateInput/HeadingText.tsx @@ -40,13 +40,16 @@ const TextWrapper = styled.span<{ clickable: boolean }>` align-items: center; gap: 4px; cursor: ${({ clickable }) => (clickable ? 'pointer' : 'default')}; - transition: opacity var(${UI.ANIMATION_DURATION}) ease-in-out; + transition: + opacity var(${UI.ANIMATION_DURATION}) ease-in-out, + text-decoration-color var(${UI.ANIMATION_DURATION}) ease-in-out; + text-decoration: underline; + text-decoration-style: dashed; + text-decoration-thickness: 1px; + text-underline-offset: 2px; + text-decoration-color: var(${UI.COLOR_TEXT_OPACITY_25}); &:hover { - text-decoration: underline; - text-decoration-style: dashed; - text-decoration-thickness: 1px; - text-underline-offset: 2px; text-decoration-color: var(${UI.COLOR_TEXT_OPACITY_70}); } ` diff --git a/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.cosmos.tsx b/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.cosmos.tsx index b03535f199..b157546d70 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.cosmos.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.cosmos.tsx @@ -8,7 +8,6 @@ const defaultProps: SettingsProps = { customDeadlineTimestamp: null, limitPricePosition: 'between', limitPriceLocked: false, - columnLayout: 'DEFAULT', ordersTableOnLeft: false, isUsdValuesMode: false, }, diff --git a/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.tsx b/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.tsx index 4b756b6253..eda0a7226f 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/pure/Settings/index.tsx @@ -110,17 +110,16 @@ const POSITION_LABELS = { bottom: 'Bottom', } -const COLUMN_LAYOUT_LABELS = { - DEFAULT: 'Default view', - VIEW_2: 'Limit price / Fills at / Distance', - VIEW_3: 'Limit price / Fills at + Distance / Market', -} - export function Settings({ state, onStateChanged }: SettingsProps) { - const { showRecipient, partialFillsEnabled, limitPricePosition, limitPriceLocked, columnLayout, ordersTableOnLeft } = - state + const { + showRecipient, + partialFillsEnabled, + limitPricePosition, + limitPriceLocked, + ordersTableOnLeft, + isUsdValuesMode, + } = state const [isOpen, setIsOpen] = useState(false) - const [isColumnLayoutOpen, setIsColumnLayoutOpen] = useState(false) const handleSelect = useCallback( (value: LimitOrdersSettingsState['limitPricePosition']) => (e: React.MouseEvent) => { @@ -131,22 +130,11 @@ export function Settings({ state, onStateChanged }: SettingsProps) { [onStateChanged], ) - const handleColumnLayoutSelect = (value: LimitOrdersSettingsState['columnLayout']) => (e: React.MouseEvent) => { - e.stopPropagation() - onStateChanged({ columnLayout: value }) - setIsColumnLayoutOpen(false) - } - const toggleDropdown = (e: React.MouseEvent) => { e.stopPropagation() setIsOpen(!isOpen) } - const toggleColumnLayoutDropdown = (e: React.MouseEvent) => { - e.stopPropagation() - setIsColumnLayoutOpen(!isColumnLayoutOpen) - } - return ( Limit Order Settings @@ -182,6 +170,13 @@ export function Settings({ state, onStateChanged }: SettingsProps) { toggle={() => onStateChanged({ limitPriceLocked: !limitPriceLocked })} /> + onStateChanged({ isUsdValuesMode: !isUsdValuesMode })} + /> + - Limit Price Position - + Limit price position {POSITION_LABELS[limitPricePosition]} @@ -205,26 +199,6 @@ export function Settings({ state, onStateChanged }: SettingsProps) { - - - - Column Layout - - - - {COLUMN_LAYOUT_LABELS[columnLayout]} - - {Object.entries(COLUMN_LAYOUT_LABELS).map(([value, label]) => ( - - {label} - - ))} - - - ) } diff --git a/apps/cowswap-frontend/src/modules/limitOrders/state/limitOrdersSettingsAtom.ts b/apps/cowswap-frontend/src/modules/limitOrders/state/limitOrdersSettingsAtom.ts index 7f1d8c4b11..4f1a259712 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/state/limitOrdersSettingsAtom.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/state/limitOrdersSettingsAtom.ts @@ -12,8 +12,6 @@ import { alternativeOrderReadWriteAtomFactory, } from 'modules/trade/state/alternativeOrder' -export type ColumnLayoutType = 'DEFAULT' | 'VIEW_2' | 'VIEW_3' - export interface LimitOrdersSettingsState { readonly showRecipient: boolean readonly partialFillsEnabled: boolean @@ -21,7 +19,6 @@ export interface LimitOrdersSettingsState { readonly customDeadlineTimestamp: Timestamp | null readonly limitPricePosition: 'top' | 'between' | 'bottom' readonly limitPriceLocked: boolean - readonly columnLayout: ColumnLayoutType readonly ordersTableOnLeft: boolean readonly isUsdValuesMode: boolean } @@ -31,9 +28,8 @@ export const defaultLimitOrdersSettings: LimitOrdersSettingsState = { partialFillsEnabled: true, deadlineMilliseconds: defaultLimitOrderDeadline.value, customDeadlineTimestamp: null, - limitPricePosition: 'top', - limitPriceLocked: true, - columnLayout: 'DEFAULT', + limitPricePosition: 'bottom', + limitPriceLocked: false, ordersTableOnLeft: false, isUsdValuesMode: false, } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts index 853aab2b91..dadde89121 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react' import { Order, PENDING_STATES } from 'legacy/state/orders/actions' +import { useSetIsOrderUnfillable } from 'legacy/state/orders/hooks' import { getIsComposableCowOrder } from 'utils/orderUtils/getIsComposableCowOrder' import { getIsNotComposableCowOrder } from 'utils/orderUtils/getIsNotComposableCowOrder' @@ -32,6 +33,8 @@ export function useOrdersTableList( chainId: number, balancesAndAllowances: any, ): OrdersTableList { + const setIsOrderUnfillable = useSetIsOrderUnfillable() + const allSortedOrders = useMemo(() => { return groupOrdersTable(allOrders).sort(ordersSorter) }, [allOrders]) @@ -58,6 +61,11 @@ export function useOrdersTableList( const params = getOrderParams(chainId, balancesAndAllowances, order) const isUnfillable = params.hasEnoughBalance === false || params.hasEnoughAllowance === false + // Set the unfillable flag on the order if it's pending and unfillable + if (isPending && isUnfillable && order.isUnfillable !== isUnfillable) { + setIsOrderUnfillable({ chainId, id: order.id, isUnfillable }) + } + // Only add to unfillable if the order is both pending and unfillable if (isPending && isUnfillable) { acc.unfillable.push(item) @@ -81,5 +89,5 @@ export function useOrdersTableList( unfillable: unfillable.slice(0, ORDERS_LIMIT), all: all.slice(0, ORDERS_LIMIT), } - }, [allSortedOrders, orderType, chainId, balancesAndAllowances]) + }, [allSortedOrders, orderType, chainId, balancesAndAllowances, setIsOrderUnfillable]) } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx index 2f727fdaf0..73fc734495 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx @@ -30,7 +30,7 @@ import { useValidatePageUrlParams } from './hooks/useValidatePageUrlParams' import { OPEN_TAB, ORDERS_TABLE_TABS, ALL_ORDERS_TAB } from '../../const/tabs' import { OrdersTableContainer } from '../../pure/OrdersTableContainer' -import { ColumnLayout, LAYOUT_MAP } from '../../pure/OrdersTableContainer/tableHeaders' + import { OrderActions } from '../../pure/OrdersTableContainer/types' import { TabOrderTypes } from '../../types' import { buildOrdersTableUrl } from '../../utils/buildOrdersTableUrl' @@ -128,10 +128,6 @@ export function OrdersTableWidget({ const injectedWidgetParams = useInjectedWidgetParams() const [searchTerm, setSearchTerm] = useState('') const limitOrdersSettings = useAtomValue(limitOrdersSettingsAtom) - const columnLayout = useMemo( - () => LAYOUT_MAP[limitOrdersSettings.columnLayout] || ColumnLayout.DEFAULT, - [limitOrdersSettings.columnLayout], - ) const balancesState = useTokensBalances() const allowancesState = useTokensAllowances() @@ -162,7 +158,13 @@ export function OrdersTableWidget({ }, [ordersList, currentTabId]) const tabs = useMemo(() => { - return ORDERS_TABLE_TABS.map((tab) => { + return ORDERS_TABLE_TABS.filter((tab) => { + // Only include the unfillable tab if there are unfillable orders + if (tab.id === 'unfillable') { + return getOrdersListByIndex(ordersList, tab.id).length > 0 + } + return true + }).map((tab) => { return { ...tab, isActive: tab.id === currentTabId, count: getOrdersListByIndex(ordersList, tab.id).length } }) }, [currentTabId, ordersList]) @@ -294,7 +296,6 @@ export function OrdersTableWidget({ pendingActivities={pendingActivity} injectedWidgetParams={injectedWidgetParams} searchTerm={searchTerm} - columnLayout={columnLayout} > {(currentTabId === OPEN_TAB.id || currentTabId === 'all' || currentTabId === 'unfillable') && orders.length > 0 && } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts index a3e234a718..4afdeeb8b8 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts @@ -53,6 +53,15 @@ export function getOrderStatusTitleAndColor(order: ParsedOrder): { title: string } } + // Handle unfillable orders + if (order.isUnfillable) { + return { + title: 'Unfillable', + color: `var(${UI.COLOR_DANGER_TEXT})`, + background: `var(${UI.COLOR_DANGER_BG})`, + } + } + // Finally, map order status to their display version return { title: orderStatusTitleMap[order.status], diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/index.tsx index 5e40b59f38..ac754b7187 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/index.tsx @@ -13,7 +13,7 @@ const Wrapper = styled.div<{ widthAuto?: boolean clickable?: boolean }>` - --height: 28px; + --height: 26px; --statusColor: ${({ color }) => color}; --statusBackground: ${({ background }) => background}; @@ -40,7 +40,7 @@ const Wrapper = styled.div<{ top: 0; background: var(--statusBackground); z-index: 1; - border-radius: ${({ withWarning }) => (withWarning ? '9px 0 0 9px' : '9px')}; + border-radius: 16px; } ` @@ -50,6 +50,12 @@ const StatusContent = styled.div` gap: 4px; position: relative; z-index: 2; + + svg { + width: 14px; + height: 14px; + fill: currentColor; + } ` type OrderStatusBoxProps = { @@ -63,25 +69,23 @@ type OrderStatusBoxProps = { export function OrderStatusBox({ order, widthAuto, withWarning, onClick, WarningTooltip }: OrderStatusBoxProps) { const { title, color, background } = getOrderStatusTitleAndColor(order) - const content = {title} + const content = ( + + {withWarning && WarningTooltip && {null}} + {title} + + ) return ( - <> - - {content} - - {withWarning && WarningTooltip && ( - - <> - - )} - + + {content} + ) } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx index cda6d61cd5..b9b0a7896d 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx @@ -1,4 +1,5 @@ import AlertTriangle from '@cowprotocol/assets/cow-swap/alert.svg' +import allowanceIcon from '@cowprotocol/assets/images/icon-allowance.svg' import { ZERO_FRACTION } from '@cowprotocol/common-const' import { Command } from '@cowprotocol/types' import { UI } from '@cowprotocol/ui' @@ -50,6 +51,17 @@ const UnfillableLabel = styled.span` align-items: center; justify-content: flex-start; gap: 3px; + + svg { + width: 14px; + height: 14px; + fill: currentColor; + } + + svg > path { + fill: currentColor; + stroke: none; + } ` const ApprovalLink = styled.button` @@ -120,6 +132,9 @@ export function EstimatedExecutionPrice(props: EstimatedExecutionPriceProps) { const unfillableLabel = ( + {(warningText === 'Insufficient allowance' || warningText === 'Insufficient balance') && ( + + )} {warningText} {warningText === 'Insufficient allowance' && onApprove && ( Set approval diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/OrderWarning.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/OrderWarning.tsx index 3f50ba73aa..d52aac3138 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/OrderWarning.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/OrderWarning.tsx @@ -1,6 +1,6 @@ import React from 'react' -import AlertTriangle from '@cowprotocol/assets/cow-swap/alert.svg' +import alertCircle from '@cowprotocol/assets/cow-swap/alert-circle.svg' import { Command } from '@cowprotocol/types' import { ButtonSecondary, TokenSymbol, UI, HoverTooltip } from '@cowprotocol/ui' @@ -117,13 +117,13 @@ export function WarningTooltip({ if (showIcon) { return ( - + } + bgColor={`var(${UI.COLOR_DANGER_BG})`} + color={`var(${UI.COLOR_DANGER_TEXT})`} + Icon={} /> {children} @@ -134,8 +134,8 @@ export function WarningTooltip({ {children} diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index cbd63f9469..6a2b58c15e 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -35,7 +35,6 @@ import * as styledEl from './styled' import { OrderParams } from '../../../utils/getOrderParams' import { OrderStatusBox } from '../../OrderStatusBox' import { CheckboxCheckmark, TableRow, TableRowCheckbox, TableRowCheckboxWrapper } from '../styled' -import { ColumnLayout } from '../tableHeaders' import { OrderActions } from '../types' // Constants @@ -74,7 +73,6 @@ export interface OrderRowProps { prices: PendingOrderPrices | undefined | null spotPrice: Price | undefined | null isRateInverted: boolean - showLimitPrice: boolean isHistoryTab: boolean isRowSelectable: boolean isRowSelected: boolean @@ -83,13 +81,11 @@ export interface OrderRowProps { onClick: Command orderActions: OrderActions children?: React.ReactNode - columnLayout?: ColumnLayout } export function OrderRow({ order, isRateInverted: isGloballyInverted, - showLimitPrice, isHistoryTab, isRowSelectable, isRowSelected, @@ -100,7 +96,6 @@ export function OrderRow({ prices, spotPrice, children, - columnLayout = ColumnLayout.DEFAULT, }: OrderRowProps) { const { buyAmount, rateInfoParams, hasEnoughAllowance, hasEnoughBalance, chainId } = orderParams const { creationTime, expirationTime, status } = order @@ -247,20 +242,6 @@ export function OrderRow({ ) } - const renderDistanceToMarket = () => ( - <> - {isUnfillable ? ( - '-' - ) : priceDiffs?.percentage && Number(priceDiffs.percentage.toFixed(4)) >= MIN_PERCENTAGE_TO_DISPLAY ? ( - - {priceDiffs.percentage.toFixed(2)}% - - ) : ( - '-' - )} - - ) - const renderMarketPrice = () => ( <> {spotPrice ? ( @@ -274,13 +255,7 @@ export function OrderRow({ ) return ( - + {/*Checkbox for multiple cancellation*/} {isRowSelectable && !isHistoryTab && ( @@ -309,32 +284,9 @@ export function OrderRow({ {/* Non-history tab columns */} {!isHistoryTab ? ( <> - {/* Price columns based on layout */} - {columnLayout === ColumnLayout.DEFAULT && ( - <> - - {showLimitPrice ? renderLimitPrice() : renderFillsAt()} - - {renderDistanceToMarket()} - {renderMarketPrice()} - - )} - - {columnLayout === ColumnLayout.VIEW_2 && ( - <> - {renderLimitPrice()} - {renderFillsAt()} - {renderDistanceToMarket()} - - )} - - {columnLayout === ColumnLayout.VIEW_3 && ( - <> - {renderLimitPrice()} - {renderFillsAtWithDistance()} - {renderMarketPrice()} - - )} + {renderLimitPrice()} + {renderFillsAtWithDistance()} + {renderMarketPrice()} {/* Expires and Created for open orders */} diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/styled.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/styled.tsx index fde0b5b2ea..c87cf25b3d 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/styled.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/styled.tsx @@ -8,16 +8,17 @@ import { RateWrapper } from 'common/pure/RateInfo' export const WarningIndicator = styled.button<{ hasBackground?: boolean }>` --height: 28px; margin: 0; - background: ${({ hasBackground = true }) => (hasBackground ? `var(${UI.COLOR_ALERT_BG})` : 'transparent')}; - color: var(${UI.COLOR_ALERT_TEXT}); + background: ${({ hasBackground = true }) => (hasBackground ? `var(${UI.COLOR_DANGER_BG})` : 'transparent')}; + color: var(${UI.COLOR_DANGER}); line-height: 0; border: 0; - padding: 0 5px; + padding: 0; width: auto; height: var(--height); border-radius: 0 9px 9px 0; svg { + cursor: help; color: inherit; } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx index a34144ee2c..33f4887496 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Media, UI } from '@cowprotocol/ui' @@ -16,7 +16,7 @@ import { isOrderOffChainCancellable } from 'common/utils/isOrderOffChainCancella import { OrderRow } from './OrderRow' import { CheckboxCheckmark, TableHeader, TableRowCheckbox, TableRowCheckboxWrapper } from './styled' import { TableGroup } from './TableGroup' -import { ColumnLayout, createTableHeaders } from './tableHeaders' +import { createTableHeaders } from './tableHeaders' import { OrderActions } from './types' import { HISTORY_TAB, ORDERS_TABLE_PAGE_SIZE } from '../../const/tabs' @@ -97,7 +97,6 @@ const Rows = styled.div` export interface OrdersTableProps { currentTab: string allowsOffchainSigning: boolean - currentPageNumber: number chainId: SupportedChainId pendingOrdersPrices: PendingOrdersPrices orders: OrderTableItem[] @@ -105,7 +104,7 @@ export interface OrdersTableProps { balancesAndAllowances: BalancesAndAllowances getSpotPrice: (params: SpotPricesKeyParams) => Price | null orderActions: OrderActions - columnLayout?: ColumnLayout + currentPageNumber: number } export function OrdersTable({ @@ -119,10 +118,8 @@ export function OrdersTable({ getSpotPrice, orderActions, currentPageNumber, - columnLayout, }: OrdersTableProps) { const buildOrdersTableUrl = useGetBuildOrdersTableUrl() - const [showLimitPrice, setShowLimitPrice] = useState(false) const checkboxRef = useRef(null) const step = currentPageNumber * ORDERS_TABLE_PAGE_SIZE @@ -172,10 +169,7 @@ export function OrdersTable({ checkbox.checked = allOrdersSelected }, [allOrdersSelected, selectedOrders.length]) - const tableHeaders = useMemo( - () => createTableHeaders(showLimitPrice, setShowLimitPrice, columnLayout), - [showLimitPrice, columnLayout], - ) + const tableHeaders = useMemo(() => createTableHeaders(), []) const visibleHeaders = useMemo(() => { const isHistoryTab = currentTab === HISTORY_TAB.id @@ -191,11 +185,7 @@ export function OrdersTable({ <> - + {visibleHeaders.map((header) => { if (header.id === 'checkbox' && (!isRowSelectable || currentTab === HISTORY_TAB.id)) { return null @@ -252,11 +242,9 @@ export function OrdersTable({ spotPrice={spotPrice} prices={pendingOrdersPrices[order.id]} isRateInverted={false} - showLimitPrice={showLimitPrice} orderParams={getOrderParams(chainId, balancesAndAllowances, order)} onClick={() => orderActions.selectReceiptOrder(order)} orderActions={orderActions} - columnLayout={columnLayout} /> ) } else { @@ -272,7 +260,6 @@ export function OrdersTable({ spotPrice={spotPrice} prices={pendingOrdersPrices[item.parent.id]} isRateInverted={false} - showLimitPrice={showLimitPrice} orderActions={orderActions} /> ) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTabs.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTabs.tsx index ce129d5d03..a2372aeec9 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTabs.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTabs.tsx @@ -1,6 +1,8 @@ +import alertCircle from '@cowprotocol/assets/cow-swap/alert-circle.svg' import { Media, UI } from '@cowprotocol/ui' import { Trans } from '@lingui/macro' +import SVG from 'react-inlinesvg' import { Link } from 'react-router-dom' import styled from 'styled-components/macro' @@ -14,13 +16,20 @@ const Tabs = styled.div` margin: 0; ` -const TabButton = styled(Link)<{ active: string }>` - display: inline-block; - background: ${({ active }) => (active === 'true' ? `var(${UI.COLOR_TEXT_OPACITY_10})` : 'transparent')}; - color: ${({ active }) => (active === 'true' ? `var(${UI.COLOR_TEXT_PAPER})` : 'inherit')}; +const TabButton = styled(Link)<{ active: string; isUnfillable?: boolean }>` + display: inline-flex; + align-items: center; + gap: 4px; + background: ${({ active, isUnfillable }) => + active === 'true' + ? isUnfillable + ? `var(${UI.COLOR_DANGER_BG})` + : `var(${UI.COLOR_TEXT_OPACITY_10})` + : 'transparent'}; + color: ${({ active, isUnfillable }) => + isUnfillable ? `var(${UI.COLOR_DANGER})` : active === 'true' ? `var(${UI.COLOR_TEXT_PAPER})` : 'inherit'}; font-weight: ${({ active }) => (active === 'true' ? '600' : '400')}; border-radius: 14px; - border: 1px solid var(${UI.COLOR_TEXT_OPACITY_10}); text-decoration: none; font-size: 13px; padding: 10px; @@ -36,9 +45,23 @@ const TabButton = styled(Link)<{ active: string }>` } &:hover { - background: ${({ active }) => - active === 'true' ? `var(${UI.COLOR_TEXT_OPACITY_10})` : `var(${UI.COLOR_TEXT_OPACITY_10})`}; - color: inherit; + background: ${({ active, isUnfillable }) => + active === 'true' + ? isUnfillable + ? `var(${UI.COLOR_DANGER_BG})` + : `var(${UI.COLOR_TEXT_OPACITY_10})` + : `var(${UI.COLOR_TEXT_OPACITY_10})`}; + color: ${({ isUnfillable }) => (isUnfillable ? `var(${UI.COLOR_DANGER})` : 'inherit')}; + } + + > svg { + width: 14px; + height: 14px; + fill: currentColor; + } + + > svg > path { + fill: currentColor; } ` @@ -55,15 +78,20 @@ export function OrdersTabs({ tabs }: OrdersTabsProps) { return ( - {tabs.map((tab, index) => ( - - {tab.title} ({tab.count}) - - ))} + {tabs.map((tab, index) => { + const isUnfillable = tab.id === 'unfillable' + return ( + + {isUnfillable && } + {tab.title} ({tab.count}) + + ) + })} ) } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/TableGroup.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/TableGroup.tsx index d545967624..5a352f98cc 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/TableGroup.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/TableGroup.tsx @@ -33,7 +33,6 @@ export interface TableGroupProps { prices: PendingOrderPrices | undefined | null spotPrice: Price | undefined | null isRateInverted: boolean - showLimitPrice: boolean isHistoryTab: boolean isRowSelectable: boolean isRowSelected: boolean @@ -48,7 +47,6 @@ export function TableGroup(props: TableGroupProps) { prices, spotPrice, isRateInverted, - showLimitPrice, isHistoryTab, isRowSelectable, isRowSelected, @@ -74,7 +72,6 @@ export function TableGroup(props: TableGroupProps) { spotPrice, prices, isRateInverted, - showLimitPrice, orderActions, } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx index 83875b35ce..836b8272a5 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/index.tsx @@ -14,7 +14,6 @@ import { Web3Status } from 'modules/wallet/containers/Web3Status' import { OrdersTable } from './OrdersTable' import { OrdersTabs } from './OrdersTabs' -import { ColumnLayout } from './tableHeaders' import { OrderActions } from './types' import { ALL_ORDERS_TAB, HISTORY_TAB, OPEN_TAB, UNFILLABLE_TAB } from '../../const/tabs' @@ -186,7 +185,6 @@ interface OrdersProps { pendingOrdersPrices: any getSpotPrice: any searchTerm?: string - columnLayout?: ColumnLayout } export function OrdersTableContainer({ @@ -208,7 +206,6 @@ export function OrdersTableContainer({ pendingActivities, injectedWidgetParams, searchTerm, - columnLayout, }: OrdersProps) { const currentTab = useMemo(() => { const activeTab = tabs.find((tab) => tab.isActive) @@ -311,7 +308,6 @@ export function OrdersTableContainer({ currentPageNumber={currentPageNumber} pendingOrdersPrices={pendingOrdersPrices} getSpotPrice={getSpotPrice} - columnLayout={columnLayout} /> ) } diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/styled.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/styled.tsx index cd161df303..75f47d8bcc 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/styled.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/styled.tsx @@ -1,12 +1,7 @@ import { Media, UI } from '@cowprotocol/ui' -import { transparentize } from 'color2k' import styled from 'styled-components/macro' -import { RateWrapper } from 'common/pure/RateInfo' - -import { ColumnLayout } from './tableHeaders' - export const SettingsContainer = styled.div` display: flex; align-items: center; @@ -35,22 +30,6 @@ export const SettingsLabel = styled.span` opacity: 0.7; ` -export const LayoutSelector = styled.select` - background: var(${UI.COLOR_PAPER_DARKER}); - color: inherit; - border: 1px solid var(${UI.COLOR_TEXT_OPACITY_10}); - border-radius: 8px; - padding: 4px 8px; - font-size: 12px; - cursor: pointer; - margin-left: 8px; - - &:focus { - outline: none; - border-color: var(${UI.COLOR_TEXT}); - } -` - export const TableWrapper = styled.div` width: 100%; overflow-x: auto; @@ -63,14 +42,14 @@ export const TableWrapper = styled.div` } ` -export const TableHeader = styled.div<{ isHistoryTab: boolean; isRowSelectable: boolean; columnLayout?: ColumnLayout }>` +export const TableHeader = styled.div<{ isHistoryTab: boolean; isRowSelectable: boolean }>` --header-height: 26px; --row-height: 41px; --checkboxSize: 16px; --checkBoxBorderRadius: 3px; display: grid; gap: 14px; - grid-template-columns: ${({ isHistoryTab, isRowSelectable, columnLayout }) => { + grid-template-columns: ${({ isHistoryTab, isRowSelectable }) => { if (isHistoryTab) { return `minmax(200px, 2.5fr) repeat(4, minmax(110px, 1fr)) @@ -80,16 +59,7 @@ export const TableHeader = styled.div<{ isHistoryTab: boolean; isRowSelectable: } const checkboxColumn = isRowSelectable ? 'var(--checkboxSize)' : '' - const baseColumns = `${checkboxColumn}` - - switch (columnLayout) { - case ColumnLayout.VIEW_2: - return `${baseColumns} minmax(180px,2fr) minmax(120px,1fr) minmax(120px,1fr) 60px minmax(120px,1fr) minmax(80px,90px) minmax(80px,0.8fr) 24px` - case ColumnLayout.VIEW_3: - return `${baseColumns} minmax(160px,2fr) minmax(120px,1fr) minmax(140px,1fr) minmax(120px,1fr) minmax(120px,1fr) minmax(80px,90px) minmax(80px,0.8fr) 24px` - default: - return `${baseColumns} minmax(200px, 2.5fr) minmax(140px,1fr) 60px minmax(110px,1fr) minmax(110px,1fr) minmax(80px,90px) minmax(80px,0.8fr) 24px` - } + return `${checkboxColumn} minmax(160px,2fr) minmax(120px,1fr) minmax(140px,1fr) minmax(120px,1fr) minmax(120px,1fr) minmax(80px,90px) minmax(80px,0.8fr) 24px` }}; grid-template-rows: minmax(var(--header-height), 1fr); align-items: center; @@ -113,7 +83,6 @@ export const TableRow = styled(TableHeader)<{ isChildOrder?: boolean isHistoryTab: boolean isRowSelectable: boolean - columnLayout?: ColumnLayout }>` grid-template-rows: minmax(var(--row-height), 1fr); background: ${({ isChildOrder }) => (isChildOrder ? `var(${UI.COLOR_PAPER_DARKER})` : 'transparent')}; @@ -129,19 +98,16 @@ export const TableRow = styled(TableHeader)<{ &::before { display: ${({ isChildOrder }) => (isChildOrder ? 'inline-block' : 'none')}; - color: ${({ theme }) => transparentize(theme.info, 0.6)}; + color: inherit; content: '↳'; text-decoration: none !important; + opacity: 0.6; } } &:last-child { border-bottom: 0; } - - ${RateWrapper} { - text-align: left; - } ` export const CheckboxCheckmark = styled.span` diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/tableHeaders.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/tableHeaders.tsx index 11a148ce3a..1d6d046921 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/tableHeaders.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/tableHeaders.tsx @@ -1,38 +1,8 @@ import { ReactNode } from 'react' import { Trans } from '@lingui/macro' -import { Repeat } from 'react-feather' import styled from 'styled-components/macro' -export enum ColumnLayout { - DEFAULT = 'DEFAULT', - VIEW_2 = 'VIEW_2', - VIEW_3 = 'VIEW_3', -} - -export const LAYOUT_MAP: Record = { - DEFAULT: ColumnLayout.DEFAULT, - VIEW_2: ColumnLayout.VIEW_2, - VIEW_3: ColumnLayout.VIEW_3, -} as const - -const StyledArrowControl = styled.div` - display: inline-flex; - margin-left: 5px; - cursor: pointer; - opacity: 0.7; - transition: opacity 0.2s ease-in-out; - - &:hover { - opacity: 1; - } - - > svg { - width: 14px; - height: 14px; - } -` - const HeaderElement = styled.div<{ doubleRow?: boolean }>` display: flex; flex-direction: column; @@ -69,92 +39,35 @@ const CORE_COLUMNS = { }, } -// Price-related columns for different layouts -const PRICE_COLUMNS = { - DEFAULT: (showLimitPrice: boolean, setShowLimitPrice: (value: boolean) => void): TableHeaderConfig[] => [ - { - id: 'fillsAt', - content: showLimitPrice ? Limit price : Fills at, - showInHistory: false, - order: 3, - extraComponent: ( - setShowLimitPrice(!showLimitPrice)}> - - - ), - }, - { - id: 'distanceToMarket', - content: ( - - Distance
- to market -
- ), - showInHistory: false, - order: 4, - }, - { - id: 'marketPrice', - content: Market price, - showInHistory: false, - order: 5, - }, - ], - VIEW_2: (): TableHeaderConfig[] => [ - { - id: 'limitPrice', - content: Limit price, - showInHistory: false, - order: 3, - }, - { - id: 'fillsAt', - content: Fills at, - showInHistory: false, - order: 4, - }, - { - id: 'distanceToMarket', - content: ( - - Distance
- to market -
- ), - showInHistory: false, - order: 5, - }, - ], - VIEW_3: (): TableHeaderConfig[] => [ - { - id: 'limitPrice', - content: Limit price, - showInHistory: false, - order: 3, - }, - { - id: 'fillsAtWithDistance', - content: ( - - Fills at - - Distance to market - - - ), - showInHistory: false, - doubleRow: true, - order: 4, - }, - { - id: 'marketPrice', - content: Market price, - showInHistory: false, - order: 5, - }, - ], -} +// Price columns for the standard layout +const PRICE_COLUMNS: TableHeaderConfig[] = [ + { + id: 'limitPrice', + content: Limit price, + showInHistory: false, + order: 3, + }, + { + id: 'fillsAtWithDistance', + content: ( + + Fills at + + Distance to market + + + ), + showInHistory: false, + doubleRow: true, + order: 4, + }, + { + id: 'marketPrice', + content: Market price, + showInHistory: false, + order: 5, + }, +] // Columns that appear after price columns const DETAIL_COLUMNS: TableHeaderConfig[] = [ @@ -213,25 +126,9 @@ const DETAIL_COLUMNS: TableHeaderConfig[] = [ }, ] -export const createTableHeaders = ( - showLimitPrice: boolean, - setShowLimitPrice: (value: boolean) => void, - columnLayout: ColumnLayout = ColumnLayout.DEFAULT, -): TableHeaderConfig[] => { - // Get the appropriate price columns based on layout - const priceColumns = (() => { - switch (columnLayout) { - case ColumnLayout.VIEW_2: - return PRICE_COLUMNS.VIEW_2() - case ColumnLayout.VIEW_3: - return PRICE_COLUMNS.VIEW_3() - default: - return PRICE_COLUMNS.DEFAULT(showLimitPrice, setShowLimitPrice) - } - })() - +export const createTableHeaders = (): TableHeaderConfig[] => { // Combine all columns and sort by order - return [CORE_COLUMNS.CHECKBOX, CORE_COLUMNS.TRADE, ...priceColumns, ...DETAIL_COLUMNS].sort( + return [CORE_COLUMNS.CHECKBOX, CORE_COLUMNS.TRADE, ...PRICE_COLUMNS, ...DETAIL_COLUMNS].sort( (a, b) => a.order - b.order, ) } diff --git a/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts b/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts index d85b05113f..0f875c3053 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts @@ -37,6 +37,7 @@ export interface ParsedOrder { id: string owner: string isCancelling: boolean | undefined + isUnfillable?: boolean receiver: string | undefined inputToken: Token outputToken: Token @@ -108,6 +109,7 @@ export const parseOrder = (order: Order): ParsedOrder => { id: order.id, owner: order.owner, isCancelling: order.isCancelling, + isUnfillable: order.isUnfillable, inputToken: order.inputToken, outputToken: order.outputToken, kind: order.kind, diff --git a/libs/assets/src/images/icon-allowance.svg b/libs/assets/src/images/icon-allowance.svg new file mode 100644 index 0000000000..71a2e32594 --- /dev/null +++ b/libs/assets/src/images/icon-allowance.svg @@ -0,0 +1 @@ + \ No newline at end of file