@vectara/vectara-ui 16.6.0 → 16.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ type Props = React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaEl
2
2
  fullWidth?: boolean;
3
3
  isInvalid?: boolean;
4
4
  resizable?: boolean;
5
+ autoGrow?: boolean;
5
6
  };
6
7
  export declare const VuiTextArea: import("react").ForwardRefExoticComponent<Omit<Props, "ref"> & import("react").RefAttributes<HTMLTextAreaElement | null>>;
7
8
  export {};
@@ -10,14 +10,39 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
- import { forwardRef } from "react";
13
+ import { forwardRef, useLayoutEffect, useRef } from "react";
14
14
  import classNames from "classnames";
15
15
  export const VuiTextArea = forwardRef((_a, ref) => {
16
- var { className, fullWidth, isInvalid, resizable } = _a, rest = __rest(_a, ["className", "fullWidth", "isInvalid", "resizable"]);
16
+ var { className, fullWidth, isInvalid, resizable, autoGrow } = _a, rest = __rest(_a, ["className", "fullWidth", "isInvalid", "resizable", "autoGrow"]);
17
17
  const classes = classNames("vuiTextArea", {
18
18
  "vuiTextArea-isInvalid": isInvalid,
19
- "vuiTextArea--fullWidth": fullWidth
19
+ "vuiTextArea--fullWidth": fullWidth,
20
+ "vuiTextArea--autoGrow": autoGrow
20
21
  }, className);
21
22
  const style = Object.assign(Object.assign({}, rest.style), { resize: resizable ? "vertical" : "none" });
22
- return _jsx("textarea", Object.assign({}, rest, { ref: ref, className: classes, style: style }));
23
+ const internalRef = useRef(null);
24
+ const setRefs = (el) => {
25
+ internalRef.current = el;
26
+ if (typeof ref === "function")
27
+ ref(el);
28
+ else if (ref)
29
+ ref.current = el;
30
+ };
31
+ // Auto-grow the textarea to fit its content. Reset to "auto" first so
32
+ // scrollHeight reflects the natural height after deletions, not the
33
+ // previously-set inline height.
34
+ useLayoutEffect(() => {
35
+ if (!autoGrow)
36
+ return;
37
+ const el = internalRef.current;
38
+ if (!el)
39
+ return;
40
+ el.style.height = "auto";
41
+ // scrollHeight excludes borders, but with box-sizing: border-box the
42
+ // assigned height includes them — without this offset the content would
43
+ // overflow by the border width and show a spurious scrollbar.
44
+ const borderY = el.offsetHeight - el.clientHeight;
45
+ el.style.height = `${el.scrollHeight + borderY}px`;
46
+ }, [autoGrow, rest.value]);
47
+ return _jsx("textarea", Object.assign({}, rest, { ref: setRefs, className: classes, style: style }));
23
48
  });
@@ -16,3 +16,9 @@
16
16
  .vuiTextArea--fullWidth {
17
17
  width: 100%;
18
18
  }
19
+
20
+ .vuiTextArea--autoGrow {
21
+ min-height: 0;
22
+ line-height: 1.6;
23
+ max-height: 12rem;
24
+ }
@@ -2,10 +2,11 @@ type Props = {
2
2
  labelFor?: string;
3
3
  label?: string;
4
4
  labelSize?: "s" | "xs";
5
+ labelRightContent?: React.ReactNode;
5
6
  children: React.ReactElement;
6
7
  helpText?: React.ReactNode;
7
8
  errors?: string[];
8
9
  isRequired?: boolean;
9
10
  };
10
- export declare const VuiFormGroup: ({ children, labelFor, helpText, label, labelSize, errors, isRequired }: Props) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const VuiFormGroup: ({ children, labelFor, helpText, label, labelSize, labelRightContent, errors, isRequired }: Props) => import("react/jsx-runtime").JSX.Element;
11
12
  export {};
@@ -9,8 +9,9 @@ import { VuiTextInput } from "../form/input/TextInput";
9
9
  import { VuiNumberInput } from "../form/input/NumberInput";
