Add responsive text input component

This commit is contained in:
Sinclair Chen 2022-10-09 18:45:03 -07:00
parent dc51e2cf46
commit 2d0cf6f0f4
19 changed files with 88 additions and 62 deletions

View File

@ -6,6 +6,7 @@ import { Col } from './layout/col'
import { ENV_CONFIG } from 'common/envs/constants'
import { Row } from './layout/row'
import { AddFundsModal } from './add-funds-modal'
import { Input } from './input'
export function AmountInput(props: {
amount: number | undefined
@ -44,9 +45,9 @@ export function AmountInput(props: {
<span className="text-greyscale-4 absolute top-1/2 my-auto ml-2 -translate-y-1/2">
{label}
</span>
<input
<Input
className={clsx(
'placeholder:text-greyscale-4 border-greyscale-2 rounded-md pl-9',
'pl-9',
error && 'input-error',
'w-24 md:w-auto',
inputClassName

View File

@ -10,6 +10,7 @@ import { formatPercent } from 'common/util/format'
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
import { tradingAllowed } from 'web/lib/firebase/contracts'
import { Linkify } from '../linkify'
import { Input } from '../input'
export function AnswerItem(props: {
answer: Answer
@ -74,8 +75,8 @@ export function AnswerItem(props: {
<Row className="items-center justify-end gap-4 self-end sm:self-start">
{!wasResolvedTo &&
(showChoice === 'checkbox' ? (
<input
className="input input-bordered w-24 justify-self-end text-2xl"
<Input
className="w-24 justify-self-end !text-2xl"
type="number"
placeholder={`${roundedProb}`}
maxLength={9}

View File

@ -41,6 +41,7 @@ import { AdjustmentsIcon } from '@heroicons/react/solid'
import { Button } from './button'
import { Modal } from './layout/modal'
import { Title } from './title'
import { Input } from './input'
export const SORTS = [
{ label: 'Newest', value: 'newest' },
@ -438,13 +439,13 @@ function ContractSearchControls(props: {
return (
<Col className={clsx('bg-base-200 top-0 z-20 gap-3 pb-3', className)}>
<Row className="gap-1 sm:gap-2">
<input
<Input
type="text"
value={query}
onChange={(e) => updateQuery(e.target.value)}
onBlur={trackCallback('search', { query: query })}
placeholder={'Search'}
className="input input-bordered w-full"
placeholder="Search"
className="w-full"
autoFocus={autoFocus}
/>
{!isMobile && !query && (

View File

@ -40,6 +40,7 @@ import {
BountiedContractBadge,
BountiedContractSmallBadge,
} from 'web/components/contract/bountied-contract-badge'
import { Input } from '../input'
export type ShowTime = 'resolve-date' | 'close-date'
@ -445,17 +446,17 @@ function EditableCloseDate(props: {
<Col className="rounded bg-white px-8 pb-8">
<Subtitle text="Edit market close time" />
<Row className="z-10 mr-2 mt-4 w-full shrink-0 flex-wrap items-center gap-2">
<input
<Input
type="date"
className="input input-bordered w-full shrink-0 sm:w-fit"
className="w-full shrink-0 sm:w-fit"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseDate(e.target.value)}
min={Date.now()}
value={closeDate}
/>
<input
<Input
type="time"
className="input input-bordered w-full shrink-0 sm:w-max"
className="w-full shrink-0 sm:w-max"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseHoursMinutes(e.target.value)}
min="00:00"

View File

@ -8,6 +8,7 @@ import { Avatar } from 'web/components/avatar'
import { Row } from 'web/components/layout/row'
import { searchInAny } from 'common/util/parse'
import { UserLink } from 'web/components/user-link'
import { Input } from './input'
export function FilterSelectUsers(props: {
setSelectedUsers: (users: User[]) => void
@ -50,13 +51,13 @@ export function FilterSelectUsers(props: {
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<UserIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
<input
<Input
type="text"
name="user name"
id="user name"
value={query}
onChange={(e) => setQuery(e.target.value)}
className="input input-bordered block w-full pl-10 focus:border-gray-300 "
className="block w-full pl-10"
placeholder="Austin Chen"
/>
</div>

View File

@ -8,6 +8,7 @@ import { Title } from '../title'
import { User } from 'common/user'
import { MAX_GROUP_NAME_LENGTH } from 'common/group'
import { createGroup } from 'web/lib/firebase/api'
import { Input } from '../input'
export function CreateGroupButton(props: {
user: User
@ -104,9 +105,8 @@ export function CreateGroupButton(props: {
<div className="form-control w-full">
<label className="mb-2 ml-1 mt-0">Group name</label>
<input
<Input
placeholder={'Your group name'}
className="input input-bordered resize-none"
disabled={isSubmitting}
value={name}
maxLength={MAX_GROUP_NAME_LENGTH}

View File

@ -10,6 +10,7 @@ import { Modal } from 'web/components/layout/modal'
import { FilterSelectUsers } from 'web/components/filter-select-users'
import { User } from 'common/user'
import { useMemberIds } from 'web/hooks/use-group'
import { Input } from '../input'
export function EditGroupButton(props: { group: Group; className?: string }) {
const { group, className } = props
@ -54,9 +55,8 @@ export function EditGroupButton(props: { group: Group; className?: string }) {
<span className="mb-1">Group name</span>
</label>
<input
<Input
placeholder="Your group name"
className="input input-bordered resize-none"
disabled={isSubmitting}
value={name}
onChange={(e) => setName(e.target.value || '')}

22
web/components/input.tsx Normal file
View File

@ -0,0 +1,22 @@
import clsx from 'clsx'
import React from 'react'
/** Text input. Wraps html `<input>` */
export const Input = (props: JSX.IntrinsicElements['input']) => {
const { className, ...rest } = props
return (
<input
className={clsx('input input-bordered text-base md:text-sm', className)}
{...rest}
/>
)
}
/*
TODO: replace daisyui style with our own. For reference:
james: text-lg placeholder:text-gray-400
inga: placeholder:text-greyscale-4 border-greyscale-2 rounded-md
austin: border-gray-300 text-gray-400 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm
*/

View File

@ -13,6 +13,7 @@ import { Button } from '../button'
import { getManalinkUrl } from 'web/pages/links'
import { DuplicateIcon } from '@heroicons/react/outline'
import { QRCode } from '../qr-code'
import { Input } from '../input'
export function CreateLinksButton(props: {
user: User
@ -120,8 +121,8 @@ function CreateManalinkForm(props: {
<span className="absolute mx-3 mt-3.5 text-sm text-gray-400">
M$
</span>
<input
className="input input-bordered w-full pl-10"
<Input
className="w-full pl-10"
type="number"
min="1"
value={newManalink.amount}
@ -136,8 +137,7 @@ function CreateManalinkForm(props: {
<div className="flex flex-col gap-2 md:flex-row">
<div className="form-control w-full md:w-1/2">
<label className="label">Uses</label>
<input
className="input input-bordered"
<Input
type="number"
min="1"
value={newManalink.maxUses ?? ''}
@ -146,7 +146,7 @@ function CreateManalinkForm(props: {
return { ...m, maxUses: parseInt(e.target.value) }
})
}
></input>
/>
</div>
<div className="form-control w-full md:w-1/2">
<label className="label">Expires in</label>

View File

@ -4,6 +4,7 @@ import { ReactNode } from 'react'
import React from 'react'
import { Col } from './layout/col'
import { Spacer } from './layout/spacer'
import { Input } from './input'
export function NumberInput(props: {
numberString: string
@ -32,9 +33,9 @@ export function NumberInput(props: {
return (
<Col className={className}>
<label className="input-group">
<input
<Input
className={clsx(
'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400',
'max-w-[200px] !text-lg',
error && 'input-error',
inputClassName
)}

View File

@ -2,6 +2,7 @@ import clsx from 'clsx'
import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
import { getPseudoProbability } from 'common/pseudo-numeric'
import { BucketInput } from './bucket-input'
import { Input } from './input'
import { Col } from './layout/col'
import { Spacer } from './layout/spacer'
@ -30,11 +31,8 @@ export function ProbabilityInput(props: {
return (
<Col className={className}>
<label className="input-group">
<input
className={clsx(
'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400',
inputClassName
)}
<Input
className={clsx('max-w-[200px] !text-lg', inputClassName)}
type="number"
max={99}
min={1}

View File

@ -1,3 +1,4 @@
import { Input } from './input'
import { Row } from './layout/row'
export function ProbabilitySelector(props: {
@ -10,10 +11,10 @@ export function ProbabilitySelector(props: {
return (
<Row className="items-center gap-2">
<label className="input-group input-group-lg text-lg">
<input
<Input
type="number"
value={probabilityInt}
className="input input-bordered input-md w-28 text-lg"
className="input-md w-28 !text-lg"
disabled={isSubmitting}
min={1}
max={99}

View File

@ -24,6 +24,7 @@ import { getUser } from 'web/lib/firebase/users'
import { SiteLink } from 'web/components/site-link'
import { User } from 'common/user'
import { SEO } from 'web/components/SEO'
import { Input } from 'web/components/input'
export async function getStaticProps() {
let txns = await getAllCharityTxns()
@ -171,11 +172,11 @@ export default function Charity(props: {
/>
<Spacer h={10} />
<input
<Input
type="text"
onChange={(e) => debouncedQuery(e.target.value)}
placeholder="Find a charity"
className="input input-bordered mb-6 w-full"
className="mb-6 w-full"
/>
</Col>
<div className="grid max-w-xl grid-flow-row grid-cols-1 gap-4 self-center lg:max-w-full lg:grid-cols-2 xl:grid-cols-3">

View File

@ -9,6 +9,7 @@ import {
urlParamStore,
} from 'web/hooks/use-persistent-state'
import { PAST_BETS } from 'common/user'
import { Input } from 'web/components/input'
const MAX_CONTRACTS_RENDERED = 100
@ -88,12 +89,12 @@ export default function ContractSearchFirestore(props: {
<div>
{/* Show a search input next to a sort dropdown */}
<div className="mt-2 mb-8 flex justify-between gap-2">
<input
<Input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search markets"
className="input input-bordered w-full"
className="w-full"
/>
<select
className="select select-bordered"

View File

@ -39,6 +39,7 @@ import { SiteLink } from 'web/components/site-link'
import { Button } from 'web/components/button'
import { AddFundsModal } from 'web/components/add-funds-modal'
import ShortToggle from 'web/components/widgets/short-toggle'
import { Input } from 'web/components/input'
export const getServerSideProps = redirectIfLoggedOut('/', async (_, creds) => {
return { props: { auth: await getUserAndPrivateUser(creds.uid) } }
@ -329,9 +330,9 @@ export function NewContract(props: {
</label>
<Row className="gap-2">
<input
<Input
type="number"
className="input input-bordered w-32"
className="w-32"
placeholder="LOW"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setMinString(e.target.value)}
@ -340,9 +341,9 @@ export function NewContract(props: {
disabled={isSubmitting}
value={minString ?? ''}
/>
<input
<Input
type="number"
className="input input-bordered w-32"
className="w-32"
placeholder="HIGH"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setMaxString(e.target.value)}
@ -374,9 +375,8 @@ export function NewContract(props: {
</label>
<Row className="gap-2">
<input
<Input
type="number"
className="input input-bordered"
placeholder="Initial value"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setInitialValueString(e.target.value)}
@ -446,19 +446,17 @@ export function NewContract(props: {
className={'col-span-4 sm:col-span-2'}
/>
</Row>
<Row>
<input
<Row className='mt-4 gap-2'>
<Input
type={'date'}
className="input input-bordered mt-4"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseDate(e.target.value)}
min={Math.round(Date.now() / MINUTE_MS) * MINUTE_MS}
disabled={isSubmitting}
value={closeDate}
/>
<input
<Input
type={'time'}
className="input input-bordered mt-4 ml-2"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseHoursMinutes(e.target.value)}
min={'00:00'}

View File

@ -17,6 +17,7 @@ import { MAX_QUESTION_LENGTH } from 'common/contract'
import { NoSEO } from 'web/components/NoSEO'
import ShortToggle from 'web/components/widgets/short-toggle'
import { removeUndefinedProps } from 'common/util/object'
import { Input } from 'web/components/input'
export default function CreateDateDocPage() {
const user = useUser()
@ -94,9 +95,8 @@ export default function CreateDateDocPage() {
<Col className="gap-8">
<Col className="max-w-[160px] justify-start gap-4">
<div className="">Birthday</div>
<input
<Input
type={'date'}
className="input input-bordered"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setBirthday(e.target.value)}
max={Math.round(Date.now() / MINUTE_MS) * MINUTE_MS}

View File

@ -20,6 +20,7 @@ import { SEO } from 'web/components/SEO'
import { GetServerSideProps } from 'next'
import { authenticateOnServer } from 'web/lib/firebase/server-auth'
import { useUser } from 'web/hooks/use-user'
import { Input } from 'web/components/input'
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const creds = await authenticateOnServer(ctx)
@ -106,12 +107,12 @@ export default function Groups(props: {
title: 'All',
content: (
<Col>
<input
<Input
type="text"
onChange={(e) => debouncedQuery(e.target.value)}
placeholder="Search groups"
value={query}
className="input input-bordered mb-4 w-full"
className="mb-4 w-full"
/>
<div className="flex flex-wrap justify-center gap-4">
@ -134,12 +135,12 @@ export default function Groups(props: {
title: 'My Groups',
content: (
<Col>
<input
<Input
type="text"
value={query}
onChange={(e) => debouncedQuery(e.target.value)}
placeholder="Search your groups"
className="input input-bordered mb-4 w-full"
className="mb-4 w-full"
/>
<div className="flex flex-wrap justify-center gap-4">

View File

@ -48,6 +48,7 @@ import {
} from 'web/hooks/use-contracts'
import { ProfitBadge } from 'web/components/profit-badge'
import { LoadingIndicator } from 'web/components/loading-indicator'
import { Input } from 'web/components/input'
export default function Home() {
const user = useUser()
@ -99,10 +100,10 @@ export default function Home() {
<Row
className={'mb-2 w-full items-center justify-between gap-4 sm:gap-8'}
>
<input
<Input
type="text"
placeholder={'Search'}
className="input input-bordered w-full"
className="w-full"
onClick={() => Router.push('/search')}
/>
<CustomizeButton justIcon />

View File

@ -5,6 +5,7 @@ import Link from 'next/link'
import React, { useState } from 'react'
import Textarea from 'react-expanding-textarea'
import { ConfirmationButton } from 'web/components/confirmation-button'
import { Input } from 'web/components/input'
import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row'
import { Page } from 'web/components/page'
@ -50,9 +51,8 @@ function EditUserField(props: {
onBlur={updateField}
/>
) : (
<input
<Input
type="text"
className="input input-bordered"
value={value}
onChange={(e) => setValue(e.target.value || '')}
onBlur={updateField}
@ -152,10 +152,9 @@ export default function ProfilePage(props: {
<div>
<label className="label">Display name</label>
<input
<Input
type="text"
placeholder="Display name"
className="input input-bordered"
value={name}
onChange={(e) => setName(e.target.value || '')}
onBlur={updateDisplayName}
@ -164,10 +163,9 @@ export default function ProfilePage(props: {
<div>
<label className="label">Username</label>
<input
<Input
type="text"
placeholder="Username"
className="input input-bordered"
value={username}
onChange={(e) => setUsername(e.target.value || '')}
onBlur={updateUsername}
@ -199,10 +197,9 @@ export default function ProfilePage(props: {
<div>
<label className="label">API key</label>
<div className="input-group w-full">
<input
<Input
type="text"
placeholder="Click refresh to generate key"
className="input input-bordered w-full"
value={apiKey}
readOnly
/>