Skip to content

Commit

Permalink
fix(Textarea): autoResize時にvalue, defaultValueの初期値でも高さを反映するように修正 (#5330)
Browse files Browse the repository at this point in the history
Co-authored-by: Yuta Yamamoto <[email protected]>
  • Loading branch information
Qs-F and yt-ymmt authored Jan 28, 2025
1 parent cc84b2a commit 8d09ffa
Showing 1 changed file with 47 additions and 33 deletions.
80 changes: 47 additions & 33 deletions packages/smarthr-ui/src/components/Textarea/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import React, {
ChangeEvent,
ComponentPropsWithRef,
ReactNode,
forwardRef,
Expand Down Expand Up @@ -98,6 +99,20 @@ const textarea = tv({
},
})

const calculateIdealRows = (
element?: HTMLTextAreaElement | null,
maxRows: number = Number.MAX_SAFE_INTEGER,
): number => {
if (!element) {
return 0
}
// 現在の入力値に応じた行数
const currentInputValueRows = Math.floor(
element.scrollHeight / (defaultHtmlFontSize * Number(lineHeight.normal)),
)
return currentInputValueRows < maxRows ? currentInputValueRows : maxRows
}

export const Textarea = forwardRef<HTMLTextAreaElement, Props & ElementProps>(
(
{
Expand Down Expand Up @@ -189,12 +204,6 @@ export const Textarea = forwardRef<HTMLTextAreaElement, Props & ElementProps>(
() => textareaRef.current,
)

useEffect(() => {
if (autoFocus && textareaRef && textareaRef.current) {
textareaRef.current.focus()
}
}, [autoFocus])

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedUpdateCount = useCallback(
debounce((value: string) => {
Expand All @@ -221,49 +230,54 @@ export const Textarea = forwardRef<HTMLTextAreaElement, Props & ElementProps>(
)

const handleChange = useCallback(
(event: React.ChangeEvent<HTMLTextAreaElement>) => {
if (onChange) {
onChange(event)
}

(e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (maxLetters) {
const inputValue = event.currentTarget.value
const inputValue = e.currentTarget.value
debouncedUpdateCount(inputValue)
debouncedUpdateSrCounterMessage(inputValue)
}

onChange?.(e)
},
[debouncedUpdateCount, maxLetters, onChange, debouncedUpdateSrCounterMessage],
)

const handleInput = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!autoResize) {
return onInput && onInput(e)
}
// autoFocus時に、フォーカスを当てる
useEffect(() => {
if (autoFocus && textareaRef && textareaRef.current) {
textareaRef.current.focus()
}
}, [autoFocus])

// autoResize時に、初期値での高さを指定
useEffect(() => {
if (!autoResize) {
return
}
if (!textareaRef.current) {
return
}

const previousRows = e.target.rows
// 消したことを検知できないので必ず初期化
e.target.rows = rows
setInterimRows(calculateIdealRows(textareaRef.current, maxRows))
}, [setInterimRows, maxRows, autoResize])

const currentRows = Math.floor(
e.target.scrollHeight / (defaultHtmlFontSize * Number(lineHeight.normal)),
)
const handleInput = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
// rowsを初期化 TextareaのscrollHeightが文字列削除時に変更されないため
e.target.rows = 0

if (previousRows === currentRows) {
if (autoResize) {
const currentRows = calculateIdealRows(e.target, maxRows)
// rowsを直接反映 Textareaのrows propsが状態を変更しても反映されないため
e.target.rows = currentRows
} else if (maxRows < currentRows) {
// 最大まで達したとき高さが潰れないように代入
e.target.rows = maxRows
setInterimRows(currentRows)
}

setInterimRows(currentRows < maxRows ? currentRows : maxRows)

if (onInput) {
onInput(e)
}
onInput?.(e)
},
[autoResize, maxRows, onInput, rows],
[autoResize, maxRows, onInput],
)

const { textareaStyleProps, counterStyle, counterTextStyle } = useMemo(() => {
const { textareaEl, counter, counterText } = textarea()
return {
Expand Down

0 comments on commit 8d09ffa

Please sign in to comment.