jvetrau-ds 0.2.8 → 0.2.9

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/select/select.tsx"],"names":["classnames","jsx"],"mappings":";;;;;AASA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAUA,qBAAA,CAAW,QAAA,EAAU,SAAA,EAAW;AAAA,IAC9C,OAAO,KAAA,KAAU,OAAA;AAAA,IACjB,SAAS,KAAA,KAAU;AAAA,GAClB,CAAA;AAEH,EAAA,uBACEC,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,EACd,QAAA,kBAAAA,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,eAAA;AAAA,MACV,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"select.cjs","sourcesContent":["import React from 'react';\n//import './select.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface SelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n state?: 'default' | 'error' | 'success';\n}\n\nconst Select: React.FC<SelectProps> = ({\n state = 'default',\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const classes = classnames('select', className, {\n error: state === 'error',\n success: state === 'success',\n });\n\n return (\n <div className={classes}>\n <select\n className=\"select__field\"\n disabled={disabled}\n {...props}\n >\n {children}\n </select>\n </div>\n );\n};\n\nexport default Select;"]}
1
+ {"version":3,"sources":["../../../src/components/select/select.tsx"],"names":["classnames","jsx"],"mappings":";;;;;AAcA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAUA,qBAAA;AAAA,IACd,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,iBACE,KAAA,KAAU,OAAA;AAAA,MACZ,mBACE,KAAA,KAAU,SAAA;AAAA,MACZ,uBACE,KAAA,KAAU,OAAA;AAAA,MACZ,yBACE,KAAA,KAAU,SAAA;AAAA,MACZ,qBACE,KAAA,KAAU;AAAA;AACd,GACF;AAEA,EAAA,uBACEC,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,EACd,QAAA,kBAAAA,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,eAAA;AAAA,MACV,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"select.cjs","sourcesContent":["import React from 'react';\nimport { classnames } from '../helpers/classnames';\n\ntype SelectWidth =\n | 'short'\n | 'default'\n | 'max';\n\nexport interface SelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n state?: 'default' | 'error' | 'success';\n width?: SelectWidth;\n}\n\nconst Select: React.FC<SelectProps> = ({\n state = 'default',\n width = 'default',\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const classes = classnames(\n 'select',\n className,\n {\n 'select--error':\n state === 'error',\n 'select--success':\n state === 'success',\n 'select--width-short':\n width === 'short',\n 'select--width-default':\n width === 'default',\n 'select--width-max':\n width === 'max',\n },\n );\n\n return (\n <div className={classes}>\n <select\n className=\"select__field\"\n disabled={disabled}\n {...props}\n >\n {children}\n </select>\n </div>\n );\n};\n\nexport default Select;\n"]}
@@ -30,7 +30,7 @@
30
30
 
31
31
  position: relative;
32
32
  display: inline-block;
33
- width: 100%;
33
+ box-sizing: border-box;
34
34
 
35
35
  /* border */
36
36
  --select-border-width: var(--size-border-width-base);
@@ -51,6 +51,21 @@
51
51
  --select-transition-duration: 0.2s;
52
52
  }
53
53
 
54
+ .select--width-short {
55
+ /* size */
56
+ width: fit-content;
57
+ }
58
+
59
+ .select--width-default {
60
+ /* size */
61
+ width: 400px;
62
+ }
63
+
64
+ .select--width-max {
65
+ /* size */
66
+ width: 100%;
67
+ }
68
+
54
69
  .select__field {
55
70
  /* color */
56
71
  background: var(--select-bg);
@@ -61,6 +76,7 @@
61
76
  letter-spacing: inherit;
62
77
 
63
78
  /* size */
79
+ box-sizing: border-box;
64
80
  width: 100%;
65
81
  height: var(--select-height);
66
82
  padding-left: var(--select-padding-x);
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
+ type SelectWidth = 'short' | 'default' | 'max';
2
3
  export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
3
4
  state?: 'default' | 'error' | 'success';
5
+ width?: SelectWidth;
4
6
  }
5
7
  declare const Select: React.FC<SelectProps>;
6
8
  export default Select;
@@ -3,15 +3,23 @@ import { classnames } from '../helpers/classnames';
3
3
 
