mado-ui 0.3.1 → 0.4.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.
Files changed (148) hide show
  1. package/css/index.css +69 -14
  2. package/dist/components/button.d.ts +74 -29
  3. package/dist/components/details.d.ts +5 -7
  4. package/dist/components/drop-down.d.ts +4 -3
  5. package/dist/components/form/fieldset.d.ts +35 -7
  6. package/dist/components/form/index.d.ts +5 -6
  7. package/dist/components/form/input/date/index.d.ts +36 -0
  8. package/dist/components/form/input/index.d.ts +78 -12
  9. package/dist/components/form/submit-button.d.ts +3 -4
  10. package/dist/components/form/textarea.d.ts +1 -1
  11. package/dist/components/ghost.d.ts +1 -1
  12. package/dist/components/heading.d.ts +1 -1
  13. package/dist/components/iframe.d.ts +15 -0
  14. package/dist/components/index.d.ts +10 -11
  15. package/dist/components/link.d.ts +62 -16
  16. package/dist/components/modal.d.ts +1 -1
  17. package/dist/components/time.d.ts +1 -1
  18. package/dist/components/tooltip.d.ts +1 -1
  19. package/dist/components.esm.js +612 -138
  20. package/dist/components.esm.js.map +1 -1
  21. package/dist/components.js +613 -135
  22. package/dist/components.js.map +1 -1
  23. package/dist/hooks/index.d.ts +4 -3
  24. package/dist/hooks/use-fieldset-context.d.ts +22 -0
  25. package/dist/hooks/use-form-context.d.ts +18 -10
  26. package/dist/hooks.esm.js +99 -5
  27. package/dist/hooks.esm.js.map +1 -1
  28. package/dist/hooks.js +100 -3
  29. package/dist/hooks.js.map +1 -1
  30. package/dist/icons/3-people.d.ts +2 -2
  31. package/dist/icons/3-rectangles-desktop-fill.d.ts +2 -2
  32. package/dist/icons/3-rectangles-desktop.d.ts +2 -2
  33. package/dist/icons/airplane.d.ts +2 -2
  34. package/dist/icons/arrow-triangle-2-circlepath-circle-fill.d.ts +2 -2
  35. package/dist/icons/arrow-triangle-2-circlepath-circle.d.ts +2 -2
  36. package/dist/icons/bag-fill.d.ts +2 -2
  37. package/dist/icons/banknote.d.ts +2 -2
  38. package/dist/icons/bell-fill.d.ts +2 -2
  39. package/dist/icons/bolt-car.d.ts +2 -2
  40. package/dist/icons/bolt-fill.d.ts +2 -2
  41. package/dist/icons/bolt-ring-closed.d.ts +2 -2
  42. package/dist/icons/bolt-trianglebadge-exclamationmark.d.ts +2 -2
  43. package/dist/icons/book-fill.d.ts +2 -2
  44. package/dist/icons/bookmark-fill.d.ts +2 -2
  45. package/dist/icons/briefcase-fill.d.ts +2 -2
  46. package/dist/icons/bubble-left-fill.d.ts +2 -2
  47. package/dist/icons/building-2-fill.d.ts +2 -2
  48. package/dist/icons/calendar.d.ts +2 -2
  49. package/dist/icons/camera-fill.d.ts +2 -2
  50. package/dist/icons/car-fill.d.ts +2 -2
  51. package/dist/icons/cart-fill.d.ts +2 -2
  52. package/dist/icons/chart-bar-doc-horizontal.d.ts +2 -2
  53. package/dist/icons/checkmark-seal.d.ts +2 -2
  54. package/dist/icons/checkmark.d.ts +2 -2
  55. package/dist/icons/chevron-down.d.ts +2 -2
  56. package/dist/icons/chevron-left-forwardslash-chevron-right.d.ts +2 -2
  57. package/dist/icons/chevron-left.d.ts +2 -2
  58. package/dist/icons/chevron-right.d.ts +2 -2
  59. package/dist/icons/chevron-up-chevron-down.d.ts +2 -2
  60. package/dist/icons/circle-fill.d.ts +2 -2
  61. package/dist/icons/clock-badge-checkmark.d.ts +2 -2
  62. package/dist/icons/clock-fill.d.ts +2 -2
  63. package/dist/icons/cloud-fill.d.ts +2 -2
  64. package/dist/icons/cube-fill.d.ts +2 -2
  65. package/dist/icons/curve-point-left.d.ts +2 -2
  66. package/dist/icons/dial-high.d.ts +2 -2
  67. package/dist/icons/doc-fill.d.ts +2 -2
  68. package/dist/icons/doc-on-clipboard.d.ts +2 -2
  69. package/dist/icons/doc-on-doc-fill.d.ts +2 -2
  70. package/dist/icons/doc-on-doc.d.ts +2 -2
  71. package/dist/icons/doc-text-magnifyingglass.d.ts +2 -2
  72. package/dist/icons/dollar-sign.d.ts +2 -2
  73. package/dist/icons/ellipsis-circle-fill.d.ts +2 -2
  74. package/dist/icons/ellipsis-circle.d.ts +2 -2
  75. package/dist/icons/envelope-fill.d.ts +2 -2
  76. package/dist/icons/envelope.d.ts +2 -2
  77. package/dist/icons/exclamationmark-octagon.d.ts +2 -2
  78. package/dist/icons/eye.d.ts +2 -2
  79. package/dist/icons/figure-water-fitness.d.ts +2 -2
  80. package/dist/icons/flag-fill.d.ts +2 -2
  81. package/dist/icons/flame-fill.d.ts +2 -2
  82. package/dist/icons/folder-fill.d.ts +2 -2
  83. package/dist/icons/folder.d.ts +2 -2
  84. package/dist/icons/gearshape-fill.d.ts +2 -2
  85. package/dist/icons/gearshape.d.ts +2 -2
  86. package/dist/icons/gift-fill.d.ts +2 -2
  87. package/dist/icons/globe-americas-fill.d.ts +2 -2
  88. package/dist/icons/hare-fill.d.ts +2 -2
  89. package/dist/icons/house-deskclock.d.ts +2 -2
  90. package/dist/icons/house-fill.d.ts +2 -2
  91. package/dist/icons/house.d.ts +2 -2
  92. package/dist/icons/iphone-house.d.ts +2 -2
  93. package/dist/icons/light-ribbon.d.ts +2 -2
  94. package/dist/icons/lightbulb-fill.d.ts +2 -2
  95. package/dist/icons/lightbulb-led.d.ts +2 -2
  96. package/dist/icons/list-bullet-clipboard-fill.d.ts +2 -2
  97. package/dist/icons/magnifyingglass.d.ts +2 -2
  98. package/dist/icons/map-pin-ellipse.d.ts +2 -2
  99. package/dist/icons/minus-plus-batterblock.d.ts +2 -2
  100. package/dist/icons/network-shield.d.ts +2 -2
  101. package/dist/icons/network.d.ts +2 -2
  102. package/dist/icons/newspaper-fill.d.ts +2 -2
  103. package/dist/icons/number.d.ts +2 -2
  104. package/dist/icons/paperplane-fill.d.ts +2 -2
  105. package/dist/icons/person-crop-square.d.ts +2 -2
  106. package/dist/icons/person-fill-questionmark.d.ts +2 -2
  107. package/dist/icons/person-fill.d.ts +2 -2
  108. package/dist/icons/person.d.ts +2 -2
  109. package/dist/icons/phone-arrow-up-right.d.ts +2 -2
  110. package/dist/icons/phone-fill.d.ts +2 -2
  111. package/dist/icons/phone.d.ts +2 -2
  112. package/dist/icons/play-rectangle-fill.d.ts +2 -2
  113. package/dist/icons/plus.d.ts +2 -2
  114. package/dist/icons/qrcode.d.ts +2 -2
  115. package/dist/icons/rectangle-portrait-and-arrow-left-fill.d.ts +2 -2
  116. package/dist/icons/rectangle-portrait-and-arrow-left.d.ts +2 -2
  117. package/dist/icons/sensor.d.ts +2 -2
  118. package/dist/icons/signature.d.ts +2 -2
  119. package/dist/icons/solar-panel.d.ts +2 -2
  120. package/dist/icons/square-and-arrow-down-fill.d.ts +2 -2
  121. package/dist/icons/square-and-arrow-down.d.ts +2 -2
  122. package/dist/icons/square-and-arrow-up-fill.d.ts +2 -2
  123. package/dist/icons/square-and-arrow-up.d.ts +2 -2
  124. package/dist/icons/square-and-pencil-fill.d.ts +2 -2
  125. package/dist/icons/square-and-pencil.d.ts +2 -2
  126. package/dist/icons/text-bubble.d.ts +2 -2
  127. package/dist/icons/trash-fill.d.ts +2 -2
  128. package/dist/icons/trash.d.ts +2 -2
  129. package/dist/icons/tree.d.ts +2 -2
  130. package/dist/icons/umbrella-fill.d.ts +2 -2
  131. package/dist/icons/xmark.d.ts +2 -2
  132. package/dist/icons.esm.js.map +1 -1
  133. package/dist/icons.js.map +1 -1
  134. package/dist/index.esm.js +612 -138
  135. package/dist/index.esm.js.map +1 -1
  136. package/dist/index.js +613 -135
  137. package/dist/index.js.map +1 -1
  138. package/dist/types/index.d.ts +1 -2
  139. package/dist/types/utils.d.ts +20 -0
  140. package/dist/utils/get-date.d.ts +17 -0
  141. package/dist/utils/index.d.ts +8 -9
  142. package/dist/utils/string-manipulation.d.ts +38 -3
  143. package/dist/utils/tw-sort.d.ts +1 -1
  144. package/dist/utils.esm.js +112 -21
  145. package/dist/utils.esm.js.map +1 -1
  146. package/dist/utils.js +113 -20
  147. package/dist/utils.js.map +1 -1
  148. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -208,6 +208,9 @@ function getMonth(month = d) {
208
208
  formattedMonth = `0${formattedMonth}`;
209
209
  return formattedMonth;
210
210
  }
