Add maxLength counter in Growing Textarea (#95)
This commit is contained in:
parent
065630decb
commit
20ca55e1b2
|
@ -144,6 +144,7 @@ export const EditView = (props: EditViewProps) => {
|
||||||
class={styles.titleInput}
|
class={styles.titleInput}
|
||||||
placeholder={t('Header')}
|
placeholder={t('Header')}
|
||||||
initialValue={form.title}
|
initialValue={form.title}
|
||||||
|
maxLength={100}
|
||||||
/>
|
/>
|
||||||
<Show when={formErrors.title}>
|
<Show when={formErrors.title}>
|
||||||
<div class={styles.validationError}>{formErrors.title}</div>
|
<div class={styles.validationError}>{formErrors.title}</div>
|
||||||
|
@ -154,6 +155,7 @@ export const EditView = (props: EditViewProps) => {
|
||||||
class={styles.subtitleInput}
|
class={styles.subtitleInput}
|
||||||
placeholder={t('Subheader')}
|
placeholder={t('Subheader')}
|
||||||
initialValue={form.subtitle}
|
initialValue={form.subtitle}
|
||||||
|
maxLength={100}
|
||||||
/>
|
/>
|
||||||
<Editor
|
<Editor
|
||||||
shoutId={props.shout.id}
|
shoutId={props.shout.id}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
.GrowingTextarea {
|
.GrowingTextarea {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.growWrap {
|
.growWrap {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -42,4 +43,34 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maxLength {
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.6rem;
|
||||||
|
bottom: -1.2rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
border: 3px solid rgba(#ccc, 0.5);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 1.3rem;
|
||||||
|
line-height: 1;
|
||||||
|
user-select: none;
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.limited {
|
||||||
|
animation: blink 0.8s;
|
||||||
|
animation-iteration-count: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
50% {
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import styles from './GrowingTextarea.module.scss'
|
import styles from './GrowingTextarea.module.scss'
|
||||||
import { createSignal } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
initialValue?: string
|
initialValue?: string
|
||||||
value: (string) => void
|
value: (string) => void
|
||||||
|
maxLength?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GrowingTextarea = (props: Props) => {
|
export const GrowingTextarea = (props: Props) => {
|
||||||
const [value, setValue] = createSignal('')
|
const [value, setValue] = createSignal('')
|
||||||
|
const [isFocused, setIsFocused] = createSignal(false)
|
||||||
const handleChangeValue = (event) => {
|
const handleChangeValue = (event) => {
|
||||||
setValue(event.target.value)
|
setValue(event.target.value)
|
||||||
props.value(event.target.value)
|
props.value(event.target.value)
|
||||||
|
@ -31,14 +33,27 @@ export const GrowingTextarea = (props: Props) => {
|
||||||
<div class={clsx(styles.growWrap, props.class)} data-replicated-value={value()}>
|
<div class={clsx(styles.growWrap, props.class)} data-replicated-value={value()}>
|
||||||
<textarea
|
<textarea
|
||||||
rows={1}
|
rows={1}
|
||||||
|
maxlength={props.maxLength}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class={clsx(styles.textInput, props.class)}
|
class={clsx(styles.textInput, props.class)}
|
||||||
value={props.initialValue}
|
value={props.initialValue}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onInput={(event) => handleChangeValue(event)}
|
onInput={(event) => handleChangeValue(event)}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
|
onFocus={() => setIsFocused(true)}
|
||||||
|
onBlur={() => setIsFocused(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={props.maxLength && value()}>
|
||||||
|
<div
|
||||||
|
class={clsx(styles.maxLength, {
|
||||||
|
[styles.visible]: isFocused(),
|
||||||
|
[styles.limited]: value().length === props.maxLength
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{`${value().length} / ${props.maxLength}`}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user