From c28134c4c22f60f2a25d113a0eca14f99282ba51 Mon Sep 17 00:00:00 2001 From: Bogdan Iasinovschi Date: Mon, 9 Dec 2024 12:30:04 +0200 Subject: [PATCH] Rebrand Governance page modals (#509) * Adapt Input component to small size * Update Delegate modal * Update Delegation action modal * Update Undelegate confirmation modal * Update Vot on proposal modal * Shorten class names * Remove value=undefined --- src/components/button/button.tsx | 4 + .../button/variants/primary.module.scss | 22 ++++++ src/components/icons/index.ts | 6 +- src/components/icons/radio-button-fill.tsx | 10 +++ src/components/icons/radio-button.tsx | 9 +++ src/components/input/input.module.scss | 74 ++++++++----------- src/components/input/input.tsx | 31 ++++---- .../radio-button/radio-button.module.scss | 33 ++++++++- src/components/radio-button/radio-button.tsx | 44 +++++++---- .../vote-form/vote-form.module.scss | 28 +++++-- .../proposal-details/vote-form/vote-form.tsx | 41 ++++++---- .../choose-delegate-action.module.scss | 35 ++++++++- .../choose-delegate-action.tsx | 41 +++++++++- .../forms/delegate/delegate-form.tsx | 35 ++++----- .../forms/delegate/delegate.module.scss | 54 ++++++++++++-- src/styles/fonts.module.scss | 8 ++ 16 files changed, 354 insertions(+), 121 deletions(-) create mode 100644 src/components/icons/radio-button-fill.tsx create mode 100644 src/components/icons/radio-button.tsx diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx index 6855e725..64c5e4b0 100644 --- a/src/components/button/button.tsx +++ b/src/components/button/button.tsx @@ -23,6 +23,7 @@ export interface Props extends BreakpointsProps { | 'link-gray'; size?: Size; disabled?: boolean; + destructive?: boolean; href?: string; theme?: 'light' | 'dark'; onClick?: () => void; @@ -32,6 +33,7 @@ export interface Props extends BreakpointsProps { const Button = ({ children, disabled, + destructive, type = 'primary', size = 'md', onClick, @@ -56,6 +58,7 @@ const Button = ({ styles[theme], styles[sizeClass], { [styles.disabled]: disabled }, + { [styles.destructive]: destructive }, className )} {...(isExternal(href) ? { target: '_blank', rel: 'noopener noreferrer' } : {})} @@ -71,6 +74,7 @@ const Button = ({ styles[theme], styles[sizeClass], { [styles.disabled]: disabled }, + { [styles.destructive]: destructive }, className )} onClick={onClick} diff --git a/src/components/button/variants/primary.module.scss b/src/components/button/variants/primary.module.scss index 47aec2da..b079976e 100644 --- a/src/components/button/variants/primary.module.scss +++ b/src/components/button/variants/primary.module.scss @@ -3,6 +3,11 @@ color: $color-base-light; border: none; + &.destructive { + color: $color-base-light; + background-color: $color-action-error-500; + } + &.xxs { padding: 0 10px; border-radius: 24px; @@ -51,6 +56,23 @@ background-color: $color-blue-10; } + &.destructive { + &:hover { + color: $color-base-light; + background-color: $color-action-error-700; + } + + &:active { + color: $color-base-light; + background-color: $color-action-error-600; + } + + &:disabled { + color: $color-action-error-50; + background-color: $color-action-error-200; + } + } + &.dark { background-color: $color-blue-400; diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts index 815f1d5b..035f7547 100644 --- a/src/components/icons/index.ts +++ b/src/components/icons/index.ts @@ -8,16 +8,20 @@ import { ErrorCircleIcon } from './error-circle'; import { ErrorCircleFillIcon } from './error-circle-fill'; import { HelpOutlineIcon } from './help-outline'; import { InfoCircleIcon } from './info-circle'; +import { RadioButtonIcon } from './radio-button'; +import { RadioButtonFillIcon } from './radio-button-fill'; export { CheckCircleIcon, CheckCircleFillIcon, - CheckIcon, CheckboxRadioIcon, + CheckIcon, CloseIcon, CrossIcon, ErrorCircleIcon, ErrorCircleFillIcon, HelpOutlineIcon, InfoCircleIcon, + RadioButtonIcon, + RadioButtonFillIcon, }; diff --git a/src/components/icons/radio-button-fill.tsx b/src/components/icons/radio-button-fill.tsx new file mode 100644 index 00000000..c3a1e5a2 --- /dev/null +++ b/src/components/icons/radio-button-fill.tsx @@ -0,0 +1,10 @@ +import { ComponentProps } from 'react'; + +export const RadioButtonFillIcon = (props: ComponentProps<'svg'>) => { + return ( + + + + + ); +}; diff --git a/src/components/icons/radio-button.tsx b/src/components/icons/radio-button.tsx new file mode 100644 index 00000000..b258f4bd --- /dev/null +++ b/src/components/icons/radio-button.tsx @@ -0,0 +1,9 @@ +import { ComponentPropsWithoutRef } from 'react'; + +export const RadioButtonIcon = (props: ComponentPropsWithoutRef<'svg'>) => { + return ( + + + + ); +}; diff --git a/src/components/input/input.module.scss b/src/components/input/input.module.scss index 106cc9a2..10e0315f 100644 --- a/src/components/input/input.module.scss +++ b/src/components/input/input.module.scss @@ -9,73 +9,61 @@ opacity: 0.4; pointer-events: none; } - &.block { - width: 100%; - .input, - input { - width: 100%; - } - } -} - -@mixin normal { - @include font-heading-7; -} - -@mixin large { - @include font-heading-4; } .input { display: inline-block; max-width: 100%; - &.textCenter { - input { - text-align: center; - } - } - input { min-width: 30px; background-color: transparent; - color: $color-dark-blue-400; border-width: 0; outline: none; - - &:focus { - color: $color-dark-blue-400; - } - - &::placeholder { - color: $color-dark-blue-400; - } + box-shadow: none; + text-align: center; } &.normal { input { - @include normal; + @include font-heading-7; + color: $color-dark-blue-400; + + &:focus { + color: $color-dark-blue-400; + } + + &::placeholder { + color: $color-dark-blue-400; + } } - } - &.large { - input { - @include large; + @media (min-width: $sm) { + min-width: 50px; + + input { + @include font-heading-4; + } } } - &:not(.normal):not(.large) { + &.small { input { - @include normal; - } - } + @include font-body-8; + color: $color-dark-blue-100; - @media (min-width: $sm) { - min-width: 50px; + &:focus { + color: $color-dark-blue-100; + } + + &::placeholder { + color: $color-dark-blue-100; + } + } - &:not(.normal):not(.large) { + @media (min-width: $sm) { input { - @include large; + @include font-body-2; } } } diff --git a/src/components/input/input.tsx b/src/components/input/input.tsx index d754636e..c3e739a3 100644 --- a/src/components/input/input.tsx +++ b/src/components/input/input.tsx @@ -7,46 +7,49 @@ import styles from './input.module.scss'; type Props = { onChange: ChangeEventHandler; value: string; - size?: 'normal' | 'large'; + size?: 'normal' | 'small'; disabled?: boolean; type?: 'text' | 'number'; placeholder?: string; id?: string; - block?: boolean; allowNegative?: boolean; autoFocus?: boolean; }; -const Input = ({ size, type = 'text', block, disabled, allowNegative, ...componentProps }: Props) => { +const Input = ({ + size = 'normal', + type = 'text', + disabled, + allowNegative, + value, + placeholder, + ...componentProps +}: Props) => { return (
{type === 'text' && ( )} {type === 'number' && ( )}
diff --git a/src/components/radio-button/radio-button.module.scss b/src/components/radio-button/radio-button.module.scss index e828e5e6..c0098599 100644 --- a/src/components/radio-button/radio-button.module.scss +++ b/src/components/radio-button/radio-button.module.scss @@ -1,12 +1,13 @@ @import '../../styles/variables.module.scss'; @import '../../styles/fonts.module.scss'; -.radioButtonLabel { +.label { position: relative; display: flex; align-items: center; user-select: none; cursor: pointer; + transition: color 0.2s ease; @include font-body-12; color: $color-dark-blue-50; @@ -33,13 +34,32 @@ } } -.radioButtonInput { +.labelLarge { + @include font-heading-7; + padding-left: 40px; + height: auto; + + &.warning { + color: $color-action-error-600; + + &:hover:not(.checked) { + color: $color-action-error-400; + } + } + + @media (min-width: $sm) { + @include font-heading-4; + padding-left: 40px; + } +} + +.input { position: absolute; left: 0; opacity: 0; } -.radioButtonCheckmark { +.icon { position: absolute; left: 0; display: flex; @@ -57,3 +77,10 @@ } } } + +.iconLarge { + svg { + width: 27px; + height: 27px; + } +} diff --git a/src/components/radio-button/radio-button.tsx b/src/components/radio-button/radio-button.tsx index 6201c2e8..f1442fd3 100644 --- a/src/components/radio-button/radio-button.tsx +++ b/src/components/radio-button/radio-button.tsx @@ -2,34 +2,50 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; import styles from './radio-button.module.scss'; import { triggerOnEnter } from '../modal'; -import { CheckboxRadioIcon, CheckCircleFillIcon } from '../icons'; +import { CheckboxRadioIcon, CheckCircleFillIcon, RadioButtonFillIcon, RadioButtonIcon } from '../icons'; + +type Radio = { type: 'radio'; size?: 'default'; color?: undefined }; +type RadioLarge = { type?: 'radio'; size: 'large'; color?: 'default' | 'success' | 'warning' }; +type Checkbox = { type?: 'checkbox'; size?: 'default'; color?: undefined }; type Props = { checked: boolean; children: ReactNode; name?: string; - type?: 'radio' | 'checkbox'; onChange: () => void; -}; +} & (Radio | RadioLarge | Checkbox); const RadioButton = (props: Props) => { - const { checked, children, name, type = 'radio', onChange } = props; + const { checked, children, name, type = 'radio', size = 'default', color = 'default', onChange } = props; + + if (size === 'large') { + return ( +
+ +
+ ); + } return (
-
); diff --git a/src/pages/proposal-commons/proposal-details/vote-form/vote-form.module.scss b/src/pages/proposal-commons/proposal-details/vote-form/vote-form.module.scss index f6f472f9..fe21b0ab 100644 --- a/src/pages/proposal-commons/proposal-details/vote-form/vote-form.module.scss +++ b/src/pages/proposal-commons/proposal-details/vote-form/vote-form.module.scss @@ -1,12 +1,30 @@ @import '../../../../styles/variables.module.scss'; +@import '../../../../styles/fonts.module.scss'; -.voteForm { - text-align: center; +.voteFormContent { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 0; + + @media (min-width: $sm) { + gap: 24px; + padding: 32px 0; + } } -.voteFormContent { - margin-top: $space-xxxl; - margin-bottom: 2 * $space-xxl; +.voteFormFooter { display: flex; justify-content: center; + width: 100%; + + > button { + width: 100%; + } + + @media (min-width: $sm) { + > button { + width: auto; + } + } } diff --git a/src/pages/proposal-commons/proposal-details/vote-form/vote-form.tsx b/src/pages/proposal-commons/proposal-details/vote-form/vote-form.tsx index bb030954..1206d57e 100644 --- a/src/pages/proposal-commons/proposal-details/vote-form/vote-form.tsx +++ b/src/pages/proposal-commons/proposal-details/vote-form/vote-form.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import Button from '../../../../components/button'; import RadioButton from '../../../../components/radio-button/radio-button'; import styles from './vote-form.module.scss'; +import { ModalFooter, ModalHeader } from '../../../../components/modal'; export type VotingChoice = 'for' | 'against'; @@ -17,22 +18,32 @@ const VoteForm = (props: Props) => { const isVotingFor = checked === 'for'; return ( -
-
Vote on Proposal #{voteId}
-
-
- setChecked('for')} checked={isVotingFor}> -

For

-
- setChecked('against')} checked={!isVotingFor}> -

Against

-
-
+ <> + {`Vote on Proposal #${voteId}`} + +
+ setChecked('against')} + checked={!isVotingFor} + > + Against + + setChecked('for')} checked={isVotingFor}> + For +
- -
+ + +
+ +
+
+ ); }; diff --git a/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.module.scss b/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.module.scss index 05ad7ab2..7902f732 100644 --- a/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.module.scss +++ b/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.module.scss @@ -1,7 +1,38 @@ @import '../../../../styles/variables.module.scss'; +@import '../../../../styles/fonts.module.scss'; .actions { - & > * { - margin: 0 $space-sm; + display: flex; + flex-direction: column-reverse; + gap: 24px; + margin-top: -8px; + + &, + > button { + width: 100%; + } + + @media (min-width: $sm) { + flex-direction: row; + justify-content: center; + gap: 32px; + margin-top: 24px; + + > button { + width: auto; + } + } +} + +.confirmationText { + margin-top: 24px; + text-align: center; + text-wrap: pretty; + text-align: center; + color: $color-action-error-800; + @include font-body-15; + + @media (min-width: $sm) { + @include font-body-9; } } diff --git a/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.tsx b/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.tsx index 03c0a200..8f015588 100644 --- a/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.tsx +++ b/src/pages/proposals/forms/choose-delegate-action/choose-delegate-action.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import Button from '../../../../components/button'; import { ModalFooter, ModalHeader } from '../../../../components/modal'; import styles from './choose-delegate-action.module.scss'; @@ -12,17 +13,53 @@ interface Props { const ChooseDelegateAction = (props: Props) => { const { onUpdateDelegation, onUndelegate, canUpdateDelegation, canUndelegate } = props; + const [confirmationStep, setConfirmationStep] = useState(false); + + if (confirmationStep) { + return ( + <> + Confirm delegation action + + +
+ + + +
+ +

Once altered, your delegation cannot be changed again for 7 days.

+
+ + ); + } + return ( <> Choose delegation action
- -
diff --git a/src/pages/proposals/forms/delegate/delegate-form.tsx b/src/pages/proposals/forms/delegate/delegate-form.tsx index 512ac110..60e74a26 100644 --- a/src/pages/proposals/forms/delegate/delegate-form.tsx +++ b/src/pages/proposals/forms/delegate/delegate-form.tsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import classNames from 'classnames'; import Button from '../../../../components/button'; import { Input } from '../../../../components/input'; import { ModalFooter, ModalHeader } from '../../../../components/modal'; @@ -8,7 +7,6 @@ import { useApi3Pool } from '../../../../contracts'; import { utils, constants } from 'ethers'; import * as notifications from '../../../../components/notifications'; import { messages } from '../../../../utils/messages'; -import globalStyles from '../../../../styles/global-styles.module.scss'; import styles from './delegate.module.scss'; import { handleTransactionError } from '../../../../utils'; import { convertToAddressOrThrow } from '../../../../logic/proposals/encoding/ens-name'; @@ -67,25 +65,28 @@ const DelegateVotesForm = (props: Props) => { <> Delegate my votes to: -
- { - setDelegationAddress(e.target.value); - setError(''); - }} - autoFocus - /> - -
- You will not be able to vote on proposals while your votes are delegated. Your delegate can vote for you. +
+
+ { + setDelegationAddress(e.target.value); + setError(''); + }} + autoFocus + />
+ +

+ You will not be able to vote on proposals while your votes are delegated. Your delegate can vote for you. +

- diff --git a/src/pages/proposals/forms/delegate/delegate.module.scss b/src/pages/proposals/forms/delegate/delegate.module.scss index 6a9858a3..2b94349c 100644 --- a/src/pages/proposals/forms/delegate/delegate.module.scss +++ b/src/pages/proposals/forms/delegate/delegate.module.scss @@ -1,12 +1,56 @@ @import '../../../../styles/variables.module.scss'; +@import '../../../../styles/fonts.module.scss'; -.error { - margin-top: $space-xl; - color: $error-color; +.delegateFormModalContent { + display: flex; + flex-direction: column; + gap: 48px; text-align: center; + width: 100%; +} + +.inputWrapper { + display: flex; + justify-content: center; + padding: 6px 16px; + height: 50px; + box-sizing: border-box; + overflow: hidden; + + @media (min-width: $sm) { + padding: 24px 0 0 0; + height: 82px; + } } .subtext { - color: $tertiary-color; - margin-top: $space-xxxl; + @include font-body-15; + color: $color-dark-blue-400; + text-align: center; + padding: 0 24px; + text-wrap: pretty; + + @media (min-width: $sm) { + @include font-body-9; + padding: 0; + } +} + +.delegateButton { + width: 100%; + + @media (min-width: $sm) { + width: auto; + } +} + +.error { + @include font-body-15; + margin-top: 48px; + color: $color-action-error-800; + text-align: center; + + @media (min-width: $sm) { + @include font-body-9; + } } diff --git a/src/styles/fonts.module.scss b/src/styles/fonts.module.scss index 7b8cce54..b498f4bd 100644 --- a/src/styles/fonts.module.scss +++ b/src/styles/fonts.module.scss @@ -1,3 +1,11 @@ +@mixin font-body-2 { + font-family: Karla; + font-size: 20px; + font-style: normal; + font-weight: 600; + line-height: 150%; /* 30px */ +} + @mixin font-body-3 { font-family: Karla; font-size: 20px;