Skip to content

Commit

Permalink
Merge branch 'main' into prioritize-user-entries-over-onchain
Browse files Browse the repository at this point in the history
  • Loading branch information
KillariDev authored Nov 25, 2024
2 parents 88f235b + 47f8066 commit 30f2f18
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 84 deletions.
36 changes: 28 additions & 8 deletions app/css/interceptor.css
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,16 @@ a.dropdown-item.is-active, button.dropdown-item.is-active {
.card-header-icon {
background-color: transparent;
color: var(--text-color);

.icon:has(svg[aria-label=chevron-icon]) {
width: 1rem;
height: 1rem;
transition: transform 200ms;

.card-header:has(+ .card-content) & {
transform: rotateX(180deg);
}
}
}

.card-header-icon:disabled {
Expand Down Expand Up @@ -1721,26 +1731,27 @@ header:has(form[role='search']) h1 {
}

.multiline-card {
--bg-color: #484848;
--bg-color: transparent;
--button-color: #77738ccc;
--image-size: 2.25rem;
--min-text-width: 3ch;
--min-text-width: 0;
--pad-x: 0;
--pad-y: 0;
--gap-x: 0.5rem;
--gap-y: 0.0625rem;
--edge-roundness: 3px;

font: inherit;
display: inline-grid;
grid-template-columns: [left] minmax(0, min-content) [data] minmax(0, max-content) [right];
grid-template-rows: [top] min-content [sub] min-content [bottom];
column-gap: var(--gap-x);
row-gap: 2px;
row-gap: var(--gap-y);
padding-block: var(--pad-y);
padding-inline: var(--pad-x);
background-color: var(--bg-color);
border-radius: var(--edge-roundness);
min-width: calc(var(--min-text-width) + var(--image-size) + (var(-pad-x) * 2) + var(--gap-x));
min-width: calc(var(--min-text-width) + var(--image-size) + (var(--pad-x) * 2) + var(--gap-x));

data {
line-height: 1em;
Expand All @@ -1754,8 +1765,8 @@ header:has(form[role='search']) h1 {
background: var(--bg-color);
border: 0 none;
padding: 0;
cursor: pointer;

&:not(:disabled) { cursor: pointer }
&:hover, &:focus { background: var(--bg-color) }
}

Expand Down Expand Up @@ -1794,18 +1805,27 @@ header:has(form[role='search']) h1 {
}
}

> data { grid-area: top / left / bottom / right }
> data {
grid-area: top / left / bottom / right;

&:not(:has(+:disabled)) {
.multiline-card:hover &, .multiline-card:focus-within & { visibility: hidden }
}
}

> button {
grid-area: top / left / bottom / right;
display: inline-grid;
align-items: baseline;
grid-template-columns: minmax(0, 1fr) min-content;
background-color: var(--bg-color);
opacity: 0;
outline: none;
position: relative;
visibility: hidden;

&:is(.multiline-card:hover *, .multiline-card:focus-within *) { opacity: 1 }
&:not(:disabled) {
.multiline-card:hover &, .multiline-card:focus-within & { visibility: visible }
}

&:hover, &:focus {
> span {
Expand Down
3 changes: 2 additions & 1 deletion app/ts/components/pages/InterceptorAccess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Page } from '../../types/exportedSettingsTypes.js'
import { ReadonlySignal, useComputed, useSignal } from '@preact/signals'
import { RpcEntries } from '../../types/rpc.js'
import { ModifyAddressWindowState } from '../../types/visualizer-types.js'
import { ChevronIcon } from '../subcomponents/icons.js'

function Title({ icon, title} : {icon: string | undefined, title: string}) {
return <span style = 'font-weight: 900; line-height: 48px'>
Expand Down Expand Up @@ -52,7 +53,7 @@ function AssociatedTogether({ associatedAddresses, renameAddressCallBack }: { as
}
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showLogs
Expand Down
6 changes: 3 additions & 3 deletions app/ts/components/pages/PersonalSign.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { QuarantineReasons } from '../simulationExplaining/Transactions.js'
import { GnosisSafeVisualizer } from '../simulationExplaining/customExplainers/GnosisSafeVisualizer.js'
import { EditEnsNamedHashCallBack } from '../subcomponents/ens.js'
import { ViewSelector, ViewSelector as Viewer } from '../subcomponents/ViewSelector.js'
import { XMarkIcon } from '../subcomponents/icons.js'
import { ChevronIcon, XMarkIcon } from '../subcomponents/icons.js'
import { TransactionInput } from '../subcomponents/ParsedInputData.js'
import { ErrorComponent } from '../subcomponents/Error.js'
import { PendingTransactionOrSignableMessage } from '../../types/accessRequest.js'
Expand Down Expand Up @@ -358,7 +358,7 @@ function ExtraDetails({ visualizedPersonalSignRequest, renameAddressCallBack }:
Extra details
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showSummary
Expand Down Expand Up @@ -387,7 +387,7 @@ function RawMessage({ visualizedPersonalSignRequest }: ExtraDetailsCardParams) {
Raw message
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showSummary
Expand Down
12 changes: 6 additions & 6 deletions app/ts/components/simulationExplaining/SimulationSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Website } from '../../types/websiteAccessTypes.js'
import { extractTokenEvents } from '../../background/metadataUtils.js'
import { EditEnsNamedHashCallBack } from '../subcomponents/ens.js'
import { EnrichedEthereumInputData } from '../../types/EnrichedEthereumData.js'
import { XMarkIcon } from '../subcomponents/icons.js'
import { ChevronIcon, XMarkIcon } from '../subcomponents/icons.js'
import { TransactionInput } from '../subcomponents/ParsedInputData.js'
import { sendPopupMessageToBackgroundPage } from '../../background/backgroundUtils.js'
import { IntegerInput } from '../subcomponents/AutosizingInput.js'
Expand Down Expand Up @@ -428,7 +428,7 @@ export function TokenLogAnalysisCard({ simTx, renameAddressCallBack }: TokenLogA
{ tokenResults.length === 0 ? `No ${ tokenEventsPlural }` : `${ tokenResults.length > 1 ? `${ upperCaseFirstCharacter(convertNumberToCharacterRepresentationIfSmallEnough(tokenResults.length)) } ${ tokenEventsPlural }` : tokenEventsSingular }` }
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showLogs
Expand Down Expand Up @@ -463,7 +463,7 @@ export function NonTokenLogAnalysisCard({ simTx, addressMetaData, renameAddressC
{ nonTokenLogs.length === 0 ? 'No non-token events' : `${ upperCaseFirstCharacter(convertNumberToCharacterRepresentationIfSmallEnough(nonTokenLogs.length)) } non-token event${ nonTokenLogs.length > 1 ? 's' : '' }` }
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showLogs
Expand Down Expand Up @@ -510,7 +510,7 @@ export function TransactionsAccountChangesCard({ simTx, renameAddressCallBack, a
{ numberOfChanges === 0 ? 'No changes in accounts' : `${ upperCaseFirstCharacter(convertNumberToCharacterRepresentationIfSmallEnough(numberOfChanges)) } account${ numberOfChanges > 1 ? 's' : '' } changing` }
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showSummary
Expand Down Expand Up @@ -698,7 +698,7 @@ export function SimulationSummary(param: SimulationSummaryParams) {
{ notOwnAddresses.length === 0 ? 'No changes in other accounts' : `${ upperCaseFirstCharacter(convertNumberToCharacterRepresentationIfSmallEnough(notOwnAddresses.length)) } other account${ notOwnAddresses.length > 1 ? 's' : '' } changing` }
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showOtherAccountChanges
Expand Down Expand Up @@ -755,7 +755,7 @@ export function RawTransactionDetailsCard({ transaction, renameAddressCallBack,
Raw transaction information
</p>
<div class = 'card-header-icon'>
<span class = 'icon' style = 'color: var(--text-color); font-weight: unset; font-size: 0.8em;'> V </span>
<span class = 'icon'><ChevronIcon /></span>
</div>
</header>
{ !showSummary
Expand Down
127 changes: 87 additions & 40 deletions app/ts/components/subcomponents/MultilineCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,88 +4,135 @@ import { Tooltip, TooltipConfig } from './Tooltip.js'
import { clipboardCopy } from './clipboardcopy.js'
import { CopyIcon } from './icons.js'

export type CardIcon = {
component: () => JSX.Element
onClick?: () => void
tooltipText?: string
}

export type MultilineCardProps = {
icon: CardIcon
icon: ActionableIconProps
label: ActionableTextProps
note: ActionableTextProps
style?: JSX.CSSProperties
}

export const MultilineCard = ({ icon, label, note, style }: MultilineCardProps) => {
return (
<figure class = 'multiline-card' role = 'figure' style = { style }>
<ActionableIcon { ...icon } />
<ActionableText { ...label } />
<ActionableText { ...note } />
</figure>
)
}

export type ActionableIconProps = {
onClick: 'clipboard-copy'
icon: () => JSX.Element
copyValue?: string
copySuccessMessage: string
hintText?: string
} | {
onClick: JSX.MouseEventHandler<HTMLButtonElement>
icon: () => JSX.Element
hintText?: string
} | {
onClick: undefined
icon: () => JSX.Element
}

const ActionableIcon = (props: ActionableIconProps) => {
const tooltipConfig = useSignal<TooltipConfig | undefined>(undefined)

const copyTextToClipboard = async (event: JSX.TargetedMouseEvent<HTMLButtonElement>) => {
event.currentTarget.blur()
await clipboardCopy(event.currentTarget.value)
tooltipConfig.value = { message: 'Copied!', x: event.clientX, y: event.clientY }
const copySuccessMessage = props.onClick === 'clipboard-copy' && props.copySuccessMessage ? props.copySuccessMessage : 'Copied!'
tooltipConfig.value = { message: copySuccessMessage, x: event.clientX, y: event.clientY }
}

const CardIcon = icon.component
const defaultAction:TextAction = { label: 'Copy', icon: () => <CopyIcon />, onClick: copyTextToClipboard }
const CardIcon = props.icon
const handleClick = props.onClick ? props.onClick === 'clipboard-copy' ? copyTextToClipboard : props.onClick : undefined
const copyValue = props.onClick === 'clipboard-copy' ? props.copyValue : undefined
const hintText = props.onClick ? props.hintText : undefined

return (
<>
<figure class = 'multiline-card' role = 'figure' style = { style }>
<span role = 'img'>
<button type = 'button' onClick = { icon.onClick || copyTextToClipboard } tabIndex = { -1 } title = { icon.tooltipText || label.displayText }><CardIcon /></button>
</span>
<ActionableText { ...label } action = { !label.action ? defaultAction : label.action } />
<ActionableText { ...note } action = { !note.action ? defaultAction : note.action } />
</figure>
<Tooltip config = { tooltipConfig } />
</>
<span role = 'img'>
<button type = 'button' onClick = { handleClick } tabIndex = { -1 } value = { copyValue } title = { hintText } disabled = { !props.onClick }>
<CardIcon />
<Tooltip config = { tooltipConfig } />
</button>
</span>
)
}

export type TextAction = {
label: string
icon: () => JSX.Element
onClick?: JSX.MouseEventHandler<HTMLButtonElement>
type TextNodeProps = {
displayText: string,
value: string
}

const TextNode = ({ displayText, value }: TextNodeProps) => <data class = 'truncate text-legible' value = { value || displayText }>{ displayText }</data>

export type ActionableTextProps = {
onClick: 'clipboard-copy'
displayText: string
copyValue?: string
copySuccessMessage?: string
} | {
onClick: JSX.MouseEventHandler<HTMLButtonElement>
displayText: string
buttonLabel: string
buttonIcon: () => JSX.Element
} | {
onClick?: undefined
displayText: string
value?: string
action: TextAction | 'noaction' | undefined
}

type TextNodeProps = {
displayText: string,
value: string
}
const ActionableText = (props: ActionableTextProps) => {
const tooltipConfig = useSignal<TooltipConfig | undefined>(undefined)

const TextNode = ({ displayText, value }: TextNodeProps) => <data class = 'truncate text-legible' value = { value || displayText }>{ displayText }</data>
const copyTextToClipboard = async (event: JSX.TargetedMouseEvent<HTMLButtonElement>) => {
event.currentTarget.blur()
await clipboardCopy(event.currentTarget.value)
tooltipConfig.value = {
message: props.onClick === 'clipboard-copy' && props.copySuccessMessage ? props.copySuccessMessage : 'Copied!',
x: event.clientX,
y: event.clientY
}
}

const copyValue = props.onClick === 'clipboard-copy' && props.copyValue ? props.copyValue : props.displayText
const actionIcon = props.onClick ? props.onClick === 'clipboard-copy' ? () => <CopyIcon /> : props.buttonIcon : () => <></>
const actionHandler = props.onClick ? props.onClick === 'clipboard-copy' ? copyTextToClipboard : props.onClick : undefined
const actionButtonLabel = props.onClick ? props.onClick === 'clipboard-copy' ? 'Copy' : props.buttonLabel : ''

const DisplayText = () => <TextNode displayText = { props.displayText } value = { copyValue } />

const ActionableText = ({ displayText, value, action }: ActionableTextProps) => {
const DisplayText = () => <TextNode displayText = { displayText } value = { value || displayText } />
return (
<span>
<DisplayText />
{ action !== undefined && action !== 'noaction' ? <TextAction { ...action } textNode = { DisplayText } /> : <></> }
<TextAction buttonLabel = { actionButtonLabel } textNode = { DisplayText } buttonIcon = { actionIcon } onClick = { actionHandler } copyValue = { copyValue } />
<Tooltip config = { tooltipConfig } />
</span>
)
}

type TextActionProps = {
icon: () => JSX.Element
onClick: undefined
textNode: () => JSX.Element
label: string
onClick?: JSX.MouseEventHandler<HTMLButtonElement>
} | {
onClick: JSX.MouseEventHandler<HTMLButtonElement>
textNode: () => JSX.Element
buttonLabel: string
buttonIcon: () => JSX.Element
copyValue?: string
}

const TextAction = ({ textNode: DisplayText, icon: ActionIcon, label, onClick }: TextActionProps) => {
const TextAction = (props: TextActionProps) => {
const DisplayText = props.textNode
const ActionIcon = props.onClick ? props.buttonIcon : () => <></>

return (
<button type = 'button' onClick = { onClick } value = { '' } tabIndex = { 1 }>
<button type = 'button' onClick = { props.onClick } value = { props.onClick ? props.copyValue : undefined } tabIndex = { 1 } disabled = { !props.onClick }>
<DisplayText />
<span>
<ActionIcon />
<span>{ label }</span>
<span>{ props.onClick ? props.buttonLabel : '' }</span>
</span>
</button>
)
Expand Down
Loading

0 comments on commit 30f2f18

Please sign in to comment.