584 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			584 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { FC, ReactNode, useCallback, useEffect, useState } from 'react'
 | 
						|
import Container from '../Container'
 | 
						|
import Flex from '../Flex'
 | 
						|
import Input from '../Input'
 | 
						|
import Select, { CreatableSelect } from '../Select'
 | 
						|
import Text from '../Text'
 | 
						|
import {
 | 
						|
  SelectOption,
 | 
						|
  TransactionState,
 | 
						|
  transactionsOptions,
 | 
						|
  TxFields,
 | 
						|
  defaultTransactionType
 | 
						|
} from '../../state/transactions'
 | 
						|
import { useSnapshot } from 'valtio'
 | 
						|
import state from '../../state'
 | 
						|
import { streamState } from '../DebugStream'
 | 
						|
import { Box, Button } from '..'
 | 
						|
import Textarea from '../Textarea'
 | 
						|
import { getFlags } from '../../state/constants/flags'
 | 
						|
import { Plus, Trash } from 'phosphor-react'
 | 
						|
import AccountSequence from '../Sequence'
 | 
						|
import { capitalize, typeIs } from '../../utils/helpers'
 | 
						|
 | 
						|
interface UIProps {
 | 
						|
  setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
 | 
						|
  resetState: (tt?: SelectOption) => TransactionState | undefined
 | 
						|
  state: TransactionState
 | 
						|
  estimateFee?: (...arg: any) => Promise<string | undefined>
 | 
						|
  switchToJson: () => void
 | 
						|
}
 | 
						|
 | 
						|
