2025-10-22 21:12:32 +00:00
|
|
|
import React, { useLayoutEffect, useCallback } from 'react';
|
2024-12-08 07:56:23 +00:00
|
|
|
import { BaseInput } from './BaseComponents/BaseInput';
|
|
|
|
|
import { useInput } from './BaseComponents/hooks/useInput';
|
2025-06-27 11:35:01 +00:00
|
|
|
import { useDynamicHeight } from '@/_hooks/useDynamicHeight';
|
2025-10-22 21:12:32 +00:00
|
|
|
import { useHeightObserver } from '@/_hooks/useHeightObserver';
|
2024-12-08 07:56:23 +00:00
|
|
|
|
2025-01-09 19:14:40 +00:00
|
|
|
export const TextArea = (props) => {
|
2024-12-08 07:56:23 +00:00
|
|
|
const inputLogic = useInput(props);
|
2026-03-12 07:22:52 +00:00
|
|
|
const {
|
|
|
|
|
properties,
|
|
|
|
|
height,
|
|
|
|
|
width,
|
|
|
|
|
id,
|
|
|
|
|
adjustComponentPositions,
|
|
|
|
|
currentLayout,
|
|
|
|
|
currentMode,
|
|
|
|
|
subContainerIndex,
|
|
|
|
|
componentType,
|
|
|
|
|
} = props;
|
2025-06-27 11:35:01 +00:00
|
|
|
const { inputRef, value } = inputLogic;
|
2025-10-06 09:42:14 +00:00
|
|
|
const isDynamicHeightEnabled = properties.dynamicHeight && currentMode === 'view';
|
2025-06-27 11:35:01 +00:00
|
|
|
|
2025-10-22 21:12:32 +00:00
|
|
|
const heightChangeValue = useHeightObserver(inputRef, isDynamicHeightEnabled);
|
|
|
|
|
const resizeTextArea = useCallback(() => {
|
|
|
|
|
if (!inputRef.current) return;
|
|
|
|
|
if (!isDynamicHeightEnabled) {
|
2025-12-19 10:57:58 +00:00
|
|
|
inputRef.current.style.height = '100%';
|
2025-06-27 11:35:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
inputRef.current.style.height = 'auto';
|
2026-04-21 13:33:14 +00:00
|
|
|
// Subtract the input container's padding + border so the outer wrapper
|
|
|
|
|
// matches the authored widget height when content fits. Without this,
|
|
|
|
|
// wrapper = textarea + container padding/border, making dynamic-height
|
|
|
|
|
// textareas visibly taller than non-dynamic ones.
|
|
|
|
|
const container = inputRef.current.parentElement;
|
|
|
|
|
const cs = container ? window.getComputedStyle(container) : null;
|
|
|
|
|
const containerPaddingAndBorder = cs
|
|
|
|
|
? parseFloat(cs.paddingTop) +
|
|
|
|
|
parseFloat(cs.paddingBottom) +
|
|
|
|
|
parseFloat(cs.borderTopWidth) +
|
|
|
|
|
parseFloat(cs.borderBottomWidth)
|
|
|
|
|
: 0;
|
|
|
|
|
const effectiveMax = Math.max(height - containerPaddingAndBorder, 0);
|
2025-10-22 21:12:32 +00:00
|
|
|
inputRef.current.style.height =
|
2026-04-21 13:33:14 +00:00
|
|
|
effectiveMax >= inputRef.current.scrollHeight ? `${effectiveMax}px` : `${inputRef.current.scrollHeight}px`;
|
2025-10-22 21:12:32 +00:00
|
|
|
}, [inputRef?.current, height, isDynamicHeightEnabled]);
|
2025-06-27 11:35:01 +00:00
|
|
|
|
2025-10-22 21:12:32 +00:00
|
|
|
useLayoutEffect(() => {
|
2025-06-27 11:35:01 +00:00
|
|
|
resizeTextArea();
|
2025-10-22 21:12:32 +00:00
|
|
|
}, [width, height, isDynamicHeightEnabled, properties.placeholder, value, heightChangeValue]);
|
2025-06-27 11:35:01 +00:00
|
|
|
|
|
|
|
|
useDynamicHeight({
|
2025-10-06 09:42:14 +00:00
|
|
|
isDynamicHeightEnabled,
|
2025-06-27 11:35:01 +00:00
|
|
|
id,
|
|
|
|
|
height,
|
2025-10-22 21:12:32 +00:00
|
|
|
value: heightChangeValue,
|
2025-06-27 11:35:01 +00:00
|
|
|
adjustComponentPositions,
|
|
|
|
|
currentLayout,
|
|
|
|
|
width,
|
2025-10-07 07:23:13 +00:00
|
|
|
visibility: inputLogic.visibility,
|
2025-10-13 05:52:36 +00:00
|
|
|
subContainerIndex,
|
2026-03-12 07:22:52 +00:00
|
|
|
componentType,
|
2025-06-27 11:35:01 +00:00
|
|
|
});
|
2024-12-08 07:56:23 +00:00
|
|
|
|
2025-12-19 10:57:58 +00:00
|
|
|
return (
|
|
|
|
|
<BaseInput
|
|
|
|
|
{...props}
|
|
|
|
|
{...inputLogic}
|
|
|
|
|
isDynamicHeightEnabled={isDynamicHeightEnabled}
|
|
|
|
|
inputType="textarea"
|
|
|
|
|
classes={{ leftIcon: 'tw-mt-0.5', loaderContainer: 'tw-mt-0.5' }}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2024-12-08 07:56:23 +00:00
|
|
|
};
|