@madecki/ui 1.5.0 → 2.0.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.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
- import { ReactNode, HTMLInputTypeAttribute } from 'react';
3
+ import { ReactNode, AriaRole, HTMLInputTypeAttribute } from 'react';
4
4
 
5
5
  type ColorVariants = "primary" | "success" | "warning" | "danger" | "info" | "white" | "gray" | "lightGray" | "darkGray" | "neutral" | "blue";
6
6
  type ThemeMode = "light" | "dark";
@@ -17,8 +17,11 @@ interface ButtonProps {
17
17
  disabled?: boolean;
18
18
  className?: string;
19
19
  type?: "button" | "submit" | "reset";
20
+ /** e.g. `radio` when used inside a `radiogroup` */
21
+ role?: AriaRole;
22
+ ariaChecked?: boolean | "mixed";
20
23
  }
21
- declare const Button: ({ variant, size, children, onClick, isActive, id, label, disabled, className, type, }: ButtonProps) => react_jsx_runtime.JSX.Element;
24
+ declare const Button: ({ variant, size, children, onClick, isActive, id, label, disabled, className, type, role, ariaChecked, }: ButtonProps) => react_jsx_runtime.JSX.Element;
22
25
 
23
26
  interface ButtonTransparentProps {
24
27
  variant: Exclude<ColorVariants, "primary">;
@@ -40,13 +43,18 @@ interface GradientButtonProps {
40
43
  }
41
44
  declare const GradientButton: ({ children, onClick, size, disabled, className, type, }: GradientButtonProps) => react_jsx_runtime.JSX.Element;
42
45
 
46
+ type LabelVisibility = "visible" | "sr-only";
47
+
43
48
  interface RadioButtonsProps {
44
- items: Omit<ButtonProps, "size" | "onClick" | "isActive">[];
49
+ label: string;
50
+ /** Default `visible`. Use `sr-only` to hide the group label visually while keeping it for assistive tech. */
51
+ labelVisibility?: LabelVisibility;
52
+ items: Omit<ButtonProps, "size" | "onClick" | "isActive" | "role" | "ariaChecked">[];
45
53
  onChange: (value: string) => void;
46
54
  size?: ButtonProps["size"];
47
55
  className?: string;
48
56
  }
49
- declare const RadioButtons: ({ items, onChange, size, className, }: RadioButtonsProps) => react_jsx_runtime.JSX.Element;
57
+ declare const RadioButtons: ({ label, labelVisibility, items, onChange, size, className, }: RadioButtonsProps) => react_jsx_runtime.JSX.Element;
50
58
 
51
59
  interface TagProps {
52
60
  variant: ColorVariants;
@@ -70,6 +78,8 @@ interface InputProps {
70
78
  defaultValue?: string;
71
79
  placeholder?: string;
72
80
  label: string;
81
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
82
+ labelVisibility?: LabelVisibility;
73
83
  variant?: "primary" | "secondary" | "tertiary";
74
84
  /** Any standard HTML `input` type (e.g. `date`, `time`, `email`). */
75
85
  type?: HTMLInputTypeAttribute;
@@ -85,7 +95,31 @@ interface InputProps {
85
95
  /** Sets `data-testid` on the native `<input>` for e2e (e.g. Playwright). */
86
96
  testId?: string;
87
97
  }
88
- declare const Input: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, variant, type, maxLength, required, pattern, title, ariaLabel, spellCheck, disabled, className, icon, testId, }: InputProps) => react_jsx_runtime.JSX.Element;
98
+ declare const Input: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, labelVisibility, variant, type, maxLength, required, pattern, title, ariaLabel, spellCheck, disabled, className, icon, testId, }: InputProps) => react_jsx_runtime.JSX.Element;
99
+
100
+ interface TextareaProps {
101
+ name: string;
102
+ onChange?: (value: string) => void;
103
+ /** When set, the textarea is controlled; the parent must update this from `onChange`. */
104
+ value?: string;
105
+ /** Initial value when uncontrolled (`value` omitted). Does not update after mount if `defaultValue` prop changes. */
106
+ defaultValue?: string;
107
+ placeholder?: string;
108
+ label: string;
109
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
110
+ labelVisibility?: LabelVisibility;
111
+ variant?: "primary" | "secondary" | "tertiary";
112
+ rows?: number;
113
+ maxLength?: number;
114
+ required?: boolean;
115
+ ariaLabel?: string;
116
+ spellCheck?: boolean;
117
+ disabled?: boolean;
118
+ className?: string;
119
+ /** Sets `data-testid` on the native `<textarea>` for e2e (e.g. Playwright). */
120
+ testId?: string;
121
+ }
122
+ declare const Textarea: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, labelVisibility, variant, rows, maxLength, required, ariaLabel, spellCheck, disabled, className, testId, }: TextareaProps) => react_jsx_runtime.JSX.Element;
89
123
 