export const TxUI: FC<UIProps> = ({
 | 
						|
  state: txState,
 | 
						|
  setState,
 | 
						|
  resetState,
 | 
						|
  estimateFee,
 | 
						|
  switchToJson
 | 
						|
}) => {
 | 
						|
  const { accounts } = useSnapshot(state)
 | 
						|
  const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } =
 | 
						|
    txState
 | 
						|
 | 
						|
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
						|
    label: acc.name,
 | 
						|
    value: acc.address
 | 
						|
  }))
 | 
						|
 | 
						|
  const flagsOptions: SelectOption[] = Object.entries(
 | 
						|
    getFlags(selectedTransaction?.value) || {}
 | 
						|
  ).map(([label, value]) => ({
 | 
						|
    label,
 | 
						|
    value
 | 
						|
  }))
 | 
						|
 | 
						|
  const [feeLoading, setFeeLoading] = useState(false)
 | 
						|
 | 
						|
  const handleSetAccount = (acc: SelectOption) => {
 | 
						|
    setState({ selectedAccount: acc })
 | 
						|
    streamState.selectedAccount = acc
 | 
						|
  }
 | 
						|
 | 
						|
  const handleSetField = useCallback(
 | 
						|
    (field: keyof TxFields, value: string, opFields?: TxFields) => {
 | 
						|
      const fields = opFields || txFields
 | 
						|
      const obj = fields[field]
 | 
						|
      setState({
 | 
						|
        txFields: {
 | 
						|
          ...fields,
 | 
						|
          [field]: typeof obj === 'object' ? { ...obj, $value: value } : value
 | 
						|
        }
 | 
						|
      })
 | 
						|
    },
 | 
						|
    [setState, txFields]
 | 
						|
  )
 | 
						|
 | 
						|
  const setRawField = useCallback(
 | 
						|
    (field: keyof TxFields, type: string, value: any) => {
 | 
						|
      // TODO $type should be a narrowed type
 | 
						|
      setState({
 | 
						|
        txFields: {
 | 
						|
          ...txFields,
 | 
						|
          [field]: {
 | 
						|
            $type: type,
 | 
						|
            $value: value
 | 
						|
          }
 | 
						|
        }
 | 
						|
      })
 | 
						|
    },
 | 
						|
    [setState, txFields]
 | 
						|
  )
 | 
						|
 | 
						|
  const handleEstimateFee = useCallback(
 | 
						|
    async (state?: TransactionState, silent?: boolean) => {
 | 
						|
      setFeeLoading(true)
 | 
						|
 | 
						|
      const fee = await estimateFee?.(state, { silent })
 | 
						|
      if (fee) handleSetField('Fee', fee, state?.txFields)
 | 
						|
 | 
						|
      setFeeLoading(false)
 | 
						|
    },
 | 
						|
    [estimateFee, handleSetField]
 | 
						|
  )
 | 
						|
 | 
						|
  const handleChangeTxType = useCallback(
 | 
						|
    (tt: SelectOption) => {
 | 
						|
      setState({ selectedTransaction: tt })
 | 
						|
 | 
						|
      const newState = resetState(tt)
 | 
						|
 | 
						|
      handleEstimateFee(newState, true)
 | 
						|
    },
 | 
						|
    [handleEstimateFee, resetState, setState]
 | 
						|
  )
 | 
						|
 | 
						|
  // default tx
 | 
						|
  useEffect(() => {
 | 
						|
    if (selectedTransaction?.value) return
 | 
						|
 | 
						|
    if (defaultTransactionType) {
 | 
						|
      handleChangeTxType(defaultTransactionType)
 | 
						|
    }
 | 
						|
  }, [handleChangeTxType, selectedTransaction?.value])
 | 
						|
 | 
						|
  const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
 | 
						|
 | 
						|
  if (flagsOptions.length) {
 | 
						|
    richFields.push('Flags')
 | 
						|
  }
 | 
						|
 | 
						|
  const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
 | 
						|
  const amountOptions = [
 | 
						|
    { label: 'XRP', value: 'xrp' },
 | 
						|
    { label: 'Token', value: 'token' }
 | 
						|
  ] as const
 | 
						|
 | 
						|
  const defaultTokenAmount = {
 | 
						|
    value: '0',
 | 
						|
    currency: '',
 | 
						|
    issuer: ''
 | 
						|
  }
 | 
						|
  return (
 | 
						|
    <Container
 | 
						|
      css={{
 | 
						|
        p: '$3 01',
 | 
						|
        fontSize: '$sm',
 | 
						|
        height: 'calc(100% - 45px)'
 | 
						|
      }}
 | 
						|
    >
 | 
						|
      <Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
 | 
						|
        <TxField label="Transaction type">
 | 
						|
          <Select
 | 
						|
            instanceId="transactionsType"
 | 
						|
            placeholder="Select transaction type"
 | 
						|
            options={transactionsOptions}
 | 
						|
            hideSelectedOptions
 | 
						|
            value={selectedTransaction}
 | 
						|
            onChange={(tt: any) => handleChangeTxType(tt)}
 | 
						|
          />
 | 
						|
        </TxField>
 | 
						|
        <TxField label="Account">
 | 
						|
          <Select
 | 
						|
            instanceId="from-account"
 | 
						|
            placeholder="Select your account"
 | 
						|
            options={accountOptions}
 | 
						|
            value={selectedAccount}
 | 
						|
            onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
 | 
						|
          />
 | 
						|
        </TxField>
 | 
						|
        <TxField label="Sequence">
 | 
						|
          <AccountSequence address={selectedAccount?.value} />
 | 
						|
        </TxField>
 | 
						|
        {richFields.includes('Flags') && (
 | 
						|
          <TxField label="Flags">
 | 
						|
            <Select
 | 
						|
              isClearable
 | 
						|
              instanceId="flags"
 | 
						|
              placeholder="Select flags to apply"
 | 
						|
              menuPosition="fixed"
 | 
						|
              value={selectedFlags}
 | 
						|
              isMulti
 | 
						|
              options={flagsOptions}
 | 
						|
              onChange={flags => setState({ selectedFlags: flags as any })}
 | 
						|
              closeMenuOnSelect={
 | 
						|
                selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
 | 
						|
              }
 | 
						|
            />
 | 
						|
          </TxField>
 | 
						|
        )}
 | 
						|
        {otherFields.map(field => {
 | 
						|
          let _value = txFields[field]
 | 
						|
 | 
						|
          let value: string | undefined
 | 
						|
          if (typeIs(_value, 'object')) {
 | 
						|
            if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) {
 | 
						|
              value = JSON.stringify(_value.$value, null, 2)
 | 
						|
            } else {
 | 
						|
              value = _value.$value?.toString()
 | 
						|
            }
 | 
						|
          } else {
 | 
						|
            value = _value?.toString()
 | 
						|
          }
 | 
						|
 | 
						|
          const isAccount = typeIs(_value, 'object') && _value.$type === 'account'
 | 
						|
          const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
 | 
						|
          const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
 | 
						|
          const isJson = typeof _value === 'object' && _value.$type === 'json'
 | 
						|
          const isFee = field === 'Fee'
 | 
						|
          let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
 | 
						|
          if (rows && rows > 5) rows = 5
 | 
						|
          let tokenAmount = defaultTokenAmount
 | 
						|
          if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
 | 
						|
            tokenAmount = {
 | 
						|
              value: _value.$value.value,
 | 
						|
              currency: _value.$value.currency,
 | 
						|
              issuer: _value.$value.issuer
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (isXrpAmount || isTokenAmount) {
 | 
						|
            return (
 | 
						|
              <TxField key={field} label={field}>
 | 
						|
                <Flex fluid css={{ alignItems: 'center' }}>
 | 
						|
                  {isTokenAmount ? (
 | 
						|
                    <Flex
 | 
						|
                      fluid
 | 
						|
                      row
 | 
						|
                      align="center"
 | 
						|
                      justify="space-between"
 | 
						|
                      css={{ position: 'relative' }}
 | 
						|
                    >
 | 
						|
                      {/*  <Input
 | 
						|
                        type="text"
 | 
						|
                        placeholder="Issuer"
 | 
						|
                        value={tokenAmount.issuer}
 | 
						|
                        onChange={e =>
 | 
						|
                          setRawField(field, 'amount.token', {
 | 
						|
                            ...tokenAmount,
 | 
						|
                            issuer: e.target.value
 | 
						|
                          })
 | 
						|
                        }
 | 
						|
                      /> */}
 | 
						|
                      <Input
 | 
						|
                        type="text"
 | 
						|
                        value={tokenAmount.currency}
 | 
						|
                        placeholder="Currency"
 | 
						|
                        onChange={e => {
 | 
						|
                          setRawField(field, 'amount.token', {
 | 
						|
                            ...tokenAmount,
 | 
						|
                            currency: e.target.value
 | 
						|
                          })
 | 
						|
                        }}
 | 
						|
                      />
 | 
						|
                      <Input
 | 
						|
                        css={{ mx: '$1' }}
 | 
						|
                        type="number"
 | 
						|
                        value={tokenAmount.value}
 | 
						|
                        placeholder="Value"
 | 
						|
                        onChange={e => {
 | 
						|
                          setRawField(field, 'amount.token', {
 | 
						|
                            ...tokenAmount,
 | 
						|
                            value: e.target.value
 | 
						|
                          })
 | 
						|
                        }}
 | 
						|
                      />
 | 
						|
                      <Box css={{ width: '50%' }}>
 | 
						|
                        <CreatableAccount
 | 
						|
                          value={tokenAmount.issuer}
 | 
						|
                          field={'Issuer' as any}
 | 
						|
                          placeholder="Issuer"
 | 
						|
                          setField={(_, value = '') => {
 | 
						|
                            setRawField(field, 'amount.token', {
 | 
						|
                              ...tokenAmount,
 | 
						|
                              issuer: value
 | 
						|
                            })
 | 
						|
                          }}
 | 
						|
                        />
 | 
						|
                      </Box>
 | 
						|
                    </Flex>
 | 
						|
                  ) : (
 | 
						|
                    <Input
 | 
						|
                      css={{ flex: 'inherit' }}
 | 
						|
                      type="number"
 | 
						|
                      value={value}
 | 
						|
                      onChange={e => handleSetField(field, e.target.value)}
 | 
						|
                    />
 | 
						|
                  )}
 | 
						|
                  <Box
 | 
						|
                    css={{
 | 
						|
                      ml: '$2',
 | 
						|
                      width: '150px'
 | 
						|
                    }}
 | 
						|
                  >
 | 
						|
                    <Select
 | 
						|
                      instanceId="currency-type"
 | 
						|
                      options={amountOptions}
 | 
						|
                      value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
 | 
						|
                      onChange={(e: any) => {
 | 
						|
                        const opt = e as typeof amountOptions[number]
 | 
						|
                        if (opt.value === 'xrp') {
 | 
						|
                          setRawField(field, 'amount.xrp', '0')
 | 
						|
                        } else {
 | 
						|
                          setRawField(field, 'amount.token', defaultTokenAmount)
 | 
						|
                        }
 | 
						|
                      }}
 | 
						|
                    />
 | 
						|
                  </Box>
 | 
						|
                </Flex>
 | 
						|
              </TxField>
 | 
						|
            )
 | 
						|
          }
 | 
						|
          if (isAccount) {
 | 
						|
            return (
 | 
						|
              <TxField key={field} label={field}>
 | 
						|
                <CreatableAccount value={value} field={field} setField={handleSetField} />
 | 
						|
              </TxField>
 | 
						|
            )
 | 
						|
          }
 | 
						|
          return (
 | 
						|
            <TxField key={field} label={field}>
 | 
						|
              {isJson ? (
 | 
						|
                <Textarea
 | 
						|
                  rows={rows}
 | 
						|
                  value={value}
 | 
						|
                  spellCheck={false}
 | 
						|
                  onChange={switchToJson}
 | 
						|
                  css={{
 | 
						|
                    flex: 'inherit',
 | 
						|
                    resize: 'vertical'
 | 
						|
                  }}
 | 
						|
                />
 | 
						|
              ) : (
 | 
						|
                <Input
 | 
						|
                  type={isFee ? 'number' : 'text'}
 | 
						|
                  value={value}
 | 
						|
                  onChange={e => {
 | 
						|
                    if (isFee) {
 | 
						|
                      const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
 | 
						|
                      handleSetField(field, val)
 | 
						|
                    } else {
 | 
						|
                      handleSetField(field, e.target.value)
 | 
						|
                    }
 | 
						|
                  }}
 | 
						|
                  onKeyPress={
 | 
						|
                    isFee
 | 
						|
                      ? e => {
 | 
						|
                          if (e.key === '.' || e.key === ',') {
 | 
						|
                            e.preventDefault()
 | 
						|
                          }
 | 
						|
                        }
 | 
						|
                      : undefined
 | 
						|
                  }
 | 
						|
                  css={{
 | 
						|
                    flex: 'inherit',
 | 
						|
                    '-moz-appearance': 'textfield',
 | 
						|
                    '&::-webkit-outer-spin-button': {
 | 
						|
                      '-webkit-appearance': 'none',
 | 
						|
                      margin: 0
 | 
						|
                    },
 | 
						|
                    '&::-webkit-inner-spin-button ': {
 | 
						|
                      '-webkit-appearance': 'none',
 | 
						|
                      margin: 0
 | 
						|
                    }
 | 
						|
                  }}
 | 
						|
                />
 | 
						|
              )}
 | 
						|
              {isFee && (
 | 
						|
                <Button
 | 
						|
                  size="xs"
 | 
						|
                  variant="primary"
 | 
						|
                  outline
 | 
						|
                  disabled={txState.txIsDisabled}
 | 
						|
                  isDisabled={txState.txIsDisabled}
 | 
						|
                  isLoading={feeLoading}
 | 
						|
                  css={{
 | 
						|
                    position: 'absolute',
 | 
						|
                    right: '$2',
 | 
						|
                    fontSize: '$xs',
 | 
						|
                    cursor: 'pointer',
 | 
						|
                    alignContent: 'center',
 | 
						|
                    display: 'flex'
 | 
						|
                  }}
 | 
						|
                  onClick={() => handleEstimateFee()}
 | 
						|
                >
 | 
						|
                  Suggest
 | 
						|
                </Button>
 | 
						|
              )}
 | 
						|
            </TxField>
 | 
						|
          )
 | 
						|
        })}
 | 
						|
        <TxField multiLine label="Hook parameters">
 | 
						|
          <Flex column fluid>
 | 
						|
            {Object.entries(hookParameters).map(([id, { label, value }]) => (
 | 
						|
              <Flex column key={id} css={{ mb: '$2' }}>
 | 
						|
                <Flex row>
 | 
						|
                  <Input
 | 
						|
                    placeholder="Parameter name"
 | 
						|
                    value={label}
 | 
						|
                    onChange={e => {
 | 
						|
                      setState({
 | 
						|
                        hookParameters: {
 | 
						|
                          ...hookParameters,
 | 
						|
                          [id]: { label: e.target.value, value }
 | 
						|
                        }
 | 
						|
                      })
 | 
						|
                    }}
 | 
						|
                  />
 | 
						|
                  <Input
 | 
						|
                    css={{ mx: '$2' }}
 | 
						|
                    placeholder="Value"
 | 
						|
                    value={value}
 | 
						|
                    onChange={e => {
 | 
						|
                      setState({
 | 
						|
                        hookParameters: {
 | 
						|
                          ...hookParameters,
 | 
						|
                          [id]: { label, value: e.target.value }
 | 
						|
                        }
 | 
						|
                      })
 | 
						|
                    }}
 | 
						|
                  />
 | 
						|
                  <Button
 | 
						|
                    onClick={() => {
 | 
						|
                      const { [id]: _, ...rest } = hookParameters
 | 
						|
                      setState({ hookParameters: rest })
 | 
						|
                    }}
 | 
						|
                    variant="destroy"
 | 
						|
                  >
 | 
						|
                    <Trash weight="regular" size="16px" />
 | 
						|
                  </Button>
 | 
						|
                </Flex>
 | 
						|
              </Flex>
 | 
						|
            ))}
 | 
						|
            <Button
 | 
						|
              outline
 | 
						|
              fullWidth
 | 
						|
              type="button"
 | 
						|
              onClick={() => {
 | 
						|
                const id = Object.keys(hookParameters).length
 | 
						|
                setState({
 | 
						|
                  hookParameters: { ...hookParameters, [id]: { label: '', value: '' } }
 | 
						|
                })
 | 
						|
              }}
 | 
						|
            >
 | 
						|
              <Plus size="16px" />
 | 
						|
              Add Hook Parameter
 | 
						|
            </Button>
 | 
						|
          </Flex>
 | 
						|
        </TxField>
 | 
						|
        <TxField multiLine label="Memos">
 | 
						|
          <Flex column fluid>
 | 
						|
            {Object.entries(memos).map(([id, memo]) => (
 | 
						|
              <Flex column key={id} css={{ mb: '$2' }}>
 | 
						|
                <Flex
 | 
						|
                  row
 | 
						|
                  css={{
 | 
						|
                    flexWrap: 'wrap',
 | 
						|
                    width: '100%'
 | 
						|
                  }}
 | 
						|
                >
 | 
						|
                  <Input
 | 
						|
                    placeholder="Memo type"
 | 
						|
                    value={memo.type}
 | 
						|
                    onChange={e => {
 | 
						|
                      setState({
 | 
						|
                        memos: {
 | 
						|
                          ...memos,
 | 
						|
                          [id]: { ...memo, type: e.target.value }
 | 
						|
                        }
 | 
						|
                      })
 | 
						|
                    }}
 | 
						|
                  />
 | 
						|
                  <Input
 | 
						|
                    placeholder="Data"
 | 
						|
                    css={{ mx: '$2' }}
 | 
						|
                    value={memo.data}
 | 
						|
                    onChange={e => {
 | 
						|
                      setState({
 | 
						|
                        memos: {
 | 
						|
                          ...memos,
 | 
						|
                          [id]: { ...memo, data: e.target.value }
 | 
						|
                        }
 | 
						|
                      })
 | 
						|
                    }}
 | 
						|
                  />
 | 
						|
                  <Input
 | 
						|
                    placeholder="Format"
 | 
						|
                    value={memo.format}
 | 
						|
                    onChange={e => {
 | 
						|
                      setState({
 | 
						|
                        memos: {
 | 
						|
                          ...memos,
 | 
						|
                          [id]: { ...memo, format: e.target.value }
 | 
						|
                        }
 | 
						|
                      })
 | 
						|
                    }}
 | 
						|
                  />
 | 
						|
                  <Button
 | 
						|
                    css={{ ml: '$2' }}
 | 
						|
                    onClick={() => {
 | 
						|
                      const { [id]: _, ...rest } = memos
 | 
						|
                      setState({ memos: rest })
 | 
						|
                    }}
 | 
						|
                    variant="destroy"
 | 
						|
                  >
 | 
						|
                    <Trash weight="regular" size="16px" />
 | 
						|
                  </Button>
 | 
						|
                </Flex>
 | 
						|
              </Flex>
 | 
						|
            ))}
 | 
						|
            <Button
 | 
						|
              outline
 | 
						|
              fullWidth
 | 
						|
              type="button"
 | 
						|
              onClick={() => {
 | 
						|
                const id = Object.keys(memos).length
 | 
						|
                setState({
 | 
						|
                  memos: { ...memos, [id]: { data: '', format: '', type: '' } }
 | 
						|
                })
 | 
						|
              }}
 | 
						|
            >
 | 
						|
              <Plus size="16px" />
 | 
						|
              Add Memo
 | 
						|
            </Button>
 | 
						|
          </Flex>
 | 
						|
        </TxField>
 | 
						|
      </Flex>
 | 
						|
    </Container>
 | 
						|
  )
 | 
						|
}
 | 
						|
 | 
						|
