Add styled expanding textarea component
This commit is contained in:
parent
2d0cf6f0f4
commit
43217754c8
|
@ -1,6 +1,5 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import { findBestMatch } from 'string-similarity'
|
import { findBestMatch } from 'string-similarity'
|
||||||
|
|
||||||
import { FreeResponseContract } from 'common/contract'
|
import { FreeResponseContract } from 'common/contract'
|
||||||
|
@ -26,6 +25,7 @@ import { MAX_ANSWER_LENGTH } from 'common/answer'
|
||||||
import { withTracking } from 'web/lib/service/analytics'
|
import { withTracking } from 'web/lib/service/analytics'
|
||||||
import { lowerCase } from 'lodash'
|
import { lowerCase } from 'lodash'
|
||||||
import { Button } from '../button'
|
import { Button } from '../button'
|
||||||
|
import { ExpandingInput } from '../expanding-input'
|
||||||
|
|
||||||
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
|
@ -122,10 +122,10 @@ export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
|
||||||
<Col className="gap-4 rounded">
|
<Col className="gap-4 rounded">
|
||||||
<Col className="flex-1 gap-2 px-4 xl:px-0">
|
<Col className="flex-1 gap-2 px-4 xl:px-0">
|
||||||
<div className="mb-1">Add your answer</div>
|
<div className="mb-1">Add your answer</div>
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => changeAnswer(e.target.value)}
|
onChange={(e) => changeAnswer(e.target.value)}
|
||||||
className="textarea textarea-bordered w-full resize-none"
|
className="w-full"
|
||||||
placeholder="Type your answer..."
|
placeholder="Type your answer..."
|
||||||
rows={1}
|
rows={1}
|
||||||
maxLength={MAX_ANSWER_LENGTH}
|
maxLength={MAX_ANSWER_LENGTH}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import { XIcon } from '@heroicons/react/solid'
|
import { XIcon } from '@heroicons/react/solid'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
import { Row } from '../layout/row'
|
import { Row } from '../layout/row'
|
||||||
|
import { ExpandingInput } from '../expanding-input'
|
||||||
|
|
||||||
export function MultipleChoiceAnswers(props: {
|
export function MultipleChoiceAnswers(props: {
|
||||||
answers: string[]
|
answers: string[]
|
||||||
|
@ -27,10 +27,10 @@ export function MultipleChoiceAnswers(props: {
|
||||||
{answers.map((answer, i) => (
|
{answers.map((answer, i) => (
|
||||||
<Row className="mb-2 items-center gap-2 align-middle">
|
<Row className="mb-2 items-center gap-2 align-middle">
|
||||||
{i + 1}.{' '}
|
{i + 1}.{' '}
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
value={answer}
|
value={answer}
|
||||||
onChange={(e) => setAnswer(i, e.target.value)}
|
onChange={(e) => setAnswer(i, e.target.value)}
|
||||||
className="textarea textarea-bordered ml-2 w-full resize-none"
|
className="ml-2 w-full"
|
||||||
placeholder="Type your answer..."
|
placeholder="Type your answer..."
|
||||||
rows={1}
|
rows={1}
|
||||||
maxLength={MAX_ANSWER_LENGTH}
|
maxLength={MAX_ANSWER_LENGTH}
|
||||||
|
|
|
@ -20,11 +20,11 @@ import { getProbability } from 'common/calculate'
|
||||||
import { createMarket } from 'web/lib/firebase/api'
|
import { createMarket } from 'web/lib/firebase/api'
|
||||||
import { removeUndefinedProps } from 'common/util/object'
|
import { removeUndefinedProps } from 'common/util/object'
|
||||||
import { FIXED_ANTE } from 'common/economy'
|
import { FIXED_ANTE } from 'common/economy'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import { useTextEditor } from 'web/components/editor'
|
import { useTextEditor } from 'web/components/editor'
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { CopyLinkButton } from '../copy-link-button'
|
import { CopyLinkButton } from '../copy-link-button'
|
||||||
|
import { ExpandingInput } from '../expanding-input'
|
||||||
|
|
||||||
type challengeInfo = {
|
type challengeInfo = {
|
||||||
amount: number
|
amount: number
|
||||||
|
@ -153,9 +153,9 @@ function CreateChallengeForm(props: {
|
||||||
{contract ? (
|
{contract ? (
|
||||||
<span className="underline">{contract.question}</span>
|
<span className="underline">{contract.question}</span>
|
||||||
) : (
|
) : (
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
placeholder="e.g. Will a Democrat be the next president?"
|
placeholder="e.g. Will a Democrat be the next president?"
|
||||||
className="input input-bordered mt-1 w-full resize-none"
|
className="mt-1 w-full"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
maxLength={MAX_QUESTION_LENGTH}
|
maxLength={MAX_QUESTION_LENGTH}
|
||||||
value={challengeInfo.question}
|
value={challengeInfo.question}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
|
|
||||||
import { Contract, MAX_DESCRIPTION_LENGTH } from 'common/contract'
|
import { Contract, MAX_DESCRIPTION_LENGTH } from 'common/contract'
|
||||||
import { exhibitExts } from 'common/util/parse'
|
import { exhibitExts } from 'common/util/parse'
|
||||||
import { useAdmin } from 'web/hooks/use-admin'
|
import { useAdmin } from 'web/hooks/use-admin'
|
||||||
|
@ -15,6 +13,7 @@ import { Button } from '../button'
|
||||||
import { Spacer } from '../layout/spacer'
|
import { Spacer } from '../layout/spacer'
|
||||||
import { Editor, Content as ContentType } from '@tiptap/react'
|
import { Editor, Content as ContentType } from '@tiptap/react'
|
||||||
import { insertContent } from '../editor/utils'
|
import { insertContent } from '../editor/utils'
|
||||||
|
import { ExpandingInput } from '../expanding-input'
|
||||||
|
|
||||||
export function ContractDescription(props: {
|
export function ContractDescription(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -138,8 +137,8 @@ function EditQuestion(props: {
|
||||||
|
|
||||||
return editing ? (
|
return editing ? (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
className="textarea textarea-bordered mb-1 h-24 w-full resize-none"
|
className="mb-1 h-24 w-full"
|
||||||
rows={2}
|
rows={2}
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => setText(e.target.value || '')}
|
onChange={(e) => setText(e.target.value || '')}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Spacer } from 'web/components/layout/spacer'
|
import { Spacer } from 'web/components/layout/spacer'
|
||||||
import { Title } from 'web/components/title'
|
import { Title } from 'web/components/title'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
|
|
||||||
import { TextEditor, useTextEditor } from 'web/components/editor'
|
import { TextEditor, useTextEditor } from 'web/components/editor'
|
||||||
import { createPost } from 'web/lib/firebase/api'
|
import { createPost } from 'web/lib/firebase/api'
|
||||||
|
@ -10,6 +9,7 @@ import Router from 'next/router'
|
||||||
import { MAX_POST_TITLE_LENGTH } from 'common/post'
|
import { MAX_POST_TITLE_LENGTH } from 'common/post'
|
||||||
import { postPath } from 'web/lib/firebase/posts'
|
import { postPath } from 'web/lib/firebase/posts'
|
||||||
import { Group } from 'common/group'
|
import { Group } from 'common/group'
|
||||||
|
import { ExpandingInput } from './expanding-input'
|
||||||
|
|
||||||
export function CreatePost(props: { group?: Group }) {
|
export function CreatePost(props: { group?: Group }) {
|
||||||
const [title, setTitle] = useState('')
|
const [title, setTitle] = useState('')
|
||||||
|
@ -60,9 +60,8 @@ export function CreatePost(props: { group?: Group }) {
|
||||||
Title<span className={'text-red-700'}> *</span>
|
Title<span className={'text-red-700'}> *</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
placeholder="e.g. Elon Mania Post"
|
placeholder="e.g. Elon Mania Post"
|
||||||
className="input input-bordered resize-none"
|
|
||||||
autoFocus
|
autoFocus
|
||||||
maxLength={MAX_POST_TITLE_LENGTH}
|
maxLength={MAX_POST_TITLE_LENGTH}
|
||||||
value={title}
|
value={title}
|
||||||
|
@ -74,9 +73,8 @@ export function CreatePost(props: { group?: Group }) {
|
||||||
Subtitle<span className={'text-red-700'}> *</span>
|
Subtitle<span className={'text-red-700'}> *</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
placeholder="e.g. How Elon Musk is getting everyone's attention"
|
placeholder="e.g. How Elon Musk is getting everyone's attention"
|
||||||
className="input input-bordered resize-none"
|
|
||||||
autoFocus
|
autoFocus
|
||||||
maxLength={MAX_POST_TITLE_LENGTH}
|
maxLength={MAX_POST_TITLE_LENGTH}
|
||||||
value={subtitle}
|
value={subtitle}
|
||||||
|
|
16
web/components/expanding-input.tsx
Normal file
16
web/components/expanding-input.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import Textarea from 'react-expanding-textarea'
|
||||||
|
|
||||||
|
/** Expanding `<textarea>` with same style as input.tsx */
|
||||||
|
export const ExpandingInput = (props: Parameters<typeof Textarea>[0]) => {
|
||||||
|
const { className, ...rest } = props
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
className={clsx(
|
||||||
|
'textarea textarea-bordered resize-none text-[16px] md:text-[14px]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -7,13 +7,13 @@ import { User } from 'common/user'
|
||||||
import { ManalinkCard, ManalinkInfo } from 'web/components/manalink-card'
|
import { ManalinkCard, ManalinkInfo } from 'web/components/manalink-card'
|
||||||
import { createManalink } from 'web/lib/firebase/manalinks'
|
import { createManalink } from 'web/lib/firebase/manalinks'
|
||||||
import { Modal } from 'web/components/layout/modal'
|
import { Modal } from 'web/components/layout/modal'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Button } from '../button'
|
import { Button } from '../button'
|
||||||
import { getManalinkUrl } from 'web/pages/links'
|
import { getManalinkUrl } from 'web/pages/links'
|
||||||
import { DuplicateIcon } from '@heroicons/react/outline'
|
import { DuplicateIcon } from '@heroicons/react/outline'
|
||||||
import { QRCode } from '../qr-code'
|
import { QRCode } from '../qr-code'
|
||||||
import { Input } from '../input'
|
import { Input } from '../input'
|
||||||
|
import { ExpandingInput } from '../expanding-input'
|
||||||
|
|
||||||
export function CreateLinksButton(props: {
|
export function CreateLinksButton(props: {
|
||||||
user: User
|
user: User
|
||||||
|
@ -165,10 +165,9 @@ function CreateManalinkForm(props: {
|
||||||
</div>
|
</div>
|
||||||
<div className="form-control w-full">
|
<div className="form-control w-full">
|
||||||
<label className="label">Message</label>
|
<label className="label">Message</label>
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
placeholder={defaultMessage}
|
placeholder={defaultMessage}
|
||||||
maxLength={200}
|
maxLength={200}
|
||||||
className="input input-bordered resize-none"
|
|
||||||
autoFocus
|
autoFocus
|
||||||
value={newManalink.message}
|
value={newManalink.message}
|
||||||
rows="3"
|
rows="3"
|
||||||
|
|
|
@ -2,7 +2,6 @@ import router, { useRouter } from 'next/router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import { Spacer } from 'web/components/layout/spacer'
|
import { Spacer } from 'web/components/layout/spacer'
|
||||||
import { getUserAndPrivateUser } from 'web/lib/firebase/users'
|
import { getUserAndPrivateUser } from 'web/lib/firebase/users'
|
||||||
import { Contract, contractPath } from 'web/lib/firebase/contracts'
|
import { Contract, contractPath } from 'web/lib/firebase/contracts'
|
||||||
|
@ -40,6 +39,7 @@ import { Button } from 'web/components/button'
|
||||||
import { AddFundsModal } from 'web/components/add-funds-modal'
|
import { AddFundsModal } from 'web/components/add-funds-modal'
|
||||||
import ShortToggle from 'web/components/widgets/short-toggle'
|
import ShortToggle from 'web/components/widgets/short-toggle'
|
||||||
import { Input } from 'web/components/input'
|
import { Input } from 'web/components/input'
|
||||||
|
import { ExpandingInput } from 'web/components/expanding-input'
|
||||||
|
|
||||||
export const getServerSideProps = redirectIfLoggedOut('/', async (_, creds) => {
|
export const getServerSideProps = redirectIfLoggedOut('/', async (_, creds) => {
|
||||||
return { props: { auth: await getUserAndPrivateUser(creds.uid) } }
|
return { props: { auth: await getUserAndPrivateUser(creds.uid) } }
|
||||||
|
@ -105,9 +105,8 @@ export default function Create(props: { auth: { user: User } }) {
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
placeholder="e.g. Will the Democrats win the 2024 US presidential election?"
|
placeholder="e.g. Will the Democrats win the 2024 US presidential election?"
|
||||||
className="input input-bordered resize-none"
|
|
||||||
autoFocus
|
autoFocus
|
||||||
maxLength={MAX_QUESTION_LENGTH}
|
maxLength={MAX_QUESTION_LENGTH}
|
||||||
value={question}
|
value={question}
|
||||||
|
@ -446,7 +445,7 @@ export function NewContract(props: {
|
||||||
className={'col-span-4 sm:col-span-2'}
|
className={'col-span-4 sm:col-span-2'}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Row className='mt-4 gap-2'>
|
<Row className="mt-4 gap-2">
|
||||||
<Input
|
<Input
|
||||||
type={'date'}
|
type={'date'}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
|
|
||||||
import { DateDoc } from 'common/post'
|
import { DateDoc } from 'common/post'
|
||||||
import { useTextEditor, TextEditor } from 'web/components/editor'
|
import { useTextEditor, TextEditor } from 'web/components/editor'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
|
@ -18,6 +16,7 @@ import { NoSEO } from 'web/components/NoSEO'
|
||||||
import ShortToggle from 'web/components/widgets/short-toggle'
|
import ShortToggle from 'web/components/widgets/short-toggle'
|
||||||
import { removeUndefinedProps } from 'common/util/object'
|
import { removeUndefinedProps } from 'common/util/object'
|
||||||
import { Input } from 'web/components/input'
|
import { Input } from 'web/components/input'
|
||||||
|
import { ExpandingInput } from 'web/components/expanding-input'
|
||||||
|
|
||||||
export default function CreateDateDocPage() {
|
export default function CreateDateDocPage() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -122,8 +121,7 @@ export default function CreateDateDocPage() {
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Col className="gap-2">
|
<Col className="gap-2">
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
className="input input-bordered resize-none"
|
|
||||||
maxLength={MAX_QUESTION_LENGTH}
|
maxLength={MAX_QUESTION_LENGTH}
|
||||||
value={question}
|
value={question}
|
||||||
onChange={(e) => setQuestion(e.target.value || '')}
|
onChange={(e) => setQuestion(e.target.value || '')}
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { PrivateUser, User } from 'common/user'
|
||||||
import { cleanDisplayName, cleanUsername } from 'common/util/clean-username'
|
import { cleanDisplayName, cleanUsername } from 'common/util/clean-username'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
import { ConfirmationButton } from 'web/components/confirmation-button'
|
import { ConfirmationButton } from 'web/components/confirmation-button'
|
||||||
|
import { ExpandingInput } from 'web/components/expanding-input'
|
||||||
import { Input } from 'web/components/input'
|
import { Input } from 'web/components/input'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
|
@ -44,8 +44,8 @@ function EditUserField(props: {
|
||||||
<label className="label">{label}</label>
|
<label className="label">{label}</label>
|
||||||
|
|
||||||
{field === 'bio' ? (
|
{field === 'bio' ? (
|
||||||
<Textarea
|
<ExpandingInput
|
||||||
className="textarea textarea-bordered w-full resize-none"
|
className="w-full"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
onBlur={updateField}
|
onBlur={updateField}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user