211
+ function getMonthIndexFromName(name) {
212
+ return monthNamesList.findIndex(monthName => monthName === name);
213
+ }
211
214
  /**
212
215
  * ### Get Month Name
213
216
  * - Returns the name of the specified month
@@ -233,6 +236,30 @@ function getSeconds(seconds = d) {
233
236
  formattedSeconds = `0${formattedSeconds}`;
234
237
  return formattedSeconds;
235
238
  }
239
+ /**
240
+ * ### Get User Readable Date
241
+ * - Returns a string of the current date in a user-friendly way
242
+ * - Includes `'Yesterday'`, '`Today'`, and `'Tomorrow'`
243
+ * @param date (default: `new Date()`)
244
+ * @returns {'Today'|'Tomorrow'|'Yesterday'|string} `'weekday, month day, year'` | `'Yesterday'` | `'Today'` | `'Tomorrow'`
245
+ */
246
+ function getUserReadableDate(date = d) {
247
+ const dateTime = date.getTime();
248
+ const today = new Date(), isToday = dateTime === today.getTime();
249
+ if (isToday)
250
+ return 'Today';
251
+ const yesterday = new Date(today.getDate() - 1), isYesterday = dateTime === yesterday.getTime();
252
+ if (isYesterday)
253
+ return 'Yesterday';
254
+ const tomorrow = new Date(today.getDate() + 1), isTomorrow = dateTime === tomorrow.getTime();
255
+ if (isTomorrow)
256
+ return 'Tomorrow';
257
+ const thisYear = today.getFullYear(), isSameYear = date.getFullYear() === thisYear;
258
+ const fullDateString = toFullDateString(date, {
259
+ year: !isSameYear,
260
+ });
261
+ return fullDateString;
262
+ }
236
263
  /**
237
264
  * ### Get Weekday Name
238
265
  * - Returns the weekday name of the specified day
@@ -245,6 +272,45 @@ function getWeekdayName(weekday = d) {
245
272
  // Return the name of the day of the week
246
273
  return weekdayNamesList[weekday.getDay()];
247
274
  }
275
+ /**
276
+ * ### To Full Date String
277
+ * - Returns a formatted string to display the date
278
+ * @param {Date} date (default: `new Date()`)
279
+ * @param {ToFullDateStringOptionsProps} options Change how to display the weekday, month name, day of the month, and year.
280
+ * @returns {string} '`weekday`, `month` `day`, `year`'
281
+ */
282
+ function toFullDateString(date = d, options) {
283
+ let weekdayName = getWeekdayName(date), monthName = getMonthName(date), dayOfMonth = date.getDate(), year = date.getFullYear().toString();
284
+ if (options) {
285
+ const includesDay = options.day !== false, includesMonth = options.month !== false, includesYear = options.year !== false;
286
+ {
287
+ weekdayName = weekdayName.slice(0, 3);
288
+ if (includesMonth || includesDay || includesYear)
289
+ weekdayName += ', ';
290
+ }
291
+ if (includesMonth) {
292
+ if (options.month === 'code')
293
+ monthName = monthName.slice(0, 3);
294
+ if (includesDay)
295
+ monthName += ' ';
296
+ }
297
+ else {
298
+ monthName = '';
299
+ }
300
+ if (!includesDay)
301
+ dayOfMonth = '';
302
+ if (includesYear) {
303
+ if (options.year === 'code')
304
+ year = year.slice(-2);
305
+ if (includesMonth || includesDay)
306
+ year = ', ' + year;
307
+ }
308
+ else {
309
+ year = '';
310
+ }
311
+ }
312
+ return weekdayName + monthName + dayOfMonth + year;
313
+ }
248
314
 