90
124
  type SelectOption = {
91
125
  value: string;
@@ -94,6 +128,8 @@ type SelectOption = {
94
128
  type BaseSelectProps = {
95
129
  name: string;
96
130
  label: string;
131
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
132
+ labelVisibility?: LabelVisibility;
97
133
  options: SelectOption[];
98
134
  placeholder?: string;
99
135
  variant?: "primary" | "secondary" | "tertiary";
@@ -209,13 +245,14 @@ declare const Heading: ({ children, level, size, weight, color, className, }: He
209
245
 
210
246
  interface TextProps {
211
247
  children: ReactNode;
248
+ id?: string;
212
249
  size?: "xs" | "sm" | "md" | "lg";
213
250
  weight?: "normal" | "medium" | "semibold" | "bold";
214
251
  color?: "default" | "muted" | "primary" | "success" | "warning" | "danger";
215
252
  as?: "p" | "span" | "div" | "label";
216
253
  className?: string;
217
254
  }
218
- declare const Text: ({ children, size, weight, color, as: Tag, className, }: TextProps) => react_jsx_runtime.JSX.Element;
255
+ declare const Text: ({ children, id, size, weight, color, as: Tag, className, }: TextProps) => react_jsx_runtime.JSX.Element;
219
256
 
220
257
  interface HeartProps {
221
258
  variant?: "outline" | "filled" | "gradient";
@@ -258,4 +295,4 @@ declare const TwitterIcon: ({ className, size, withWrapper, }: SocialIconProps)
258
295
  declare const LinkedInIcon: ({ className, size, withWrapper, }: SocialIconProps) => react_jsx_runtime.JSX.Element;
259
296
  declare const InstagramIcon: ({ className, size, withWrapper, }: SocialIconProps) => react_jsx_runtime.JSX.Element;
260
297
 
261
- export { BlockQuote, type BlockQuoteProps, Button, type ButtonProps, ButtonTransparent, type ButtonTransparentProps, type ColorVariants, Container, type ContainerProps, ContentBox, type ContentBoxProps, GradientButton, type GradientButtonProps, Grid, GridItem, type GridItemProps, type GridProps, Heading, type HeadingProps, Heart, type HeartProps, Hr, type HrProps, Info, type InfoProps, Input, type InputProps, InstagramIcon, LinkedInIcon, type MultiSelectProps, RadioButtons, type RadioButtonsProps, Search, type SearchProps, Select, type SelectOption, type SelectProps, Share, type ShareProps, type SingleSelectProps, type Size, type SocialIconProps, Spinner, SpinnerOverlay, type SpinnerOverlayProps, type SpinnerProps, Stack, type StackProps, type Tab, Tabs, type TabsProps, Tag, type TagProps, Text, type TextProps, type ThemeMode, TwitterIcon, Warning, type WarningProps };
298
+ export { BlockQuote, type BlockQuoteProps, Button, type ButtonProps, ButtonTransparent, type ButtonTransparentProps, type ColorVariants, Container, type ContainerProps, ContentBox, type ContentBoxProps, GradientButton, type GradientButtonProps, Grid, GridItem, type GridItemProps, type GridProps, Heading, type HeadingProps, Heart, type HeartProps, Hr, type HrProps, Info, type InfoProps, Input, type InputProps, InstagramIcon, type LabelVisibility, LinkedInIcon, type MultiSelectProps, RadioButtons, type RadioButtonsProps, Search, type SearchProps, Select, type SelectOption, type SelectProps, Share, type ShareProps, type SingleSelectProps, type Size, type SocialIconProps, Spinner, SpinnerOverlay, type SpinnerOverlayProps, type SpinnerProps, Stack, type StackProps, type Tab, Tabs, type TabsProps, Tag, type TagProps, Text, type TextProps, Textarea, type TextareaProps, type ThemeMode, TwitterIcon, Warning, type WarningProps };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
- import { ReactNode, HTMLInputTypeAttribute } from 'react';
3
+ import { ReactNode, AriaRole, HTMLInputTypeAttribute } from 'react';
4
4
 
5
5
  type ColorVariants = "primary" | "success" | "warning" | "danger" | "info" | "white" | "gray" | "lightGray" | "darkGray" | "neutral" | "blue";
6
6
  type ThemeMode = "light" | "dark";
@@ -17,8 +17,11 @@ interface ButtonProps {
17
17
  disabled?: boolean;
18
18
  className?: string;
19
19
  type?: "button" | "submit" | "reset";
20
+ /** e.g. `radio` when used inside a `radiogroup` */
21
+ role?: AriaRole;
22
+ ariaChecked?: boolean | "mixed";
20
23
  }
21
- declare const Button: ({ variant, size, children, onClick, isActive, id, label, disabled, className, type, }: ButtonProps) => react_jsx_runtime.JSX.Element;
24
+ declare const Button: ({ variant, size, children, onClick, isActive, id, label, disabled, className, type, role, ariaChecked, }: ButtonProps) => react_jsx_runtime.JSX.Element;
22
25
 
23
26
  interface ButtonTransparentProps {
24
27
  variant: Exclude<ColorVariants, "primary">;
@@ -40,13 +43,18 @@ interface GradientButtonProps {
40
43
  }
41
44
  declare const GradientButton: ({ children, onClick, size, disabled, className, type, }: GradientButtonProps) => react_jsx_runtime.JSX.Element;
42
45
 
46
+ type LabelVisibility = "visible" | "sr-only";
47
+
43
48
  interface RadioButtonsProps {
44
- items: Omit<ButtonProps, "size" | "onClick" | "isActive">[];
49
+ label: string;
50
+ /** Default `visible`. Use `sr-only` to hide the group label visually while keeping it for assistive tech. */
51
+ labelVisibility?: LabelVisibility;
52
+ items: Omit<ButtonProps, "size" | "onClick" | "isActive" | "role" | "ariaChecked">[];
45
53
  onChange: (value: string) => void;
46
54
  size?: ButtonProps["size"];
47
55
  className?: string;
48
56
  }
49
- declare const RadioButtons: ({ items, onChange, size, className, }: RadioButtonsProps) => react_jsx_runtime.JSX.Element;
57
+ declare const RadioButtons: ({ label, labelVisibility, items, onChange, size, className, }: RadioButtonsProps) => react_jsx_runtime.JSX.Element;
50
58
 
51
59
  interface TagProps {
52
60
  variant: ColorVariants;
@@ -70,6 +78,8 @@ interface InputProps {
70
78
  defaultValue?: string;
71
79
  placeholder?: string;
72
80
  label: string;
81
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
82
+ labelVisibility?: LabelVisibility;
73
83
  variant?: "primary" | "secondary" | "tertiary";
74
84
  /** Any standard HTML `input` type (e.g. `date`, `time`, `email`). */
75
85
  type?: HTMLInputTypeAttribute;
@@ -85,7 +95,31 @@ interface InputProps {
85
95
  /** Sets `data-testid` on the native `<input>` for e2e (e.g. Playwright). */
86
96
  testId?: string;
87
97
  }
88
- declare const Input: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, variant, type, maxLength, required, pattern, title, ariaLabel, spellCheck, disabled, className, icon, testId, }: InputProps) => react_jsx_runtime.JSX.Element;
98
+ declare const Input: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, labelVisibility, variant, type, maxLength, required, pattern, title, ariaLabel, spellCheck, disabled, className, icon, testId, }: InputProps) => react_jsx_runtime.JSX.Element;
99
+
100
+ interface TextareaProps {
101
+ name: string;
102
+ onChange?: (value: string) => void;
103
+ /** When set, the textarea is controlled; the parent must update this from `onChange`. */
104
+ value?: string;
105
+ /** Initial value when uncontrolled (`value` omitted). Does not update after mount if `defaultValue` prop changes. */
106
+ defaultValue?: string;
107
+ placeholder?: string;
108
+ label: string;
109
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
110
+ labelVisibility?: LabelVisibility;
111
+ variant?: "primary" | "secondary" | "tertiary";
112
+ rows?: number;
113
+ maxLength?: number;
114
+ required?: boolean;
115
+ ariaLabel?: string;
116
+ spellCheck?: boolean;
117
+ disabled?: boolean;
118
+ className?: string;
119
+ /** Sets `data-testid` on the native `<textarea>` for e2e (e.g. Playwright). */
120
+ testId?: string;
121
+ }
122
+ declare const Textarea: ({ name, onChange, value: valueProp, defaultValue, placeholder, label, labelVisibility, variant, rows, maxLength, required, ariaLabel, spellCheck, disabled, className, testId, }: TextareaProps) => react_jsx_runtime.JSX.Element;
89
123
 
90
124
  type SelectOption = {
91
125
  value: string;
@@ -94,6 +128,8 @@ type SelectOption = {
94
128
  type BaseSelectProps = {
95
129
  name: string;
96
130
  label: string;
131
+ /** Default `visible`. Use `sr-only` to hide the label visually while keeping it for assistive tech. */
132
+ labelVisibility?: LabelVisibility;
97
133
  options: SelectOption[];
98
134
  placeholder?: string;
99
135
  variant?: "primary" | "secondary" | "tertiary";
@@ -209,13 +245,14 @@ declare const Heading: ({ children, level, size, weight, color, className, }: He
209
245
 
210
246
  interface TextProps {
211
247
  children: ReactNode;
248
+ id?: string;
212
249
  size?: "xs" | "sm" | "md" | "lg";
213
250
  weight?: "normal" | "medium" | "semibold" | "bold";
214
251
  color?: "default" | "muted" | "primary" | "success" | "warning" | "danger";
215
252
  as?: "p" | "span" | "div" | "label";
216
253
  className?: string;
217
254
  }
218
- declare const Text: ({ children, size, weight, color, as: Tag, className, }: TextProps) => react_jsx_runtime.JSX.Element;
255
+ declare const Text: ({ children, id, size, weight, color, as: Tag, className, }: TextProps) => react_jsx_runtime.JSX.Element;
219
256
 
220
257
  interface HeartProps {
221
258
  variant?: "outline" | "filled" | "gradient";
@@ -258,4 +295,4 @@ declare const TwitterIcon: ({ className, size, withWrapper, }: SocialIconProps)
258
295
  declare const LinkedInIcon: ({ className, size, withWrapper, }: SocialIconProps) => react_jsx_runtime.JSX.Element;
259
296
  declare const InstagramIcon: ({ className, size, withWrapper, }: SocialIconProps) => react_jsx_runtime.JSX.Element;
260
297
 
261
- export { BlockQuote, type BlockQuoteProps, Button, type ButtonProps, ButtonTransparent, type ButtonTransparentProps, type ColorVariants, Container, type ContainerProps, ContentBox, type ContentBoxProps, GradientButton, type GradientButtonProps, Grid, GridItem, type GridItemProps, type GridProps, Heading, type HeadingProps, Heart, type HeartProps, Hr, type HrProps, Info, type InfoProps, Input, type InputProps, InstagramIcon, LinkedInIcon, type MultiSelectProps, RadioButtons, type RadioButtonsProps, Search, type SearchProps, Select, type SelectOption, type SelectProps, Share, type ShareProps, type SingleSelectProps, type Size, type SocialIconProps, Spinner, SpinnerOverlay, type SpinnerOverlayProps, type SpinnerProps, Stack, type StackProps, type Tab, Tabs, type TabsProps, Tag, type TagProps, Text, type TextProps, type ThemeMode, TwitterIcon, Warning, type WarningProps };
298
+ export { BlockQuote, type BlockQuoteProps, Button, type ButtonProps, ButtonTransparent, type ButtonTransparentProps, type ColorVariants, Container, type ContainerProps, ContentBox, type ContentBoxProps, GradientButton, type GradientButtonProps, Grid, GridItem, type GridItemProps, type GridProps, Heading, type HeadingProps, Heart, type HeartProps, Hr, type HrProps, Info, type InfoProps, Input, type InputProps, InstagramIcon, type LabelVisibility, LinkedInIcon, type MultiSelectProps, RadioButtons, type RadioButtonsProps, Search, type SearchProps, Select, type SelectOption, type SelectProps, Share, type ShareProps, type SingleSelectProps, type Size, type SocialIconProps, Spinner, SpinnerOverlay, type SpinnerOverlayProps, type SpinnerProps, Stack, type StackProps, type Tab, Tabs, type TabsProps, Tag, type TagProps, Text, type TextProps, Textarea, type TextareaProps, type ThemeMode, TwitterIcon, Warning, type WarningProps };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, createElement, useId, useMemo, useRef, useCallback, useEffect } from 'react';
2
+ import { useId, useState, createElement, useMemo, useRef, useCallback, useEffect } from 'react';
3
3
 
4
4
  // src/components/Tag/tagSurfaceClassNames.ts
5
5
  function getTagSurfaceClassNames({
@@ -99,7 +99,9 @@ var Button = ({
99
99
  label,
100
100
  disabled,
101
101
  className = "",
102
- type = "button"
102
+ type = "button",
103
+ role,
104
+ ariaChecked
103
105
  }) => {
104
106
  if (typeof isActive === "boolean" && id === void 0) {
105
107
  throw Error("If button has isActive props, it must have id props too");
@@ -116,6 +118,8 @@ var Button = ({
116
118
  "button",
117
119
  {
118
120
  type,
121
+ role,
122
+ "aria-checked": ariaChecked,
119
123
  className: surfaceClassName,
120
124
  onClick: () => {
121
125
  if (isActive === true) {
@@ -252,27 +256,106 @@ var GradientButton = ({
252
256
  }
253
257
  );
254
258
  };
259
+ var sizeStyles = {
260
+ xs: "text-xs",
261
+ sm: "text-sm",
262
+ md: "text-md",
263
+ lg: "text-lg"
264
+ };
265
+ var weightStyles = {
266
+ normal: "font-normal",
267
+ medium: "font-medium",
268
+ semibold: "font-semibold",
269
+ bold: "font-bold"
270
+ };
271
+ var colorStyles = {
272
+ default: "text-white dark:text-white",
273
+ muted: "text-lightgray dark:text-lightgray",
274
+ primary: "text-primary dark:text-white",
275
+ success: "text-success",
276
+ warning: "text-warning",
277
+ danger: "text-danger"
278
+ };
279
+ var Text = ({
280
+ children,
281
+ id,
282
+ size = "md",
283
+ weight = "normal",
284
+ color = "default",
285
+ as: Tag2 = "p",
286
+ className = ""
287
+ }) => {
288
+ return /* @__PURE__ */ jsx(
289
+ Tag2,
290
+ {
291
+ id,
292
+ className: `${sizeStyles[size]} ${weightStyles[weight]} ${colorStyles[color]} ${className}`,
293
+ children
294
+ }
295
+ );
296
+ };
297
+ function FormFieldLabel({
298
+ label,
299
+ labelVisibility,
300
+ id
301
+ }) {
302
+ return /* @__PURE__ */ jsx(
303
+ Text,
304
+ {
305
+ as: "span",
306
+ id,
307
+ size: "sm",
308
+ weight: "medium",
309
+ color: "muted",
310
+ className: labelVisibility === "sr-only" ? "sr-only" : "mb-2 block",
311
+ children: label
312
+ }
313
+ );
314
+ }
255
315
  var RadioButtons = ({
316
+ label,
317
+ labelVisibility = "visible",
256
318
  items,
257
319
  onChange,
258
320
  size = "md",
259
321
  className = ""
260
322
  }) => {
323
+ const labelId = useId();
261
324
  const [selectedButton, setSelectedButton] = useState();
262
325
  const onButtonClick = (id) => {
263
326
  setSelectedButton(id);
264
327
  onChange(id ?? "");
265
328
  };
266
- return /* @__PURE__ */ jsx("div", { className: `flex flex-wrap gap-2 ${className}`, children: items.map((item) => /* @__PURE__ */ createElement(
267
- Button,
268
- {
269
- ...item,
270
- size,
271
- key: item.id,
272
- isActive: selectedButton === item.id,
273
- onClick: onButtonClick
274
- }
275
- )) });
329
+ return /* @__PURE__ */ jsxs("div", { className, children: [
330
+ /* @__PURE__ */ jsx(
331
+ FormFieldLabel,
332
+ {
333
+ id: labelId,
334
+ label,
335
+ labelVisibility
336
+ }
337
+ ),
338
+ /* @__PURE__ */ jsx(
339
+ "div",
340
+ {
341
+ role: "radiogroup",
342
+ "aria-labelledby": labelId,
343
+ className: "flex flex-wrap gap-2",
344
+ children: items.map((item) => /* @__PURE__ */ createElement(
345
+ Button,
346
+ {
347
+ ...item,
348
+ size,
349
+ key: item.id,
350
+ role: "radio",
351
+ ariaChecked: selectedButton === item.id,
352
+ isActive: selectedButton === item.id,
353
+ onClick: onButtonClick
354
+ }
355
+ ))
356
+ }
357
+ )
358
+ ] });
276
359
  };
277
360
  var Tag = ({
278
361
  variant,
@@ -304,6 +387,7 @@ var Input = ({
304
387
  defaultValue,
305
388
  placeholder,
306
389
  label,
390
+ labelVisibility = "visible",
307
391
  variant = "primary",
308
392
  type = "text",
309
393
  maxLength,
@@ -317,6 +401,7 @@ var Input = ({
317
401
  icon,
318
402
  testId
319
403
  }) => {
404
+ const labelId = useId();
320
405
  const isControlled = valueProp !== void 0;
321
406
  const [internalValue, setInternalValue] = useState(() => defaultValue ?? "");
322
407
  const [isFocused, setIsFocused] = useState(false);
@@ -352,8 +437,15 @@ var Input = ({
352
437
  }
353
438
  onChange?.(next);
354
439
  };
355
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("label", { htmlFor: name, children: [
356
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
440
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("label", { htmlFor: name, className: "block", children: [
441
+ /* @__PURE__ */ jsx(
442
+ FormFieldLabel,
443
+ {
444
+ id: labelId,
445
+ label,
446
+ labelVisibility
447
+ }
448
+ ),
357
449
  /* @__PURE__ */ jsxs("div", { className: inputWrapperClassNames.join(" "), children: [
358
450
  icon && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center pl-4", children: icon }),
359
451
  /* @__PURE__ */ jsx(
@@ -373,7 +465,7 @@ var Input = ({
373
465
  required,
374
466
  pattern,
375
467
  title,
376
- "aria-label": ariaLabel || label || name,
468
+ "aria-label": ariaLabel,
377
469
  spellCheck,
378
470
  disabled,
379
471
  "data-testid": testId
@@ -382,6 +474,94 @@ var Input = ({
382
474
  ] })
383
475
  ] }) });
384
476
  };
477
+ var Textarea = ({
478
+ name,
479
+ onChange,
480
+ value: valueProp,
481
+ defaultValue,
482
+ placeholder,
483
+ label,
484
+ labelVisibility = "visible",
485
+ variant = "primary",
486
+ rows = 4,
487
+ maxLength,
488
+ required = false,
489
+ ariaLabel,
490
+ spellCheck,
491
+ disabled = false,
492
+ className = "",
493
+ testId
494
+ }) => {
495
+ const labelId = useId();
496
+ const isControlled = valueProp !== void 0;
497
+ const [internalValue, setInternalValue] = useState(() => defaultValue ?? "");
498
+ const [isFocused, setIsFocused] = useState(false);
499
+ const value = isControlled ? valueProp : internalValue;
500
+ const fieldClassNames = [
501
+ "min-h-[6rem] resize-y rounded-sm font-sans z-10 w-full"
502
+ ];
503
+ const spacings = "py-4 px-5";
504
+ const outline = "outline-hidden";
505
+ fieldClassNames.push(spacings, outline);
506
+ const inputWrapperClassNames = ["rounded-smb p-px w-full"];
507
+ if (isFocused) {
508
+ inputWrapperClassNames.push("bg-gradient");
509
+ } else if (variant === "primary" || variant === "tertiary") {
510
+ inputWrapperClassNames.push("bg-lightgray");
511
+ }
512
+ switch (variant) {
513
+ case "primary":
514
+ fieldClassNames.push("text-primary bg-neutral");
515
+ break;
516
+ case "secondary":
517
+ fieldClassNames.push("text-neutral bg-neutral dark:bg-gray");
518
+ break;
519
+ case "tertiary":
520
+ fieldClassNames.push("text-neutral bg-neutral dark:bg-primary");
521
+ break;
522
+ }
523
+ if (disabled) {
524
+ fieldClassNames.push("cursor-not-allowed opacity-50");
525
+ }
526
+ const onFieldChange = (event) => {
527
+ const next = event.target.value;
528
+ if (!isControlled) {
529
+ setInternalValue(next);
530
+ }
531
+ onChange?.(next);
532
+ };
533
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("label", { htmlFor: name, className: "block", children: [
534
+ /* @__PURE__ */ jsx(
535
+ FormFieldLabel,
536
+ {
537
+ id: labelId,
538
+ label,
539
+ labelVisibility
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsx("div", { className: inputWrapperClassNames.join(" "), children: /* @__PURE__ */ jsx(
543
+ "textarea",
544
+ {
545
+ id: name,
546
+ name,
547
+ rows,
548
+ placeholder,
549
+ value,
550
+ className: fieldClassNames.join(" "),
551
+ autoComplete: "off",
552
+ onChange: onFieldChange,
553
+ onFocus: () => setIsFocused(true),
554
+ onBlur: () => setIsFocused(false),
555
+ maxLength,
556
+ required,
557
+ "aria-label": ariaLabel,
558
+ spellCheck,
559
+ disabled,
560
+ "data-testid": testId
561
+ }
562
+ ) })
563
+ ] }) });
564
+ };
385
565
  function optionTestSlug(value) {
386
566
  return value.replace(/[^a-zA-Z0-9_-]/g, "_");
387
567
  }
@@ -466,6 +646,7 @@ function Select(props) {
466
646
  const {
467
647
  name,
468
648
  label,
649
+ labelVisibility = "visible",
469
650
  options,
470
651
  placeholder = "Select\u2026",
471
652
  variant = "primary",
@@ -473,6 +654,7 @@ function Select(props) {
473
654
  className = "",
474
655
  testId: testIdProp
475
656
  } = props;
657
+ const labelId = useId();
476
658
  const isMulti = props.multi === true;
477
659
  const singleValueProp = !isMulti ? props.value : void 0;
478
660
  const multiValueProp = isMulti ? props.value : void 0;
@@ -668,8 +850,15 @@ function Select(props) {
668
850
  const activeDescendant = open && filteredOptions[highlightIndex] ? `${name}-option-${optionTestSlug(filteredOptions[highlightIndex].value)}` : void 0;
669
851
  const listboxClass = "absolute left-0 right-0 top-full z-50 mt-1 max-h-60 overflow-auto rounded-sm border border-lightgray bg-neutral py-1 shadow-lg dark:border-gray dark:bg-gray dark:text-white";
670
852
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: `relative ${className}`.trim(), children: [
671
- /* @__PURE__ */ jsxs("label", { htmlFor: name, children: [
672
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
853
+ /* @__PURE__ */ jsxs("label", { htmlFor: name, className: "block", children: [
854
+ /* @__PURE__ */ jsx(
855
+ FormFieldLabel,
856
+ {
857
+ id: labelId,
858
+ label,
859
+ labelVisibility
860
+ }
861
+ ),
673
862
  /* @__PURE__ */ jsx("div", { className: inputWrapperClassNames.join(" "), children: /* @__PURE__ */ jsxs("div", { className: innerFieldClassNames.join(" "), children: [
674
863
  /* @__PURE__ */ jsx(
675
864
  "input",
@@ -683,7 +872,6 @@ function Select(props) {
683
872
  disabled,
684
873
  placeholder,
685
874
  value: inputValue,
686
- "aria-label": label,
687
875
  "aria-expanded": open,
688
876
  "aria-haspopup": "listbox",
689
877
  "aria-controls": listboxId,
@@ -708,7 +896,7 @@ function Select(props) {
708
896
  id: listboxId,
709
897
  role: "listbox",
710
898
  "aria-multiselectable": isMulti,
711
- "aria-label": label,
899
+ "aria-labelledby": labelId,
712
900
  "data-testid": `${baseTestId}-listbox`,
713
901
  tabIndex: -1,
714
902
  className: listboxClass,
@@ -860,7 +1048,7 @@ var ContentBox = ({
860
1048
  }
861
1049
  );
862
1050
  };
863
- var sizeStyles = {
1051
+ var sizeStyles2 = {
864
1052
  sm: "max-w-screen-sm",
865
1053
  md: "max-w-screen-md",
866
1054
  lg: "max-w-screen-lg",
@@ -877,7 +1065,7 @@ var Container = ({
877
1065
  return /* @__PURE__ */ jsx(
878
1066
  "div",
879
1067
  {
880
- className: `w-full px-5 ${sizeStyles[size]} ${centeredClass} ${className}`,
1068
+ className: `w-full px-5 ${sizeStyles2[size]} ${centeredClass} ${className}`,
881
1069
  children
882
1070
  }
883
1071
  );
@@ -972,7 +1160,7 @@ var GridItem = ({
972
1160
  }) => {
973
1161
  return /* @__PURE__ */ jsx("div", { className: `${colSpanStyles[colSpan]} ${className}`, children });
974
1162
  };
975
- var sizeStyles2 = {
1163
+ var sizeStyles3 = {
976
1164
  xs: "text-xs",
977
1165
  sm: "text-sm",
978
1166
  md: "text-md",
@@ -982,13 +1170,13 @@ var sizeStyles2 = {
982
1170
  "3xl": "text-3xl",
983
1171
  "4xl": "text-4xl"
984
1172
  };
985
- var weightStyles = {
1173
+ var weightStyles2 = {
986
1174
  normal: "font-normal",
987
1175
  medium: "font-medium",
988
1176
  semibold: "font-semibold",
989
1177
  bold: "font-bold"
990
1178
  };
991
- var colorStyles = {
1179
+ var colorStyles2 = {
992
1180
  default: "text-white dark:text-white",
993
1181
  muted: "text-lightgray dark:text-lightgray",
994
1182
  primary: "text-primary dark:text-white",
@@ -1017,47 +1205,11 @@ var Heading = ({
1017
1205
  return createElement(
1018
1206
  tag,
1019
1207
  {
1020
- className: `${sizeStyles2[resolvedSize]} ${weightStyles[weight]} ${colorStyles[color]} ${className}`
1208
+ className: `${sizeStyles3[resolvedSize]} ${weightStyles2[weight]} ${colorStyles2[color]} ${className}`
1021
1209
  },
1022
1210
  children
1023
1211
  );
1024
1212
  };
1025
- var sizeStyles3 = {
1026
- xs: "text-xs",
1027
- sm: "text-sm",
1028
- md: "text-md",
1029
- lg: "text-lg"
1030
- };
1031
- var weightStyles2 = {
1032
- normal: "font-normal",
1033
- medium: "font-medium",
1034
- semibold: "font-semibold",
1035
- bold: "font-bold"
1036
- };
1037
- var colorStyles2 = {
1038
- default: "text-white dark:text-white",
1039
- muted: "text-lightgray dark:text-lightgray",
1040
- primary: "text-primary dark:text-white",
1041
- success: "text-success",
1042
- warning: "text-warning",
1043
- danger: "text-danger"
1044
- };
1045
- var Text = ({
1046
- children,
1047
- size = "md",
1048
- weight = "normal",
1049
- color = "default",
1050
- as: Tag2 = "p",
1051
- className = ""
1052
- }) => {
1053
- return /* @__PURE__ */ jsx(
1054
- Tag2,
1055
- {
1056
- className: `${sizeStyles3[size]} ${weightStyles2[weight]} ${colorStyles2[color]} ${className}`,
1057
- children
1058
- }
1059
- );
1060
- };
1061
1213
  var Heart = ({
1062
1214
  variant = "outline",
1063
1215
  className = "",
@@ -1428,6 +1580,6 @@ var InstagramIcon = ({
1428
1580
  return icon;
1429
1581
  };
1430
1582
 
1431
- export { BlockQuote, Button, ButtonTransparent, Container, ContentBox, GradientButton, Grid, GridItem, Heading, Heart, Hr, Info, Input, InstagramIcon, LinkedInIcon, RadioButtons, Search, Select, Share, Spinner, SpinnerOverlay, Stack, Tabs, Tag, Text, TwitterIcon, Warning };
1583
+ export { BlockQuote, Button, ButtonTransparent, Container, ContentBox, GradientButton, Grid, GridItem, Heading, Heart, Hr, Info, Input, InstagramIcon, LinkedInIcon, RadioButtons, Search, Select, Share, Spinner, SpinnerOverlay, Stack, Tabs, Tag, Text, Textarea, TwitterIcon, Warning };
1432
1584
  //# sourceMappingURL=index.js.map
1433
1585
  //# sourceMappingURL=index.js.map