@douglasneuroinformatics/libui 4.0.2 → 4.1.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.
@@ -25,7 +25,7 @@ import { FormDataType, FormContent, PartialNullableFormDataType } from '@douglas
25
25
  import { z } from 'zod';
26
26
  import * as _radix_ui_react_hover_card from '@radix-ui/react-hover-card';
27
27
  import * as LabelPrimitive from '@radix-ui/react-label';
28
- import { L as Language } from './types-CMuti1SJ.js';
28
+ import { L as Language } from './types-Dm7os_cB.js';
29
29
  import * as _radix_ui_react_menubar from '@radix-ui/react-menubar';
30
30
  import { MenubarMenuProps } from '@radix-ui/react-menubar';
31
31
  import * as _radix_ui_react_popover from '@radix-ui/react-popover';
@@ -1283,8 +1283,18 @@ type FormProps<TSchema extends z.ZodType<FormDataType>, TData extends z.TypeOf<T
1283
1283
  left?: React.ReactNode;
1284
1284
  right?: React.ReactNode;
1285
1285
  };
1286
+ beforeSubmit?: (data: NoInfer<TData>) => Promisable<{
1287
+ errorMessage: string;
1288
+ success: false;
1289
+ } | {
1290
+ success: true;
1291
+ }>;
1286
1292
  className?: string;
1287
1293
  content: FormContent<TData>;
1294
+ customStyles?: {
1295
+ resetBtn?: string;
1296
+ submitBtn?: string;
1297
+ };
1288
1298
  fieldsFooter?: React.ReactNode;
1289
1299
  id?: string;
1290
1300
  initialValues?: PartialNullableFormDataType<NoInfer<TData>>;
@@ -1298,7 +1308,7 @@ type FormProps<TSchema extends z.ZodType<FormDataType>, TData extends z.TypeOf<T
1298
1308
  suspendWhileSubmitting?: boolean;
1299
1309
  validationSchema: z.ZodType<TData>;
1300
1310
  };
1301
- declare const Form: <TSchema extends z.ZodType<FormDataType>, TData extends z.TypeOf<TSchema> = z.TypeOf<TSchema>>({ additionalButtons, className, content, fieldsFooter, id, initialValues, onError, onSubmit, preventResetValuesOnReset, readOnly, resetBtn, revalidateOnBlur, submitBtnLabel, suspendWhileSubmitting, validationSchema, ...props }: FormProps<TSchema, TData>) => react_jsx_runtime.JSX.Element;
1311
+ declare const Form: <TSchema extends z.ZodType<FormDataType>, TData extends z.TypeOf<TSchema> = z.TypeOf<TSchema>>({ additionalButtons, beforeSubmit, className, content, customStyles, fieldsFooter, id, initialValues, onError, onSubmit, preventResetValuesOnReset, readOnly, resetBtn, revalidateOnBlur, submitBtnLabel, suspendWhileSubmitting, validationSchema, ...props }: FormProps<TSchema, TData>) => react_jsx_runtime.JSX.Element;
1302
1312
 
