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

<img width="333" alt="image"
src="https://github.com/webstudio-is/webstudio/assets/5077042/808ed6a8-3232-439e-b1eb-98981f85d21f">

## 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
This commit is contained in:
Ivan Starkov authored and GitHub committed 2023-10-06 11:40:43 +00:00
1 parent 10ca57d5ad
commit e930c2b34a
2 files changed
+149

No files matched your search

@@ -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 (
<>
<StorySection title="Autogrow textarea">
<StoryGrid css={{ width: 200 }}>
<Box css={{ backgroundColor: "black", color: "white", padding: 8 }}>
<AutogrowTextArea
defaultValue={`1\n2\n3\n4\n5\n6\n7\n8`}
placeholder="Enter value..."
/>
</Box>
</StoryGrid>
<Box css={{ height: 16 }} />
<StoryGrid css={{ width: 200 }}>
<Box css={{ backgroundColor: "black", color: "white", padding: 8 }}>
<AutogrowTextArea
state="invalid"
value="invalid value"
placeholder="Enter value..."
/>
</Box>
</StoryGrid>
</StorySection>
<StorySection title="Autogrow textarea with scroll and max-height">
<StoryGrid css={{ width: 200 }}>
<Box css={{ backgroundColor: "black", color: "white", padding: 8 }}>
<Grid>
<ScrollArea css={{ maxHeight: 100 }}>
<AutogrowTextArea
defaultValue={`1\n2\n3\n4\n5\n6\n7\n8`}
placeholder="Enter value..."
/>
</ScrollArea>
</Grid>
</Box>
</StoryGrid>
</StorySection>
</>
);
};
Demo.storyName = "Autogrow Text Area";
@@ -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<HTMLTextAreaElement>
) => {
// use
const [textValue, setTextValue] = useControllableState({
prop: value,
defaultProp: defaultValue,
onChange,
});
return (
<Grid>
<div
className={style({
css: { visibility: "hidden", ...css },
state,
className,
})}
>
{textValue}{" "}
</div>
<textarea
rows={1}
value={textValue}
onChange={(event) => setTextValue(event.target.value)}
spellCheck={false}
className={style({
state,
className,
})}
ref={ref}
{...props}
/>
</Grid>
);
}
);
AutogrowTextArea.displayName = "AutogrowTextArea";