10
10
  import { VuiTextArea } from "../form/textArea/TextArea";
11
11
  import { VuiSelect } from "../form/select/Select";
12
+ import { VuiFlexContainer } from "../flex/FlexContainer";
12
13
  const VALIDATION_ALLOWLIST = [VuiTextInput, VuiNumberInput, VuiTextArea, VuiSelect];
13
- export const VuiFormGroup = ({ children, labelFor, helpText, label, labelSize = "s", errors, isRequired }) => {
14
+ export const VuiFormGroup = ({ children, labelFor, helpText, label, labelSize = "s", labelRightContent, errors, isRequired }) => {
14
15
  const ariaProps = {
15
16
  "aria-describedby": ""
16
17
  };
@@ -33,5 +34,5 @@ export const VuiFormGroup = ({ children, labelFor, helpText, label, labelSize =
33
34
  cloneProps.isInvalid = errors && errors.length > 0;
34
35
  }
35
36
  const content = cloneElement(children, cloneProps);
36
- return (_jsxs("div", { children: [label && (_jsxs(_Fragment, { children: [_jsxs(VuiLabel, Object.assign({ labelFor: labelFor, size: labelSize }, { children: [label, isRequired && " (required)"] })), _jsx(VuiSpacer, { size: labelSize === "s" ? "xs" : "xxs" })] })), helpText && (_jsxs(_Fragment, { children: [_jsx(VuiText, Object.assign({ size: "xs", id: ariaDescribedByLabel }, { children: _jsx("p", { children: _jsx(VuiTextColor, Object.assign({ color: "subdued" }, { children: helpText })) }) })), _jsx(VuiSpacer, { size: "xs" })] })), errorMessages && (_jsxs(_Fragment, { children: [errorMessages, _jsx(VuiSpacer, { size: "xs" })] })), content] }));
37
+ return (_jsxs("div", { children: [(label || labelRightContent) && (_jsxs(_Fragment, { children: [_jsxs(VuiFlexContainer, Object.assign({ justifyContent: "spaceBetween", alignItems: "center", spacing: "s" }, { children: [label ? (_jsxs(VuiLabel, Object.assign({ labelFor: labelFor, size: labelSize }, { children: [label, isRequired && " (required)"] }))) : (_jsx("span", {})), labelRightContent] })), _jsx(VuiSpacer, { size: labelSize === "s" ? "xs" : "xxs" })] })), helpText && (_jsxs(_Fragment, { children: [_jsx(VuiText, Object.assign({ size: "xs", id: ariaDescribedByLabel }, { children: _jsx("p", { children: _jsx(VuiTextColor, Object.assign({ color: "subdued" }, { children: helpText })) }) })), _jsx(VuiSpacer, { size: "xs" })] })), errorMessages && (_jsxs(_Fragment, { children: [errorMessages, _jsx(VuiSpacer, { size: "xs" })] })), content] }));
37
38
  };
@@ -3007,6 +3007,12 @@ h2.react-datepicker__current-month {
3007
3007
  width: 100%;
3008
3008
  }
3009
3009
 
3010
+ .vuiTextArea--autoGrow {
3011
+ min-height: 0;
3012
+ line-height: 1.6;
3013
+ max-height: 12rem;
3014
+ }
3015
+
3010
3016
  .vuiGridContainer {
3011
3017
  container-type: inline-size;
3012
3018
  width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectara/vectara-ui",
3
- "version": "16.6.0",
3
+ "version": "16.8.0",
4
4
  "homepage": "./",
5
5
  "description": "Vectara's design system, codified as a React and Sass component library",
6
6
  "author": "Vectara",
@@ -0,0 +1,35 @@
1
+ import { useState } from "react";
2
+ import { VuiFormGroup, VuiSelect, VuiTextInput } from "../../../lib";
3
+
4
+ type limitMode = "static" | "dynamic" | "agent" | "disabled";
5
+
6
+ const limitModeOptions = [
7
+ { text: "Statically defined", value: "static" as const },
8
+ { text: "Dynamically defined", value: "dynamic" as const },
9
+ { text: "Agent defined", value: "agent" as const },
10
+ { text: "Disabled", value: "disabled" as const }
11
+ ];
12
+
13
+ export const LabelRightContent = () => {
14
+ const [limitMode, setlimitMode] = useState<limitMode>("static");
15
+
16
+ return (
17
+ <div>
18
+ <VuiFormGroup
19
+ label="Limit"
20
+ labelFor="limitInput"
21
+ helpText="Maximum number of results to return Default: 10"
22
+ labelRightContent={
23
+ <VuiSelect
24
+ size="s"
25
+ options={limitModeOptions}
26
+ value={limitMode}
27
+ onChange={(event) => setlimitMode(event.target.value as limitMode)}
28
+ />
29
+ }
30
+ >
31
+ <VuiTextInput id="limitInput" value="5" onChange={() => undefined} />
32
+ </VuiFormGroup>
33
+ </div>
34
+ );
35
+ };
@@ -2,11 +2,13 @@ import { FormGroup } from "./FormGroup";
2
2
  import { NonFormElement } from "./NonFormElement";
3
3
  import { NoLabel } from "./NoLabel";
4
4
  import { Size } from "./Size";
5
+ import { LabelRightContent } from "./LabelRightContent";
5
6
 
6
7
  const FormGroupSource = require("!!raw-loader!./FormGroup");
7
8
  const NonFormElementSource = require("!!raw-loader!./NonFormElement");
8
9
  const NoLabelSource = require("!!raw-loader!./NoLabel");
9
10
  const SizeSource = require("!!raw-loader!./Size");
11
+ const LabelRightContentSource = require("!!raw-loader!./LabelRightContent");
10
12
 
11
13
  export const formGroup = {
12
14
  name: "Form Group",
@@ -31,6 +33,11 @@ export const formGroup = {
31
33
  name: "Label size",
32
34
  component: <Size />,
33
35
  source: SizeSource.default.toString()
36
+ },
37
+ {
38
+ name: "With label right aligned content",
39
+ component: <LabelRightContent />,
40
+ source: LabelRightContentSource.default.toString()
34
41
  }
35
42
  ]
36
43
  };
@@ -0,0 +1,18 @@
1
+ import { useState } from "react";
2
+ import { VuiTextArea } from "../../../lib";
3
+
4
+ export const TextAreaAutoGrow = () => {
5
+ const [value, setValue] = useState<string | undefined>();
6
+
7
+ return (
8
+ <VuiTextArea
9
+ id="autoGrowTextArea"
10
+ value={value}
11
+ onChange={(event) => setValue(event.target.value)}
12
+ autoGrow
13
+ rows={1}
14
+ fullWidth
15
+ placeholder="Type a few lines — the textarea grows to fit its content"
16
+ />
17
+ );
18
+ };
@@ -1,8 +1,10 @@
1
1
  import { TextArea } from "./TextArea";
2
2
  import { TextAreaResizeable } from "./TextAreaResizeable";
3
+ import { TextAreaAutoGrow } from "./TextAreaAutoGrow";
3
4
 
4
5
  const TextAreaSource = require("!!raw-loader!./TextArea");
5
6
  const TextAreaResizeableSource = require("!!raw-loader!./TextAreaResizeable");
7
+ const TextAreaAutoGrowSource = require("!!raw-loader!./TextAreaAutoGrow");
6
8
 
7
9
  export const textArea = {
8
10
  name: "Text Area",
@@ -17,6 +19,11 @@ export const textArea = {
17
19
  name: "Resizable Text Area",
18
20
  component: <TextAreaResizeable />,
19
21
  source: TextAreaResizeableSource.default.toString()
22
+ },
23
+ {
24
+ name: "Auto-Grow Text Area",
25
+ component: <TextAreaAutoGrow />,
26
+ source: TextAreaAutoGrowSource.default.toString()
20
27
  }
21
28
  ]
22
29
  };