1303
1313
  type HeadingProps = {
1304
1314
  children: string;
@@ -327,7 +327,7 @@ var BUTTON_ICON_SIZE = {
327
327
  sm: 14
328
328
  };
329
329
  var buttonVariants = cva(
330
- "flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
330
+ "flex items-center justify-center whitespace-nowrap cursor-pointer rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
331
331
  {
332
332
  defaultVariants: {
333
333
  size: "md",
@@ -1861,7 +1861,7 @@ var DialogOverlay = forwardRef63(function DialogOverlay2({ className, ...props }
1861
1861
  Overlay2,
1862
1862
  {
1863
1863
  className: cn(
1864
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1864
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 duration-200",
1865
1865
  className
1866
1866
  ),
1867
1867
  ref,
@@ -1879,7 +1879,7 @@ var DialogContent = forwardRef64(function DialogContent2({ children, className,
1879
1879
  Content6,
1880
1880
  {
1881
1881
  className: cn(
1882
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
1882
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
1883
1883
  className
1884
1884
  ),
1885
1885
  ref,
@@ -2018,25 +2018,32 @@ var ErrorFallback = ({ error }) => {
2018
2018
  useEffect2(() => {
2019
2019
  console.error(error);
2020
2020
  }, [error]);
2021
- return /* @__PURE__ */ jsxs24("div", { className: "flex min-h-screen flex-col items-center justify-center gap-1 p-3 text-center", children: [
2022
- /* @__PURE__ */ jsx90("h1", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Unexpected Error" }),
2023
- /* @__PURE__ */ jsx90("h3", { className: "text-3xl font-extrabold tracking-tight sm:text-4xl md:text-5xl", children: "Something Went Wrong" }),
2024
- /* @__PURE__ */ jsx90("p", { className: "mt-2 max-w-prose text-sm text-muted-foreground sm:text-base", children: "We apologize for the inconvenience. Please contact us for further assistance." }),
2025
- /* @__PURE__ */ jsx90("div", { className: "mt-6", children: /* @__PURE__ */ jsxs24(
2026
- "button",
2027
- {
2028
- className: "text-sky-800 underline-offset-4 hover:text-sky-700 hover:underline dark:text-sky-200 dark:hover:text-sky-300",
2029
- type: "button",
2030
- onClick: () => {
2031
- window.location.assign(window.location.origin);
2032
- },
2033
- children: [
2034
- "Reload Page",
2035
- /* @__PURE__ */ jsx90("span", { "aria-hidden": "true", children: " \u2192" })
2036
- ]
2037
- }
2038
- ) })
2039
- ] });
2021
+ return /* @__PURE__ */ jsxs24(
2022
+ "div",
2023
+ {
2024
+ className: "flex min-h-screen flex-col items-center justify-center gap-1 p-3 text-center",
2025
+ "data-testid": "error-fallback",
2026
+ children: [
2027
+ /* @__PURE__ */ jsx90("h1", { className: "text-muted-foreground text-sm font-semibold tracking-wide uppercase", children: "Unexpected Error" }),
2028
+ /* @__PURE__ */ jsx90("h3", { className: "text-3xl font-extrabold tracking-tight sm:text-4xl md:text-5xl", children: "Something Went Wrong" }),
2029
+ /* @__PURE__ */ jsx90("p", { className: "text-muted-foreground mt-2 max-w-prose text-sm sm:text-base", children: "We apologize for the inconvenience. Please contact us for further assistance." }),
2030
+ /* @__PURE__ */ jsx90("div", { className: "mt-6", children: /* @__PURE__ */ jsxs24(
2031
+ "button",
2032
+ {
2033
+ className: "text-sky-800 underline-offset-4 hover:text-sky-700 hover:underline dark:text-sky-200 dark:hover:text-sky-300",
2034
+ type: "button",
2035
+ onClick: () => {
2036
+ window.location.assign(window.location.origin);
2037
+ },
2038
+ children: [
2039
+ "Reload Page",
2040
+ /* @__PURE__ */ jsx90("span", { "aria-hidden": "true", children: " \u2192" })
2041
+ ]
2042
+ }
2043
+ ) })
2044
+ ]
2045
+ }
2046
+ );
2040
2047
  };
2041
2048
 
2042
2049
  // src/components/ErrorBoundary/ErrorBoundary.tsx
@@ -2133,10 +2140,10 @@ var Heading = ({ children, className, variant }) => {
2133
2140
  import "react";
2134
2141
  import { CircleAlertIcon } from "lucide-react";
2135
2142
  import { jsx as jsx94, jsxs as jsxs26 } from "react/jsx-runtime";
2136
- var ErrorMessage = ({ error }) => {
2137
- return error ? /* @__PURE__ */ jsx94("div", { className: "space-y-1.5", children: error.map((message) => /* @__PURE__ */ jsxs26("div", { className: "flex w-full items-center text-sm font-medium text-destructive", children: [
2143
+ var ErrorMessage = ({ className, error }) => {
2144
+ return error ? /* @__PURE__ */ jsx94("div", { className: "space-y-1.5", children: error.map((message) => /* @__PURE__ */ jsxs26("div", { className: cn("text-destructive flex w-full items-center text-sm font-medium", className), children: [
2138
2145
  /* @__PURE__ */ jsx94(CircleAlertIcon, { className: "mr-1", style: { strokeWidth: "2px" } }),
2139
- /* @__PURE__ */ jsx94("span", { children: message })
2146
+ /* @__PURE__ */ jsx94("span", { "data-testid": "error-message-text", children: message })
2140
2147
  ] }, message)) ?? null }) : null;
2141
2148
  };
2142
2149
 
@@ -2160,7 +2167,7 @@ import { useEffect as useEffect4, useRef as useRef3 } from "react";
2160
2167
  import { match as match2 } from "ts-pattern";
2161
2168
 
2162
2169
  // src/components/Form/NumberField/NumberFieldInput.tsx
2163
- import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
2170
+ import { useEffect as useEffect3, useId as useId2, useRef as useRef2, useState as useState4 } from "react";
2164
2171
  import { parseNumber } from "@douglasneuroinformatics/libjs";
2165
2172
 
2166
2173
  // src/components/Input/Input.tsx
@@ -2271,6 +2278,7 @@ var NumberFieldInput = ({
2271
2278
  setValue,
2272
2279
  value
2273
2280
  }) => {
2281
+ const id = useId2();
2274
2282
  const [inputValue, setInputValue] = useState4(value?.toString() ?? "");
2275
2283
  const valueRef = useRef2(value);
2276
2284
  const parseInputValue = (value2) => {
@@ -2304,13 +2312,14 @@ var NumberFieldInput = ({
2304
2312
  }, [value]);
2305
2313
  return /* @__PURE__ */ jsxs28(FieldGroup, { name, children: [
2306
2314
  /* @__PURE__ */ jsxs28(FieldGroup.Row, { children: [
2307
- /* @__PURE__ */ jsx101(Label3, { children: label }),
2315
+ /* @__PURE__ */ jsx101(Label3, { htmlFor: id, children: label }),
2308
2316
  /* @__PURE__ */ jsx101(FieldGroup.Description, { description })
2309
2317
  ] }),
2310
2318
  /* @__PURE__ */ jsx101(
2311
2319
  Input,
2312
2320
  {
2313
2321
  disabled: disabled || readOnly,
2322
+ id,
2314
2323
  max,
2315
2324
  min,
2316
2325
  name,
@@ -3467,8 +3476,10 @@ function getInitialValues(values) {
3467
3476
  import { jsx as jsx136, jsxs as jsxs47 } from "react/jsx-runtime";
3468
3477
  var Form = ({
3469
3478
  additionalButtons,
3479
+ beforeSubmit,
3470
3480
  className,
3471
3481
  content,
3482
+ customStyles,
3472
3483
  fieldsFooter,
3473
3484
  id,
3474
3485
  initialValues,
@@ -3492,6 +3503,7 @@ var Form = ({
3492
3503
  const [isSubmitting, setIsSubmitting] = useState8(false);
3493
3504
  const handleError = (error) => {
3494
3505
  const fieldErrors = {};
3506
+ const rootErrors2 = [];
3495
3507
  for (const issue of error.issues) {
3496
3508
  if (issue.path.length > 0) {
3497
3509
  const current = get(fieldErrors, issue.path);
@@ -3501,10 +3513,11 @@ var Form = ({
3501
3513
  set(fieldErrors, issue.path, [issue.message]);
3502
3514
  }
3503
3515
  } else {
3504
- setRootErrors((prevErrors) => [...prevErrors, issue.message]);
3516
+ rootErrors2.push(issue.message);
3505
3517
  }
3506
3518
  }
3507
3519
  setErrors(fieldErrors);
3520
+ setRootErrors(rootErrors2);
3508
3521
  if (onError) {
3509
3522
  onError(error);
3510
3523
  }
@@ -3524,6 +3537,14 @@ var Form = ({
3524
3537
  handleError(result.error);
3525
3538
  return;
3526
3539
  }
3540
+ if (beforeSubmit) {
3541
+ const beforeSubmitResult = await beforeSubmit(result.data);
3542
+ if (!beforeSubmitResult.success) {
3543
+ setErrors({});
3544
+ setRootErrors([beforeSubmitResult.errorMessage]);
3545
+ return;
3546
+ }
3547
+ }
3527
3548
  try {
3528
3549
  setIsSubmitting(true);
3529
3550
  await Promise.all([
@@ -3567,7 +3588,7 @@ var Form = ({
3567
3588
  return /* @__PURE__ */ jsxs47("div", { className: "flex flex-col space-y-6 [&:not(:first-child)]:pt-8", children: [
3568
3589
  /* @__PURE__ */ jsxs47("div", { className: "space-y-1", children: [
3569
3590
  fieldGroup.title && /* @__PURE__ */ jsx136(Heading, { className: "text-base", variant: "h4", children: fieldGroup.title }),
3570
- fieldGroup.description && /* @__PURE__ */ jsx136("p", { className: "text-sm italic leading-tight text-muted-foreground", children: fieldGroup.description })
3591
+ fieldGroup.description && /* @__PURE__ */ jsx136("p", { className: "text-muted-foreground text-sm leading-tight italic", children: fieldGroup.description })
3571
3592
  ] }),
3572
3593
  /* @__PURE__ */ jsx136(
3573
3594
  FieldsComponent,
@@ -3592,6 +3613,7 @@ var Form = ({
3592
3613
  values
3593
3614
  }
3594
3615
  ),
3616
+ Boolean(rootErrors.length) && /* @__PURE__ */ jsx136(ErrorMessage, { className: "-mt-3", error: rootErrors }),
3595
3617
  fieldsFooter,
3596
3618
  /* @__PURE__ */ jsxs47("div", { className: "flex w-full gap-3", children: [
3597
3619
  additionalButtons?.left,
@@ -3599,7 +3621,7 @@ var Form = ({
3599
3621
  Button,
3600
3622
  {
3601
3623
  "aria-label": "Submit",
3602
- className: "flex w-full items-center justify-center gap-2",
3624
+ className: cn("flex w-full items-center justify-center gap-2", customStyles?.submitBtn),
3603
3625
  disabled: readOnly || isSuspended,
3604
3626
  type: "submit",
3605
3627
  variant: "primary",
@@ -3628,7 +3650,7 @@ var Form = ({
3628
3650
  Button,
3629
3651
  {
3630
3652
  "aria-label": "Reset",
3631
- className: "block w-full",
3653
+ className: cn("block w-full", customStyles?.resetBtn),
3632
3654
  disabled: readOnly,
3633
3655
  type: "button",
3634
3656
  variant: "secondary",
@@ -3637,8 +3659,7 @@ var Form = ({
3637
3659
  }
3638
3660
  ),
3639
3661
  additionalButtons?.right
3640
- ] }),
3641
- Boolean(rootErrors.length) && /* @__PURE__ */ jsx136(ErrorMessage, { error: rootErrors })
3662
+ ] })
3642
3663
  ]
3643
3664
  }
3644
3665
  );