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.
- package/css/index.css +69 -14
- package/dist/components/button.d.ts +74 -29
- package/dist/components/details.d.ts +5 -7
- package/dist/components/drop-down.d.ts +4 -3
- 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 +3 -4
- 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 +62 -16
- 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 +612 -138
- package/dist/components.esm.js.map +1 -1
- package/dist/components.js +613 -135
- 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 +612 -138
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +613 -135
- 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/components.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
|
|
@@ -346,7 +484,7 @@ const lineLiftClasses = twJoin([
|
|
|
346
484
|
scaleYClasses,
|
|
347
485
|
'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',
|
|
348
486
|
]);
|
|
349
|
-
const fillClasses = twJoin(baseClasses, 'whitespace-nowrap transition-[
|
|
487
|
+
const fillClasses = 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');
|
|
350
488
|
// Define theme-specific fill color transition classes
|
|
351
489
|
const getFillColorTransitionClasses = (theme = 'blue', customTheme) => {
|
|
352
490
|
let fillColorTransitionClasses = twJoin(fillClasses, 'transition-transform after:bg-(--theme-color)');
|
|
@@ -399,7 +537,7 @@ const getFillColorTransitionClasses = (theme = 'blue', customTheme) => {
|
|
|
399
537
|
};
|
|
400
538
|
// Define theme-specific fill center classes
|
|
401
539
|
const getFillCenterClasses = (theme = 'blue', customTheme) => {
|
|
402
|
-
let fillCenterColorClasses = twJoin(fillClasses, 'after:scale-x-50 after:scale-y-[0.25] after:bg-(--theme-color)/0 after:transition-[
|
|
540
|
+
let fillCenterColorClasses = 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)');
|
|
403
541
|
switch (theme) {
|
|
404
542
|
case 'blue':
|
|
405
543
|
fillCenterColorClasses = twJoin(fillCenterColorClasses, 'after:[--theme-color:var(--color-ui-blue)]');
|
|
@@ -458,7 +596,7 @@ const multilineLineCenterClasses = twJoin([multilineXClasses, 'bg-[position:50%_
|
|
|
458
596
|
const multilineLineLiftClasses = twJoin(multilineLineClasses, 'bg-[size:auto_0px] focus-visible:bg-[size:auto_2px] active:bg-[size:auto_2px] pointer-fine:hover:bg-[size:auto_2px]');
|
|
459
597
|
const multilineFillBaseClasses = twJoin(multilineBaseClasses, 'rounded px-0.5 py-0.75 focus-visible:text-zinc-50 active:text-zinc-50 pointer-fine:hover:text-zinc-50');
|
|
460
598
|
const getMultilineFillColorClasses = (theme = 'blue', customTheme) => {
|
|
461
|
-
let multilineFillColorClasses = twJoin(multilineFillBaseClasses, 'from-(--theme-color) to-(--theme-color) transition-[background-
|
|
599
|
+
let multilineFillColorClasses = twJoin(multilineFillBaseClasses, 'from-(--theme-color) to-(--theme-color) transition-[background-size,color]');
|
|
462
600
|
switch (theme) {
|
|
463
601
|
case 'blue':
|
|
464
602
|
multilineFillColorClasses = twJoin(multilineFillColorClasses, '[--theme-color:var(--color-ui-blue)]');
|
|
@@ -507,7 +645,7 @@ const getMultilineFillColorClasses = (theme = 'blue', customTheme) => {
|
|
|
507
645
|
return multilineFillColorClasses;
|
|
508
646
|
};
|
|
509
647
|
const getMultilineFillClasses = (theme = 'blue', customTheme) => {
|
|
510
|
-
let multilineFillColorClasses = twJoin(multilineFillBaseClasses, 'from-(--theme-color)/0 to-(--theme-color)/0 bg-[size:50%_0px] bg-[position:50%_50%] transition-[background-
|
|
648
|
+
let multilineFillColorClasses = 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%]');
|
|
511
649
|
switch (theme) {
|
|
512
650
|
case 'blue':
|
|
513
651
|
multilineFillColorClasses = twJoin(multilineFillColorClasses, '[--theme-color:var(--color-ui-blue)]');
|
|
@@ -647,6 +785,71 @@ function Link({ as, className, customTheme, theme = 'blue', type, ...props }) {
|
|
|
647
785
|
const LinkElement = as || Anchor;
|
|
648
786
|
return jsx(LinkElement, { ...props, className: twMerge(linkClasses, className) });
|
|
649
787
|
}
|
|
788
|
+
/**
|
|
789
|
+
* # createLink
|
|
790
|
+
* Creates an extended Link component with additional theme options.
|
|
791
|
+
*
|
|
792
|
+
* @param config - Configuration object defining new themes and defaults
|
|
793
|
+
* @returns A new Link component with extended theme support
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* ```tsx
|
|
797
|
+
* const MyLink = createLink({
|
|
798
|
+
* as: NextLink,
|
|
799
|
+
* className: 'font-bold',
|
|
800
|
+
* type: 'fill',
|
|
801
|
+
* theme: {
|
|
802
|
+
* primary: {
|
|
803
|
+
* customTheme: {
|
|
804
|
+
* fill: 'after:[--theme-color:var(--color-primary-500)]',
|
|
805
|
+
* multilineFill: '[--theme-color:var(--color-primary-500)]'
|
|
806
|
+
* },
|
|
807
|
+
* className: 'text-white'
|
|
808
|
+
* }
|
|
809
|
+
* }
|
|
810
|
+
* })
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
813
|
+
function createLink(config) {
|
|
814
|
+
return function ExtendedLink({ theme, className, customTheme, type, as, ...props }) {
|
|
815
|
+
const finalType = type !== undefined ? type : config.type, finalTheme = theme !== undefined ? theme : config.defaultTheme;
|
|
816
|
+
const configClassName = config.className;
|
|
817
|
+
const shouldOverrideElement = !Boolean(as) && Boolean(config.as);
|
|
818
|
+
const linkProps = {
|
|
819
|
+
...props,
|
|
820
|
+
className: undefined,
|
|
821
|
+
customTheme: undefined,
|
|
822
|
+
type: finalType,
|
|
823
|
+
};
|
|
824
|
+
if (shouldOverrideElement) {
|
|
825
|
+
linkProps.as = config.as;
|
|
826
|
+
}
|
|
827
|
+
else if (as) {
|
|
828
|
+
linkProps.as = as;
|
|
829
|
+
}
|
|
830
|
+
if (finalTheme && typeof finalTheme === 'string' && config.theme && finalTheme in config.theme) {
|
|
831
|
+
const extendedTheme = config.theme[finalTheme];
|
|
832
|
+
if (customTheme)
|
|
833
|
+
return (jsx(Link, { ...linkProps, theme: 'custom', customTheme: customTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
|
|
834
|
+
let resolvedCustomTheme;
|
|
835
|
+
if (extendedTheme.customTheme.themeColor) {
|
|
836
|
+
const isMultilineType = finalType ? finalType.includes('multiline') : false;
|
|
837
|
+
resolvedCustomTheme = {
|
|
838
|
+
themeColor: isMultilineType
|
|
839
|
+
? extendedTheme.customTheme.themeColor.multilineFill
|
|
840
|
+
: extendedTheme.customTheme.themeColor.fill,
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
resolvedCustomTheme = {
|
|
845
|
+
classes: extendedTheme.customTheme.classes,
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return (jsx(Link, { ...linkProps, theme: 'custom', customTheme: resolvedCustomTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
|
|
849
|
+
}
|
|
850
|
+
return (jsx(Link, { ...linkProps, theme: finalTheme, className: twMerge(configClassName, className), customTheme: customTheme }));
|
|
851
|
+
};
|
|
852
|
+
}
|
|
650
853
|
|
|
651
854
|
/**
|
|
652
855
|
* # Button
|
|
@@ -732,9 +935,76 @@ function Button({ className, customTheme, gradient = false, padding = 'md', roun
|
|
|
732
935
|
themeClasses,
|
|
733
936
|
className,
|
|
734
937
|
]);
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
return jsx(
|
|
938
|
+
if ('href' in props && !props.as && props.href)
|
|
939
|
+
return jsx(Button$1, { ...props, as: Anchor, className: buttonClasses });
|
|
940
|
+
return jsx(Button$1, { ...props, className: buttonClasses });
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* # createButton
|
|
944
|
+
* Creates an extended Button component with additional theme options.
|
|
945
|
+
*
|
|
946
|
+
* @param extendedThemes - Configuration object defining new themes
|
|
947
|
+
* @returns A new Button component with extended theme support
|
|
948
|
+
*
|
|
949
|
+
* @example
|
|
950
|
+
* ```tsx
|
|
951
|
+
* const MyButton = createButton({
|
|
952
|
+
* as: {
|
|
953
|
+
* default: 'div',
|
|
954
|
+
* link: NextLink
|
|
955
|
+
* },
|
|
956
|
+
* className: 'min-w-64',
|
|
957
|
+
* padding: 'sm',
|
|
958
|
+
* rounded: 'full',
|
|
959
|
+
* theme: {
|
|
960
|
+
* primary: {
|
|
961
|
+
* customTheme: { themeColor: '[--theme-color:var(--color-primary-500)]' },
|
|
962
|
+
* className: 'text-white'
|
|
963
|
+
* }
|
|
964
|
+
* }
|
|
965
|
+
* })
|
|
966
|
+
* ```
|
|
967
|
+
*/
|
|
968
|
+
function createButton(config) {
|
|
969
|
+
return function ExtendedButton({ theme, className, customTheme, gradient, padding, rounded, as, ...props }) {
|
|
970
|
+
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;
|
|
971
|
+
const configClassName = config.className;
|
|
972
|
+
const shouldOverrideElement = !Boolean(as) && Boolean(config.as);
|
|
973
|
+
const getOverrideElement = () => {
|
|
974
|
+
if (!config.as)
|
|
975
|
+
return undefined;
|
|
976
|
+
if (typeof config.as === 'function' || typeof config.as === 'string')
|
|
977
|
+
return config.as;
|
|
978
|
+
const hasHref = 'href' in props && props.href;
|
|
979
|
+
if (hasHref && config.as.link) {
|
|
980
|
+
return config.as.link;
|
|
981
|
+
}
|
|
982
|
+
else if (!hasHref && config.as.default) {
|
|
983
|
+
return config.as.default;
|
|
984
|
+
}
|
|
985
|
+
return undefined;
|
|
986
|
+
};
|
|
987
|
+
const buttonProps = {
|
|
988
|
+
...props,
|
|
989
|
+
className: undefined,
|
|
990
|
+
customTheme: undefined,
|
|
991
|
+
gradient: finalGradient,
|
|
992
|
+
padding: finalPadding,
|
|
993
|
+
rounded: finalRounded,
|
|
994
|
+
};
|
|
995
|
+
if (shouldOverrideElement) {
|
|
996
|
+
const overrideElement = getOverrideElement();
|
|
997
|
+
if (overrideElement)
|
|
998
|
+
buttonProps.as = overrideElement;
|
|
999
|
+
}
|
|
1000
|
+
else if (as)
|
|
1001
|
+
buttonProps.as = as;
|
|
1002
|
+
if (finalTheme && typeof finalTheme === 'string' && config.theme && finalTheme in config.theme) {
|
|
1003
|
+
const extendedTheme = config.theme[finalTheme];
|
|
1004
|
+
return (jsx(Button, { ...buttonProps, theme: 'custom', customTheme: customTheme || extendedTheme.customTheme, className: twMerge(configClassName, extendedTheme.className, className) }));
|
|
1005
|
+
}
|
|
1006
|
+
return (jsx(Button, { ...buttonProps, theme: finalTheme, className: twMerge(configClassName, className), customTheme: customTheme }));
|
|
1007
|
+
};
|
|
738
1008
|
}
|
|
739
1009
|
|
|
740
1010
|
function ChevronDown(props) {
|
|
@@ -749,10 +1019,8 @@ function xmark(props) {
|
|
|
749
1019
|
return (jsx("svg", { viewBox: '0 0 64 64', ...props, children: 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' }) }));
|
|
750
1020
|
}
|
|
751
1021
|
|
|
752
|
-
function DetailsSummary({ arrow = true,
|
|
753
|
-
return (
|
|
754
|
-
// @ts-expect-error Button has some extra props
|
|
755
|
-
jsx(DisclosureButton, { ...props, as: as || (Button), className: twMerge('w-full', className, Boolean(arrow) && 'grid grid-cols-[1fr_1rem] gap-2'), role: 'summary', children: bag => (jsxs(Fragment$1, { children: [typeof children === 'function' ? children(bag) : children, arrow &&
|
|
1022
|
+
function DetailsSummary({ arrow = true, children, className, ...props }) {
|
|
1023
|
+
return (jsx(DisclosureButton, { ...props, as: (Button), className: twMerge('w-full', className, Boolean(arrow) && 'grid grid-cols-[1fr_1rem] gap-2'), role: 'summary', children: bag => (jsxs(Fragment$1, { children: [typeof children === 'function' ? children(bag) : children, arrow &&
|
|
756
1024
|
(typeof arrow === 'boolean' ? (jsx(ChevronDown, { className: 'absolute top-1/2 right-3 block w-4 -translate-y-1/2' })) : (arrow))] })) }));
|
|
757
1025
|
}
|
|
758
1026
|
function DetailsBody({ children, className, ...props }) {
|
|
@@ -769,7 +1037,7 @@ function DropDownButton({ arrow = true, as, children, className, ...props }) {
|
|
|
769
1037
|
function DropDownItem({ as, ...props }) {
|
|
770
1038
|
return jsx(MenuItem, { as: as || 'div', ...props });
|
|
771
1039
|
}
|
|
772
|
-
function DropDownItems({ anchor, children, className, style, ...props }) {
|
|
1040
|
+
function DropDownItems({ anchor, children, className, containerClassName, style, ...props }) {
|
|
773
1041
|
const getAnchorProps = () => {
|
|
774
1042
|
let initialAnchor = { gap: '1rem', padding: '1rem', to: 'bottom start' };
|
|
775
1043
|
if (anchor) {
|
|
@@ -781,11 +1049,11 @@ function DropDownItems({ anchor, children, className, style, ...props }) {
|
|
|
781
1049
|
return initialAnchor;
|
|
782
1050
|
};
|
|
783
1051
|
const anchorProps = getAnchorProps();
|
|
784
|
-
return (jsx(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 => (jsx("div", { className: 'overflow-y-
|
|
1052
|
+
return (jsx(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 => (jsx("div", { className: 'overflow-y-scroll', children: 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 }) })) }));
|
|
785
1053
|
}
|
|
786
1054
|
function DropDownSection({ children, label, labelProps, separatorAbove, separatorBelow, ...props }) {
|
|
787
1055
|
const { labelClassName, ...restLabelProps } = { labelClassName: labelProps?.className || '', ...labelProps };
|
|
788
|
-
return (jsx(MenuSection, { ...props, children: sectionBag => (jsxs(Fragment$1, { children: [separatorAbove && jsx(DropDownSeparator, {}), 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, {})] })) }));
|
|
789
1057
|
}
|
|
790
1058
|
function DropDownSeparator({ className, ...props }) {
|
|
791
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) }));
|
|
@@ -845,12 +1113,106 @@ function createFastContext(defaultInitialState) {
|
|
|
845
1113
|
function defineField(fieldDefinition) {
|
|
846
1114
|
return fieldDefinition;
|
|
847
1115
|
}
|
|
848
|
-
|
|
1116
|
+
function isStringField(field) {
|
|
1117
|
+
return Boolean(field.type !== 'object' && field.type !== 'array');
|
|
1118
|
+
}
|
|
1119
|
+
const { Provider: Provider$2, useStore: useStore$2 } = createFastContext([]);
|
|
849
1120
|
function FormContextProvider({ children }) {
|
|
850
|
-
return jsx(Provider$
|
|
1121
|
+
return jsx(Provider$2, { children: children });
|
|
851
1122
|
}
|
|
852
1123
|
function useFormContext() {
|
|
853
|
-
|
|
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 }];
|
|
854
1216
|
}
|
|
855
1217
|
|
|
856
1218
|
const DEFAULT_STATUS = 'incomplete';
|
|
@@ -862,9 +1224,18 @@ function useFormStatus() {
|
|
|
862
1224
|
return useStore(store => store);
|
|
863
1225
|
}
|
|
864
1226
|
|
|
865
|
-
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;
|
|
866
1230
|
const { className: legendClassName, ...restLegendProps } = legendProps || {};
|
|
867
|
-
|
|
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] })) }) }));
|
|
868
1239
|
}
|
|
869
1240
|
|
|
870
1241
|
/**
|
|
@@ -3056,15 +3427,17 @@ function ArrowSvg({ className, ...props }) {
|
|
|
3056
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' })] }));
|
|
3057
3428
|
}
|
|
3058
3429
|
|
|
3059
|
-
|
|
3060
|
-
|
|
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 ? '*' : '');
|
|
3061
3435
|
if (placeholder === '*')
|
|
3062
3436
|
placeholder = name + (required && !label ? '*' : '');
|
|
3063
3437
|
if (label === '*')
|
|
3064
3438
|
label = name;
|
|
3065
3439
|
const uniqueID = useId(), fieldContextID = toLowerCase(name, [, '_']) + '§' + uniqueID;
|
|
3066
|
-
|
|
3067
|
-
invalid = true;
|
|
3440
|
+
const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
|
|
3068
3441
|
const getFieldContextType = () => {
|
|
3069
3442
|
switch (type) {
|
|
3070
3443
|
case 'email':
|
|
@@ -3082,26 +3455,27 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3082
3455
|
}
|
|
3083
3456
|
};
|
|
3084
3457
|
const fieldContextType = getFieldContextType();
|
|
3085
|
-
const
|
|
3086
|
-
type: fieldContextType,
|
|
3087
|
-
id: fieldContextID,
|
|
3088
|
-
invalid,
|
|
3089
|
-
name,
|
|
3090
|
-
required,
|
|
3091
|
-
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3092
|
-
});
|
|
3458
|
+
const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
|
|
3093
3459
|
useEffect(() => {
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3460
|
+
const initialFieldContext = defineField({
|
|
3461
|
+
type: fieldContextType,
|
|
3462
|
+
id: fieldContextID,
|
|
3463
|
+
invalid,
|
|
3464
|
+
name,
|
|
3465
|
+
required,
|
|
3466
|
+
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3099
3467
|
});
|
|
3468
|
+
if (isInFieldset) {
|
|
3469
|
+
fieldsetContextFunctions.registerField(initialFieldContext);
|
|
3470
|
+
return () => {
|
|
3471
|
+
fieldsetContextFunctions.removeField(initialFieldContext.id);
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
formContextFunctions.registerField(initialFieldContext);
|
|
3100
3475
|
return () => {
|
|
3101
|
-
|
|
3476
|
+
formContextFunctions.removeField(initialFieldContext.id);
|
|
3102
3477
|
};
|
|
3103
|
-
}, [
|
|
3104
|
-
const fieldContext = formContext?.find(({ id: fieldID }) => fieldID === initialFieldContext.id) || initialFieldContext;
|
|
3478
|
+
}, [isInFieldset]);
|
|
3105
3479
|
const validateField = (validValue) => {
|
|
3106
3480
|
const noValue = !validValue || validValue === '';
|
|
3107
3481
|
if (!required && noValue)
|
|
@@ -3117,15 +3491,86 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3117
3491
|
if (!isEmail(validValue))
|
|
3118
3492
|
errorMessageList.push('This is not a valid email.');
|
|
3119
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;
|
|
3120
3521
|
case 'number':
|
|
3121
|
-
|
|
3522
|
+
const valueAsNumber = Number(validValue);
|
|
3523
|
+
if (isNaN(valueAsNumber))
|
|
3122
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
|
+
}
|
|
3123
3564
|
break;
|
|
3124
3565
|
case 'tel':
|
|
3125
3566
|
if (!isPhoneNumber(validValue))
|
|
3126
3567
|
errorMessageList.push('This is not a valid phone number.');
|
|
3127
3568
|
break;
|
|
3128
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.`);
|
|
3129
3574
|
if (errorMessageList.length === 0)
|
|
3130
3575
|
return true;
|
|
3131
3576
|
setErrorMessage(errorMessageList.join(' '));
|
|
@@ -3137,19 +3582,15 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3137
3582
|
return;
|
|
3138
3583
|
}
|
|
3139
3584
|
const { currentTarget } = e, { value: newValue } = currentTarget;
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
if (invalidField !== field.invalid)
|
|
3150
|
-
updatedField.invalid = invalidField;
|
|
3151
|
-
return [...otherFields, updatedField];
|
|
3152
|
-
});
|
|
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
|
+
}
|
|
3153
3594
|
onChange?.(e);
|
|
3154
3595
|
};
|
|
3155
3596
|
const handleBlur = e => {
|
|
@@ -3160,32 +3601,21 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3160
3601
|
const { currentTarget } = e, { value: newValue } = currentTarget;
|
|
3161
3602
|
if (required)
|
|
3162
3603
|
validateField(newValue);
|
|
3604
|
+
let processedValue = newValue;
|
|
3163
3605
|
switch (type) {
|
|
3164
3606
|
case 'email':
|
|
3165
|
-
|
|
3166
|
-
if (!prevContext)
|
|
3167
|
-
return [];
|
|
3168
|
-
const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
|
|
3169
|
-
if (!field)
|
|
3170
|
-
throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
|
|
3171
|
-
const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
|
|
3172
|
-
const updatedField = { ...field, value: newValue.toLowerCase() };
|
|
3173
|
-
return [...otherFields, updatedField];
|
|
3174
|
-
});
|
|
3607
|
+
processedValue = newValue.toLowerCase();
|
|
3175
3608
|
break;
|
|
3176
3609
|
case 'tel':
|
|
3177
|
-
|
|
3178
|
-
if (!prevContext)
|
|
3179
|
-
return [];
|
|
3180
|
-
const field = prevContext.find(({ id: fieldID }) => fieldID === initialFieldContext.id);
|
|
3181
|
-
if (!field)
|
|
3182
|
-
throw new Error(`Field with id "${initialFieldContext.id}" not found in form context.`);
|
|
3183
|
-
const otherFields = prevContext.filter(({ id: fieldID }) => fieldID !== initialFieldContext.id);
|
|
3184
|
-
const updatedField = { ...field, value: formatPhoneNumber(newValue, '1') };
|
|
3185
|
-
return [...otherFields, updatedField];
|
|
3186
|
-
});
|
|
3610
|
+
processedValue = formatPhoneNumber(newValue, options);
|
|
3187
3611
|
break;
|
|
3188
3612
|
}
|
|
3613
|
+
if (isInFieldset) {
|
|
3614
|
+
fieldsetContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3615
|
+
}
|
|
3616
|
+
else {
|
|
3617
|
+
formContextFunctions.updateField(fieldContextID, { value: processedValue });
|
|
3618
|
+
}
|
|
3189
3619
|
onBlur?.(e);
|
|
3190
3620
|
};
|
|
3191
3621
|
const restFieldProps = fieldProps
|
|
@@ -3205,12 +3635,12 @@ function Input({ checked, className, defaultValue, description, descriptionProps
|
|
|
3205
3635
|
// user-invalid styles
|
|
3206
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))]',
|
|
3207
3637
|
// Custom styles
|
|
3208
|
-
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'
|
|
3209
3639
|
? descriptionProps?.className(bag)
|
|
3210
3640
|
: descriptionProps?.className), children: description }))] }));
|
|
3211
3641
|
}
|
|
3212
3642
|
|
|
3213
|
-
function SubmitButton({ children, className, customTheme, error, incomplete, loading, success, type
|
|
3643
|
+
function SubmitButton({ children, className, customTheme, error, incomplete, loading, success, type, ...props }) {
|
|
3214
3644
|
const [formStatus] = useFormStatus();
|
|
3215
3645
|
const getFormStatusButtonClasses = () => {
|
|
3216
3646
|
switch (formStatus) {
|
|
@@ -3258,40 +3688,43 @@ function SubmitButton({ children, className, customTheme, error, incomplete, loa
|
|
|
3258
3688
|
}
|
|
3259
3689
|
};
|
|
3260
3690
|
const dataFormState = getDataFormState();
|
|
3261
|
-
return (jsx(Button, { ...props, ...dataFormState,
|
|
3691
|
+
return (jsx(Button, { ...props, ...dataFormState, className: twMerge([formStatusButtonClasses, 'w-full text-white data-loading:text-black', className]), customTheme: {
|
|
3262
3692
|
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),
|
|
3263
|
-
}, theme: 'custom', type: type, children: buttonText }));
|
|
3693
|
+
}, theme: 'custom', type: type || 'submit', children: buttonText }));
|
|
3264
3694
|
}
|
|
3265
3695
|
|
|
3266
3696
|
function Textarea({ className, defaultValue, description, descriptionProps, disabled, fieldProps, invalid = true, label, labelProps, name, onBlur, onChange, placeholder, ref, required = true, value, ...props }) {
|
|
3267
|
-
const [formContext,
|
|
3697
|
+
const [formContext, formContextFunctions] = useFormContext();
|
|
3698
|
+
const [fieldsetContext, fieldsetContextFunctions] = useFieldsetContext();
|
|
3699
|
+
const [errorMessage, setErrorMessage] = useState(undefined);
|
|
3268
3700
|
if (placeholder === '*')
|
|
3269
3701
|
placeholder = name + (required && !label ? '*' : '');
|
|
3270
3702
|
if (label === '*')
|
|
3271
3703
|
label = name;
|
|
3272
|
-
const uniqueID = useId()
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
const initialFieldContext = defineField({
|
|
3276
|
-
type: 'textarea',
|
|
3277
|
-
id: fieldContextID,
|
|
3278
|
-
invalid,
|
|
3279
|
-
name,
|
|
3280
|
-
required,
|
|
3281
|
-
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3282
|
-
});
|
|
3704
|
+
const uniqueID = useId();
|
|
3705
|
+
const fieldContextID = toLowerCase(name, [' ', '_']) + '§' + uniqueID;
|
|
3706
|
+
const isInFieldset = fieldsetContext && !fieldsetContext.decorative;
|
|
3283
3707
|
useEffect(() => {
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3708
|
+
const initialFieldContext = defineField({
|
|
3709
|
+
type: 'textarea',
|
|
3710
|
+
id: fieldContextID,
|
|
3711
|
+
invalid,
|
|
3712
|
+
name,
|
|
3713
|
+
required,
|
|
3714
|
+
value: value ? `${value}` : defaultValue ? `${defaultValue}` : '',
|
|
3289
3715
|
});
|
|
3716
|
+
if (isInFieldset) {
|
|
3717
|
+
fieldsetContextFunctions.registerField(initialFieldContext);
|
|
3718
|
+
return () => {
|
|
3719
|
+
fieldsetContextFunctions.removeField(initialFieldContext.id);
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
formContextFunctions.registerField(initialFieldContext);
|
|
3290
3723
|
return () => {
|
|
3291
|
-
|
|
3724
|
+
formContextFunctions.removeField(initialFieldContext.id);
|
|
3292
3725
|
};
|
|
3293
|
-
}, [
|
|
3294
|
-
const fieldContext = formContext?.find(({ id: fieldID }) => fieldID ===
|
|
3726
|
+
}, [isInFieldset]);
|
|
3727
|
+
const fieldContext = (isInFieldset ? fieldsetContext.fieldList : formContext)?.find(({ id: fieldID }) => fieldID === fieldContextID);
|
|
3295
3728
|
const validateField = (validValue) => {
|
|
3296
3729
|
const noValue = !validValue || validValue === '';
|
|
3297
3730
|
if (!required && noValue)
|
|
@@ -3302,6 +3735,10 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3302
3735
|
setErrorMessage(errorMessageList.join(' '));
|
|
3303
3736
|
return false;
|
|
3304
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.`);
|
|
3305
3742
|
if (errorMessageList.length === 0)
|
|
3306
3743
|
return true;
|
|
3307
3744
|
setErrorMessage(errorMessageList.join(' '));
|
|
@@ -3312,22 +3749,41 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3312
3749
|
e.preventDefault();
|
|
3313
3750
|
return;
|
|
3314
3751
|
}
|
|
3315
|
-
const { currentTarget } = e
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
}
|
|
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
|
+
}
|
|
3329
3766
|
onChange?.(e);
|
|
3330
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
|
+
};
|
|
3331
3787
|
const restFieldProps = fieldProps
|
|
3332
3788
|
? Object.fromEntries(Object.entries(fieldProps).filter(([key]) => key !== 'className'))
|
|
3333
3789
|
: {};
|
|
@@ -3345,23 +3801,34 @@ function Textarea({ className, defaultValue, description, descriptionProps, disa
|
|
|
3345
3801
|
// user-invalid styles
|
|
3346
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))]',
|
|
3347
3803
|
// Custom styles
|
|
3348
|
-
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'
|
|
3349
3805
|
? descriptionProps?.className(bag)
|
|
3350
3806
|
: descriptionProps?.className), children: description }))] }));
|
|
3351
3807
|
}
|
|
3352
3808
|
|
|
3353
|
-
// import { findComponentByType } from '../../utils'
|
|
3354
3809
|
function FormComponent({ as, children, className, handleSubmit, onError, onSubmit, onSuccess, ...props }) {
|
|
3355
3810
|
const [formContext] = useFormContext(), [formStatus, setFormStatus] = useFormStatus();
|
|
3356
|
-
|
|
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]);
|
|
3357
3823
|
useEffect(() => {
|
|
3358
3824
|
if (!formContext)
|
|
3359
3825
|
return;
|
|
3360
|
-
if (formStatus !== 'incomplete' &&
|
|
3826
|
+
if (formStatus !== 'incomplete' &&
|
|
3827
|
+
formContext.find(context => context.type !== 'array' && context.type !== 'object' && context.invalid))
|
|
3361
3828
|
setFormStatus?.('incomplete');
|
|
3362
|
-
if (formStatus !== 'ready' &&
|
|
3829
|
+
if (formStatus !== 'ready' && everyFieldIsValid())
|
|
3363
3830
|
setFormStatus?.('ready');
|
|
3364
|
-
}, [formContext]);
|
|
3831
|
+
}, [formContext, everyFieldIsValid]);
|
|
3365
3832
|
const processSubmit = handleSubmit ||
|
|
3366
3833
|
(async (e) => {
|
|
3367
3834
|
e.preventDefault();
|
|
@@ -3434,9 +3901,16 @@ function ModalTrigger({ as, ...props }) {
|
|
|
3434
3901
|
const Element = as || Button$1;
|
|
3435
3902
|
return jsx(Element, { ...props });
|
|
3436
3903
|
}
|
|
3904
|
+
function ModalTitle(props) {
|
|
3905
|
+
return jsx(DialogTitle, { ...props });
|
|
3906
|
+
}
|
|
3437
3907
|
function ModalDialog(props) {
|
|
3438
3908
|
return jsx("div", { ...props });
|
|
3439
3909
|
}
|
|
3910
|
+
function ModalClose({ as, ...props }) {
|
|
3911
|
+
const Element = as || Button$1;
|
|
3912
|
+
return jsx(Element, { ...props });
|
|
3913
|
+
}
|
|
3440
3914
|
function Modal({ children, className, onClose, onOpen, place = 'bottom' }) {
|
|
3441
3915
|
const [bodyElement, setBodyElement] = useState(null);
|
|
3442
3916
|
useEffect(() => {
|
|
@@ -3602,5 +4076,5 @@ function Time({ children, dateObject, dateTime, day, hours, milliseconds, minute
|
|
|
3602
4076
|
return (jsx("time", { dateTime: dateAndTime, ref: ref, ...props, children: dateDisplay }));
|
|
3603
4077
|
}
|
|
3604
4078
|
|
|
3605
|
-
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 };
|
|
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 };
|
|
3606
4080
|
//# sourceMappingURL=components.esm.js.map
|