UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

141 lines • 8.56 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useCallback, useEffect, useImperativeHandle, useRef } from 'react'; import clsx from 'clsx'; import { useDensityMode } from '@awsui/component-toolkit/internal'; import InternalButton from '../button/internal'; import { convertAutoComplete } from '../input/utils'; import { getBaseProps } from '../internal/base-component'; import { useFormFieldContext } from '../internal/context/form-field-context'; import { fireKeyboardEvent, fireNonCancelableEvent } from '../internal/events'; import * as tokens from '../internal/generated/styles/tokens'; import { useVisualRefresh } from '../internal/hooks/use-visual-mode'; import WithNativeAttributes from '../internal/utils/with-native-attributes'; import { getPromptInputStyles } from './styles'; import styles from './styles.css.js'; import testutilStyles from './test-classes/styles.css.js'; const InternalPromptInput = React.forwardRef(({ value, actionButtonAriaLabel, actionButtonIconName, actionButtonIconUrl, actionButtonIconSvg, actionButtonIconAlt, ariaLabel, autoComplete, autoFocus, disableActionButton, disableBrowserAutocorrect, disabled, maxRows, minRows, name, onAction, onBlur, onChange, onFocus, onKeyDown, onKeyUp, placeholder, readOnly, spellcheck, customPrimaryAction, secondaryActions, secondaryContent, disableSecondaryActionsPaddings, disableSecondaryContentPaddings, nativeTextareaAttributes, style, __internalRootRef, ...rest }, ref) => { const { ariaLabelledby, ariaDescribedby, controlId, invalid, warning } = useFormFieldContext(rest); const baseProps = getBaseProps(rest); const textareaRef = useRef(null); const isRefresh = useVisualRefresh(); const isCompactMode = useDensityMode(textareaRef) === 'compact'; const PADDING = isRefresh ? tokens.spaceXxs : tokens.spaceXxxs; const LINE_HEIGHT = tokens.lineHeightBodyM; const DEFAULT_MAX_ROWS = 3; useImperativeHandle(ref, () => ({ focus(...args) { var _a; (_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.focus(...args); }, select() { var _a; (_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.select(); }, setSelectionRange(...args) { var _a; (_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.setSelectionRange(...args); }, }), [textareaRef]); const handleKeyDown = (event) => { fireKeyboardEvent(onKeyDown, event); if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) { if (event.currentTarget.form && !event.isDefaultPrevented()) { event.currentTarget.form.requestSubmit(); } event.preventDefault(); fireNonCancelableEvent(onAction, { value }); } }; const handleChange = (event) => { fireNonCancelableEvent(onChange, { value: event.target.value }); adjustTextareaHeight(); }; const hasActionButton = actionButtonIconName || actionButtonIconSvg || actionButtonIconUrl || customPrimaryAction; const adjustTextareaHeight = useCallback(() => { if (textareaRef.current) { // this is required so the scrollHeight becomes dynamic, otherwise it will be locked at the highest value for the size it reached e.g. 500px textareaRef.current.style.height = 'auto'; const minTextareaHeight = `calc(${LINE_HEIGHT} + ${tokens.spaceScaledXxs} * 2)`; // the min height of Textarea with 1 row if (maxRows === -1) { const scrollHeight = `calc(${textareaRef.current.scrollHeight}px)`; textareaRef.current.style.height = `max(${scrollHeight}, ${minTextareaHeight})`; } else { const maxRowsHeight = `calc(${maxRows <= 0 ? DEFAULT_MAX_ROWS : maxRows} * (${LINE_HEIGHT} + ${PADDING} / 2) + ${PADDING})`; const scrollHeight = `calc(${textareaRef.current.scrollHeight}px)`; textareaRef.current.style.height = `min(max(${scrollHeight}, ${minTextareaHeight}), ${maxRowsHeight})`; } } }, [maxRows, LINE_HEIGHT, PADDING]); useEffect(() => { const handleResize = () => { adjustTextareaHeight(); }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, [adjustTextareaHeight]); useEffect(() => { adjustTextareaHeight(); }, [value, adjustTextareaHeight, maxRows, isCompactMode]); const attributes = { 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, 'aria-invalid': invalid ? 'true' : undefined, name, placeholder, autoFocus, className: clsx(styles.textarea, testutilStyles.textarea, { [styles.invalid]: invalid, [styles.warning]: warning, }), autoComplete: convertAutoComplete(autoComplete), spellCheck: spellcheck, disabled, readOnly: readOnly ? true : undefined, rows: minRows, onKeyDown: handleKeyDown, onKeyUp: onKeyUp && (event => fireKeyboardEvent(onKeyUp, event)), // We set a default value on the component in order to force it into the controlled mode. value: value || '', onChange: handleChange, onBlur: onBlur && (() => fireNonCancelableEvent(onBlur)), onFocus: onFocus && (() => fireNonCancelableEvent(onFocus)), }; if (disableBrowserAutocorrect) { attributes.autoCorrect = 'off'; attributes.autoCapitalize = 'off'; } const action = (React.createElement("div", { className: clsx(styles['primary-action'], testutilStyles['primary-action']) }, customPrimaryAction !== null && customPrimaryAction !== void 0 ? customPrimaryAction : (React.createElement(InternalButton, { className: clsx(styles['action-button'], testutilStyles['action-button']), ariaLabel: actionButtonAriaLabel, disabled: disabled || readOnly || disableActionButton, __focusable: readOnly, iconName: actionButtonIconName, iconUrl: actionButtonIconUrl, iconSvg: actionButtonIconSvg, iconAlt: actionButtonIconAlt, onClick: () => fireNonCancelableEvent(onAction, { value }), variant: "icon" })))); return (React.createElement("div", { ...baseProps, "aria-label": ariaLabel, className: clsx(styles.root, testutilStyles.root, baseProps.className, { [styles['textarea-readonly']]: readOnly, [styles['textarea-invalid']]: invalid, [styles['textarea-warning']]: warning && !invalid, [styles.disabled]: disabled, }), ref: __internalRootRef, role: "region", style: getPromptInputStyles(style) }, secondaryContent && (React.createElement("div", { className: clsx(styles['secondary-content'], testutilStyles['secondary-content'], { [styles['with-paddings']]: !disableSecondaryContentPaddings, [styles.invalid]: invalid, [styles.warning]: warning, }) }, secondaryContent)), React.createElement("div", { className: styles['textarea-wrapper'] }, React.createElement(WithNativeAttributes, { ...attributes, tag: "textarea", componentName: "PromptInput", nativeAttributes: nativeTextareaAttributes, ref: textareaRef, id: controlId }), hasActionButton && !secondaryActions && action), secondaryActions && (React.createElement("div", { className: clsx(styles['action-stripe'], { [styles.invalid]: invalid, [styles.warning]: warning, }) }, React.createElement("div", { className: clsx(styles['secondary-actions'], testutilStyles['secondary-actions'], { [styles['with-paddings']]: !disableSecondaryActionsPaddings, [styles['with-paddings-and-actions']]: !disableSecondaryActionsPaddings && hasActionButton, [styles.invalid]: invalid, [styles.warning]: warning, }) }, secondaryActions), React.createElement("div", { className: styles.buffer, onClick: () => { var _a; return (_a = textareaRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } }), hasActionButton && action)))); }); export default InternalPromptInput; //# sourceMappingURL=internal.js.map