4
4
  const Select = ({
5
5
  state = "default",
6
+ width = "default",
6
7
  className = "",
7
8
  disabled = false,
8
9
  children,
9
10
  ...props
10
11
  }) => {
11
- const classes = classnames("select", className, {
12
- error: state === "error",
13
- success: state === "success"
14
- });
12
+ const classes = classnames(
13
+ "select",
14
+ className,
15
+ {
16
+ "select--error": state === "error",
17
+ "select--success": state === "success",
18
+ "select--width-short": width === "short",
19
+ "select--width-default": width === "default",
20
+ "select--width-max": width === "max"
21
+ }
22
+ );
15
23
  return /* @__PURE__ */ jsx("div", { className: classes, children: /* @__PURE__ */ jsx(
16
24
  "select",
17
25
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/select/select.tsx"],"names":[],"mappings":";;;AASA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,QAAA,EAAU,SAAA,EAAW;AAAA,IAC9C,OAAO,KAAA,KAAU,OAAA;AAAA,IACjB,SAAS,KAAA,KAAU;AAAA,GAClB,CAAA;AAEH,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,EACd,QAAA,kBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,eAAA;AAAA,MACV,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"select.js","sourcesContent":["import React from 'react';\n//import './select.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface SelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n state?: 'default' | 'error' | 'success';\n}\n\nconst Select: React.FC<SelectProps> = ({\n state = 'default',\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const classes = classnames('select', className, {\n error: state === 'error',\n success: state === 'success',\n });\n\n return (\n <div className={classes}>\n <select\n className=\"select__field\"\n disabled={disabled}\n {...props}\n >\n {children}\n </select>\n </div>\n );\n};\n\nexport default Select;"]}
1
+ {"version":3,"sources":["../../../src/components/select/select.tsx"],"names":[],"mappings":";;;AAcA,MAAM,SAAgC,CAAC;AAAA,EACrC,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACd,QAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,iBACE,KAAA,KAAU,OAAA;AAAA,MACZ,mBACE,KAAA,KAAU,SAAA;AAAA,MACZ,uBACE,KAAA,KAAU,OAAA;AAAA,MACZ,yBACE,KAAA,KAAU,SAAA;AAAA,MACZ,qBACE,KAAA,KAAU;AAAA;AACd,GACF;AAEA,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,EACd,QAAA,kBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,eAAA;AAAA,MACV,QAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"select.js","sourcesContent":["import React from 'react';\nimport { classnames } from '../helpers/classnames';\n\ntype SelectWidth =\n | 'short'\n | 'default'\n | 'max';\n\nexport interface SelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n state?: 'default' | 'error' | 'success';\n width?: SelectWidth;\n}\n\nconst Select: React.FC<SelectProps> = ({\n state = 'default',\n width = 'default',\n className = '',\n disabled = false,\n children,\n ...props\n}) => {\n const classes = classnames(\n 'select',\n className,\n {\n 'select--error':\n state === 'error',\n 'select--success':\n state === 'success',\n 'select--width-short':\n width === 'short',\n 'select--width-default':\n width === 'default',\n 'select--width-max':\n width === 'max',\n },\n );\n\n return (\n <div className={classes}>\n <select\n className=\"select__field\"\n disabled={disabled}\n {...props}\n >\n {children}\n </select>\n </div>\n );\n};\n\nexport default Select;\n"]}
@@ -1,23 +1,87 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
4
5
  var classnames = require('../helpers/classnames');
5
6
 
7
+ function parsePixelValue(value) {
8
+ const parsed = Number.parseFloat(value);
9
+ return Number.isFinite(parsed) ? parsed : 0;
10
+ }
6
11
  const Textarea = ({
7
12
  state = "default",
13
+ width = "default",
14
+ autoGrow = false,
8
15
  className = "",
9
16
  disabled = false,
17
+ onInput,
10
18
  ...props
11
19
  }) => {
12
- const classes = classnames.classnames("textarea", className, {
13
- error: state === "error",
14
- success: state === "success"
15
- });
20
+ const textareaRef = react.useRef(null);
21
+ const resizeToContent = react.useCallback(() => {
22
+ const textarea = textareaRef.current;
23
+ if (!autoGrow || !textarea) {
24
+ return;
25
+ }
26
+ const computedStyle = window.getComputedStyle(textarea);
27
+ const lineHeight = parsePixelValue(
28
+ computedStyle.lineHeight
29
+ );
30
+ const borderHeight = parsePixelValue(
31
+ computedStyle.borderTopWidth
32
+ ) + parsePixelValue(
33
+ computedStyle.borderBottomWidth
34
+ );
35
+ const minHeight = parsePixelValue(
36
+ computedStyle.minHeight
37
+ );
38
+ const previousMinHeight = textarea.style.minHeight;
39
+ textarea.style.minHeight = "0px";
40
+ textarea.style.setProperty(
41
+ "--textarea-auto-height",
42
+ "0px"
43
+ );
44
+ const contentHeight = textarea.scrollHeight;
45
+ textarea.style.minHeight = previousMinHeight;
46
+ const nextHeight = Math.max(
47
+ contentHeight + lineHeight + borderHeight,
48
+ minHeight
49
+ );
50
+ textarea.style.setProperty(
51
+ "--textarea-auto-height",
52
+ `${nextHeight}px`
53
+ );
54
+ }, [autoGrow]);
55
+ react.useLayoutEffect(() => {
56
+ resizeToContent();
57
+ }, [
58
+ resizeToContent,
59
+ props.value,
60
+ width
61
+ ]);
62
+ const handleInput = (event) => {
63
+ resizeToContent();
64
+ onInput?.(event);
65
+ };
66
+ const classes = classnames.classnames(
67
+ "textarea",
68
+ className,
69
+ {
70
+ "textarea--error": state === "error",
71
+ "textarea--success": state === "success",
72
+ "textarea--width-short": width === "short",
73
+ "textarea--width-default": width === "default",
74
+ "textarea--width-max": width === "max",
75
+ "textarea--auto-grow": autoGrow
76
+ }
77
+ );
16
78
  return /* @__PURE__ */ jsxRuntime.jsx(
17
79
  "textarea",
18
80
  {
81
+ ref: textareaRef,
19
82
  className: classes,
20
83
  disabled,
84
+ onInput: handleInput,
21
85
  ...props
22
86
  }
23
87
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/textarea/textarea.tsx"],"names":["classnames","jsx"],"mappings":";;;;;AASA,MAAM,WAAoC,CAAC;AAAA,EACzC,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAUA,qBAAA,CAAW,UAAA,EAAY,SAAA,EAAW;AAAA,IAChD,OAAO,KAAA,KAAU,OAAA;AAAA,IACjB,SAAS,KAAA,KAAU;AAAA,GAClB,CAAA;AAEH,EAAA,uBACEC,cAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"textarea.cjs","sourcesContent":["import React from 'react';\n// import './textarea.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface TextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n state?: 'default' | 'error' | 'success';\n}\n\nconst Textarea: React.FC<TextareaProps> = ({\n state = 'default',\n className = '',\n disabled = false,\n ...props\n}) => {\n const classes = classnames('textarea', className, {\n error: state === 'error',\n success: state === 'success',\n });\n\n return (\n <textarea\n className={classes}\n disabled={disabled}\n {...props}\n />\n );\n};\n\nexport default Textarea;"]}
1
+ {"version":3,"sources":["../../../src/components/textarea/textarea.tsx"],"names":["useRef","useCallback","useLayoutEffect","classnames","jsx"],"mappings":";;;;;;AAmBA,SAAS,gBAAgB,KAAA,EAAe;AACtC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA;AAEtC,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,GACzB,MAAA,GACA,CAAA;AACN;AAEA,MAAM,WAAoC,CAAC;AAAA,EACzC,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,SAAA;AAAA,EACR,QAAA,GAAW,KAAA;AAAA,EACX,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,OAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,WAAA,GACJA,aAA4B,IAAI,CAAA;AAElC,EAAA,MAAM,eAAA,GACJC,kBAAY,MAAM;AAChB,IAAA,MAAM,WACJ,WAAA,CAAY,OAAA;AAEd,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,QAAA,EAAU;AAC1B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,aAAA,GACJ,MAAA,CAAO,gBAAA,CAAiB,QAAQ,CAAA;AAElC,IAAA,MAAM,UAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,YAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB,GACA,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,SAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,iBAAA,GACJ,SAAS,KAAA,CAAM,SAAA;AAEjB,IAAA,QAAA,CAAS,MAAM,SAAA,GACb,KAAA;AAEF,IAAA,QAAA,CAAS,KAAA,CAAM,WAAA;AAAA,MACb,wBAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,gBACJ,QAAA,CAAS,YAAA;AAEX,IAAA,QAAA,CAAS,MAAM,SAAA,GACb,iBAAA;AAEF,IAAA,MAAM,aAAa,IAAA,CAAK,GAAA;AAAA,MACtB,gBACE,UAAA,GACA,YAAA;AAAA,MACF;AAAA,KACF;AAEA,IAAA,QAAA,CAAS,KAAA,CAAM,WAAA;AAAA,MACb,wBAAA;AAAA,MACA,GAAG,UAAU,CAAA,EAAA;AAAA,KACf;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEf,EAAAC,qBAAA,CAAgB,MAAM;AACpB,IAAA,eAAA,EAAgB;AAAA,EAClB,CAAA,EAAG;AAAA,IACD,eAAA;AAAA,IACA,KAAA,CAAM,KAAA;AAAA,IACN;AAAA,GACD,CAAA;AAED,EAAA,MAAM,WAAA,GAAc,CAClB,KAAA,KACG;AACH,IAAA,eAAA,EAAgB;AAChB,IAAA,OAAA,GAAU,KAAK,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAUC,qBAAA;AAAA,IACd,UAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,mBACE,KAAA,KAAU,OAAA;AAAA,MACZ,qBACE,KAAA,KAAU,SAAA;AAAA,MACZ,yBACE,KAAA,KAAU,OAAA;AAAA,MACZ,2BACE,KAAA,KAAU,SAAA;AAAA,MACZ,uBACE,KAAA,KAAU,KAAA;AAAA,MACZ,qBAAA,EACE;AAAA;AACJ,GACF;AAEA,EAAA,uBACEC,cAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACA,OAAA,EAAS,WAAA;AAAA,MACR,GAAG;AAAA;AAAA,GACN;AAEJ,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"textarea.cjs","sourcesContent":["import React, {\n useCallback,\n useLayoutEffect,\n useRef,\n} from 'react';\nimport { classnames } from '../helpers/classnames';\n\ntype TextareaWidth =\n | 'short'\n | 'default'\n | 'max';\n\nexport interface TextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n state?: 'default' | 'error' | 'success';\n width?: TextareaWidth;\n autoGrow?: boolean;\n}\n\nfunction parsePixelValue(value: string) {\n const parsed = Number.parseFloat(value);\n\n return Number.isFinite(parsed)\n ? parsed\n : 0;\n}\n\nconst Textarea: React.FC<TextareaProps> = ({\n state = 'default',\n width = 'default',\n autoGrow = false,\n className = '',\n disabled = false,\n onInput,\n ...props\n}) => {\n const textareaRef =\n useRef<HTMLTextAreaElement>(null);\n\n const resizeToContent =\n useCallback(() => {\n const textarea =\n textareaRef.current;\n\n if (!autoGrow || !textarea) {\n return;\n }\n\n const computedStyle =\n window.getComputedStyle(textarea);\n\n const lineHeight =\n parsePixelValue(\n computedStyle.lineHeight,\n );\n\n const borderHeight =\n parsePixelValue(\n computedStyle.borderTopWidth,\n ) +\n parsePixelValue(\n computedStyle.borderBottomWidth,\n );\n\n const minHeight =\n parsePixelValue(\n computedStyle.minHeight,\n );\n\n const previousMinHeight =\n textarea.style.minHeight;\n\n textarea.style.minHeight =\n '0px';\n\n textarea.style.setProperty(\n '--textarea-auto-height',\n '0px',\n );\n\n const contentHeight =\n textarea.scrollHeight;\n\n textarea.style.minHeight =\n previousMinHeight;\n\n const nextHeight = Math.max(\n contentHeight +\n lineHeight +\n borderHeight,\n minHeight,\n );\n\n textarea.style.setProperty(\n '--textarea-auto-height',\n `${nextHeight}px`,\n );\n }, [autoGrow]);\n\n useLayoutEffect(() => {\n resizeToContent();\n }, [\n resizeToContent,\n props.value,\n width,\n ]);\n\n const handleInput = (\n event: React.InputEvent<HTMLTextAreaElement>,\n ) => {\n resizeToContent();\n onInput?.(event);\n };\n\n const classes = classnames(\n 'textarea',\n className,\n {\n 'textarea--error':\n state === 'error',\n 'textarea--success':\n state === 'success',\n 'textarea--width-short':\n width === 'short',\n 'textarea--width-default':\n width === 'default',\n 'textarea--width-max':\n width === 'max',\n 'textarea--auto-grow':\n autoGrow,\n },\n );\n\n return (\n <textarea\n ref={textareaRef}\n className={classes}\n disabled={disabled}\n onInput={handleInput}\n {...props}\n />\n );\n};\n\nexport default Textarea;\n"]}
@@ -26,6 +26,7 @@
26
26
  --textarea-padding-x: var(--padding-control-horz);
27
27
  --textarea-padding-y: var(--padding-control-horz);
28
28
 
29
+ box-sizing: border-box;
29
30
  min-height: var(--textarea-min-height);
30
31
  padding: var(--textarea-padding-y) var(--textarea-padding-x);
31
32
 
@@ -55,6 +56,30 @@
55
56
  background-color var(--textarea-transition-duration) ease;
56
57
  }
57
58
 
59
+ .textarea--width-short {
60
+ /* size */
61
+ width: auto;
62
+ }
63
+
64
+ .textarea--width-default {
65
+ /* size */
66
+ width: 400px;
67
+ }
68
+
69
+ .textarea--width-max {
70
+ /* size */
71
+ width: 100%;
72
+ }
73
+
74
+ .textarea--auto-grow {
75
+ /* size */
76
+ height: var(--textarea-auto-height);
77
+
78
+ /* motion */
79
+ overflow-y: hidden;
80
+ resize: none;
81
+ }
82
+
58
83
  .textarea::placeholder {
59
84
  color: var(--textarea-placeholder);
60
85
  }
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
+ type TextareaWidth = 'short' | 'default' | 'max';
2
3
  export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
3
4
  state?: 'default' | 'error' | 'success';
5
+ width?: TextareaWidth;
6
+ autoGrow?: boolean;
4
7
  }
5
8
  declare const Textarea: React.FC<TextareaProps>;
6
9
  export default Textarea;
@@ -1,21 +1,85 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useCallback, useLayoutEffect } from 'react';
2
3
  import { classnames } from '../helpers/classnames';
3
4
 
5
+ function parsePixelValue(value) {
6
+ const parsed = Number.parseFloat(value);
7
+ return Number.isFinite(parsed) ? parsed : 0;
8
+ }
4
9
  const Textarea = ({
5
10
  state = "default",
11
+ width = "default",
12
+ autoGrow = false,
6
13
  className = "",
7
14
  disabled = false,
15
+ onInput,
8
16
  ...props
9
17
  }) => {
10
- const classes = classnames("textarea", className, {
11
- error: state === "error",
12
- success: state === "success"
13
- });
18
+ const textareaRef = useRef(null);
19
+ const resizeToContent = useCallback(() => {
20
+ const textarea = textareaRef.current;
21
+ if (!autoGrow || !textarea) {
22
+ return;
23
+ }
24
+ const computedStyle = window.getComputedStyle(textarea);
25
+ const lineHeight = parsePixelValue(
26
+ computedStyle.lineHeight
27
+ );
28
+ const borderHeight = parsePixelValue(
29
+ computedStyle.borderTopWidth
30
+ ) + parsePixelValue(
31
+ computedStyle.borderBottomWidth
32
+ );
33
+ const minHeight = parsePixelValue(
34
+ computedStyle.minHeight
35
+ );
36
+ const previousMinHeight = textarea.style.minHeight;
37
+ textarea.style.minHeight = "0px";
38
+ textarea.style.setProperty(
39
+ "--textarea-auto-height",
40
+ "0px"
41
+ );
42
+ const contentHeight = textarea.scrollHeight;
43
+ textarea.style.minHeight = previousMinHeight;
44
+ const nextHeight = Math.max(
45
+ contentHeight + lineHeight + borderHeight,
46
+ minHeight
47
+ );
48
+ textarea.style.setProperty(
49
+ "--textarea-auto-height",
50
+ `${nextHeight}px`
51
+ );
52
+ }, [autoGrow]);
53
+ useLayoutEffect(() => {
54
+ resizeToContent();
55
+ }, [
56
+ resizeToContent,
57
+ props.value,
58
+ width
59
+ ]);
60
+ const handleInput = (event) => {
61
+ resizeToContent();
62
+ onInput?.(event);
63
+ };
64
+ const classes = classnames(
65
+ "textarea",
66
+ className,
67
+ {
68
+ "textarea--error": state === "error",
69
+ "textarea--success": state === "success",
70
+ "textarea--width-short": width === "short",
71
+ "textarea--width-default": width === "default",
72
+ "textarea--width-max": width === "max",
73
+ "textarea--auto-grow": autoGrow
74
+ }
75
+ );
14
76
  return /* @__PURE__ */ jsx(
15
77
  "textarea",
16
78
  {
79
+ ref: textareaRef,
17
80
  className: classes,
18
81
  disabled,
82
+ onInput: handleInput,
19
83
  ...props
20
84
  }
21
85
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/textarea/textarea.tsx"],"names":[],"mappings":";;;AASA,MAAM,WAAoC,CAAC;AAAA,EACzC,KAAA,GAAQ,SAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,OAAA,GAAU,UAAA,CAAW,UAAA,EAAY,SAAA,EAAW;AAAA,IAChD,OAAO,KAAA,KAAU,OAAA;AAAA,IACjB,SAAS,KAAA,KAAU;AAAA,GAClB,CAAA;AAEH,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"textarea.js","sourcesContent":["import React from 'react';\n// import './textarea.css';\nimport { classnames } from '../helpers/classnames';\n\nexport interface TextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n state?: 'default' | 'error' | 'success';\n}\n\nconst Textarea: React.FC<TextareaProps> = ({\n state = 'default',\n className = '',\n disabled = false,\n ...props\n}) => {\n const classes = classnames('textarea', className, {\n error: state === 'error',\n success: state === 'success',\n });\n\n return (\n <textarea\n className={classes}\n disabled={disabled}\n {...props}\n />\n );\n};\n\nexport default Textarea;"]}
1
+ {"version":3,"sources":["../../../src/components/textarea/textarea.tsx"],"names":[],"mappings":";;;;AAmBA,SAAS,gBAAgB,KAAA,EAAe;AACtC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA;AAEtC,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,GACzB,MAAA,GACA,CAAA;AACN;AAEA,MAAM,WAAoC,CAAC;AAAA,EACzC,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,SAAA;AAAA,EACR,QAAA,GAAW,KAAA;AAAA,EACX,SAAA,GAAY,EAAA;AAAA,EACZ,QAAA,GAAW,KAAA;AAAA,EACX,OAAA;AAAA,EACA,GAAG;AACL,CAAA,KAAM;AACJ,EAAA,MAAM,WAAA,GACJ,OAA4B,IAAI,CAAA;AAElC,EAAA,MAAM,eAAA,GACJ,YAAY,MAAM;AAChB,IAAA,MAAM,WACJ,WAAA,CAAY,OAAA;AAEd,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,QAAA,EAAU;AAC1B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,aAAA,GACJ,MAAA,CAAO,gBAAA,CAAiB,QAAQ,CAAA;AAElC,IAAA,MAAM,UAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,YAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB,GACA,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,SAAA,GACJ,eAAA;AAAA,MACE,aAAA,CAAc;AAAA,KAChB;AAEF,IAAA,MAAM,iBAAA,GACJ,SAAS,KAAA,CAAM,SAAA;AAEjB,IAAA,QAAA,CAAS,MAAM,SAAA,GACb,KAAA;AAEF,IAAA,QAAA,CAAS,KAAA,CAAM,WAAA;AAAA,MACb,wBAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,gBACJ,QAAA,CAAS,YAAA;AAEX,IAAA,QAAA,CAAS,MAAM,SAAA,GACb,iBAAA;AAEF,IAAA,MAAM,aAAa,IAAA,CAAK,GAAA;AAAA,MACtB,gBACE,UAAA,GACA,YAAA;AAAA,MACF;AAAA,KACF;AAEA,IAAA,QAAA,CAAS,KAAA,CAAM,WAAA;AAAA,MACb,wBAAA;AAAA,MACA,GAAG,UAAU,CAAA,EAAA;AAAA,KACf;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEf,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,eAAA,EAAgB;AAAA,EAClB,CAAA,EAAG;AAAA,IACD,eAAA;AAAA,IACA,KAAA,CAAM,KAAA;AAAA,IACN;AAAA,GACD,CAAA;AAED,EAAA,MAAM,WAAA,GAAc,CAClB,KAAA,KACG;AACH,IAAA,eAAA,EAAgB;AAChB,IAAA,OAAA,GAAU,KAAK,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACd,UAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,MACE,mBACE,KAAA,KAAU,OAAA;AAAA,MACZ,qBACE,KAAA,KAAU,SAAA;AAAA,MACZ,yBACE,KAAA,KAAU,OAAA;AAAA,MACZ,2BACE,KAAA,KAAU,SAAA;AAAA,MACZ,uBACE,KAAA,KAAU,KAAA;AAAA,MACZ,qBAAA,EACE;AAAA;AACJ,GACF;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAW,OAAA;AAAA,MACX,QAAA;AAAA,MACA,OAAA,EAAS,WAAA;AAAA,MACR,GAAG;AAAA;AAAA,GACN;AAEJ,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"textarea.js","sourcesContent":["import React, {\n useCallback,\n useLayoutEffect,\n useRef,\n} from 'react';\nimport { classnames } from '../helpers/classnames';\n\ntype TextareaWidth =\n | 'short'\n | 'default'\n | 'max';\n\nexport interface TextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n state?: 'default' | 'error' | 'success';\n width?: TextareaWidth;\n autoGrow?: boolean;\n}\n\nfunction parsePixelValue(value: string) {\n const parsed = Number.parseFloat(value);\n\n return Number.isFinite(parsed)\n ? parsed\n : 0;\n}\n\nconst Textarea: React.FC<TextareaProps> = ({\n state = 'default',\n width = 'default',\n autoGrow = false,\n className = '',\n disabled = false,\n onInput,\n ...props\n}) => {\n const textareaRef =\n useRef<HTMLTextAreaElement>(null);\n\n const resizeToContent =\n useCallback(() => {\n const textarea =\n textareaRef.current;\n\n if (!autoGrow || !textarea) {\n return;\n }\n\n const computedStyle =\n window.getComputedStyle(textarea);\n\n const lineHeight =\n parsePixelValue(\n computedStyle.lineHeight,\n );\n\n const borderHeight =\n parsePixelValue(\n computedStyle.borderTopWidth,\n ) +\n parsePixelValue(\n computedStyle.borderBottomWidth,\n );\n\n const minHeight =\n parsePixelValue(\n computedStyle.minHeight,\n );\n\n const previousMinHeight =\n textarea.style.minHeight;\n\n textarea.style.minHeight =\n '0px';\n\n textarea.style.setProperty(\n '--textarea-auto-height',\n '0px',\n );\n\n const contentHeight =\n textarea.scrollHeight;\n\n textarea.style.minHeight =\n previousMinHeight;\n\n const nextHeight = Math.max(\n contentHeight +\n lineHeight +\n borderHeight,\n minHeight,\n );\n\n textarea.style.setProperty(\n '--textarea-auto-height',\n `${nextHeight}px`,\n );\n }, [autoGrow]);\n\n useLayoutEffect(() => {\n resizeToContent();\n }, [\n resizeToContent,\n props.value,\n width,\n ]);\n\n const handleInput = (\n event: React.InputEvent<HTMLTextAreaElement>,\n ) => {\n resizeToContent();\n onInput?.(event);\n };\n\n const classes = classnames(\n 'textarea',\n className,\n {\n 'textarea--error':\n state === 'error',\n 'textarea--success':\n state === 'success',\n 'textarea--width-short':\n width === 'short',\n 'textarea--width-default':\n width === 'default',\n 'textarea--width-max':\n width === 'max',\n 'textarea--auto-grow':\n autoGrow,\n },\n );\n\n return (\n <textarea\n ref={textareaRef}\n className={classes}\n disabled={disabled}\n onInput={handleInput}\n {...props}\n />\n );\n};\n\nexport default Textarea;\n"]}
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  export { default as Button, buttonLink, } from './components/button';
6
6
  export type { ButtonProps, } from './components/button';
7
7
  export { default as Input } from './components/input';
8
- export type { InputProps, InputWidth, } from './components/input';
8
+ export type { InputProps } from './components/input';
9
9
  export { default as Textarea } from './components/textarea';
10
10
  export type { TextareaProps } from './components/textarea';
11
11
  export { default as Select } from './components/select';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvetrau-ds",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Multi-brand React design system with semantic tokens",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/styles.css CHANGED
@@ -2758,7 +2758,7 @@
2758
2758
  --flip-list-open-content-gap:
2759
2759
  var(--font-h2-margin);
2760
2760
  --flip-list-trigger-column-gap:
2761
- var(--margin-item);
2761
+ var(--margin-icon);
2762
2762
  --flip-list-scroll-margin-block-start:
2763
2763
  0px;
2764
2764
 
@@ -2831,74 +2831,92 @@
2831
2831
  /* motion */
2832
2832
  }
2833
2833
 
2834
- .flip-list__trigger {
2834
+ .flip-list__status {
2835
+ /* color */
2836
+
2837
+ /* typography */
2838
+
2839
+ /* size */
2840
+ --flip-list-status-gap:
2841
+ var(--margin-tag);
2842
+
2843
+ display: block;
2844
+ align-self: flex-start;
2845
+ min-width: 0;
2846
+ max-width: 100%;
2847
+ margin:
2848
+ 0
2849
+ 0
2850
+ var(--flip-list-status-gap)
2851
+ 0;
2852
+
2853
+ /* border */
2854
+
2855
+ /* depth */
2856
+
2857
+ /* motion */
2858
+ }
2859
+
2860
+ .flip-list__heading {
2835
2861
  /* color */
2836
- background: transparent;
2837
- color: var(--flip-list-trigger-color);
2838
2862
 
2839
2863
  /* typography */
2840
- font: inherit;
2841
- text-align: left;
2842
2864
 
2843
2865
  /* size */
2844
2866
  display: flex;
2845
2867
  align-items: center;
2846
- justify-content: space-between;
2847
2868
  gap: var(--flip-list-trigger-column-gap);
2848
2869
  width: 100%;
2849
2870
  min-width: 0;
2850
2871
  max-width: 100%;
2851
- padding: 0;
2852
2872
 
2853
2873
  /* border */
2854
- border: 0;
2855
2874
 
2856
2875
  /* depth */
2857
2876
 
2858
2877
  /* motion */
2859
- cursor: pointer;
2860
2878
  }
2861
2879
 
2862
- .flip-list__trigger:focus-visible {
2880
+ .flip-list__trigger {
2863
2881
  /* color */
2882
+ background: transparent;
2883
+ color: var(--flip-list-trigger-color);
2864
2884
 
2865
2885
  /* typography */
2886
+ font: inherit;
2887
+ text-align: left;
2866
2888
 
2867
2889
  /* size */
2890
+ display: flex;
2891
+ flex: 0 1 auto;
2892
+ align-items: center;
2893
+ min-width: 0;
2894
+ max-width: 100%;
2895
+ padding: 0;
2868
2896
 
2869
2897
  /* border */
2870
- outline:
2871
- var(--size-border-width-focus)
2872
- solid
2873
- currentColor;
2874
- outline-offset:
2875
- var(--size-border-offset-focus);
2898
+ border: 0;
2876
2899
 
2877
2900
  /* depth */
2878
2901
 
2879
2902
  /* motion */
2903
+ cursor: pointer;
2880
2904
  }
2881
2905
 
2882
- .flip-list__status {
2906
+ .flip-list__trigger:focus-visible {
2883
2907
  /* color */
2884
2908
 
2885
2909
  /* typography */
2886
2910
 
2887
2911
  /* size */
2888
- --flip-list-status-gap:
2889
- var(--margin-tag);
2890
-
2891
- display: block;
2892
- align-self: flex-start;
2893
- min-width: 0;
2894
- max-width: 100%;
2895
- margin:
2896
- 0
2897
- 0
2898
- var(--flip-list-status-gap)
2899
- 0;
2900
2912
 
2901
2913
  /* border */
2914
+ outline:
2915
+ var(--size-border-width-focus)
2916
+ solid
2917
+ currentColor;
2918
+ outline-offset:
2919
+ var(--size-border-offset-focus);
2902
2920
 
2903
2921
  /* depth */
2904
2922
 
@@ -2927,7 +2945,6 @@
2927
2945
  text-underline-offset: from-font;
2928
2946
 
2929
2947
  /* size */
2930
- flex: 1 1 auto;
2931
2948
  min-width: 0;
2932
2949
  max-width: 100%;
2933
2950
  overflow-wrap: anywhere;
@@ -2955,6 +2972,21 @@
2955
2972
  /* motion */
2956
2973
  }
2957
2974
 
2975
+ .flip-list__status-icon {
2976
+ /* color */
2977
+
2978
+ /* typography */
2979
+
2980
+ /* size */
2981
+ flex: 0 0 auto;
2982
+
2983
+ /* border */
2984
+
2985
+ /* depth */
2986
+
2987
+ /* motion */
2988
+ }
2989
+
2958
2990
  .flip-list__meta {
2959
2991
  /* color */
2960
2992
  color: var(--flip-list-meta-color);
@@ -2976,6 +3008,7 @@
2976
3008
  flex: 0 0 auto;
2977
3009
  min-width: 0;
2978
3010
  max-width: 100%;
3011
+ margin-left: auto;
2979
3012
 
2980
3013
  /* border */
2981
3014
 
@@ -4264,7 +4297,7 @@
4264
4297
 
4265
4298
  position: relative;
4266
4299
  display: inline-block;
4267
- width: 100%;
4300
+ box-sizing: border-box;
4268
4301
 
4269
4302
  /* border */
4270
4303
  --select-border-width: var(--size-border-width-base);
@@ -4285,6 +4318,21 @@
4285
4318
  --select-transition-duration: 0.2s;
4286
4319
  }
4287
4320
 
4321
+ .select--width-short {
4322
+ /* size */
4323
+ width: fit-content;
4324
+ }
4325
+
4326
+ .select--width-default {
4327
+ /* size */
4328
+ width: 400px;
4329
+ }
4330
+
4331
+ .select--width-max {
4332
+ /* size */
4333
+ width: 100%;
4334
+ }
4335
+
4288
4336
  .select__field {
4289
4337
  /* color */
4290
4338
  background: var(--select-bg);
@@ -4295,6 +4343,7 @@
4295
4343
  letter-spacing: inherit;
4296
4344
 
4297
4345
  /* size */
4346
+ box-sizing: border-box;
4298
4347
  width: 100%;
4299
4348
  height: var(--select-height);
4300
4349
  padding-left: var(--select-padding-x);
@@ -4700,6 +4749,7 @@
4700
4749
  --textarea-padding-x: var(--padding-control-horz);
4701
4750
  --textarea-padding-y: var(--padding-control-horz);
4702
4751
 
4752
+ box-sizing: border-box;
4703
4753
  min-height: var(--textarea-min-height);
4704
4754
  padding: var(--textarea-padding-y) var(--textarea-padding-x);
4705
4755
 
@@ -4729,6 +4779,30 @@
4729
4779
  background-color var(--textarea-transition-duration) ease;
4730
4780
  }
4731
4781
 
4782
+ .textarea--width-short {
4783
+ /* size */
4784
+ width: auto;
4785
+ }
4786
+
4787
+ .textarea--width-default {
4788
+ /* size */
4789
+ width: 400px;
4790
+ }
4791
+
4792
+ .textarea--width-max {
4793
+ /* size */
4794
+ width: 100%;
4795
+ }
4796
+
4797
+ .textarea--auto-grow {
4798
+ /* size */
4799
+ height: var(--textarea-auto-height);
4800
+
4801
+ /* motion */
4802
+ overflow-y: hidden;
4803
+ resize: none;
4804
+ }
4805
+
4732
4806
  .textarea::placeholder {
4733
4807
  color: var(--textarea-placeholder);
4734
4808
  }