mado-ui 0.3.2 → 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.
- package/css/index.css +41 -6
- package/dist/components/button.d.ts +3 -23
- package/dist/components/details.d.ts +2 -2
- package/dist/components/drop-down.d.ts +1 -1
- package/dist/components/form/fieldset.d.ts +35 -7
- package/dist/components/form/index.d.ts +5 -6
- package/dist/components/form/input/date/index.d.ts +36 -0
- package/dist/components/form/input/index.d.ts +78 -12
- package/dist/components/form/submit-button.d.ts +1 -1
- package/dist/components/form/textarea.d.ts +1 -1
- package/dist/components/ghost.d.ts +1 -1
- package/dist/components/heading.d.ts +1 -1
- package/dist/components/iframe.d.ts +15 -0
- package/dist/components/index.d.ts +10 -11
- package/dist/components/link.d.ts +3 -14
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/time.d.ts +1 -1
- package/dist/components/tooltip.d.ts +1 -1
- package/dist/components.esm.js +466 -122
- package/dist/components.esm.js.map +1 -1
- package/dist/components.js +465 -119
- package/dist/components.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -3
- package/dist/hooks/use-fieldset-context.d.ts +22 -0
- package/dist/hooks/use-form-context.d.ts +18 -10
- package/dist/hooks.esm.js +99 -5
- package/dist/hooks.esm.js.map +1 -1
- package/dist/hooks.js +100 -3
- package/dist/hooks.js.map +1 -1
- package/dist/icons/3-people.d.ts +2 -2
- package/dist/icons/3-rectangles-desktop-fill.d.ts +2 -2
- package/dist/icons/3-rectangles-desktop.d.ts +2 -2
- package/dist/icons/airplane.d.ts +2 -2
- package/dist/icons/arrow-triangle-2-circlepath-circle-fill.d.ts +2 -2
- package/dist/icons/arrow-triangle-2-circlepath-circle.d.ts +2 -2
- package/dist/icons/bag-fill.d.ts +2 -2
- package/dist/icons/banknote.d.ts +2 -2
- package/dist/icons/bell-fill.d.ts +2 -2
- package/dist/icons/bolt-car.d.ts +2 -2
- package/dist/icons/bolt-fill.d.ts +2 -2
- package/dist/icons/bolt-ring-closed.d.ts +2 -2
- package/dist/icons/bolt-trianglebadge-exclamationmark.d.ts +2 -2
- package/dist/icons/book-fill.d.ts +2 -2
- package/dist/icons/bookmark-fill.d.ts +2 -2
- package/dist/icons/briefcase-fill.d.ts +2 -2
- package/dist/icons/bubble-left-fill.d.ts +2 -2
- package/dist/icons/building-2-fill.d.ts +2 -2
- package/dist/icons/calendar.d.ts +2 -2
- package/dist/icons/camera-fill.d.ts +2 -2
- package/dist/icons/car-fill.d.ts +2 -2
- package/dist/icons/cart-fill.d.ts +2 -2
- package/dist/icons/chart-bar-doc-horizontal.d.ts +2 -2
- package/dist/icons/checkmark-seal.d.ts +2 -2
- package/dist/icons/checkmark.d.ts +2 -2
- package/dist/icons/chevron-down.d.ts +2 -2
- package/dist/icons/chevron-left-forwardslash-chevron-right.d.ts +2 -2
- package/dist/icons/chevron-left.d.ts +2 -2
- package/dist/icons/chevron-right.d.ts +2 -2
- package/dist/icons/chevron-up-chevron-down.d.ts +2 -2
- package/dist/icons/circle-fill.d.ts +2 -2
- package/dist/icons/clock-badge-checkmark.d.ts +2 -2
- package/dist/icons/clock-fill.d.ts +2 -2
- package/dist/icons/cloud-fill.d.ts +2 -2
- package/dist/icons/cube-fill.d.ts +2 -2
- package/dist/icons/curve-point-left.d.ts +2 -2
- package/dist/icons/dial-high.d.ts +2 -2
- package/dist/icons/doc-fill.d.ts +2 -2
- package/dist/icons/doc-on-clipboard.d.ts +2 -2
- package/dist/icons/doc-on-doc-fill.d.ts +2 -2
- package/dist/icons/doc-on-doc.d.ts +2 -2
- package/dist/icons/doc-text-magnifyingglass.d.ts +2 -2
- package/dist/icons/dollar-sign.d.ts +2 -2
- package/dist/icons/ellipsis-circle-fill.d.ts +2 -2
- package/dist/icons/ellipsis-circle.d.ts +2 -2
- package/dist/icons/envelope-fill.d.ts +2 -2
- package/dist/icons/envelope.d.ts +2 -2
- package/dist/icons/exclamationmark-octagon.d.ts +2 -2
- package/dist/icons/eye.d.ts +2 -2
- package/dist/icons/figure-water-fitness.d.ts +2 -2
- package/dist/icons/flag-fill.d.ts +2 -2
- package/dist/icons/flame-fill.d.ts +2 -2
- package/dist/icons/folder-fill.d.ts +2 -2
- package/dist/icons/folder.d.ts +2 -2
- package/dist/icons/gearshape-fill.d.ts +2 -2
- package/dist/icons/gearshape.d.ts +2 -2
- package/dist/icons/gift-fill.d.ts +2 -2
- package/dist/icons/globe-americas-fill.d.ts +2 -2
- package/dist/icons/hare-fill.d.ts +2 -2
- package/dist/icons/house-deskclock.d.ts +2 -2
- package/dist/icons/house-fill.d.ts +2 -2
- package/dist/icons/house.d.ts +2 -2
- package/dist/icons/iphone-house.d.ts +2 -2
- package/dist/icons/light-ribbon.d.ts +2 -2
- package/dist/icons/lightbulb-fill.d.ts +2 -2
- package/dist/icons/lightbulb-led.d.ts +2 -2
- package/dist/icons/list-bullet-clipboard-fill.d.ts +2 -2
- package/dist/icons/magnifyingglass.d.ts +2 -2
- package/dist/icons/map-pin-ellipse.d.ts +2 -2
- package/dist/icons/minus-plus-batterblock.d.ts +2 -2
- package/dist/icons/network-shield.d.ts +2 -2
- package/dist/icons/network.d.ts +2 -2
- package/dist/icons/newspaper-fill.d.ts +2 -2
- package/dist/icons/number.d.ts +2 -2
- package/dist/icons/paperplane-fill.d.ts +2 -2
- package/dist/icons/person-crop-square.d.ts +2 -2
- package/dist/icons/person-fill-questionmark.d.ts +2 -2
- package/dist/icons/person-fill.d.ts +2 -2
- package/dist/icons/person.d.ts +2 -2
- package/dist/icons/phone-arrow-up-right.d.ts +2 -2
- package/dist/icons/phone-fill.d.ts +2 -2
- package/dist/icons/phone.d.ts +2 -2
- package/dist/icons/play-rectangle-fill.d.ts +2 -2
- package/dist/icons/plus.d.ts +2 -2
- package/dist/icons/qrcode.d.ts +2 -2
- package/dist/icons/rectangle-portrait-and-arrow-left-fill.d.ts +2 -2
- package/dist/icons/rectangle-portrait-and-arrow-left.d.ts +2 -2
- package/dist/icons/sensor.d.ts +2 -2
- package/dist/icons/signature.d.ts +2 -2
- package/dist/icons/solar-panel.d.ts +2 -2
- package/dist/icons/square-and-arrow-down-fill.d.ts +2 -2
- package/dist/icons/square-and-arrow-down.d.ts +2 -2
- package/dist/icons/square-and-arrow-up-fill.d.ts +2 -2
- package/dist/icons/square-and-arrow-up.d.ts +2 -2
- package/dist/icons/square-and-pencil-fill.d.ts +2 -2
- package/dist/icons/square-and-pencil.d.ts +2 -2
- package/dist/icons/text-bubble.d.ts +2 -2
- package/dist/icons/trash-fill.d.ts +2 -2
- package/dist/icons/trash.d.ts +2 -2
- package/dist/icons/tree.d.ts +2 -2
- package/dist/icons/umbrella-fill.d.ts +2 -2
- package/dist/icons/xmark.d.ts +2 -2
- package/dist/icons.esm.js.map +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.esm.js +466 -122
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +465 -119
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -2
- package/dist/types/utils.d.ts +20 -0
- package/dist/utils/get-date.d.ts +17 -0
- package/dist/utils/index.d.ts +8 -9
- package/dist/utils/string-manipulation.d.ts +38 -3
- package/dist/utils/tw-sort.d.ts +1 -1
- package/dist/utils.esm.js +112 -21
- package/dist/utils.esm.js.map +1 -1
- package/dist/utils.js +113 -20
- package/dist/utils.js.map +1 -1
- package/package.json +4 -4
package/dist/index.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
2
2
|
import { extendTailwindMerge, twJoin } from 'tailwind-merge';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { Children, isValidElement, Fragment, createContext, useContext, useSyncExternalStore, useRef,
|
|
5
|
-
import { Button as Button$1,
|
|
4
|
+
import { Children, isValidElement, Fragment, createContext, useContext, useSyncExternalStore, useRef, useCallback, useEffect, Suspense, useId, useLayoutEffect, cloneElement, useState } from 'react';
|
|
5
|
+
import { Button as Button$1, DisclosureButton, DisclosurePanel, Disclosure, MenuButton, MenuItem, MenuItems, MenuSection, MenuHeading, MenuSeparator, Menu, Fieldset as Fieldset$1, Legend, Field, Label, Input as Input$1, Description, Textarea as Textarea$1, DialogTitle, Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
|
|
6
6
|
import * as ReactDOM from 'react-dom';
|
|
7
7
|
import { createPortal } from 'react-dom';
|
|
8
8
|
|
|
@@ -188,6 +188,9 @@ function getMonth(month = d) {
|
|
|
188
188
|
formattedMonth = `0${formattedMonth}`;
|
|
189
189
|
return formattedMonth;
|
|
190
190
|
}
|
|
191
|
+
function getMonthIndexFromName(name) {
|
|
192
|
+
return monthNamesList.findIndex(monthName => monthName === name);
|
|
193
|
+
}
|
|
191
194
|
/**
|
|
192
195
|
* ### Get Month Name
|
|
193
196
|
* - Returns the name of the specified month
|
|
@@ -213,6 +216,30 @@ function getSeconds(seconds = d) {
|
|
|
213
216
|
formattedSeconds = `0${formattedSeconds}`;
|
|
214
217
|
return formattedSeconds;
|
|
215
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* ### Get User Readable Date
|
|
221
|
+
* - Returns a string of the current date in a user-friendly way
|
|
222
|
+
* - Includes `'Yesterday'`, '`Today'`, and `'Tomorrow'`
|
|
223
|
+
* @param date (default: `new Date()`)
|
|
224
|
+
* @returns {'Today'|'Tomorrow'|'Yesterday'|string} `'weekday, month day, year'` | `'Yesterday'` | `'Today'` | `'Tomorrow'`
|
|
225
|
+
*/
|
|
226
|
+
function getUserReadableDate(date = d) {
|
|
227
|
+
const dateTime = date.getTime();
|
|
228
|
+
const today = new Date(), isToday = dateTime === today.getTime();
|
|
229
|
+
if (isToday)
|
|
230
|
+
return 'Today';
|
|
231
|
+
const yesterday = new Date(today.getDate() - 1), isYesterday = dateTime === yesterday.getTime();
|
|
232
|
+
if (isYesterday)
|
|
233
|
+
return 'Yesterday';
|
|
234
|
+
const tomorrow = new Date(today.getDate() + 1), isTomorrow = dateTime === tomorrow.getTime();
|
|
235
|
+
if (isTomorrow)
|
|
236
|
+
return 'Tomorrow';
|
|
237
|
+
const thisYear = today.getFullYear(), isSameYear = date.getFullYear() === thisYear;
|
|
238
|
+
const fullDateString = toFullDateString(date, {
|
|
239
|
+
year: !isSameYear,
|
|
240
|
+
});
|
|
241
|
+
return fullDateString;
|
|
242
|
+
}
|
|
216
243
|
/**
|
|
217
244
|
* ### Get Weekday Name
|
|
218
245
|
* - Returns the weekday name of the specified day
|
|
@@ -225,6 +252,45 @@ function getWeekdayName(weekday = d) {
|
|
|
225
252
|
// Return the name of the day of the week
|
|
226
253
|
return weekdayNamesList[weekday.getDay()];
|
|
227
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* ### To Full Date String
|
|
257
|
+
* - Returns a formatted string to display the date
|
|
258
|
+
* @param {Date} date (default: `new Date()`)
|
|
259
|
+
* @param {ToFullDateStringOptionsProps} options Change how to display the weekday, month name, day of the month, and year.
|
|
260
|
+
* @returns {string} '`weekday`, `month` `day`, `year`'
|
|
261
|
+
*/
|
|
262
|
+
function toFullDateString(date = d, options) {
|
|
263
|
+
let weekdayName = getWeekdayName(date), monthName = getMonthName(date), dayOfMonth = date.getDate(), year = date.getFullYear().toString();
|
|
264
|
+
if (options) {
|
|
265
|
+
const includesDay = options.day !== false, includesMonth = options.month !== false, includesYear = options.year !== false;
|
|
266
|
+
{
|
|
267
|
+
weekdayName = weekdayName.slice(0, 3);
|
|
268
|
+
if (includesMonth || includesDay || includesYear)
|
|
269
|
+
weekdayName += ', ';
|
|
270
|
+
}
|
|
271
|
+
if (includesMonth) {
|
|
272
|
+
if (options.month === 'code')
|
|
273
|
+
monthName = monthName.slice(0, 3);
|
|
274
|
+
if (includesDay)
|
|
275
|
+
monthName += ' ';
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
monthName = '';
|
|
279
|
+
}
|
|
280
|
+
if (!includesDay)
|
|
281
|
+
dayOfMonth = '';
|
|
282
|
+
if (includesYear) {
|
|
283
|
+
if (options.year === 'code')
|
|
284
|
+
year = year.slice(-2);
|
|
285
|
+
if (includesMonth || includesDay)
|
|
286
|
+
year = ', ' + year;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
year = '';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return weekdayName + monthName + dayOfMonth + year;
|
|
293
|
+
}
|
|
228
294
|
|
|
229
295
|
function findComponentByType(children, componentType) {
|
|
230
296
|
const childrenArray = Children.toArray(children);
|
|
@@ -273,25 +339,97 @@ function isPhoneNumber(tel) {
|
|
|
273
339
|
/**
|
|
274
340
|
* # Format Phone Number
|
|
275
341
|
* Converts any string containing at least 10 numbers to a formatted phone number
|
|
276
|
-
* @param {string}
|
|
277
|
-
* @
|
|
342
|
+
* @param {string} phoneNumber
|
|
343
|
+
* @param options
|
|
344
|
+
* @property {string} `countryCode`
|
|
345
|
+
* @property {'continuous' | 'dot' | 'hyphenated' | 'none' | 'space' | 'standard'} `format`
|
|
346
|
+
*
|
|
347
|
+
* Input: a555b555c5555d
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* format: 'continuous'
|
|
351
|
+
* countryCode: '1'
|
|
352
|
+
* returns: +1 5555555555
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* format: 'dot'
|
|
356
|
+
* returns: 555.555.5555
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* format: 'hyphenated'
|
|
360
|
+
* returns: 555-555-5555
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* format: 'none'
|
|
364
|
+
* countryCode: '1'
|
|
365
|
+
* returns: +1 a555b555c5555d
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* format: 'space'
|
|
369
|
+
* returns: 555 555 5555
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* format: 'standard' (default)
|
|
373
|
+
* returns: (555) 555-5555
|
|
374
|
+
*
|
|
375
|
+
* @returns {string} string formatted
|
|
278
376
|
*/
|
|
279
|
-
function formatPhoneNumber(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
377
|
+
function formatPhoneNumber(phoneNumber, options) {
|
|
378
|
+
const format = options?.format || 'standard';
|
|
379
|
+
if (format !== 'none')
|
|
380
|
+
phoneNumber = phoneNumber.replace(/\D/g, '').slice(-10);
|
|
381
|
+
switch (format) {
|
|
382
|
+
case 'dot':
|
|
383
|
+
phoneNumber = phoneNumber
|
|
384
|
+
.split('')
|
|
385
|
+
.map((char, index) => {
|
|
386
|
+
if (index === 2)
|
|
387
|
+
return `${char}.`;
|
|
388
|
+
if (index === 5)
|
|
389
|
+
return `${char}.`;
|
|
390
|
+
return char;
|
|
391
|
+
})
|
|
392
|
+
.join('');
|
|
393
|
+
break;
|
|
394
|
+
case 'hyphenated':
|
|
395
|
+
phoneNumber = phoneNumber
|
|
396
|
+
.split('')
|
|
397
|
+
.map((char, index) => {
|
|
398
|
+
if (index === 2)
|
|
399
|
+
return `${char}-`;
|
|
400
|
+
if (index === 5)
|
|
401
|
+
return `${char}-`;
|
|
402
|
+
return char;
|
|
403
|
+
})
|
|
404
|
+
.join('');
|
|
405
|
+
break;
|
|
406
|
+
case 'space':
|
|
407
|
+
phoneNumber = phoneNumber
|
|
408
|
+
.split('')
|
|
409
|
+
.map((char, index) => {
|
|
410
|
+
if (index === 2)
|
|
411
|
+
return `${char} `;
|
|
412
|
+
if (index === 5)
|
|
413
|
+
return `${char} `;
|
|
414
|
+
return char;
|
|
415
|
+
})
|
|
416
|
+
.join('');
|
|
417
|
+
break;
|
|
418
|
+
case 'standard':
|
|
419
|
+
phoneNumber = phoneNumber
|
|
420
|
+
.split('')
|
|
421
|
+
.map((char, index) => {
|
|
422
|
+
if (index === 0)
|
|
423
|
+
return `(${char}`;
|
|
424
|
+
if (index === 2)
|
|
425
|
+
return `${char}) `;
|
|
426
|
+
if (index === 5)
|
|
427
|
+
return `${char}-`;
|
|
428
|
+
return char;
|
|
429
|
+
})
|
|
430
|
+
.join('');
|
|
431
|
+
}
|
|
432
|
+
return `${options?.countryCode ? `+${options?.countryCode} ` : ''}` + phoneNumber;
|
|
295
433
|
}
|
|
296
434
|
/**
|
|
297
435
|
* # To Lower Case
|
|
@@ -915,7 +1053,7 @@ function DropDownItems({ anchor, children, className, containerClassName, style,
|
|
|
915
1053
|
}
|
|
916
1054
|
function DropDownSection({ children, label, labelProps, separatorAbove, separatorBelow, ...props }) {
|
|
917
1055
|
const { labelClassName, ...restLabelProps } = { labelClassName: labelProps?.className || '', ...labelProps };
|
|
918
|
-
return (jsx(MenuSection, { ...props, children: sectionBag => (jsxs(Fragment$1, { children: [separatorAbove && jsx(DropDownSeparator, {}), label && (jsx(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 && jsx(DropDownSeparator, {})] })) }));
|
|
1056
|
+
return (jsx(MenuSection, { ...props, children: sectionBag => (jsxs(Fragment$1, { children: [separatorAbove && jsx(DropDownSeparator, {}), label && (jsx(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 && jsx(DropDownSeparator, {})] })) }));
|
|
919
1057
|
}
|
|
920
1058
|
function DropDownSeparator({ className, ...props }) {
|
|
921
1059
|
return (jsx(MenuSeparator, { ...props, className: bag => twMerge('my-4 block h-px rounded-full bg-neutral-950/20', typeof className === 'function' ? className(bag) : className) }));
|
|
@@ -975,12 +1113,106 @@ function createFastContext(defaultInitialState) {
|
|
|
975
1113
|
function defineField(fieldDefinition) {
|
|
976
1114
|
return fieldDefinition;
|
|
977
1115
|
}
|
|
978
|
-
|
|
1116
|
+
function isStringField(field) {
|
|
1117
|
+
return Boolean(field.type !== 'object' && field.type !== 'array');
|
|
1118
|
+
}
|
|
1119
|
+
const { Provider: Provider$2, useStore: useStore$2 } = createFastContext([]);
|
|
979
1120
|
function FormContextProvider({ children }) {
|
|
980
|
-
return jsx(Provider$
|
|
1121
|
+
return jsx(Provider$2, { children: children });
|
|
981
1122
|
}
|
|
982
1123
|
function useFormContext() {
|
|
983
|
-
|
|
1124
|
+
const [formContext, setFormContext] = useStore$2(store => store);
|
|
1125
|
+
const registerField = useCallback((field) => {
|
|
1126
|
+
setFormContext?.(prevContext => {
|
|
1127
|
+
const otherFields = (prevContext || []).filter(otherField => otherField.id !== field.id);
|
|
1128
|
+
return [...otherFields, field];
|
|
1129
|
+
});
|
|
1130
|
+
}, []);
|
|
1131
|
+
const removeField = useCallback((fieldID) => {
|
|
1132
|
+
setFormContext?.(prevContext => (prevContext || []).filter(field => field.id !== fieldID));
|
|
1133
|
+
}, []);
|
|
1134
|
+
const updateField = useCallback((fieldID, updates) => {
|
|
1135
|
+
setFormContext?.(prevContext => {
|
|
1136
|
+
const field = prevContext.find(({ id }) => id === fieldID);
|
|
1137
|
+
if (!field)
|
|
1138
|
+
throw new Error(`Field with id "${fieldID}" not found in form context.`);
|
|
1139
|
+
const otherFields = prevContext.filter(({ id }) => id !== fieldID);
|
|
1140
|
+
const updatedField = { ...field, ...updates };
|
|
1141
|
+
return [...otherFields, updatedField];
|
|
1142
|
+
});
|
|
1143
|
+
}, []);
|
|
1144
|
+
return [formContext, { registerField, removeField, updateField }];
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const { Provider: Provider$1, useStore: useStore$1 } = createFastContext(undefined);
|
|
1148
|
+
function FieldsetContextProvider({ children, initialValue, }) {
|
|
1149
|
+
return jsx(Provider$1, { initialValue: initialValue, children: children });
|
|
1150
|
+
}
|
|
1151
|
+
function useFieldsetContext() {
|
|
1152
|
+
const [fieldsetContext, setFieldsetContext] = useStore$1(store => store), [, formContextFunctions] = useFormContext();
|
|
1153
|
+
const registerField = useCallback((field) => {
|
|
1154
|
+
setFieldsetContext?.(prev => {
|
|
1155
|
+
if (!prev)
|
|
1156
|
+
return prev;
|
|
1157
|
+
const existingFieldIndex = prev.fieldList.findIndex(existingField => existingField.id === field.id);
|
|
1158
|
+
const newFieldList = existingFieldIndex >= 0
|
|
1159
|
+
? prev.fieldList.map((existingField, index) => (index === existingFieldIndex ? field : existingField))
|
|
1160
|
+
: [...prev.fieldList, field];
|
|
1161
|
+
return { ...prev, fieldList: newFieldList };
|
|
1162
|
+
});
|
|
1163
|
+
}, []);
|
|
1164
|
+
const removeField = useCallback((fieldID) => {
|
|
1165
|
+
setFieldsetContext?.(prev => {
|
|
1166
|
+
if (!prev)
|
|
1167
|
+
return prev;
|
|
1168
|
+
return {
|
|
1169
|
+
...prev,
|
|
1170
|
+
fieldList: prev.fieldList.filter(field => field.id !== fieldID),
|
|
1171
|
+
};
|
|
1172
|
+
});
|
|
1173
|
+
}, []);
|
|
1174
|
+
const updateField = useCallback((fieldID, updates) => {
|
|
1175
|
+
setFieldsetContext?.(prev => {
|
|
1176
|
+
if (!prev)
|
|
1177
|
+
return prev;
|
|
1178
|
+
return {
|
|
1179
|
+
...prev,
|
|
1180
|
+
fieldList: prev.fieldList.map(field => (field.id === fieldID ? { ...field, ...updates } : field)),
|
|
1181
|
+
};
|
|
1182
|
+
});
|
|
1183
|
+
}, []);
|
|
1184
|
+
const createFormContextEntry = useCallback((fieldsetEntry) => {
|
|
1185
|
+
if (fieldsetEntry.join) {
|
|
1186
|
+
const joinedValue = fieldsetEntry.fieldList
|
|
1187
|
+
.filter(field => isStringField(field) && field.value !== '')
|
|
1188
|
+
.map(field => field.value)
|
|
1189
|
+
.join(fieldsetEntry.join);
|
|
1190
|
+
return defineField({
|
|
1191
|
+
type: 'string',
|
|
1192
|
+
id: fieldsetEntry.id,
|
|
1193
|
+
name: fieldsetEntry.name,
|
|
1194
|
+
value: joinedValue,
|
|
1195
|
+
required: fieldsetEntry.fieldList.some(field => field.required),
|
|
1196
|
+
invalid: fieldsetEntry.fieldList.some(field => field.invalid),
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
return defineField({
|
|
1200
|
+
type: 'object',
|
|
1201
|
+
id: fieldsetEntry.id,
|
|
1202
|
+
name: fieldsetEntry.name,
|
|
1203
|
+
fields: fieldsetEntry.fieldList,
|
|
1204
|
+
});
|
|
1205
|
+
}, []);
|
|
1206
|
+
useEffect(() => {
|
|
1207
|
+
if (!fieldsetContext || fieldsetContext.decorative)
|
|
1208
|
+
return;
|
|
1209
|
+
const formContextEntry = createFormContextEntry(fieldsetContext);
|
|
1210
|
+
formContextFunctions.registerField(formContextEntry);
|
|
1211
|
+
return () => {
|
|
1212
|
+
formContextFunctions.removeField(fieldsetContext.id);
|
|
1213
|
+
};
|
|
1214
|
+
}, [fieldsetContext, formContextFunctions.registerField, formContextFunctions.removeField, createFormContextEntry]);
|
|
1215
|
+
return [fieldsetContext, { registerField, removeField, updateField }];
|
|
984
1216
|
}
|
|
985
1217
|
|
|
986
1218
|
const DEFAULT_STATUS = 'incomplete';
|
|
@@ -992,9 +1224,18 @@ function useFormStatus() {
|
|
|
992
1224
|
return useStore(store => store);
|
|
993
1225
|
}
|
|
994
1226
|
|
|
995
|
-
function Fieldset({ children, className, legend, legendProps, ...props }) {
|
|
1227
|
+
function Fieldset({ children, className, decorative = false, join, legend, legendProps, name, ...props }) {
|
|
1228
|
+
const uniqueID = useId();
|
|
1229
|
+
const fieldsetId = toLowerCase(legend || name, [' ', '_']) + '§' + uniqueID;
|
|
996
1230
|
const { className: legendClassName, ...restLegendProps } = legendProps || {};
|
|
997
|
-
|
|
1231
|
+
name = legend || name;
|
|
1232
|
+
return (jsx(FieldsetContextProvider, { initialValue: {
|
|
1233
|
+
decorative,
|
|
1234
|
+
fieldList: [],
|
|
1235
|
+
id: fieldsetId,
|
|
1236
|
+
join,
|
|
1237
|
+
name,
|
|
1238
|
+
}, children: jsx(Fieldset$1, { ...props, name: name, className: bag => twMerge('contents', typeof className === 'function' ? className(bag) : className), children: bag => (jsxs(Fragment$1, { children: [legend && (jsx(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] })) }) }));
|
|
998
1239
|
}
|
|
999
1240
|
|
|
1000
1241
|
/**
|
|
@@ -3186,15 +3427,17 @@ function ArrowSvg({ className, ...props }) {
|
|
|
3186
3427
|
return (jsxs("svg", { viewBox: '0 0 20 10', className: twMerge('h-2.5 w-5 fill-none', className), ...props, children: [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' }), 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' }), 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' })] }));
|
|
3187
3428
|
}
|
|
3188
3429
|
|
|
3189
|
-
|
|
3190
|
-
|
|
3430
|
+
const specialCharacterRegex = new RegExp(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~€‚ƒ„…†‡‰‹‘’“”•–—™›¡¢£¥§©«¬®°±¶º»¿×÷]/);
|
|
3431
|
+
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 }) {
|
|
3432
|
+
const [formContext, formContextFunctions] = useFormContext(), [fieldsetContext, fieldsetContextFunctions] = useFieldsetContext(), [errorMessage, setErrorMessage] = useState(undefined);
|
|
3433
|
+
if (type === 'password' && !placeholder)
|
|
3434
|
+
placeholder = '••••••••' + (required && !label ? '*' : '');
|
|
3191
3435
|
if (placeholder === '*')
|
|
3192
3436
|
placeholder = name + (required && !label ? '*' : '');
|
|
3193
3437
|
if (label === '*')
|
|
3194
3438
|
label = name;
|
|
3195
3439
|
const uniqueID = useId(), fieldContextID = toLowerCase(name, [, '_']) + '§' + uniqueID;
|
|
3196
|
-
|
|
3197
|
-
invalid = true;
|
|
3440
|
+
const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
|
|
3198
3441
|
const getFieldContextType = () => {
|
|
3199
3442
|
switch (type) {
|
|
3200
3443
|
case 'email':
|
|
@@ -3212,26 +3455,27 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3212
3455
|
}
|
|
3213
3456
|
};
|
|
3214
3457
|
const fieldContextType = getFieldContextType();
|
|
3215
|
-
const
|
|
3216
|
-
type: fieldContextType,
|
|
3217
|
-
id: fieldContextID,
|
|
3218
|
-
invalid,
|
|
3219
|
-
name,
|
|
3220
|
-
required,
|
|
3221
|
-
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3222
|
-
});
|
|
3458
|
+
const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
|
|
3223
3459
|
useEffect(() => {
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3460
|
+
const initialFieldContext = defineField({
|
|
3461
|
+
type: fieldContextType,
|
|
3462
|
+
id: fieldContextID,
|
|
3463
|
+
invalid,
|
|
3464
|
+
name,
|
|
3465
|
+
required,
|
|
3466
|
+
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3229
3467
|
});
|
|
3468
|
+
if (isInFieldset) {
|
|
3469
|
+
fieldsetContextFunctions.registerField(initialFieldContext);
|
|
3470
|
+
return () => {
|
|
3471
|
+
fieldsetContextFunctions.removeField(initialFieldContext.id);
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
formContextFunctions.registerField(initialFieldContext);
|
|
3230
3475
|
return () => {
|
|
3231
|
-
|
|
3476
|
+
formContextFunctions.removeField(initialFieldContext.id);
|
|
3232
3477
|
};
|
|
3233
|
-
}, [
|
|
3234
|
-
const fieldContext = formContext?.find(({ id: fieldID }) => fieldID === initialFieldContext.id) || initialFieldContext;
|
|
3478
|
+
}, [isInFieldset]);
|
|
3235
3479
|
const validateField = (validValue) => {
|
|
3236
3480
|
const noValue = !validValue || validValue === '';
|
|
3237
3481
|
if (!required && noValue)
|
|
@@ -3247,15 +3491,86 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3247
3491
|
if (!isEmail(validValue))
|
|
3248
3492
|
errorMessageList.push('This is not a valid email.');
|
|
3249
3493
|
break;
|
|
3494
|
+
case 'date':
|
|
3495
|
+
const valueAsTime = new Date().getTime();
|
|
3496
|
+
if (min && !(min instanceof Date) && typeof min !== 'number') {
|
|
3497
|
+
if (Array.isArray(min)) {
|
|
3498
|
+
const monthIndex = typeof min[1] === 'number' ? min[1] - 1 : getMonthIndexFromName(min[1]);
|
|
3499
|
+
min = new Date(min[0], monthIndex, min[2]);
|
|
3500
|
+
}
|
|
3501
|
+
else if ('year' in min && 'month' in min && 'day' in min) {
|
|
3502
|
+
const monthIndex = typeof min.month === 'number' ? min.month - 1 : getMonthIndexFromName(min.month);
|
|
3503
|
+
min = new Date(min.year, monthIndex, min.day);
|
|
3504
|
+
}
|
|
3505
|
+
if (valueAsTime < min.getTime())
|
|
3506
|
+
errorMessageList.push(`Value cannot be lower than ${getUserReadableDate(min)}.`);
|
|
3507
|
+
}
|
|
3508
|
+
if (max && !(max instanceof Date) && typeof max !== 'number') {
|
|
3509
|
+
if (Array.isArray(max)) {
|
|
3510
|
+
const monthIndex = typeof max[1] === 'number' ? max[1] - 1 : getMonthIndexFromName(max[1]);
|
|
3511
|
+
max = new Date(max[0], monthIndex, max[2]);
|
|
3512
|
+
}
|
|
3513
|
+
else if ('year' in max && 'month' in max && 'day' in max) {
|
|
3514
|
+
const monthIndex = typeof max.month === 'number' ? max.month - 1 : getMonthIndexFromName(max.month);
|
|
3515
|
+
max = new Date(max.year, monthIndex, max.day);
|
|
3516
|
+
}
|
|
3517
|
+
if (valueAsTime > max.getTime())
|
|
3518
|
+
errorMessageList.push(`Value cannot be higher than ${getUserReadableDate(max)}.`);
|
|
3519
|
+
}
|
|
3520
|
+
break;
|
|
3250
3521
|
case 'number':
|
|
3251
|
-
|
|
3522
|
+
const valueAsNumber = Number(validValue);
|
|
3523
|
+
if (isNaN(valueAsNumber))
|
|
3252
3524
|
errorMessageList.push('This is not a valid number.');
|
|
3525
|
+
if (typeof max === 'number' && valueAsNumber > max)
|
|
3526
|
+
errorMessageList.push(`Value cannot be higher than ${max}.`);
|
|
3527
|
+
if (typeof min === 'number' && valueAsNumber < min)
|
|
3528
|
+
errorMessageList.push(`Value cannot be lower than ${min}.`);
|
|
3529
|
+
break;
|
|
3530
|
+
case 'password':
|
|
3531
|
+
if (options) {
|
|
3532
|
+
const { matchPreviousInput, requireLowercaseCharacter, requireNumber, requireSpecialCharacter, requireUppercaseCharacter, } = options;
|
|
3533
|
+
if (matchPreviousInput && formContext && formContext.length >= 2) {
|
|
3534
|
+
if (isInFieldset && fieldsetContext.fieldList.length > 1) {
|
|
3535
|
+
const currentInputIndex = fieldsetContext.fieldList.findIndex(({ id: fieldID }) => fieldID === fieldContext?.id);
|
|
3536
|
+
if (currentInputIndex > 0) {
|
|
3537
|
+
const previousInput = fieldsetContext.fieldList.find((_, index) => index === currentInputIndex - 1);
|
|
3538
|
+
if (previousInput &&
|
|
3539
|
+
isStringField(previousInput) &&
|
|
3540
|
+
previousInput.value !== validValue)
|
|
3541
|
+
errorMessageList.push('Passwords must match.');
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
else {
|
|
3545
|
+
const currentInputIndex = formContext.findIndex(({ id: fieldID }) => fieldID === fieldContext?.id);
|
|
3546
|
+
if (currentInputIndex > 0) {
|
|
3547
|
+
const previousInput = formContext.find((_, index) => index === currentInputIndex - 1);
|
|
3548
|
+
if (previousInput &&
|
|
3549
|
+
isStringField(previousInput) &&
|
|
3550
|
+
previousInput.value !== validValue)
|
|
3551
|
+
errorMessageList.push('Passwords must match.');
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
if (requireLowercaseCharacter && !/[a-z]/g.test(validValue))
|
|
3556
|
+
errorMessageList.push('You must include a lowercase character.');
|
|
3557
|
+
if (requireNumber && !/[0-9]/g.test(validValue))
|
|
3558
|
+
errorMessageList.push('You must include a number.');
|
|
3559
|
+
if (requireSpecialCharacter && !specialCharacterRegex.test(validValue))
|
|
3560
|
+
errorMessageList.push('You must include a special character.');
|
|
3561
|
+
if (requireUppercaseCharacter && !/[A-Z]/g.test(validValue))
|
|
3562
|
+
errorMessageList.push('You must include an uppercase character.');
|
|
3563
|
+
}
|
|
3253
3564
|
break;
|
|
3254
3565
|
case 'tel':
|
|
3255
3566
|
if (!isPhoneNumber(validValue))
|
|
3256
3567
|
errorMessageList.push('This is not a valid phone number.');
|
|
3257
3568
|
break;
|
|
3258
3569
|
}
|
|
3570
|
+
if (props.maxLength && validValue.length > Number(props.maxLength))
|
|
3571
|
+
errorMessageList.push(`This may not have more than ${props.maxLength} characters.`);
|
|
3572
|
+
if (props.minLength && validValue.length < Number(props.minLength))
|
|
3573
|
+
errorMessageList.push(`This must have at least ${props.minLength} characters.`);
|
|
3259
3574
|
if (errorMessageList.length === 0)
|
|
3260
3575
|
return true;
|
|
3261
3576
|
setErrorMessage(errorMessageList.join(' '));
|
|
@@ -3267,19 +3582,15 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3267
3582
|
return;
|
|
3268
3583
|
}
|
|
3269
3584
|
const { currentTarget } = e, { value: newValue } = currentTarget;
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
if (invalidField !== field.invalid)
|
|
3280
|
-
updatedField.invalid = invalidField;
|
|
3281
|
-
return [...otherFields, updatedField];
|
|
3282
|
-
});
|
|
3585
|
+
if (isInFieldset) {
|
|
3586
|
+
fieldsetContextFunctions.updateField(fieldContextID, {
|
|
3587
|
+
value: newValue,
|
|
3588
|
+
invalid: validateField(newValue) === false,
|
|
3589
|
+
});
|
|
3590
|
+
}
|
|
3591
|
+
else {
|
|
3592
|
+
formContextFunctions.updateField(fieldContextID, { value: newValue, invalid: validateField(newValue) === false });
|
|
3593
|
+
}
|
|
3283
3594
|
onChange?.(e);
|
|
3284
3595
|
};
|
|
3285
3596
|
const handleBlur = e => {
|
|
@@ -3290,32 +3601,21 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3290
3601
|
const { currentTarget } = e, { value: newValue } = currentTarget;
|
|
3291
3602
|
if (required)
|
|
3292
3603
|
validateField(newValue);
|
|
3604
|
+
let processedValue = newValue;
|
|
3293
3605
|
switch (type) {
|
|
3294
3606
|
case 'email':
|
|
3295
|
-
|
|
3296
|
-
if (!prevContext)
|
|
3297
|
-
return [];
|
|
3298
|
-
const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
|
|
3299
|
-
if (!field)
|
|
3300
|
-
throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
|
|
3301
|
-
const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
|
|
3302
|
-
const updatedField = { ...field, value: newValue.toLowerCase() };
|
|
3303
|
-
return [...otherFields, updatedField];
|
|
3304
|
-
});
|
|
3607
|
+
processedValue = newValue.toLowerCase();
|
|
3305
3608
|
break;
|
|
3306
3609
|
case 'tel':
|
|
3307
|
-
|
|
3308
|
-
if (!prevContext)
|
|
3309
|
-
return [];
|
|
3310
|
-
const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
|
|
3311
|
-
if (!field)
|
|
3312
|
-
throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
|
|
3313
|
-
const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
|
|
3314
|
-
const updatedField = { ...field, value: formatPhoneNumber(newValue, '1') };
|
|
3315
|
-
return [...otherFields, updatedField];
|
|
3316
|
-
});
|
|
3610
|
+
processedValue = formatPhoneNumber(newValue, options);
|
|
3317
3611
|
break;
|
|
3318
3612
|
}
|
|
3613
|
+
if (isInFieldset) {
|
|
3614
|
+
fieldsetContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3615
|
+
}
|
|
3616
|
+
else {
|
|
3617
|
+
formContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3618
|
+
}
|
|
3319
3619
|
onBlur?.(e);
|
|
3320
3620
|
};
|
|
3321
3621
|
const restFieldProps = fieldProps
|
|
@@ -3335,7 +3635,7 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3335
3635
|
// user-invalid styles
|
|
3336
3636
|
'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))]',
|
|
3337
3637
|
// Custom styles
|
|
3338
|
-
typeof className === 'function' ? className(bag) : className), invalid: invalid, onBlur: handleBlur, onChange: handleChange, placeholder: placeholder, ref: ref, required: required, type: type, value: fieldContext?.value }), fieldContext
|
|
3638
|
+
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 && (jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [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: jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsx(Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
|
|
3339
3639
|
? descriptionProps?.className(bag)
|
|
3340
3640
|
: descriptionProps?.className), children: description }))] }));
|
|
3341
3641
|
}
|
|
@@ -3394,34 +3694,37 @@ function SubmitButton({ children, className, customTheme, error, incomplete, loa
|
|
|
3394
3694
|
}
|
|
3395
3695
|
|
|
3396
3696
|
function Textarea({ className, defaultValue, description, descriptionProps, disabled, fieldProps, invalid = true, label, labelProps, name, onBlur, onChange, placeholder, ref, required = true, value, ...props }) {
|
|
3397
|
-
const [formContext,
|
|
3697
|
+
const [formContext, formContextFunctions] = useFormContext();
|
|
3698
|
+
const [fieldsetContext, fieldsetContextFunctions] = useFieldsetContext();
|
|
3699
|
+
const [errorMessage, setErrorMessage] = useState(undefined);
|
|
3398
3700
|
if (placeholder === '*')
|
|
3399
3701
|
placeholder = name + (required && !label ? '*' : '');
|
|
3400
3702
|
if (label === '*')
|
|
3401
3703
|
label = name;
|
|
3402
|
-
const uniqueID = useId()
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
const initialFieldContext = defineField({
|
|
3406
|
-
type: 'textarea',
|
|
3407
|
-
id: fieldContextID,
|
|
3408
|
-
invalid,
|
|
3409
|
-
name,
|
|
3410
|
-
required,
|
|
3411
|
-
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3412
|
-
});
|
|
3704
|
+
const uniqueID = useId();
|
|
3705
|
+
const fieldContextID = toLowerCase(name, [' ', '_']) + '§' + uniqueID;
|
|
3706
|
+
const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
|
|
3413
3707
|
useEffect(() => {
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3708
|
+
const initialFieldContext = defineField({
|
|
3709
|
+
type: 'textarea',
|
|
3710
|
+
id: fieldContextID,
|
|
3711
|
+
invalid,
|
|
3712
|
+
name,
|
|
3713
|
+
required,
|
|
3714
|
+
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3419
3715
|
});
|
|
3716
|
+
if (isInFieldset) {
|
|
3717
|
+
fieldsetContextFunctions.registerField(initialFieldContext);
|
|
3718
|
+
return () => {
|
|
3719
|
+
fieldsetContextFunctions.removeField(initialFieldContext.id);
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
formContextFunctions.registerField(initialFieldContext);
|
|
3420
3723
|
return () => {
|
|
3421
|
-
|
|
3724
|
+
formContextFunctions.removeField(initialFieldContext.id);
|
|
3422
3725
|
};
|
|
3423
|
-
}, [
|
|
3424
|
-
const fieldContext = formContext?.find(({ id: fieldID }) => fieldID ===
|
|
3726
|
+
}, [isInFieldset]);
|
|
3727
|
+
const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
|
|
3425
3728
|
const validateField = (validValue) => {
|
|
3426
3729
|
const noValue = !validValue || validValue === '';
|
|
3427
3730
|
if (!required && noValue)
|
|
@@ -3432,6 +3735,10 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3432
3735
|
setErrorMessage(errorMessageList.join(' '));
|
|
3433
3736
|
return false;
|
|
3434
3737
|
}
|
|
3738
|
+
if (props.maxLength && validValue.length > Number(props.maxLength))
|
|
3739
|
+
errorMessageList.push(`This may not have more than ${props.maxLength} characters.`);
|
|
3740
|
+
if (props.minLength && validValue.length < Number(props.minLength))
|
|
3741
|
+
errorMessageList.push(`This must have at least ${props.minLength} characters.`);
|
|
3435
3742
|
if (errorMessageList.length === 0)
|
|
3436
3743
|
return true;
|
|
3437
3744
|
setErrorMessage(errorMessageList.join(' '));
|
|
@@ -3442,22 +3749,41 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3442
3749
|
e.preventDefault();
|
|
3443
3750
|
return;
|
|
3444
3751
|
}
|
|
3445
|
-
const { currentTarget } = e
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
}
|
|
3752
|
+
const { currentTarget } = e;
|
|
3753
|
+
const { value: newValue } = currentTarget;
|
|
3754
|
+
if (isInFieldset) {
|
|
3755
|
+
fieldsetContextFunctions.updateField(fieldContextID, {
|
|
3756
|
+
value: newValue,
|
|
3757
|
+
invalid: validateField(newValue) === false,
|
|
3758
|
+
});
|
|
3759
|
+
}
|
|
3760
|
+
else {
|
|
3761
|
+
formContextFunctions.updateField(fieldContextID, {
|
|
3762
|
+
value: newValue,
|
|
3763
|
+
invalid: validateField(newValue) === false,
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3459
3766
|
onChange?.(e);
|
|
3460
3767
|
};
|
|
3768
|
+
const handleBlur = e => {
|
|
3769
|
+
if (disabled) {
|
|
3770
|
+
e.preventDefault();
|
|
3771
|
+
return;
|
|
3772
|
+
}
|
|
3773
|
+
const { currentTarget } = e;
|
|
3774
|
+
const { value: newValue } = currentTarget;
|
|
3775
|
+
if (required)
|
|
3776
|
+
validateField(newValue);
|
|
3777
|
+
// No special processing needed for textarea like email/phone formatting
|
|
3778
|
+
const processedValue = newValue;
|
|
3779
|
+
if (isInFieldset) {
|
|
3780
|
+
fieldsetContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3781
|
+
}
|
|
3782
|
+
else {
|
|
3783
|
+
formContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3784
|
+
}
|
|
3785
|
+
onBlur?.(e);
|
|
3786
|
+
};
|
|
3461
3787
|
const restFieldProps = fieldProps
|
|
3462
3788
|
? Object.fromEntries(Object.entries(fieldProps).filter(([key]) => key !== 'className'))
|
|
3463
3789
|
: {};
|
|
@@ -3475,23 +3801,34 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3475
3801
|
// user-invalid styles
|
|
3476
3802
|
'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))]',
|
|
3477
3803
|
// Custom styles
|
|
3478
|
-
typeof className === 'function' ? className(bag) : className), id: fieldContext?.id, invalid: invalid, onChange: handleChange, placeholder: placeholder, ref: ref, required: required, value: fieldContext?.value }), fieldContext
|
|
3804
|
+
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 && (jsxs(Tooltip, { anchor: 'top-end', arrow: true, portal: true, children: [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: jsx(ExclamationmarkOctagon, { className: 'absolute top-1/2 left-1/2 size-full -translate-x-1/2 -translate-y-1/2 scale-70' }) }), jsx(TooltipPanel, { children: errorMessage })] }))] }), description && (jsx(Description, { ...restDescriptionProps, className: bag => twMerge('text-xs', typeof descriptionProps?.className === 'function'
|
|
3479
3805
|
? descriptionProps?.className(bag)
|
|
3480
3806
|
: descriptionProps?.className), children: description }))] }));
|
|
3481
3807
|
}
|
|
3482
3808
|
|
|
3483
|
-
// import { findComponentByType } from '../../utils'
|
|
3484
3809
|
function FormComponent({ as, children, className, handleSubmit, onError, onSubmit, onSuccess, ...props }) {
|
|
3485
3810
|
const [formContext] = useFormContext(), [formStatus, setFormStatus] = useFormStatus();
|
|
3486
|
-
|
|
3811
|
+
const checkField = useCallback((field) => {
|
|
3812
|
+
if (field.type !== 'array' && field.type !== 'object' && !field.invalid)
|
|
3813
|
+
return true;
|
|
3814
|
+
if (field.type === 'object')
|
|
3815
|
+
return field.fields.every(objectField => checkField(objectField));
|
|
3816
|
+
return false;
|
|
3817
|
+
}, []);
|
|
3818
|
+
const everyFieldIsValid = useCallback(() => {
|
|
3819
|
+
if (!formContext)
|
|
3820
|
+
return false;
|
|
3821
|
+
return formContext.every(field => checkField(field));
|
|
3822
|
+
}, [formContext, checkField]);
|
|
3487
3823
|
useEffect(() => {
|
|
3488
3824
|
if (!formContext)
|
|
3489
3825
|
return;
|
|
3490
|
-
if (formStatus !== 'incomplete' &&
|
|
3826
|
+
if (formStatus !== 'incomplete' &&
|
|
3827
|
+
formContext.find(context => context.type !== 'array' && context.type !== 'object' && context.invalid))
|
|
3491
3828
|
setFormStatus?.('incomplete');
|
|
3492
|
-
if (formStatus !== 'ready' &&
|
|
3829
|
+
if (formStatus !== 'ready' && everyFieldIsValid())
|
|
3493
3830
|
setFormStatus?.('ready');
|
|
3494
|
-
}, [formContext]);
|
|
3831
|
+
}, [formContext, everyFieldIsValid]);
|
|
3495
3832
|
const processSubmit = handleSubmit ||
|
|
3496
3833
|
(async (e) => {
|
|
3497
3834
|
e.preventDefault();
|
|
@@ -3564,9 +3901,16 @@ function ModalTrigger({ as, ...props }) {
|
|
|
3564
3901
|
const Element = as || Button$1;
|
|
3565
3902
|
return jsx(Element, { ...props });
|
|
3566
3903
|
}
|
|
3904
|
+
function ModalTitle(props) {
|
|
3905
|
+
return jsx(DialogTitle, { ...props });
|
|
3906
|
+
}
|
|
3567
3907
|
function ModalDialog(props) {
|
|
3568
3908
|
return jsx("div", { ...props });
|
|
3569
3909
|
}
|
|
3910
|
+
function ModalClose({ as, ...props }) {
|
|
3911
|
+
const Element = as || Button$1;
|
|
3912
|
+
return jsx(Element, { ...props });
|
|
3913
|
+
}
|
|
3570
3914
|
function Modal({ children, className, onClose, onOpen, place = 'bottom' }) {
|
|
3571
3915
|
const [bodyElement, setBodyElement] = useState(null);
|
|
3572
3916
|
useEffect(() => {
|
|
@@ -3732,5 +4076,5 @@ function Time({ children, dateObject, dateTime, day, hours, milliseconds, minute
|
|
|
3732
4076
|
return (jsx("time", { dateTime: dateAndTime, ref: ref, ...props, children: dateDisplay }));
|
|
3733
4077
|
}
|
|
3734
4078
|
|
|
3735
|
-
export { Anchor, Button, Details, DetailsBody, DetailsSummary, DropDown, DropDownButton, DropDownItem, DropDownItems, DropDownSection, DropDownSeparator, Fieldset, Form, Ghost, Heading, Input, Link, Modal, ModalDialog, ModalTrigger, SubmitButton, Textarea, Time, Tooltip, TooltipPanel, TooltipTrigger, createButton, createLink };
|
|
4079
|
+
export { Anchor, Button, Details, DetailsBody, DetailsSummary, DropDown, DropDownButton, DropDownItem, DropDownItems, DropDownSection, DropDownSeparator, Fieldset, Form, Ghost, Heading, Input, Link, Modal, ModalClose, ModalDialog, ModalTitle, ModalTrigger, SubmitButton, Textarea, Time, Tooltip, TooltipPanel, TooltipTrigger, createButton, createLink };
|
|
3736
4080
|
//# sourceMappingURL=index.esm.js.map
|