From e930c2b34af2f950c71c3d6faebd648c2b7e47d4 Mon Sep 17 00:00:00 2001 From: Ivan Starkov Date: Fri, 6 Oct 2023 15:40:43 +0400 Subject: [PATCH] feat: Autogrowing Textarea (#2424) ## Description Supports max-height and scrolling with our ScrollArea control see storybook https://638245578b47d4399fba191a-pgsxdcimyt.chromatic.com/?path=/story/library-autogrow-text-area--demo image ## Steps for reproduction https://638245578b47d4399fba191a-pgsxdcimyt.chromatic.com/?path=/story/library-autogrow-text-area--demo ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env.example` and the `builder/env-check.js` if mandatory --- .../ai-input/autogrow-text-area.stories.tsx | 52 ++++++++++ .../ai-input/autogrow-text-area.tsx | 97 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 packages/design-system/src/components/ai-input/autogrow-text-area.stories.tsx create mode 100644 packages/design-system/src/components/ai-input/autogrow-text-area.tsx diff --git a/packages/design-system/src/components/ai-input/autogrow-text-area.stories.tsx b/packages/design-system/src/components/ai-input/autogrow-text-area.stories.tsx new file mode 100644 index 0000000000..b2354272fb --- /dev/null +++ b/packages/design-system/src/components/ai-input/autogrow-text-area.stories.tsx @@ -0,0 +1,52 @@ +import { AutogrowTextArea } from "./autogrow-text-area"; +import { Box } from "../box"; +import { Grid } from "../grid"; +import { ScrollArea } from "../scroll-area"; +import { StorySection, StoryGrid } from "../storybook"; + +export default { + title: "Library/Autogrow Text Area", +}; + +export const Demo = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; +Demo.storyName = "Autogrow Text Area"; diff --git a/packages/design-system/src/components/ai-input/autogrow-text-area.tsx b/packages/design-system/src/components/ai-input/autogrow-text-area.tsx new file mode 100644 index 0000000000..baee1a5e36 --- /dev/null +++ b/packages/design-system/src/components/ai-input/autogrow-text-area.tsx @@ -0,0 +1,97 @@ +/** + * Implementation of the autogrowing text input for the AI + * https://www.figma.com/file/xCBegXEWxROLqA1Y31z2Xo/%F0%9F%93%96-Webstudio-Design-Docs?node-id=7586%3A48784&mode=dev + */ + +import { type ComponentProps, type Ref, forwardRef } from "react"; +import { type CSS, css, theme } from "../../stitches.config"; +import { textVariants } from "../text"; +import { Grid } from "../grid"; +import { useControllableState } from "@radix-ui/react-use-controllable-state"; + +const commonStyle = css(textVariants.regular, { + border: "none", + paddingRight: 0, + paddingLeft: 0, + paddingTop: 0, + paddingBottom: 0, + boxSizing: "border-box", + gridArea: "1 / 1 / 2 / 2", + background: "transparent", + color: "inherit", + textWrap: "wrap", + overflowWrap: "break-word", + whiteSpace: "pre-wrap", + overflow: "hidden", +}); + +const style = css(commonStyle, { + outline: "none", + resize: "none", + "&::placeholder": { + color: theme.colors.foregroundContrastSubtle, + }, + "&:disabled": { + color: theme.colors.foregroundDisabled, + background: theme.colors.backgroundInputDisabled, + }, + variants: { + state: { + invalid: { + color: theme.colors.foregroundDestructive, + }, + }, + }, +}); + +type Props = Omit< + ComponentProps<"textarea">, + "value" | "defaultValue" | "onChange" +> & { + css?: CSS; + state?: "invalid"; + value?: string; + defaultValue?: string; + onChange?: (value: string) => void; +}; + +export const AutogrowTextArea = forwardRef( + ( + { css, className, state, value, defaultValue, onChange, ...props }: Props, + ref: Ref + ) => { + // use + const [textValue, setTextValue] = useControllableState({ + prop: value, + defaultProp: defaultValue, + onChange, + }); + + return ( + +
+ {textValue}{" "} +
+