From 27435b3e70aa9c81529481bdad880df297f66fb7 Mon Sep 17 00:00:00 2001 From: jonybur Date: Mon, 18 Sep 2023 00:59:28 +0000 Subject: [PATCH 1/9] On-brand changes --- yarn-project/boxes/private-token/package.json | 5 +- .../boxes/private-token/postcss.config.cjs | 3 +- .../src/app/components/banner.tsx | 43 - .../src/app/components/button.tsx | 28 - .../contract_function_form.module.scss | 61 + .../app/components/contract_function_form.tsx | 52 +- .../src/app/components/dropdown.module.scss | 68 + .../src/app/components/dropdown.tsx | 84 + .../private-token/src/app/components/index.ts | 3 - .../src/app/components/popup.tsx | 24 +- .../src/app/components/select.module.scss | 65 + .../src/app/components/select.tsx | 82 + .../src/app/components/spinner.tsx | 20 - .../components/wallet_dropdown.module.scss | 5 + .../src/app/components/wallet_dropdown.tsx | 50 +- .../src/app/contract.module.scss | 8 + .../boxes/private-token/src/app/contract.tsx | 56 +- .../private-token/src/app/home.module.scss | 14 + .../boxes/private-token/src/app/home.tsx | 77 +- .../boxes/private-token/src/app/index.css | 48 +- .../boxes/private-token/src/app/terms.tsx | 10 + .../src/assets/soehne-leicht-kursiv.ttf | Bin 0 -> 98204 bytes .../src/assets/soehne-web-buch.woff2 | Bin 0 -> 34312 bytes .../assets/soehne-web-halbfett-kursiv.woff2 | Bin 0 -> 37212 bytes .../src/assets/soehne-web-halbfett.woff2 | Bin 0 -> 34931 bytes .../src/assets/soehne-web-kraftig.woff2 | Bin 0 -> 32915 bytes .../src/assets/soehne-web-leicht.woff2 | Bin 0 -> 33891 bytes .../boxes/private-token/src/typings.d.ts | 4 + .../boxes/private-token/webpack.config.js | 24 + yarn-project/yarn.lock | 4505 +++++++++++++++-- 30 files changed, 4697 insertions(+), 642 deletions(-) delete mode 100644 yarn-project/boxes/private-token/src/app/components/banner.tsx delete mode 100644 yarn-project/boxes/private-token/src/app/components/button.tsx create mode 100644 yarn-project/boxes/private-token/src/app/components/contract_function_form.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/components/dropdown.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/components/dropdown.tsx create mode 100644 yarn-project/boxes/private-token/src/app/components/select.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/components/select.tsx delete mode 100644 yarn-project/boxes/private-token/src/app/components/spinner.tsx create mode 100644 yarn-project/boxes/private-token/src/app/components/wallet_dropdown.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/contract.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/home.module.scss create mode 100644 yarn-project/boxes/private-token/src/app/terms.tsx create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-leicht-kursiv.ttf create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-web-buch.woff2 create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-web-halbfett-kursiv.woff2 create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-web-halbfett.woff2 create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-web-kraftig.woff2 create mode 100644 yarn-project/boxes/private-token/src/assets/soehne-web-leicht.woff2 create mode 100644 yarn-project/boxes/private-token/src/typings.d.ts diff --git a/yarn-project/boxes/private-token/package.json b/yarn-project/boxes/private-token/package.json index 8dcf5c1afd83..1f7542321d24 100644 --- a/yarn-project/boxes/private-token/package.json +++ b/yarn-project/boxes/private-token/package.json @@ -35,15 +35,18 @@ "rootDir": "./src" }, "dependencies": { + "@aztec/aztec-ui": "^0.1.14", "@aztec/aztec.js": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/cli": "workspace:^", "@aztec/foundation": "workspace:^", + "classnames": "^2.3.2", "formik": "^2.4.3", + "node-sass": "^9.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "sass-loader": "^13.3.2", "serve": "^14.2.1", - "tailwindcss": "^3.3.3", "yup": "^1.2.0" }, "devDependencies": { diff --git a/yarn-project/boxes/private-token/postcss.config.cjs b/yarn-project/boxes/private-token/postcss.config.cjs index 95cd618b13f4..d95f27e79590 100644 --- a/yarn-project/boxes/private-token/postcss.config.cjs +++ b/yarn-project/boxes/private-token/postcss.config.cjs @@ -1,6 +1,5 @@ -const tailwindcss = require('tailwindcss'); const autoprefixer = require('autoprefixer'); module.exports = { - plugins: [tailwindcss('./tailwind.config.cjs'), autoprefixer], + plugins: [autoprefixer], }; diff --git a/yarn-project/boxes/private-token/src/app/components/banner.tsx b/yarn-project/boxes/private-token/src/app/components/banner.tsx deleted file mode 100644 index 6157e1c93d1b..000000000000 --- a/yarn-project/boxes/private-token/src/app/components/banner.tsx +++ /dev/null @@ -1,43 +0,0 @@ -interface Props { - background: string; - direction: string; - animated?: boolean; -} - -/** - * - * @param background - background color, either "black" or "purple" - * @returns a moving banner repeating the word PRIVACY - */ -export function Banner({ background, direction, animated }: Props) { - // Determine direction - const start = !animated ? '' : direction === 'reverse' ? 'animate-marquee' : 'animate-marquee3'; - const end = !animated ? '' : direction === 'reverse' ? 'animate-marquee2' : 'animate-marquee4'; - - // Apply relevant color styles - const containerStyles = - background === 'black' - ? `relative flex overflow-x-hidden bg-indigo-950 text-orange-100` - : `relative flex overflow-x-hidden bg-orange-100 text-indigo-950`; - - return ( -
-
- {/* Generate text elements */} - {Array.from({ length: 50 }, (_, index) => ( - - PRIVACY - - ))} -
-
- {/* Generate text elements */} - {Array.from({ length: 50 }, (_, index) => ( - - PRIVACY - - ))} -
-
- ); -} diff --git a/yarn-project/boxes/private-token/src/app/components/button.tsx b/yarn-project/boxes/private-token/src/app/components/button.tsx deleted file mode 100644 index 4ac24cadb538..000000000000 --- a/yarn-project/boxes/private-token/src/app/components/button.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Spinner } from './spinner.js'; - -interface Props { - children: string; - isLoading?: boolean; - disabled?: boolean; - onClick?: () => void; -} - -export function Button({ children, isLoading, disabled, onClick }: Props) { - return ( - - ); -} diff --git a/yarn-project/boxes/private-token/src/app/components/contract_function_form.module.scss b/yarn-project/boxes/private-token/src/app/components/contract_function_form.module.scss new file mode 100644 index 000000000000..6e3a9def5674 --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/contract_function_form.module.scss @@ -0,0 +1,61 @@ +.input { + border: none; + outline-width: 0; + outline-color: rgba(0, 0, 0, 0); + padding: 2px 20px 0 20px; + width: calc(100% - 30px); + height: 45px; + color: #000; + border: 1px solid rgba(0, 0, 0, 0); + font-size: 16px; + text-align: left; + font-weight: 400; + border-radius: 10px; + text-align: left; + text-overflow: ellipsis; + transition: box-shadow .2s; + box-shadow: 0px 4px 10px rgba(0, 0, 0, .1); + -webkit-appearance: none; +} + +.label { + font-weight: 450; + font-size: 18px; + display: flex; + width: 100%; + flex-direction: column; + text-align: left; + margin-bottom: 15px; + justify-content: space-between; +} + +.inputWrapper { + width: 100%; + display: flex; + gap: 15px; +} + +.form { + display: flex; +} + +.field { + display: flex; + justify-content: start; + flex-direction: column; + align-items: flex-start; +} + +.content { + display: flex; + justify-content: space-between; + flex-direction: column; + margin: 30px; + width: 400px; + height: 250px; +} + +.actionButton { + width: 100%; + align-self: center; +} \ No newline at end of file diff --git a/yarn-project/boxes/private-token/src/app/components/contract_function_form.tsx b/yarn-project/boxes/private-token/src/app/components/contract_function_form.tsx index 89dade558b91..e76c2c12d129 100644 --- a/yarn-project/boxes/private-token/src/app/components/contract_function_form.tsx +++ b/yarn-project/boxes/private-token/src/app/components/contract_function_form.tsx @@ -1,11 +1,12 @@ +import * as Yup from 'yup'; +import { Button, Card, CardTheme, Loader } from '@aztec/aztec-ui'; import { AztecAddress, CompleteAddress, Fr } from '@aztec/aztec.js'; import { ContractAbi, FunctionAbi } from '@aztec/foundation/abi'; import { useFormik } from 'formik'; -import * as Yup from 'yup'; import { CONTRACT_ADDRESS_PARAM_NAMES, DEFAULT_PUBLIC_ADDRESS, rpcClient } from '../../config.js'; import { callContractFunction, deployContract, viewContractFunction } from '../../scripts/index.js'; import { convertArgs } from '../../scripts/util.js'; -import { Button } from './index.js'; +import styles from './contract_function_form.module.scss'; type NoirFunctionYupSchema = { // hack: add `any` at the end to get the array schema to typecheck @@ -22,7 +23,7 @@ function generateYupSchema(functionAbi: FunctionAbi) { const initialValues: NoirFunctionFormValues = {}; for (const param of functionAbi.parameters) { if (CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name)) { - // these are hex strings instead, but yup doesn't support bigint so we convert back to bigint on execution + // these are hex strings instead, but yup doesn't support bigint so we convert back to bigint on execution parameterSchema[param.name] = Yup.string().required(); initialValues[param.name] = DEFAULT_PUBLIC_ADDRESS; continue; @@ -32,7 +33,7 @@ function generateYupSchema(functionAbi: FunctionAbi) { parameterSchema[param.name] = Yup.number().required(); initialValues[param.name] = 100; break; - // not really needed for private token, since we hide the nullifier helper method which has the array input + // not really needed for private token, since we hide the nullifier helper method which has the array input case 'array': // eslint-disable-next-line no-case-declarations const arrayLength = param.type.length; @@ -132,37 +133,34 @@ export function ContractFunctionForm({ }); return ( -
-

{title || `${functionAbi.name} (${functionAbi.functionType})`}

-
-
- {functionAbi.parameters.map(input => ( -
- -
+
+ + {functionAbi.parameters.map(input => ( +
+ + {formik.touched[input.name] && formik.errors[input.name] && ( +
{formik.errors[input.name]?.toString()}
+ )}
- {formik.touched[input.name] && formik.errors[input.name] && ( -
{formik.errors[input.name]?.toString()}
- )} -
- ))} -
-
- -
- + ))} + {isLoading ? :
); } diff --git a/yarn-project/boxes/private-token/src/app/components/dropdown.module.scss b/yarn-project/boxes/private-token/src/app/components/dropdown.module.scss new file mode 100644 index 000000000000..8041aff4bc8f --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/dropdown.module.scss @@ -0,0 +1,68 @@ +.dropdownWrapper { + position: absolute; + top: 60px; + right: 0px; + border-radius: 10px; + display: flex; + overflow: hidden; + flex-direction: column; + gap: 1px; + border: 1px solid #ebeaea; + background-color: #ebeaea; + z-index: 1; +} + +.dropdownOptionBackground { + background-color: white; +} + +.dropdownOption { + font-size: 14px; + padding: 10px 25px; + white-space: nowrap; + cursor: pointer; + font-weight: 600; + justify-content: space-between; + letter-spacing: 0.5px; + display: flex; +} + +.singleOption { + text-align: center; + align-items: center; + justify-content: center; +} + +.dropdownOption.disabled { + background-image: initial; + cursor: default; + background-color: #c4c4c4; +} + +.dropdownOptionBackground:hover { + background-color: #ebeaea; +} + +.dropdownOptionBackground.disabled:hover { + background-color: white; +} + +.sublabel { + text-align: right; +} + +.sublabels { + display: flex; + flex-direction: row; + font-weight: 450; +} + +.feeOption { + gap: 5px; + display: flex; + flex-direction: column; +} + +.label { + color: #2f1f49; +} \ No newline at end of file diff --git a/yarn-project/boxes/private-token/src/app/components/dropdown.tsx b/yarn-project/boxes/private-token/src/app/components/dropdown.tsx new file mode 100644 index 000000000000..2d74e687c93d --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/dropdown.tsx @@ -0,0 +1,84 @@ +import { CSSProperties, useEffect, useRef } from 'react'; +import classnames from 'classnames'; + +import style from './dropdown.module.scss'; + +export enum DropdownType { + Simple = 'Simple', + Fees = 'Fees', +} + +function useOutsideAlerter(ref: any, cb: any) { + useEffect(() => { + /** + * Alert if clicked on outside of element + */ + function handleClickOutside(event: any) { + if (ref.current && !ref.current.contains(event.target)) { + cb('You clicked outside of me!'); + } + } + + // Bind the event listener + document.addEventListener('mousedown', handleClickOutside); + return () => { + // Unbind the event listener on clean up + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref, cb]); +} + +interface DropdownProps { + options: DropdownOption[]; + isOpen?: boolean; + className?: string; + style?: CSSProperties; + onClose?: () => void; + onClick?: (option: DropdownOption) => void; +} + +export interface DropdownOption { + value: T; + label: string; + sublabel?: string; + image?: string; + disabled?: boolean; +} + +export function Dropdown(props: DropdownProps) { + const wrapperRef = useRef(null); + useOutsideAlerter(wrapperRef, () => props.onClose && props.onClose()); + + if (!props.isOpen) { + return null; + } + + const handleClick = (option: DropdownOption) => { + if (option.disabled) { + return; + } + if (props.onClick) { + props.onClick(option); + } + if (props.onClose) { + props.onClose(); + } + }; + + return ( +
+ {props.options.map((option: DropdownOption) => ( +
handleClick(option)} + key={option.label} + > +
+ {option.image && {option.label}} +
{option.label}
+
+
+ ))} +
+ ); +} diff --git a/yarn-project/boxes/private-token/src/app/components/index.ts b/yarn-project/boxes/private-token/src/app/components/index.ts index e1b6f869eb06..1e6803a9fd17 100644 --- a/yarn-project/boxes/private-token/src/app/components/index.ts +++ b/yarn-project/boxes/private-token/src/app/components/index.ts @@ -1,5 +1,2 @@ -export * from './banner.js'; -export * from './button.js'; export * from './contract_function_form.js'; export * from './popup.js'; -export * from './spinner.js'; diff --git a/yarn-project/boxes/private-token/src/app/components/popup.tsx b/yarn-project/boxes/private-token/src/app/components/popup.tsx index c4214c944003..65d42ca33cc4 100644 --- a/yarn-project/boxes/private-token/src/app/components/popup.tsx +++ b/yarn-project/boxes/private-token/src/app/components/popup.tsx @@ -1,4 +1,4 @@ -import { Button } from './button.js'; +import { Button } from '@aztec/aztec-ui'; interface Props { children: string; @@ -9,19 +9,13 @@ interface Props { export function Popup({ children, buttonText = 'Close', isWarning = false, onClose }: Props) { return ( -
-
-
-
-
+
+
+
+
+
{isWarning && ( -
{children}
- +
{children}
+
diff --git a/yarn-project/boxes/private-token/src/app/components/select.module.scss b/yarn-project/boxes/private-token/src/app/components/select.module.scss new file mode 100644 index 000000000000..71ecf95ab7f1 --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/select.module.scss @@ -0,0 +1,65 @@ +.selectBox { + border-radius: 10px; + background-color: white; + position: relative; + font-family: Sohne; + font-size: 16px; + height: 45px; + cursor: pointer; + font-weight: 450; + + @media (max-width: 480px) { + margin: 0; + } +} + +.icon { + min-width: 12px; +} + +.border { + box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.1); +} + +.innerFrame { + height: 45px; + padding: 0 15px; + display: flex; + align-items: center; + justify-content: space-between; + overflow: hidden; + gap: 10px; +} + +.innerFrameWithButton { + padding-right: 0px; +} + +.value { + white-space: nowrap; +} + +.dropdown { + top: 45px; + left: 0%; + min-width: 130px; + width: 100%; +} + +.closeButton { + height: 30px; + padding: 15px 15px 15px 5px; + display: flex; + justify-content: flex-end; + align-items: center; +} + +.disabled { + background-color: rgba(239, 239, 239, 0.3); + color: #4a4a4a; + cursor: not-allowed; +} + +.placeholder { + color: #757575; +} \ No newline at end of file diff --git a/yarn-project/boxes/private-token/src/app/components/select.tsx b/yarn-project/boxes/private-token/src/app/components/select.tsx new file mode 100644 index 000000000000..81a4c0c7a97f --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/select.tsx @@ -0,0 +1,82 @@ +import { useState, useEffect, useRef } from 'react'; +import classnames from 'classnames'; + +import { DropdownOption, DropdownType, Dropdown } from './dropdown.js'; +import style from './select.module.scss'; + +interface SelectProps { + options: DropdownOption[]; + dropdownType?: DropdownType; + showBorder?: boolean; + allowEmptyValue?: boolean; + disabled?: boolean; + placeholder?: string; + className?: string; + value?: T; + onChange?: (value?: T) => void; +} + +function useOutsideAlerter(ref: any, setIsOpen: any) { + useEffect(() => { + function handleClickOutside(event: { target: any }) { + if (ref.current && !ref.current.contains(event.target)) { + setIsOpen(false); + } + } + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref, setIsOpen]); +} + +export function Select(props: SelectProps) { + const { showBorder = true } = props; + const [isOpen, setIsOpen] = useState(false); + const wrapperRef = useRef(null); + + useOutsideAlerter(wrapperRef, setIsOpen); + + useEffect(() => { + setIsOpen(false); + }, [props.value]); + + const handleTriggerDropdown = () => { + if (props.disabled) return; + setIsOpen(prevValue => !prevValue); + }; + + const handleOptionSelect = (option: DropdownOption) => { + handleChange(option.value); + setIsOpen(false); + }; + + const handleChange = (value?: T) => { + if (props.onChange) { + props.onChange(value); + } + }; + + const hasButton = props.value && props.allowEmptyValue; + const activeLabel = props.options.find(x => x.value === props.value)?.label; + + return ( +
+
+ + {activeLabel || props.placeholder} + + +
+
+ ); +} diff --git a/yarn-project/boxes/private-token/src/app/components/spinner.tsx b/yarn-project/boxes/private-token/src/app/components/spinner.tsx deleted file mode 100644 index 10fc7bb60ff8..000000000000 --- a/yarn-project/boxes/private-token/src/app/components/spinner.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export function Spinner() { - return ( - - ); -} diff --git a/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.module.scss b/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.module.scss new file mode 100644 index 000000000000..04d4e0079cd9 --- /dev/null +++ b/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.module.scss @@ -0,0 +1,5 @@ +.walletSelector { + position: fixed; + top: 30px; + right: 30px; +} \ No newline at end of file diff --git a/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.tsx b/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.tsx index b653c38a868d..0f54abdf5d64 100644 --- a/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.tsx +++ b/yarn-project/boxes/private-token/src/app/components/wallet_dropdown.tsx @@ -1,6 +1,9 @@ import { CompleteAddress } from '@aztec/aztec.js'; +import { Loader } from '@aztec/aztec-ui'; import { useEffect, useState } from 'react'; import { rpcClient } from '../../config.js'; +import { Select } from './select.js'; +import styles from './wallet_dropdown.module.scss'; interface Props { selected: CompleteAddress | undefined; @@ -26,33 +29,28 @@ export function WalletDropdown({ selected, onSelectChange, onError }: Props) { }); }); + const addresses = wallets + ? wallets.map(({ address }: CompleteAddress) => { + return { label: address.toShortString(), value: address.toString() }; + }) + : null; + return ( -
-
-
- {'Active Wallet: '} - {!wallets && 'loading...'} -
- {!!wallets && ( - - )} -
- {!!selected &&
{selected.address.toString()}
} +
+ {addresses ? ( + - {formik.touched[input.name] && formik.errors[input.name] && ( -
{formik.errors[input.name]?.toString()}
- )} -
- ))} - {isLoading ? :
+
+ {functionAbi.parameters.map(input => ( +
+ + + {formik.touched[input.name] && formik.errors[input.name] && ( +
{formik.errors[input.name]?.toString()}
+ )} +
+ ))} + {isLoading ? ( + + ) : ( +