249
315
  function findComponentByType(children, componentType) {
250
316
  const childrenArray = React.Children.toArray(children);
@@ -293,25 +359,97 @@ function isPhoneNumber(tel) {
293
359
  /**
294
360
  * # Format Phone Number
295
361
  * Converts any string containing at least 10 numbers to a formatted phone number
296
- * @param {string} string
297
- * @returns {string} string formatted (000) 000-0000
362
+ * @param {string} phoneNumber
363
+ * @param options
364
+ * @property {string} `countryCode`
365
+ * @property {'continuous' | 'dot' | 'hyphenated' | 'none' | 'space' | 'standard'} `format`
366
+ *
367
+ * Input: a555b555c5555d
368
+ *
369
+ * @example
370
+ * format: 'continuous'
371
+ * countryCode: '1'
372
+ * returns: +1 5555555555
373
+ *
374
+ * @example
375
+ * format: 'dot'
376
+ * returns: 555.555.5555
377
+ *
378
+ * @example
379
+ * format: 'hyphenated'
380
+ * returns: 555-555-5555
381
+ *
382
+ * @example
383
+ * format: 'none'
384
+ * countryCode: '1'
385
+ * returns: +1 a555b555c5555d
386
+ *
387
+ * @example
388
+ * format: 'space'
389
+ * returns: 555 555 5555
390
+ *
391
+ * @example
392
+ * format: 'standard' (default)
393
+ * returns: (555) 555-5555
394
+ *
395
+ * @returns {string} string formatted
298
396
  */
299
- function formatPhoneNumber(string, countryCode) {
300
- return (`${`+${countryCode} ` }` +
301
- string
302
- .replace(/\D/g, '')
303
- .slice(-10)
304
- .split('')
305
- .map((char, index) => {
306
- if (index === 0)
307
- return `(${char}`;
308
- if (index === 2)
309
- return `${char}) `;
310
- if (index === 5)
311
- return `${char}-`;
312
- return char;
313
- })
314
- .join(''));
397
+ function formatPhoneNumber(phoneNumber, options) {
398
+ const format = options?.format || 'standard';
399
+ if (format !== 'none')
400
+ phoneNumber = phoneNumber.replace(/\D/g, '').slice(-10);
401
+ switch (format) {
402
+ case 'dot':
403
+ phoneNumber = phoneNumber
404
+ .split('')
405
+ .map((char, index) => {
406
+ if (index === 2)
407
+ return `${char}.`;
408
+ if (index === 5)
409
+ return `${char}.`;
410
+ return char;
411
+ })
412
+ .join('');
413
+ break;
414
+ case 'hyphenated':
415
+ phoneNumber = phoneNumber
416
+ .split('')
417
+ .map((char, index) => {
418
+ if (index === 2)
419
+ return `${char}-`;
420
+ if (index === 5)
421
+ return `${char}-`;
422
+ return char;
423
+ })
424
+ .join('');
425
+ break;
426
+ case 'space':
427
+ phoneNumber = phoneNumber
428
+ .split('')
429
+ .map((char, index) => {
430
+ if (index === 2)
431
+ return `${char} `;
432
+ if (index === 5)
433
+ return `${char} `;
434
+ return char;
435
+ })
436
+ .join('');
437
+ break;
438
+ case 'standard':
439
+ phoneNumber = phoneNumber
440
+ .split('')
441
+ .map((char, index) => {
442
+ if (index === 0)
443
+ return `(${char}`;
444
+ if (index === 2)
445
+ return `${char}) `;
446
+ if (index === 5)
447
+ return `${char}-`;
448
+ return char;
449
+ })
450
+ .join('');
451
+ }
452
+ return `${options?.countryCode ? `+${options?.countryCode} ` : ''}` + phoneNumber;
315
453
  }
316
454
  /**
317
455
  * # To Lower Case
@@ -366,7 +504,7 @@ const lineLiftClasses = tailwindMerge.twJoin([
366
504
  scaleYClasses,
367
505
  'after:origin-bottom after:translate-y-1 after:scale-x-75 active:after:translate-y-0 active:after:scale-x-100 pointer-fine:hover:after:translate-y-0 pointer-fine:hover:after:scale-x-100',
368
506
  ]);
369
- const fillClasses = tailwindMerge.twJoin(baseClasses, 'whitespace-nowrap transition-[transform_color] after:top-1/2 after:h-[calc(100%+0.05rem)] after:w-[calc(100%+0.25rem)] after:-translate-y-1/2 after:rounded after:ease-exponential active:text-zinc-50 pointer-fine:hover:text-zinc-50');
507
+ const fillClasses = tailwindMerge.twJoin(baseClasses, 'whitespace-nowrap transition-[transform,color] after:top-1/2 after:h-[calc(100%+0.05rem)] after:w-[calc(100%+0.25rem)] after:-translate-y-1/2 after:rounded after:ease-exponential active:text-zinc-50 pointer-fine:hover:text-zinc-50');
370
508
  // Define theme-specific fill color transition classes
371
509
  const getFillColorTransitionClasses = (theme = 'blue', customTheme) => {
372
510
  let fillColorTransitionClasses = tailwindMerge.twJoin(fillClasses, 'transition-transform after:bg-(--theme-color)');
@@ -419,7 +557,7 @@ const getFillColorTransitionClasses = (theme = 'blue', customTheme) => {
419
557
  };
420
558
  // Define theme-specific fill center classes
421
559
  const getFillCenterClasses = (theme = 'blue', customTheme) => {
422
- let fillCenterColorClasses = tailwindMerge.twJoin(fillClasses, 'after:scale-x-50 after:scale-y-[0.25] after:bg-(--theme-color)/0 after:transition-[transform_background-color] active:after:scale-x-100 active:after:scale-y-100 active:after:bg-(--theme-color) pointer-fine:hover:after:scale-x-100 pointer-fine:hover:after:scale-y-100 pointer-fine:hover:after:bg-(--theme-color)');
560
+ let fillCenterColorClasses = tailwindMerge.twJoin(fillClasses, 'after:scale-x-50 after:scale-y-[0.25] after:bg-(--theme-color)/0 after:transition-[transform,background-color] active:after:scale-x-100 active:after:scale-y-100 active:after:bg-(--theme-color) pointer-fine:hover:after:scale-x-100 pointer-fine:hover:after:scale-y-100 pointer-fine:hover:after:bg-(--theme-color)');
423
561
  switch (theme) {
424
562
  case 'blue':
425
563
  fillCenterColorClasses = tailwindMerge.twJoin(fillCenterColorClasses, 'after:[--theme-color:var(--color-ui-blue)]');
@@ -478,7 +616,7 @@ const multilineLineCenterClasses = tailwindMerge.twJoin([multilineXClasses, 'bg-
478
616
  const multilineLineLiftClasses = tailwindMerge.twJoin(multilineLineClasses, 'bg-[size:auto_0px] focus-visible:bg-[size:auto_2px] active:bg-[size:auto_2px] pointer-fine:hover:bg-[size:auto_2px]');
479
617
  const multilineFillBaseClasses = tailwindMerge.twJoin(multilineBaseClasses, 'rounded px-0.5 py-0.75 focus-visible:text-zinc-50 active:text-zinc-50 pointer-fine:hover:text-zinc-50');
480
618
  const getMultilineFillColorClasses = (theme = 'blue', customTheme) => {
481
- let multilineFillColorClasses = tailwindMerge.twJoin(multilineFillBaseClasses, 'from-(--theme-color) to-(--theme-color) transition-[background-size_color]');
619
+ let multilineFillColorClasses = tailwindMerge.twJoin(multilineFillBaseClasses, 'from-(--theme-color) to-(--theme-color) transition-[background-size,color]');
482
620
  switch (theme) {
483
621
  case 'blue':
484
622
  multilineFillColorClasses = tailwindMerge.twJoin(multilineFillColorClasses, '[--theme-color:var(--color-ui-blue)]');
@@ -527,7 +665,7 @@ const getMultilineFillColorClasses = (theme = 'blue', customTheme) => {
527
665
  return multilineFillColorClasses;
528
666
  };
529
667
  const getMultilineFillClasses = (theme = 'blue', customTheme) => {
530
- let multilineFillColorClasses = tailwindMerge.twJoin(multilineFillBaseClasses, 'from-(--theme-color)/0 to-(--theme-color)/0 bg-[size:50%_0px] bg-[position:50%_50%] transition-[background-size_background-image_color] focus-visible:from-(--theme-color) focus-visible:to-(--theme-color) focus-visible:bg-[size:100%_100%] active:from-(--theme-color) active:to-(--theme-color) active:bg-[size:100%_100%] contrast-more:from-(--theme-color)/0 pointer-fine:hover:from-(--theme-color) pointer-fine:hover:to-(--theme-color) pointer-fine:hover:bg-[size:100%_100%]');
668
+ let multilineFillColorClasses = tailwindMerge.twJoin(multilineFillBaseClasses, 'from-(--theme-color)/0 to-(--theme-color)/0 bg-[size:50%_0px] bg-[position:50%_50%] transition-[background-size,background-image,color] focus-visible:from-(--theme-color) focus-visible:to-(--theme-color) focus-visible:bg-[size:100%_100%] active:from-(--theme-color) active:to-(--theme-color) active:bg-[size:100%_100%] contrast-more:from-(--theme-color)/0 pointer-fine:hover:from-(--theme-color) pointer-fine:hover:to-(--theme-color) pointer-fine:hover:bg-[size:100%_100%]');
531
669
  switch (theme) {
532
670
  case 'blue':
533
671
  multilineFillColorClasses = tailwindMerge.twJoin(multilineFillColorClasses, '[--theme-color:var(--color-ui-blue)]');
@@ -667,6 +805,71 @@ function Link({ as, className, customTheme, theme = 'blue', type, ...props }) {
667
805
  const LinkElement = as || Anchor;
668
806
  return jsxRuntime.jsx(LinkElement, { ...props, className: twMerge(linkClasses, className) });
669
807
  }
808
+ /**
809
+ * # createLink
810
+ * Creates an extended Link component with additional theme options.
811
+ *
812
+ * @param config - Configuration object defining new themes and defaults
813
+ * @returns A new Link component with extended theme support
814
+ *
815
+ * @example
816
+ * ```tsx
817
+ * const MyLink = createLink({
818
+ * as: NextLink,
819
+ * className: 'font-bold',
820
+ * type: 'fill',
821
+ * theme: {
822
+ * primary: {
823
+ * customTheme: {
824
+ * fill: 'after:[--theme-color:var(--color-primary-500)]',
825
+ * multilineFill: '[--theme-color:var(--color-primary-500)]'
826
+ * },
827
+ * className: 'text-white'
828
+ * }
829
+ * }
830
+ * })
831
+ * ```
832
+ */
833
+ function createLink(config) {
834
+ return function ExtendedLink({ theme, className, customTheme, type, as, ...props }) {
835
+ const finalType = type !== undefined ? type : config.type, finalTheme = theme !== undefined ? theme : config.defaultTheme;
836
+ const configClassName = config.className;
837
+ const shouldOverrideElement = !Boolean(as) && Boolean(config.as);
838
+ const linkProps = {
839
+ ...props,
840
+ className: undefined,
841
+ customTheme: undefined,
842
+ type: finalType,
843
+ };
844
+ if (shouldOverrideElement) {
845
+ linkProps.as = config.as;
846
+ }
847
+ else if (as) {
848
+ linkProps.as = as;
849
+ }
850
+ if (finalTheme && typeof finalTheme === 'string' && config.theme && finalTheme in config.theme) {
851
+ const extendedTheme = config.theme[finalTheme];
852
+ if (customTheme)
853
+ return (jsxRuntime.jsx(Link, { ...linkProps, theme: 'custom', customTheme: customTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
854
+ let resolvedCustomTheme;
855
+ if (extendedTheme.customTheme.themeColor) {
856
+ const isMultilineType = finalType ? finalType.includes('multiline') : false;
857
+ resolvedCustomTheme = {
858
+ themeColor: isMultilineType
859
+ ? extendedTheme.customTheme.themeColor.multilineFill
860
+ : extendedTheme.customTheme.themeColor.fill,
861
+ };
862
+ }
863
+ else {
864
+ resolvedCustomTheme = {
865
+ classes: extendedTheme.customTheme.classes,
866
+ };
867
+ }
868
+ return (jsxRuntime.jsx(Link, { ...linkProps, theme: 'custom', customTheme: resolvedCustomTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
869
+ }
870
+ return (jsxRuntime.jsx(Link, { ...linkProps, theme: finalTheme, className: twMerge(configClassName, className), customTheme: customTheme }));
871
+ };
872
+ }
670
873
 
671
874
  /**
672
875
  * # Button
@@ -752,9 +955,76 @@ function Button({ className, customTheme, gradient = false, padding = 'md', roun
752
955
  themeClasses,
753
956
  className,
754
957
  ]);
755
- const ButtonElement = 'as' in props ? props.as : props.href ? Anchor : react.Button;
756
- const { as, ...restProps } = 'as' in props ? props : { ...props, as: undefined };
757
- return jsxRuntime.jsx(ButtonElement, { ...restProps, className: buttonClasses });
958
+ if ('href' in props && !props.as && props.href)
959
+ return jsxRuntime.jsx(react.Button, { ...props, as: Anchor, className: buttonClasses });
960
+ return jsxRuntime.jsx(react.Button, { ...props, className: buttonClasses });
961
+ }
962
+ /**
963
+ * # createButton
964
+ * Creates an extended Button component with additional theme options.
965
+ *
966
+ * @param extendedThemes - Configuration object defining new themes
967
+ * @returns A new Button component with extended theme support
968
+ *
969
+ * @example
970
+ * ```tsx
971
+ * const MyButton = createButton({
972
+ * as: {
973
+ * default: 'div',
974
+ * link: NextLink
975
+ * },
976
+ * className: 'min-w-64',
977
+ * padding: 'sm',
978
+ * rounded: 'full',
979
+ * theme: {
980
+ * primary: {
981
+ * customTheme: { themeColor: '[--theme-color:var(--color-primary-500)]' },
982
+ * className: 'text-white'
983
+ * }
984
+ * }
985
+ * })
986
+ * ```
987
+ */
988
+ function createButton(config) {
989
+ return function ExtendedButton({ theme, className, customTheme, gradient, padding, rounded, as, ...props }) {
990
+ const finalGradient = gradient !== undefined ? gradient : config.gradient, finalPadding = padding !== undefined ? padding : config.padding, finalRounded = rounded !== undefined ? rounded : config.rounded, finalTheme = theme !== undefined ? theme : config.defaultTheme;
991
+ const configClassName = config.className;
992
+ const shouldOverrideElement = !Boolean(as) && Boolean(config.as);
993
+ const getOverrideElement = () => {
994
+ if (!config.as)
995
+ return undefined;
996
+ if (typeof config.as === 'function' || typeof config.as === 'string')
997
+ return config.as;
998
+ const hasHref = 'href' in props && props.href;
999
+ if (hasHref && config.as.link) {
1000
+ return config.as.link;
1001
+ }
1002
+ else if (!hasHref && config.as.default) {
1003
+ return config.as.default;
1004
+ }
1005
+ return undefined;
1006
+ };
1007
+ const buttonProps = {
1008
+ ...props,
1009
+ className: undefined,
1010
+ customTheme: undefined,
1011
+ gradient: finalGradient,
1012
+ padding: finalPadding,
1013
+ rounded: finalRounded,
1014
+ };
1015
+ if (shouldOverrideElement) {
1016
+ const overrideElement = getOverrideElement();
1017
+ if (overrideElement)
1018
+ buttonProps.as = overrideElement;
1019
+ }
1020
+ else if (as)
1021
+ buttonProps.as = as;
1022
+ if (finalTheme && typeof finalTheme === 'string' && config.theme && finalTheme in config.theme) {
1023
+ const extendedTheme = config.theme[finalTheme];
1024
+ return (jsxRuntime.jsx(Button, { ...buttonProps, theme: 'custom', customTheme: customTheme || extendedTheme.customTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
1025
+ }
1026
+ return (jsxRuntime.jsx(Button, { ...buttonProps, theme: finalTheme, className: twMerge(configClassName, className), customTheme: customTheme }));
1027
+ };
758
1028
  }
759
1029
 
760
1030
  function ChevronDown(props) {
@@ -769,10 +1039,8 @@ function xmark(props) {
769
1039
  return (jsxRuntime.jsx("svg", { viewBox: '0 0 64 64', ...props, children: jsxRuntime.jsx("path", { d: 'M1,63c0.7,0.7,1.6,1,2.6,1s1.9-0.3,2.6-1L32,37.1L57.8,63c0.7,0.7,1.5,1,2.5,1c1,0,1.9-0.3,2.6-1c0.7-0.7,1-1.6,1-2.6 c0-1-0.3-1.8-1-2.5L37.1,32L63,6.2c0.7-0.7,1-1.6,1-2.6S63.7,1.7,63,1c-0.7-0.7-1.6-1-2.6-1c-1,0-1.8,0.3-2.5,1L32,26.9L6.2,1 C5.5,0.3,4.6,0,3.6,0C2.6,0,1.7,0.3,1,1C0.3,1.7,0,2.6,0,3.6c0,1,0.3,1.9,1,2.6L26.9,32L1,57.8c-0.7,0.7-1,1.5-1,2.6 C0,61.4,0.3,62.3,1,63z' }) }));
770
1040
  }
771
1041
 
772
- function DetailsSummary({ arrow = true, as, children, className, ...props }) {
773
- return (
774
- // @ts-expect-error Button has some extra props
775
- jsxRuntime.jsx(react.DisclosureButton, { ...props, as: as || (Button), className: twMerge('w-full', className, Boolean(arrow) && 'grid grid-cols-[1fr_1rem] gap-2'), role: 'summary', children: bag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [typeof children === 'function' ? children(bag) : children, arrow &&
1042
+ function DetailsSummary({ arrow = true, children, className, ...props }) {
1043
+ return (jsxRuntime.jsx(react.DisclosureButton, { ...props, as: (Button), className: twMerge('w-full', className, Boolean(arrow) && 'grid grid-cols-[1fr_1rem] gap-2'), role: 'summary', children: bag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [typeof children === 'function' ? children(bag) : children, arrow &&
776
1044
  (typeof arrow === 'boolean' ? (jsxRuntime.jsx(ChevronDown, { className: 'absolute top-1/2 right-3 block w-4 -translate-y-1/2' })) : (arrow))] })) }));
777
1045
  }
778
1046
  function DetailsBody({ children, className, ...props }) {
@@ -789,7 +1057,7 @@ function DropDownButton({ arrow = true, as, children, className, ...props }) {
789
1057
  function DropDownItem({ as, ...props }) {
790
1058
  return jsxRuntime.jsx(react.MenuItem, { as: as || 'div', ...props });
791
1059
  }
792
- function DropDownItems({ anchor, children, className, style, ...props }) {
1060
+ function DropDownItems({ anchor, children, className, containerClassName, style, ...props }) {
793
1061
  const getAnchorProps = () => {
794
1062
  let initialAnchor = { gap: '1rem', padding: '1rem', to: 'bottom start' };
795
1063
  if (anchor) {
@@ -801,11 +1069,11 @@ function DropDownItems({ anchor, children, className, style, ...props }) {
801
1069
  return initialAnchor;
802
1070
  };
803
1071
  const anchorProps = getAnchorProps();
804
- return (jsxRuntime.jsx(react.MenuItems, { ...props, anchor: anchorProps, className: 'grid grid-rows-1fr rounded-xl shadow-xl transition-rows duration-500 ease-exponential data-closed:grid-rows-0fr', transition: true, style: { ...style, minWidth: 'var(--button-width)' }, children: bag => (jsxRuntime.jsx("div", { className: 'overflow-y-hidden', children: jsxRuntime.jsx("div", { className: twMerge('bg-neutral-50/20 px-6 py-5 backdrop-blur-md backdrop-brightness-150', className), children: typeof children === 'function' ? children(bag) : children }) })) }));
1072
+ return (jsxRuntime.jsx(react.MenuItems, { ...props, anchor: anchorProps, className: twMerge('grid grid-rows-1fr rounded-xl shadow-xl transition-rows duration-500 ease-exponential data-closed:grid-rows-0fr', containerClassName), transition: true, style: { ...style, minWidth: 'var(--button-width)' }, children: bag => (jsxRuntime.jsx("div", { className: 'overflow-y-scroll', children: jsxRuntime.jsx("div", { className: twMerge('rounded-xl bg-neutral-50/20 px-6 py-5 backdrop-blur-md backdrop-brightness-150', className), children: typeof children === 'function' ? children(bag) : children }) })) }));
805
1073
  }
806
1074
  function DropDownSection({ children, label, labelProps, separatorAbove, separatorBelow, ...props }) {
807
1075
  const { labelClassName, ...restLabelProps } = { labelClassName: labelProps?.className || '', ...labelProps };
808
- return (jsxRuntime.jsx(react.MenuSection, { ...props, children: sectionBag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [separatorAbove && jsxRuntime.jsx(DropDownSeparator, {}), jsxRuntime.jsx(react.MenuHeading, { ...restLabelProps, className: headingBag => twMerge('text-[size:larger] font-bold', typeof labelClassName === 'function' ? labelClassName(headingBag) : labelClassName), children: label }), typeof children === 'function' ? children(sectionBag) : children, separatorBelow && jsxRuntime.jsx(DropDownSeparator, {})] })) }));
1076
+ return (jsxRuntime.jsx(react.MenuSection, { ...props, children: sectionBag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [separatorAbove && jsxRuntime.jsx(DropDownSeparator, {}), label && (jsxRuntime.jsx(react.MenuHeading, { ...restLabelProps, className: headingBag => twMerge('text-[size:larger] font-bold text-current/80', typeof labelClassName === 'function' ? labelClassName(headingBag) : labelClassName), children: label })), typeof children === 'function' ? children(sectionBag) : children, separatorBelow && jsxRuntime.jsx(DropDownSeparator, {})] })) }));
809
1077
  }
810
1078
  function DropDownSeparator({ className, ...props }) {
811
1079
  return (jsxRuntime.jsx(react.MenuSeparator, { ...props, className: bag => twMerge('my-4 block h-px rounded-full bg-neutral-950/20', typeof className === 'function' ? className(bag) : className) }));
@@ -865,12 +1133,106 @@ function createFastContext(defaultInitialState) {
865
1133
  function defineField(fieldDefinition) {
866
1134
  return fieldDefinition;
867
1135
  }
868
- const { Provider: Provider$1, useStore: useStore$1 } = createFastContext([]);
1136
+ function isStringField(field) {
1137
+ return Boolean(field.type !== 'object' && field.type !== 'array');
1138
+ }
1139
+ const { Provider: Provider$2, useStore: useStore$2 } = createFastContext([]);
869
1140
  function FormContextProvider({ children }) {
870
- return jsxRuntime.jsx(Provider$1, { children: children });
1141
+ return jsxRuntime.jsx(Provider$2, { children: children });
871
1142
  }
872
1143
  function useFormContext() {
873
- return useStore$1(store => store);
1144
+ const [formContext, setFormContext] = useStore$2(store => store);
1145
+ const registerField = React.useCallback((field) => {
1146
+ setFormContext?.(prevContext => {
1147
+ const otherFields = (prevContext || []).filter(otherField => otherField.id !== field.id);
1148
+ return [...otherFields, field];
1149
+ });
1150
+ }, []);
1151
+ const removeField = React.useCallback((fieldID) => {
1152
+ setFormContext?.(prevContext => (prevContext || []).filter(field => field.id !== fieldID));
1153
+ }, []);
1154
+ const updateField = React.useCallback((fieldID, updates) => {
1155
+ setFormContext?.(prevContext => {
1156
+ const field = prevContext.find(({ id }) => id === fieldID);
1157
+ if (!field)
1158
+ throw new Error(`Field with id "${fieldID}" not found in form context.`);
1159
+ const otherFields = prevContext.filter(({ id }) => id !== fieldID);
1160
+ const updatedField = { ...field, ...updates };
1161
+ return [...otherFields, updatedField];
1162
+ });
1163
+ }, []);
1164
+ return [formContext, { registerField, removeField, updateField }];
1165
+ }
1166
+
1167
+ const { Provider: Provider$1, useStore: useStore$1 } = createFastContext(undefined);
1168
+ function FieldsetContextProvider({ children, initialValue, }) {
1169
+ return jsxRuntime.jsx(Provider$1, { initialValue: initialValue, children: children });
1170
+ }
1171
+ function useFieldsetContext() {
1172
+ const [fieldsetContext, setFieldsetContext] = useStore$1(store => store), [, formContextFunctions] = useFormContext();
1173
+ const registerField = React.useCallback((field) => {
1174
+ setFieldsetContext?.(prev => {
1175
+ if (!prev)
1176
+ return prev;
1177
+ const existingFieldIndex = prev.fieldList.findIndex(existingField => existingField.id === field.id);
1178
+ const newFieldList = existingFieldIndex >= 0
1179
+ ? prev.fieldList.map((existingField, index) => (index === existingFieldIndex ? field : existingField))
1180
+ : [...prev.fieldList, field];
1181
+ return { ...prev, fieldList: newFieldList };
1182
+ });
1183
+ }, []);
1184
+ const removeField = React.useCallback((fieldID) => {
1185
+ setFieldsetContext?.(prev => {
1186
+ if (!prev)
1187
+ return prev;
1188
+ return {
1189
+ ...prev,
1190
+ fieldList: prev.fieldList.filter(field => field.id !== fieldID),
1191
+ };
1192
+ });
1193
+ }, []);
1194
+ const updateField = React.useCallback((fieldID, updates) => {
1195
+ setFieldsetContext?.(prev => {
1196
+ if (!prev)
1197
+ return prev;
1198
+ return {
1199
+ ...prev,
1200
+ fieldList: prev.fieldList.map(field => (field.id === fieldID ? { ...field, ...updates } : field)),
1201
+ };
1202
+ });
1203
+ }, []);
1204
+ const createFormContextEntry = React.useCallback((fieldsetEntry) => {
1205
+ if (fieldsetEntry.join) {
1206
+ const joinedValue = fieldsetEntry.fieldList
1207
+ .filter(field => isStringField(field) && field.value !== '')
1208
+ .map(field => field.value)
1209
+ .join(fieldsetEntry.join);
1210
+ return defineField({
1211
+ type: 'string',
1212
+ id: fieldsetEntry.id,
1213
+ name: fieldsetEntry.name,
1214
+ value: joinedValue,
1215
+ required: fieldsetEntry.fieldList.some(field => field.required),
1216
+ invalid: fieldsetEntry.fieldList.some(field => field.invalid),
1217
+ });
1218
+ }
1219
+ return defineField({
1220
+ type: 'object',
1221
+ id: fieldsetEntry.id,
1222
+ name: fieldsetEntry.name,
1223
+ fields: fieldsetEntry.fieldList,
1224
+ });
1225
+ }, []);
1226
+ React.useEffect(() => {
1227
+ if (!fieldsetContext || fieldsetContext.decorative)
1228
+ return;
1229
+ const formContextEntry = createFormContextEntry(fieldsetContext);
1230
+ formContextFunctions.registerField(formContextEntry);
1231
+ return () => {
1232
+ formContextFunctions.removeField(fieldsetContext.id);
1233
+ };
1234
+ }, [fieldsetContext, formContextFunctions.registerField, formContextFunctions.removeField, createFormContextEntry]);
1235
+ return [fieldsetContext, { registerField, removeField, updateField }];
874
1236
  }
875
1237
 
876
1238
  const DEFAULT_STATUS = 'incomplete';
@@ -882,9 +1244,18 @@ function useFormStatus() {
882
1244
  return useStore(store => store);
883
1245
  }
884
1246
 
885
- function Fieldset({ children, className, legend, legendProps, ...props }) {
1247
+ function Fieldset({ children, className, decorative = false, join, legend, legendProps, name, ...props }) {
1248
+ const uniqueID = React.useId();
1249
+ const fieldsetId = toLowerCase(legend || name, [' ', '_']) + '§' + uniqueID;
886
1250
  const { className: legendClassName, ...restLegendProps } = legendProps || {};
887
- return (jsxRuntime.jsx(react.Fieldset, { ...props, className: bag => twMerge('contents', typeof className === 'function' ? className(bag) : className), children: bag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Legend, { ...restLegendProps, className: twMerge('text-lg font-bold sm:text-xl', typeof legendClassName === 'function' ? legendClassName(bag) : legendClassName), children: legend }), typeof children === 'function' ? children(bag) : children] })) }));
1251
+ name = legend || name;
1252
+ return (jsxRuntime.jsx(FieldsetContextProvider, { initialValue: {
1253
+ decorative,
1254
+ fieldList: [],
1255
+ id: fieldsetId,
1256
+ join,
1257
+ name,
1258
+ }, children: jsxRuntime.jsx(react.Fieldset, { ...props, name: name, className: bag => twMerge('contents', typeof className === 'function' ? className(bag) : className), children: bag => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [legend && (jsxRuntime.jsx(react.Legend, { ...restLegendProps, className: twMerge('text-xl font-bold text-current/80', typeof legendClassName === 'function' ? legendClassName(bag) : legendClassName), children: legend })), typeof children === 'function' ? children(bag) : children] })) }) }));
888
1259
  }
889
1260
 
890
1261
  /**
@@ -3076,15 +3447,17 @@ function ArrowSvg({ className, ...props }) {
3076
3447
  return (jsxRuntime.jsxs("svg", { viewBox: '0 0 20 10', className: twMerge('h-2.5 w-5 fill-none', className), ...props, children: [jsxRuntime.jsx("path", { d: 'M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z', className: 'fill-neutral-50 dark:fill-neutral-800' }), jsxRuntime.jsx("path", { d: 'M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z', className: 'fill-neutral-400 dark:fill-none' }), jsxRuntime.jsx("path", { d: 'M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z', className: 'dark:fill-neutral-600' })] }));
3077
3448
  }
3078
3449
 
3079
- function Input({ checked, className, defaultValue, description, descriptionProps, disabled, fieldProps, invalid = true, label, labelProps, name, onBlur, onChange, placeholder, ref, required = true, type, value, ...props }) {
3080
- const [formContext, setFormContext] = useFormContext(), [errorMessage, setErrorMessage] = React.useState(undefined);
3450
+ const specialCharacterRegex = new RegExp(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~€‚ƒ„…†‡‰‹‘’“”•–—™›¡¢£¥§©«¬®°±¶º»¿×÷]/);
3451
+ function Input({ checked, className, defaultValue, description, descriptionProps, disabled, fieldProps, invalid = true, label, labelProps, max, min, name, onBlur, onChange, options, placeholder, ref, required = true, type, value, ...props }) {
3452
+ const [formContext, formContextFunctions] = useFormContext(), [fieldsetContext, fieldsetContextFunctions] = useFieldsetContext(), [errorMessage, setErrorMessage] = React.useState(undefined);
3453
+ if (type === 'password' && !placeholder)
3454
+ placeholder = '••••••••' + (required && !label ? '*' : '');
3081
3455
  if (placeholder === '*')
3082
3456
  placeholder = name + (required && !label ? '*' : '');
3083
3457
  if (label === '*')
3084
3458
  label = name;
3085
3459
  const uniqueID = React.useId(), fieldContextID = toLowerCase(name, [, '_']) + '§' + uniqueID;
3086
- if (Boolean(formContext?.find(field => field.id === fieldContextID)?.invalid))
3087
- invalid = true;
3460
+ const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
3088
3461
  const getFieldContextType = () => {
3089
3462
  switch (type) {
3090
3463
  case 'email':
@@ -3102,26 +3475,27 @@ function Input({ checked, className, defaultValue, description, descriptionProps
3102
3475
  }
3103
3476
  };
3104
3477
  const fieldContextType = getFieldContextType();
3105
- const initialFieldContext = defineField({
3106
- type: fieldContextType,
3107
- id: fieldContextID,
3108
- invalid,
3109
- name,
3110
- required,
3111
- value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
3112
- });
3478
+ const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
3113
3479
  React.useEffect(() => {
3114
- if (!setFormContext)
3115
- return;
3116
- setFormContext(prevContext => {
3117
- const otherFields = (prevContext || []).filter(field => field.id !== initialFieldContext.id);
3118
- return [...otherFields, initialFieldContext];
3480
+ const initialFieldContext = defineField({
3481
+ type: fieldContextType,
3482
+ id: fieldContextID,
3483
+ invalid,
3484
+ name,
3485
+ required,
3486
+ value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
3119
3487
  });
3488
+ if (isInFieldset) {
3489
+ fieldsetContextFunctions.registerField(initialFieldContext);
3490
+ return () => {
3491
+ fieldsetContextFunctions.removeField(initialFieldContext.id);
3492
+ };
3493
+ }
3494
+ formContextFunctions.registerField(initialFieldContext);
3120
3495
  return () => {
3121
- setFormContext(prevContext => (prevContext || []).filter(field => field.id !== initialFieldContext.id));
3496
+ formContextFunctions.removeField(initialFieldContext.id);
3122
3497
  };
3123
- }, [setFormContext]);
3124
- const fieldContext = formContext?.find(({ id: fieldID }) => fieldID === initialFieldContext.id) || initialFieldContext;
3498
+ }, [isInFieldset]);
3125
3499
  const validateField = (validValue) => {
3126
3500
  const noValue = !validValue || validValue === '';
3127
3501
  if (!required && noValue)
@@ -3137,15 +3511,86 @@ function Input({ checked, className, defaultValue, description, descriptionProps
3137
3511
  if (!isEmail(validValue))
3138
3512
  errorMessageList.push('This is not a valid email.');
3139
3513
  break;
3514
+ case 'date':
3515
+ const valueAsTime = new Date().getTime();
3516
+ if (min && !(min instanceof Date) && typeof min !== 'number') {
3517
+ if (Array.isArray(min)) {
3518
+ const monthIndex = typeof min[1] === 'number' ? min[1] - 1 : getMonthIndexFromName(min[1]);
3519
+ min = new Date(min[0], monthIndex, min[2]);
3520
+ }
3521
+ else if ('year' in min && 'month' in min && 'day' in min) {
3522
+ const monthIndex = typeof min.month === 'number' ? min.month - 1 : getMonthIndexFromName(min.month);
3523
+ min = new Date(min.year, monthIndex, min.day);
3524
+ }
3525
+ if (valueAsTime < min.getTime())
3526
+ errorMessageList.push(`Value cannot be lower than ${getUserReadableDate(min)}.`);
3527
+ }
3528
+ if (max && !(max instanceof Date) && typeof max !== 'number') {
3529
+ if (Array.isArray(max)) {
3530
+ const monthIndex = typeof max[1] === 'number' ? max[1] - 1 : getMonthIndexFromName(max[1]);
3531
+ max = new Date(max[0], monthIndex, max[2]);
3532
+ }
3533
+ else if ('year' in max && 'month' in max && 'day' in max) {
3534
+ const monthIndex = typeof max.month === 'number' ? max.month - 1 : getMonthIndexFromName(max.month);
3535
+ max = new Date(max.year, monthIndex, max.day);
3536
+ }
3537
+ if (valueAsTime > max.getTime())
3538
+ errorMessageList.push(`Value cannot be higher than ${getUserReadableDate(max)}.`);
3539
+ }
3540
+ break;
3140
3541
  case 'number':
3141
- if (isNaN(Number(validValue)))
3542
+ const valueAsNumber = Number(validValue);
3543
+ if (isNaN(valueAsNumber))
3142
3544
  errorMessageList.push('This is not a valid number.');
3545
+ if (typeof max === 'number' && valueAsNumber > max)
3546
+ errorMessageList.push(`Value cannot be higher than ${max}.`);
3547
+ if (typeof min === 'number' && valueAsNumber < min)
3548
+ errorMessageList.push(`Value cannot be lower than ${min}.`);
3549
+ break;
3550
+ case 'password':
3551
+ if (options) {
3552
+ const { matchPreviousInput, requireLowercaseCharacter, requireNumber, requireSpecialCharacter, requireUppercaseCharacter, } = options;
3553
+ if (matchPreviousInput && formContext && formContext.length >= 2) {
3554
+ if (isInFieldset && fieldsetContext.fieldList.length > 1) {
3555
+ const currentInputIndex = fieldsetContext.fieldList.findIndex(({ id: fieldID }) => fieldID === fieldContext?.id);
3556
+ if (currentInputIndex > 0) {
3557
+ const previousInput = fieldsetContext.fieldList.find((_, index) => index === currentInputIndex - 1);
3558
+ if (previousInput &&
3559
+ isStringField(previousInput) &&
3560
+ previousInput.value !== validValue)
3561
+ errorMessageList.push('Passwords must match.');
3562
+ }
3563
+ }
3564
+ else {
3565
+ const currentInputIndex = formContext.findIndex(({ id: fieldID }) => fieldID === fieldContext?.id);
3566
+ if (currentInputIndex > 0) {
3567
+ const previousInput = formContext.find((_, index) => index === currentInputIndex - 1);
3568
+ if (previousInput &&
3569
+ isStringField(previousInput) &&
3570
+ previousInput.value !== validValue)
3571
+ errorMessageList.push('Passwords must match.');
3572
+ }
3573
+ }
3574
+ }
3575
+ if (requireLowercaseCharacter && !/[a-z]/g.test(validValue))
3576
+ errorMessageList.push('You must include a lowercase character.');
3577
+ if (requireNumber && !/[0-9]/g.test(validValue))
3578
+ errorMessageList.push('You must include a number.');
3579
+ if (requireSpecialCharacter && !specialCharacterRegex.test(validValue))
3580
+ errorMessageList.push('You must include a special character.');
3581
+ if (requireUppercaseCharacter && !/[A-Z]/g.test(validValue))
3582
+ errorMessageList.push('You must include an uppercase character.');
3583
+ }
3143
3584
  break;
3144
3585
  case 'tel':
3145
3586
  if (!isPhoneNumber(validValue))
3146
3587
  errorMessageList.push('This is not a valid phone number.');
3147
3588
  break;
3148
3589
  }
3590
+ if (props.maxLength && validValue.length > Number(props.maxLength))
3591
+ errorMessageList.push(`This may not have more than ${props.maxLength} characters.`);
3592
+ if (props.minLength && validValue.length < Number(props.minLength))
3593
+ errorMessageList.push(`This must have at least ${props.minLength} characters.`);
3149
3594
  if (errorMessageList.length === 0)
3150
3595
  return true;
3151
3596
  setErrorMessage(errorMessageList.join(' '));
@@ -3157,19 +3602,15 @@ function Input({ checked, className, defaultValue, description, descriptionProps
3157
3602
  return;
3158
3603
  }
3159
3604
  const { currentTarget } = e, { value: newValue } = currentTarget;
3160
- setFormContext?.(prevContext => {
3161
- if (!prevContext)
3162
- return [];
3163
- const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
3164
- if (!field)
3165
- throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
3166
- const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
3167
- const updatedField = { ...field, value: newValue };
3168
- const invalidField = validateField(newValue) === false;
3169
- if (invalidField !== field.invalid)
3170
- updatedField.invalid = invalidField;
3171
- return [...otherFields, updatedField];
3172
- });
3605
+ if (isInFieldset) {
3606
+ fieldsetContextFunctions.updateField(fieldContextID, {
3607
+ value: newValue,
3608
+ invalid: validateField(newValue) === false,
3609
+ });
3610
+ }
3611
+ else {
3612
+ formContextFunctions.updateField(fieldContextID, { value: newValue, invalid: validateField(newValue) === false });
3613
+ }
3173
3614
  onChange?.(e);
3174
3615
  };
3175
3616
  const handleBlur = e => {
@@ -3180,32 +3621,21 @@ function Input({ checked, className, defaultValue, description, descriptionProps
3180
3621
  const { currentTarget } = e, { value: newValue } = currentTarget;
3181
3622
  if (required)
3182
3623
  validateField(newValue);
3624
+ let processedValue = newValue;
3183
3625
  switch (type) {
3184
3626
  case 'email':
3185
- setFormContext?.(prevContext => {
3186
- if (!prevContext)
3187
- return [];
3188
- const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
3189
- if (!field)
3190
- throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
3191
- const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
3192
- const updatedField = { ...field, value: newValue.toLowerCase() };
3193
- return [...otherFields, updatedField];
3194
- });
3627
+ processedValue = newValue.toLowerCase();
3195
3628
  break;
3196
3629
  case 'tel':
3197
- setFormContext?.(prevContext => {
3198
- if (!prevContext)
3199
- return [];
3200
- const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
3201
- if (!field)
3202
- throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
3203
- const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
3204
- const updatedField = { ...field, value: formatPhoneNumber(newValue, '1') };
3205
- return [...otherFields, updatedField];
3206
- });
3630
+ processedValue = formatPhoneNumber(newValue, options);
3207
3631
  break;
3208
3632
  }
3633
+ if (isInFieldset) {
3634
+ fieldsetContextFunctions.updateField(fieldContextID, { value: processedValue });
3635
+ }
3636
+ else {
3637
+ formContextFunctions.updateField(fieldContextID, { value: processedValue });
3638
+ }
3209
3639
  onBlur?.(e);
3210
3640
  };
3211
3641
  const restFieldProps = fieldProps
@@ -3225,12 +3655,12 @@ function Input({ checked, className, defaultValue, description, descriptionProps
3225
3655
  // user-invalid styles
3226
3656
  'user-invalid:border-ui-red user-invalid:bg-[color-mix(in_oklab,var(--color-ui-red)_20%,var(--color-neutral-100))] user-invalid:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-100))] user-invalid:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-100))] dark:user-invalid:bg-[color-mix(in_oklab,var(--color-ui-red)_20%,var(--color-neutral-800))] dark:user-invalid:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-800))] dark:user-invalid:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-800))] user-invalid:pointer-fine:hover:bg-[color-mix(in_oklab,var(--color-ui-red)_10%,var(--color-neutral-100))] user-invalid:pointer-fine:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-100))] user-invalid:pointer-fine:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-100))] dark:user-invalid:pointer-fine:hover:bg-[color-mix(in_oklab,var(--color-ui-red)_10%,var(--color-neutral-800))] dark:user-invalid:pointer-fine:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-800))] dark:user-invalid:pointer-fine:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-800))]',
3227
3657
  // Custom styles
3228
- typeof className === 'function' ? className(bag) : className), invalid: invalid, onBlur: handleBlur, onChange: handleChange, placeholder: placeholder, ref: ref, required: required, type: type, value: fieldContext?.value }), fieldContext.invalid && errorMessage && (jsxRuntime.jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [jsxRuntime.jsx(TooltipTrigger, { as: Button, className: 'absolute top-1.25 right-1.25 z-10 size-6 min-w-0', padding: 'none', rounded: 'md', theme: 'red', children: jsxRuntime.jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsxRuntime.jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsxRuntime.jsx(react.Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
3658
+ typeof className === 'function' ? className(bag) : className), invalid: invalid, onBlur: handleBlur, onChange: handleChange, placeholder: placeholder, ref: ref, required: required, type: type, value: fieldContext?.value || '' }), fieldContext?.invalid && errorMessage && (jsxRuntime.jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [jsxRuntime.jsx(TooltipTrigger, { as: Button, className: 'absolute top-1.25 right-1.25 z-10 size-6 min-w-0', padding: 'none', rounded: 'md', theme: 'red', children: jsxRuntime.jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsxRuntime.jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsxRuntime.jsx(react.Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
3229
3659
  ? descriptionProps?.className(bag)
3230
3660
  : descriptionProps?.className), children: description }))] }));
3231
3661
  }
3232
3662
 
3233
- function SubmitButton({ children, className, customTheme, error, incomplete, loading, success, type = 'submit', ...props }) {
3663
+ function SubmitButton({ children, className, customTheme, error, incomplete, loading, success, type, ...props }) {
3234
3664
  const [formStatus] = useFormStatus();
3235
3665
  const getFormStatusButtonClasses = () => {
3236
3666
  switch (formStatus) {
@@ -3278,40 +3708,43 @@ function SubmitButton({ children, className, customTheme, error, incomplete, loa
3278
3708
  }
3279
3709
  };
3280
3710
  const dataFormState = getDataFormState();
3281
- return (jsxRuntime.jsx(Button, { ...props, ...dataFormState, as: 'button', className: twMerge([formStatusButtonClasses, 'w-full text-white data-loading:text-black', className]), customTheme: {
3711
+ return (jsxRuntime.jsx(Button, { ...props, ...dataFormState, className: twMerge([formStatusButtonClasses, 'w-full text-white data-loading:text-black', className]), customTheme: {
3282
3712
  themeColor: twMerge('data-error:[--theme-color:var(--color-ui-red)] data-incomplete:[--theme-color:var(--color-ui-grey)] data-loading:[--theme-color:var(--color-ui-yellow)] data-readonly:[--theme-color:var(--color-ui-grey)] data-ready:[--theme-color:var(--color-ui-blue)] data-success:[--theme-color:var(--color-ui-green)]', customTheme?.themeColor),
3283
- }, theme: 'custom', type: type, children: buttonText }));
3713
+ }, theme: 'custom', type: type || 'submit', children: buttonText }));
3284
3714
  }
3285
3715
 
3286
3716
  function Textarea({ className, defaultValue, description, descriptionProps, disabled, fieldProps, invalid = true, label, labelProps, name, onBlur, onChange, placeholder, ref, required = true, value, ...props }) {
3287
- const [formContext, setFormContext] = useFormContext(), [errorMessage, setErrorMessage] = React.useState(undefined);
3717
+ const [formContext, formContextFunctions] = useFormContext();
3718
+ const [fieldsetContext, fieldsetContextFunctions] = useFieldsetContext();
3719
+ const [errorMessage, setErrorMessage] = React.useState(undefined);
3288
3720
  if (placeholder === '*')
3289
3721
  placeholder = name + (required && !label ? '*' : '');
3290
3722
  if (label === '*')
3291
3723
  label = name;
3292
- const uniqueID = React.useId(), fieldContextID = toLowerCase(name, [, '_']) + '§' + uniqueID;
3293
- if (Boolean(formContext?.find(field => field.id === fieldContextID)?.invalid))
3294
- invalid = true;
3295
- const initialFieldContext = defineField({
3296
- type: 'textarea',
3297
- id: fieldContextID,
3298
- invalid,
3299
- name,
3300
- required,
3301
- value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
3302
- });
3724
+ const uniqueID = React.useId();
3725
+ const fieldContextID = toLowerCase(name, [' ', '_']) + '§' + uniqueID;
3726
+ const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
3303
3727
  React.useEffect(() => {
3304
- if (!setFormContext)
3305
- return;
3306
- setFormContext(prevContext => {
3307
- const otherFields = (prevContext || []).filter(field => field.id !== initialFieldContext.id);
3308
- return [...otherFields, initialFieldContext];
3728
+ const initialFieldContext = defineField({
3729
+ type: 'textarea',
3730
+ id: fieldContextID,
3731
+ invalid,
3732
+ name,
3733
+ required,
3734
+ value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
3309
3735
  });
3736
+ if (isInFieldset) {
3737
+ fieldsetContextFunctions.registerField(initialFieldContext);
3738
+ return () => {
3739
+ fieldsetContextFunctions.removeField(initialFieldContext.id);
3740
+ };
3741
+ }
3742
+ formContextFunctions.registerField(initialFieldContext);
3310
3743
  return () => {
3311
- setFormContext(prevContext => (prevContext || []).filter(field => field.id !== initialFieldContext.id));
3744
+ formContextFunctions.removeField(initialFieldContext.id);
3312
3745
  };
3313
- }, [setFormContext]);
3314
- const fieldContext = formContext?.find(({ id: fieldID }) => fieldID === initialFieldContext.id) || initialFieldContext;
3746
+ }, [isInFieldset]);
3747
+ const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
3315
3748
  const validateField = (validValue) => {
3316
3749
  const noValue = !validValue || validValue === '';
3317
3750
  if (!required && noValue)
@@ -3322,6 +3755,10 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
3322
3755
  setErrorMessage(errorMessageList.join(' '));
3323
3756
  return false;
3324
3757
  }
3758
+ if (props.maxLength && validValue.length > Number(props.maxLength))
3759
+ errorMessageList.push(`This may not have more than ${props.maxLength} characters.`);
3760
+ if (props.minLength && validValue.length < Number(props.minLength))
3761
+ errorMessageList.push(`This must have at least ${props.minLength} characters.`);
3325
3762
  if (errorMessageList.length === 0)
3326
3763
  return true;
3327
3764
  setErrorMessage(errorMessageList.join(' '));
@@ -3332,22 +3769,41 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
3332
3769
  e.preventDefault();
3333
3770
  return;
3334
3771
  }
3335
- const { currentTarget } = e, { value: newValue } = currentTarget;
3336
- setFormContext?.(prevContext => {
3337
- if (!prevContext)
3338
- return [];
3339
- const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
3340
- if (!field)
3341
- throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
3342
- const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
3343
- const updatedField = { ...field, value: newValue };
3344
- const invalidField = validateField(newValue) === false;
3345
- if (invalidField !== field.invalid)
3346
- updatedField.invalid = invalidField;
3347
- return [...otherFields, updatedField];
3348
- });
3772
+ const { currentTarget } = e;
3773
+ const { value: newValue } = currentTarget;
3774
+ if (isInFieldset) {
3775
+ fieldsetContextFunctions.updateField(fieldContextID, {
3776
+ value: newValue,
3777
+ invalid: validateField(newValue) === false,
3778
+ });
3779
+ }
3780
+ else {
3781
+ formContextFunctions.updateField(fieldContextID, {
3782
+ value: newValue,
3783
+ invalid: validateField(newValue) === false,
3784
+ });
3785
+ }
3349
3786
  onChange?.(e);
3350
3787
  };
3788
+ const handleBlur = e => {
3789
+ if (disabled) {
3790
+ e.preventDefault();
3791
+ return;
3792
+ }
3793
+ const { currentTarget } = e;
3794
+ const { value: newValue } = currentTarget;
3795
+ if (required)
3796
+ validateField(newValue);
3797
+ // No special processing needed for textarea like email/phone formatting
3798
+ const processedValue = newValue;
3799
+ if (isInFieldset) {
3800
+ fieldsetContextFunctions.updateField(fieldContextID, { value: processedValue });
3801
+ }
3802
+ else {
3803
+ formContextFunctions.updateField(fieldContextID, { value: processedValue });
3804
+ }
3805
+ onBlur?.(e);
3806
+ };
3351
3807
  const restFieldProps = fieldProps
3352
3808
  ? Object.fromEntries(Object.entries(fieldProps).filter(([key]) => key !== 'className'))
3353
3809
  : {};
@@ -3365,23 +3821,34 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
3365
3821
  // user-invalid styles
3366
3822
  'user-invalid:border-ui-red user-invalid:bg-[color-mix(in_oklab,var(--color-ui-red)_20%,var(--color-neutral-100))] user-invalid:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-100))] user-invalid:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-100))] dark:user-invalid:bg-[color-mix(in_oklab,var(--color-ui-red)_20%,var(--color-neutral-800))] dark:user-invalid:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-800))] dark:user-invalid:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-800))] user-invalid:pointer-fine:hover:bg-[color-mix(in_oklab,var(--color-ui-red)_10%,var(--color-neutral-100))] user-invalid:pointer-fine:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-100))] user-invalid:pointer-fine:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-100))] dark:user-invalid:pointer-fine:hover:bg-[color-mix(in_oklab,var(--color-ui-red)_10%,var(--color-neutral-800))] dark:user-invalid:pointer-fine:focus-visible:bg-[color-mix(in_oklab,var(--color-ui-red)_1%,var(--color-neutral-800))] dark:user-invalid:pointer-fine:active:bg-[color-mix(in_oklab,var(--color-ui-red)_25%,var(--color-neutral-800))]',
3367
3823
  // Custom styles
3368
- typeof className === 'function' ? className(bag) : className), id: fieldContext?.id, invalid: invalid, onChange: handleChange, placeholder: placeholder, ref: ref, required: required, value: fieldContext?.value }), fieldContext.invalid && errorMessage && (jsxRuntime.jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [jsxRuntime.jsx(TooltipTrigger, { as: Button, className: 'absolute top-1.25 right-1.25 z-10 size-6 min-w-0', padding: 'none', rounded: 'md', theme: 'red', children: jsxRuntime.jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsxRuntime.jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsxRuntime.jsx(react.Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
3824
+ typeof className === 'function' ? className(bag) : className), id: fieldContext?.id, invalid: invalid, onChange: handleChange, onBlur: handleBlur, placeholder: placeholder, ref: ref, required: required, value: fieldContext?.value || '' }), fieldContext?.invalid && errorMessage && (jsxRuntime.jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [jsxRuntime.jsx(TooltipTrigger, { as: Button, className: 'absolute top-1.25 right-1.25 z-10 size-6 min-w-0', padding: 'none', rounded: 'md', theme: 'red', children: jsxRuntime.jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsxRuntime.jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsxRuntime.jsx(react.Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
3369
3825
  ? descriptionProps?.className(bag)
3370
3826
  : descriptionProps?.className), children: description }))] }));
3371
3827
  }
3372
3828
 
3373
- // import { findComponentByType } from '../../utils'
3374
3829
  function FormComponent({ as, children, className, handleSubmit, onError, onSubmit, onSuccess, ...props }) {
3375
3830
  const [formContext] = useFormContext(), [formStatus, setFormStatus] = useFormStatus();
3376
- // const submitButton = findComponentByType(children, SubmitButton)
3831
+ const checkField = React.useCallback((field) => {
3832
+ if (field.type !== 'array' && field.type !== 'object' && !field.invalid)
3833
+ return true;
3834
+ if (field.type === 'object')
3835
+ return field.fields.every(objectField => checkField(objectField));
3836
+ return false;
3837
+ }, []);
3838
+ const everyFieldIsValid = React.useCallback(() => {
3839
+ if (!formContext)
3840
+ return false;
3841
+ return formContext.every(field => checkField(field));
3842
+ }, [formContext, checkField]);
3377
3843
  React.useEffect(() => {
3378
3844
  if (!formContext)
3379
3845
  return;
3380
- if (formStatus !== 'incomplete' && formContext.find(({ invalid }) => invalid))
3846
+ if (formStatus !== 'incomplete' &&
3847
+ formContext.find(context => context.type !== 'array' && context.type !== 'object' && context.invalid))
3381
3848
  setFormStatus?.('incomplete');
3382
- if (formStatus !== 'ready' && formContext.every(({ invalid }) => !invalid))
3849
+ if (formStatus !== 'ready' && everyFieldIsValid())
3383
3850
  setFormStatus?.('ready');
3384
- }, [formContext]);
3851
+ }, [formContext, everyFieldIsValid]);
3385
3852
  const processSubmit = handleSubmit ||
3386
3853
  (async (e) => {
3387
3854
  e.preventDefault();
@@ -3454,9 +3921,16 @@ function ModalTrigger({ as, ...props }) {
3454
3921
  const Element = as || react.Button;
3455
3922
  return jsxRuntime.jsx(Element, { ...props });
3456
3923
  }
3924
+ function ModalTitle(props) {
3925
+ return jsxRuntime.jsx(react.DialogTitle, { ...props });
3926
+ }
3457
3927
  function ModalDialog(props) {
3458
3928
  return jsxRuntime.jsx("div", { ...props });
3459
3929
  }
3930
+ function ModalClose({ as, ...props }) {
3931
+ const Element = as || react.Button;
3932
+ return jsxRuntime.jsx(Element, { ...props });
3933
+ }
3460
3934
  function Modal({ children, className, onClose, onOpen, place = 'bottom' }) {
3461
3935
  const [bodyElement, setBodyElement] = React.useState(null);
3462
3936
  React.useEffect(() => {
@@ -3640,7 +4114,9 @@ exports.Heading = Heading;
3640
4114
  exports.Input = Input;
3641
4115
  exports.Link = Link;
3642
4116
  exports.Modal = Modal;
4117
+ exports.ModalClose = ModalClose;
3643
4118
  exports.ModalDialog = ModalDialog;
4119
+ exports.ModalTitle = ModalTitle;
3644
4120
  exports.ModalTrigger = ModalTrigger;
3645
4121
  exports.SubmitButton = SubmitButton;
3646
4122
  exports.Textarea = Textarea;
@@ -3648,4 +4124,6 @@ exports.Time = Time;
3648
4124
  exports.Tooltip = Tooltip;
3649
4125
  exports.TooltipPanel = TooltipPanel;
3650
4126
  exports.TooltipTrigger = TooltipTrigger;
4127
+ exports.createButton = createButton;
4128
+ exports.createLink = createLink;
3651
4129
  //# sourceMappingURL=index.js.map