@madecki/ui 1.4.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.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,
@@ -300,11 +383,14 @@ var Tag = ({
300
383
  var Input = ({
301
384
  name,
302
385
  onChange,
386
+ value: valueProp,
303
387
  defaultValue,
304
388
  placeholder,
305
389
  label,
390
+ labelVisibility = "visible",
306
391
  variant = "primary",
307
392
  type = "text",
393
+ maxLength,
308
394
  required = false,
309
395
  pattern,
310
396
  title,
@@ -312,38 +398,54 @@ var Input = ({
312
398
  spellCheck,
313
399
  disabled = false,
314
400
  className = "",
315
- icon
401
+ icon,
402
+ testId
316
403
  }) => {
317
- const [value, setValue] = useState(defaultValue);
404
+ const labelId = useId();
405
+ const isControlled = valueProp !== void 0;
406
+ const [internalValue, setInternalValue] = useState(() => defaultValue ?? "");
318
407
  const [isFocused, setIsFocused] = useState(false);
408
+ const value = isControlled ? valueProp : internalValue;
319
409
  const inputClassNames = ["rounded-sm font-sans z-10 w-full"];
320
410
  const spacings = "py-4 px-5";
321
411
  const outline = "outline-hidden";
322
412
  inputClassNames.push(spacings, outline);
323
413
  const inputWrapperClassNames = ["flex rounded-smb p-px w-full"];
324
- if (isFocused) inputWrapperClassNames.push("bg-gradient");
414
+ if (isFocused) {
415
+ inputWrapperClassNames.push("bg-gradient");
416
+ } else if (variant === "primary" || variant === "tertiary") {
417
+ inputWrapperClassNames.push("bg-lightgray");
418
+ }
325
419
  switch (variant) {
326
420
  case "primary":
327
421
  inputClassNames.push("text-primary bg-neutral");
328
- inputWrapperClassNames.push("bg-lightgray");
329
422
  break;
330
423
  case "secondary":
331
424
  inputClassNames.push("text-neutral bg-neutral dark:bg-gray");
332
425
  break;
333
426
  case "tertiary":
334
427
  inputClassNames.push("text-neutral bg-neutral dark:bg-primary");
335
- inputWrapperClassNames.push("bg-lightgray");
336
428
  break;
337
429
  }
338
430
  if (disabled) {
339
431
  inputClassNames.push("cursor-not-allowed opacity-50");
340
432
  }
341
433
  const onInputChange = (event) => {
342
- setValue(event.target.value);
343
- onChange?.(event.target.value);
434
+ const next = event.target.value;
435
+ if (!isControlled) {
436
+ setInternalValue(next);
437
+ }
438
+ onChange?.(next);
344
439
  };
345
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("label", { htmlFor: name, children: [
346
- /* @__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
+ ),
347
449
  /* @__PURE__ */ jsxs("div", { className: inputWrapperClassNames.join(" "), children: [
348
450
  icon && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center pl-4", children: icon }),
349
451
  /* @__PURE__ */ jsx(
@@ -352,24 +454,114 @@ var Input = ({
352
454
  id: name,
353
455
  name,
354
456
  placeholder,
355
- value: value || "",
457
+ value,
356
458
  className: inputClassNames.join(" "),
357
459
  autoComplete: "off",
358
460
  onChange: onInputChange,
359
461
  onFocus: () => setIsFocused(true),
360
462
  onBlur: () => setIsFocused(false),
361
463
  type,
464
+ maxLength,
362
465
  required,
363
466
  pattern,
364
467
  title,
365
- "aria-label": ariaLabel || label || name,
468
+ "aria-label": ariaLabel,
366
469
  spellCheck,
367
- disabled
470
+ disabled,
471
+ "data-testid": testId
368
472
  }
369
473
  )
370
474
  ] })
371
475
  ] }) });
372
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
+ };
373
565
  function optionTestSlug(value) {
374
566
  return value.replace(/[^a-zA-Z0-9_-]/g, "_");
375
567
  }
@@ -454,6 +646,7 @@ function Select(props) {
454
646
  const {
455
647
  name,
456
648
  label,
649
+ labelVisibility = "visible",
457
650
  options,
458
651
  placeholder = "Select\u2026",
459
652
  variant = "primary",
@@ -461,6 +654,7 @@ function Select(props) {
461
654
  className = "",
462
655
  testId: testIdProp
463
656
  } = props;
657
+ const labelId = useId();
464
658
  const isMulti = props.multi === true;
465
659
  const singleValueProp = !isMulti ? props.value : void 0;
466
660
  const multiValueProp = isMulti ? props.value : void 0;
@@ -656,8 +850,15 @@ function Select(props) {
656
850
  const activeDescendant = open && filteredOptions[highlightIndex] ? `${name}-option-${optionTestSlug(filteredOptions[highlightIndex].value)}` : void 0;
657
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";
658
852
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: `relative ${className}`.trim(), children: [
659
- /* @__PURE__ */ jsxs("label", { htmlFor: name, children: [
660
- /* @__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
+ ),
661
862
  /* @__PURE__ */ jsx("div", { className: inputWrapperClassNames.join(" "), children: /* @__PURE__ */ jsxs("div", { className: innerFieldClassNames.join(" "), children: [
662
863
  /* @__PURE__ */ jsx(
663
864
  "input",
@@ -671,7 +872,6 @@ function Select(props) {
671
872
  disabled,
672
873
  placeholder,
673
874
  value: inputValue,
674
- "aria-label": label,
675
875
  "aria-expanded": open,
676
876
  "aria-haspopup": "listbox",
677
877
  "aria-controls": listboxId,
@@ -696,7 +896,7 @@ function Select(props) {
696
896
  id: listboxId,
697
897
  role: "listbox",
698
898
  "aria-multiselectable": isMulti,
699
- "aria-label": label,
899
+ "aria-labelledby": labelId,
700
900
  "data-testid": `${baseTestId}-listbox`,
701
901
  tabIndex: -1,
702
902
  className: listboxClass,
@@ -848,7 +1048,7 @@ var ContentBox = ({
848
1048
  }
849
1049
  );
850
1050
  };
851
- var sizeStyles = {
1051
+ var sizeStyles2 = {
852
1052
  sm: "max-w-screen-sm",
853
1053
  md: "max-w-screen-md",
854
1054
  lg: "max-w-screen-lg",
@@ -865,7 +1065,7 @@ var Container = ({
865
1065
  return /* @__PURE__ */ jsx(
866
1066
  "div",
867
1067
  {
868
- className: `w-full px-5 ${sizeStyles[size]} ${centeredClass} ${className}`,
1068
+ className: `w-full px-5 ${sizeStyles2[size]} ${centeredClass} ${className}`,
869
1069
  children
870
1070
  }
871
1071
  );
@@ -960,7 +1160,7 @@ var GridItem = ({
960
1160
  }) => {
961
1161
  return /* @__PURE__ */ jsx("div", { className: `${colSpanStyles[colSpan]} ${className}`, children });
962
1162
  };
963
- var sizeStyles2 = {
1163
+ var sizeStyles3 = {
964
1164
  xs: "text-xs",
965
1165
  sm: "text-sm",
966
1166
  md: "text-md",
@@ -970,13 +1170,13 @@ var sizeStyles2 = {
970
1170
  "3xl": "text-3xl",
971
1171
  "4xl": "text-4xl"
972
1172
  };
973
- var weightStyles = {
1173
+ var weightStyles2 = {
974
1174
  normal: "font-normal",
975
1175
  medium: "font-medium",
976
1176
  semibold: "font-semibold",
977
1177
  bold: "font-bold"
978
1178
  };
979
- var colorStyles = {
1179
+ var colorStyles2 = {
980
1180
  default: "text-white dark:text-white",
981
1181
  muted: "text-lightgray dark:text-lightgray",
982
1182
  primary: "text-primary dark:text-white",
@@ -1005,47 +1205,11 @@ var Heading = ({
1005
1205
  return createElement(
1006
1206
  tag,
1007
1207
  {
1008
- className: `${sizeStyles2[resolvedSize]} ${weightStyles[weight]} ${colorStyles[color]} ${className}`
1208
+ className: `${sizeStyles3[resolvedSize]} ${weightStyles2[weight]} ${colorStyles2[color]} ${className}`
1009
1209
  },
1010
1210
  children
1011
1211
  );
1012
1212
  };
1013
- var sizeStyles3 = {
1014
- xs: "text-xs",
1015
- sm: "text-sm",
1016
- md: "text-md",
1017
- lg: "text-lg"
1018
- };
1019
- var weightStyles2 = {
1020
- normal: "font-normal",
1021
- medium: "font-medium",
1022
- semibold: "font-semibold",
1023
- bold: "font-bold"
1024
- };
1025
- var colorStyles2 = {
1026
- default: "text-white dark:text-white",
1027
- muted: "text-lightgray dark:text-lightgray",
1028
- primary: "text-primary dark:text-white",
1029
- success: "text-success",
1030
- warning: "text-warning",
1031
- danger: "text-danger"
1032
- };
1033
- var Text = ({
1034
- children,
1035
- size = "md",
1036
- weight = "normal",
1037
- color = "default",
1038
- as: Tag2 = "p",
1039
- className = ""
1040
- }) => {
1041
- return /* @__PURE__ */ jsx(
1042
- Tag2,
1043
- {
1044
- className: `${sizeStyles3[size]} ${weightStyles2[weight]} ${colorStyles2[color]} ${className}`,
1045
- children
1046
- }
1047
- );
1048
- };
1049
1213
  var Heart = ({
1050
1214
  variant = "outline",
1051
1215
  className = "",
@@ -1416,6 +1580,6 @@ var InstagramIcon = ({
1416
1580
  return icon;
1417
1581
  };
1418
1582
 
1419
- 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 };
1420
1584
  //# sourceMappingURL=index.js.map
1421
1585
  //# sourceMappingURL=index.js.map