export const CreatableAccount: FC<{
 | 
						|
  value: string | undefined
 | 
						|
  field: keyof TxFields
 | 
						|
  placeholder?: string
 | 
						|
  setField: (field: keyof TxFields, value: string, opFields?: TxFields) => void
 | 
						|
}> = ({ value, field, setField, placeholder }) => {
 | 
						|
  const { accounts } = useSnapshot(state)
 | 
						|
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
						|
    label: acc.name,
 | 
						|
    value: acc.address
 | 
						|
  }))
 | 
						|
  const label = accountOptions.find(a => a.value === value)?.label || value
 | 
						|
  const val = {
 | 
						|
    value,
 | 
						|
    label
 | 
						|
  }
 | 
						|
  placeholder = placeholder || `${capitalize(field)} account`
 | 
						|
  return (
 | 
						|
    <CreatableSelect
 | 
						|
      isClearable
 | 
						|
      instanceId={field}
 | 
						|
      placeholder={placeholder}
 | 
						|
      options={accountOptions}
 | 
						|
      value={value ? val : undefined}
 | 
						|
      onChange={(acc: any) => setField(field, acc?.value)}
 | 
						|
    />
 | 
						|
  )
 | 
						|
}
 | 
						|
 | 
						|
export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
 | 
						|
  label,
 | 
						|
  children,
 | 
						|
  multiLine = false
 | 
						|
}) => {
 | 
						|
  return (
 | 
						|
    <Flex
 | 
						|
      row
 | 
						|
      fluid
 | 
						|
      css={{
 | 
						|
        justifyContent: 'flex-end',
 | 
						|
        alignItems: multiLine ? 'flex-start' : 'center',
 | 
						|
        position: 'relative',
 | 
						|
        mb: '$2',
 | 
						|
        mt: '1px',
 | 
						|
        pr: '1px'
 | 
						|
      }}
 | 
						|
    >
 | 
						|
      <Text muted css={{ mr: '$3', mt: multiLine ? '$2' : 0 }}>
 | 
						|
        {label}:{' '}
 | 
						|
      </Text>
 | 
						|
      <Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
 | 
						|
    </Flex>
 | 
						|
  )
 | 
						|
}
 |