mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 05:26:24 +09:00
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:
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";
|
||||
Reference in new issue
Block a user