jcicl 1.0.28 → 1.0.30

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.
@@ -0,0 +1,32 @@
1
+ import { FormContextOptions, FormContextResult, InputChangeExtension } from './types';
2
+ /**
3
+ * Factory function to create a typed form context with shared functionality.
4
+ * Fixes the stale closure bug by computing all values inside the functional update.
5
+ *
6
+ * @template T - The type of form data this context will manage
7
+ * @param options - Configuration options for the form context
8
+ * @returns Object containing the Context and Provider
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * interface MyFormData {
13
+ * name: string;
14
+ * email: string;
15
+ * [key: string]: unknown; // Required for FormFields compatibility
16
+ * }
17
+ *
18
+ * const { Context: MyFormContext, Provider: MyFormProvider } = createFormContext<MyFormData>({
19
+ * name: 'MyForm',
20
+ * defaultValues: { name: '', email: '' },
21
+ * });
22
+ * ```
23
+ */
24
+ export declare function createFormContext<T extends Record<string, unknown>>(options: FormContextOptions<T>): FormContextResult<T>;
25
+ /**
26
+ * Creates a DOB → Age autofill extension.
27
+ * When dateOfBirth field changes, automatically calculates and sets age.
28
+ *
29
+ * @param dobKey - The form field key for date of birth (default: 'dateOfBirth')
30
+ * @param ageKey - The form field key for age (default: 'age')
31
+ */
32
+ export declare function createDobToAgeExtension<T extends Record<string, unknown>>(dobKey?: keyof T & string, ageKey?: keyof T & string): InputChangeExtension<T>;
@@ -0,0 +1,74 @@
1
+ import { jsx as N } from "react/jsx-runtime";
2
+ import { createContext as w, useState as E, useEffect as P, useMemo as $ } from "react";
3
+ import { formatPhoneNumber as C } from "../utils.js";
4
+ function I(b) {
5
+ const { name: n, defaultValues: s = {}, onInputChangeExtensions: u = [], submit: m, debug: a = !1 } = b, i = w({
6
+ onFormInputChange: () => console.error(`Failed to update form input. ${n}FormContext not initialized`),
7
+ formData: s,
8
+ setFormData: () => console.error(`Failed to set form data. ${n}FormContext not initialized`),
9
+ resetForm: () => console.error(`Failed to reset form. ${n}FormContext not initialized`)
10
+ });
11
+ return { Context: i, Provider: ({ children: x }) => {
12
+ const [e, r] = E(s), d = (g, t, c) => {
13
+ r((l) => {
14
+ let o;
15
+ if (c === "checkbox")
16
+ o = t.target.checked;
17
+ else if (c === "multiDropdown") {
18
+ const h = t.target.value;
19
+ Array.isArray(h) ? o = h.map((D) => D.value) : o = [];
20
+ } else if (c === "phone") {
21
+ const [, h] = C(t.target.value);
22
+ o = h;
23
+ } else
24
+ o = t.target.value;
25
+ return o === "undefined" && (o = void 0), { ...l, [g]: o };
26
+ });
27
+ let p;
28
+ if (c === "checkbox")
29
+ p = t.target.checked;
30
+ else if (c === "phone") {
31
+ const [, l] = C(t.target.value);
32
+ p = l;
33
+ } else
34
+ p = t.target.value === "undefined" ? void 0 : t.target.value;
35
+ u.forEach((l) => {
36
+ l(g, p, r);
37
+ });
38
+ }, v = () => r(s);
39
+ P(() => {
40
+ a && console.log(`[${n}Form] Updated form data:`, e);
41
+ }, [e]);
42
+ const f = $(
43
+ () => ({
44
+ onFormInputChange: d,
45
+ formData: e,
46
+ setFormData: r,
47
+ resetForm: v,
48
+ ...m ? { submit: () => m(e) } : {}
49
+ }),
50
+ [e]
51
+ );
52
+ return /* @__PURE__ */ N(i.Provider, { value: f, children: x });
53
+ } };
54
+ }
55
+ function M(b = "dateOfBirth", n = "age") {
56
+ return (s, u, m) => {
57
+ if (s === b && u && typeof u == "string") {
58
+ const a = u.split("-");
59
+ if (a.length !== 3) return;
60
+ const i = Number(a[0]), F = Number(a[1]), x = Number(a[2]), e = /* @__PURE__ */ new Date(), r = e.getFullYear(), d = e.getMonth() + 1, v = e.getDate();
61
+ if (i > r || i < 1900)
62
+ return;
63
+ let f = r - i;
64
+ (F > d || F === d && x > v) && (f -= 1), m((g) => ({
65
+ ...g,
66
+ [n]: String(f)
67
+ }));
68
+ }
69
+ };
70
+ }
71
+ export {
72
+ M as createDobToAgeExtension,
73
+ I as createFormContext
74
+ };
@@ -0,0 +1,2 @@
1
+ export { createFormContext, createDobToAgeExtension } from './createFormContext';
2
+ export type { FormContextProps, FormContextOptions, FormContextResult, InputChangeExtension, } from './types';
@@ -0,0 +1,5 @@
1
+ import { createDobToAgeExtension as t, createFormContext as r } from "./createFormContext.js";
2
+ export {
3
+ t as createDobToAgeExtension,
4
+ r as createFormContext
5
+ };
@@ -0,0 +1,53 @@
1
+ import { PropsWithChildren } from 'react';
2
+ /**
3
+ * Base form context properties that all form contexts share.
4
+ * @template T - The type of form data this context manages
5
+ */
6
+ export interface FormContextProps<T extends Record<string, unknown>> {
7
+ /**
8
+ * Handler for form input changes - supports text, checkbox, multiDropdown, and phone types.
9
+ * Accepts any string key to maintain compatibility with FormFields component,
10
+ * but typed form data provides compile-time safety for direct property access.
11
+ */
12
+ onFormInputChange: (key: string, e: React.ChangeEvent<HTMLInputElement>, type?: string) => void;
13
+ /** Current form data state */
14
+ formData: T;
15
+ /** Direct setter for form data (use sparingly - prefer onFormInputChange) */
16
+ setFormData: React.Dispatch<React.SetStateAction<T>>;
17
+ /** Reset form to initial/empty state */
18
+ resetForm: () => void;
19
+ /** Optional submit handler (if configured in factory) */
20
+ submit?: () => void;
21
+ }
22
+ /**
23
+ * Extension function called after each input change.
24
+ * Use for side effects like auto-filling related fields (e.g., DOB → Age).
25
+ * @template T - The type of form data
26
+ */
27
+ export type InputChangeExtension<T extends Record<string, unknown>> = (key: string, value: unknown, setFormData: React.Dispatch<React.SetStateAction<T>>) => void;
28
+ /**
29
+ * Configuration options for creating a form context.
30
+ * @template T - The type of form data this context will manage
31
+ */
32
+ export interface FormContextOptions<T extends Record<string, unknown>> {
33
+ /** Name for the context (used in error messages) */
34
+ name: string;
35
+ /** Default/initial values for the form */
36
+ defaultValues?: Partial<T>;
37
+ /** Extensions called after each input change (e.g., DOB→Age autofill) */
38
+ onInputChangeExtensions?: InputChangeExtension<T>[];
39
+ /** Custom submit function */
40
+ submit?: (formData: T) => void;
41
+ /** Enable debug logging of form data changes */
42
+ debug?: boolean;
43
+ }
44
+ /**
45
+ * Return type from createFormContext factory.
46
+ * @template T - The type of form data
47
+ */
48
+ export interface FormContextResult<T extends Record<string, unknown>> {
49
+ /** The React Context object */
50
+ Context: React.Context<FormContextProps<T>>;
51
+ /** Provider component that wraps children with form state */
52
+ Provider: React.FC<PropsWithChildren>;
53
+ }
@@ -0,0 +1 @@
1
+
@@ -1,57 +1,57 @@
1
- import { jsxs as g, Fragment as j, jsx as m } from "react/jsx-runtime";
2
- import { FormInput as w } from "../FormInput/FormInput.js";
3
- import { FormSectionTitle as C, FormFieldGrid as S, FormFieldCheckboxContainer as v } from "../FormComponents/FormComponents.js";
4
- import z from "../theme.js";
5
- import { u as G } from "../.chunks/useMediaQuery.js";
6
- const $ = ({
7
- title: p,
8
- fields: s,
9
- columns: x = G(`(max-width: ${z.screenSizes.tablet})`) ? 2 : 3,
10
- columnsOverride: D,
11
- gap: f = 21,
12
- noLabel: F = !1,
13
- checkboxContainer: c = !1,
14
- formDefaults: n,
15
- onChange: h
16
- }) => /* @__PURE__ */ g(j, { children: [
17
- p && /* @__PURE__ */ m(C, { children: p }),
18
- !c && /* @__PURE__ */ m(S, { columns: x, columnsOverride: D, gap: f, children: s.map(({ key: r, label: t, type: o, limit: l, options: u, defaultValue: i, ...e }) => {
19
- const d = n[r] ? n[r] : i || (o === "multiDropdown" ? [] : "");
20
- return /* @__PURE__ */ m(
21
- w,
1
+ import { jsxs as f, Fragment as D, jsx as n } from "react/jsx-runtime";
2
+ import { FormInput as p } from "../FormInput/FormInput.js";
3
+ import { FormSectionTitle as g, FormFieldGrid as j, FormFieldCheckboxContainer as C } from "../FormComponents/FormComponents.js";
4
+ import S from "../theme.js";
5
+ import { u as z } from "../.chunks/useMediaQuery.js";
6
+ const V = ({
7
+ title: a,
8
+ fields: h,
9
+ columns: v = z(`(max-width: ${S.screenSizes.tablet})`) ? 2 : 3,
10
+ columnsOverride: w,
11
+ gap: b = 21,
12
+ noLabel: s = !1,
13
+ checkboxContainer: F = !1,
14
+ formDefaults: c,
15
+ onChange: x
16
+ }) => /* @__PURE__ */ f(D, { children: [
17
+ a && /* @__PURE__ */ n(g, { children: a }),
18
+ !F && /* @__PURE__ */ n(j, { columns: v, columnsOverride: w, gap: b, children: h.map(({ key: r, label: e, type: o, limit: d, options: l, defaultValue: i, ...u }) => {
19
+ const m = c[r] !== void 0 ? c[r] : i !== void 0 ? i : o === "multiDropdown" ? [] : o === "checkbox" ? !1 : "";
20
+ return /* @__PURE__ */ n(
21
+ p,
22
22
  {
23
- onChange: (a) => h(r, a, o),
24
- label: t,
23
+ onChange: (t) => x(r, t, o),
24
+ label: e,
25
25
  type: o,
26
- limit: l,
27
- value: d,
28
- noLabel: F,
29
- options: u,
26
+ limit: d,
27
+ ...o === "checkbox" ? { checked: !!m } : { value: m },
28
+ noLabel: s,
29
+ options: l,
30
30
  multiple: o === "multiDropdown",
31
- ...e
31
+ ...u
32
32
  },
33
33
  r
34
34
  );
35
35
  }) }),
36
- c && /* @__PURE__ */ m(v, { children: s.map(({ key: r, label: t, type: o, limit: l, options: u, defaultValue: i, ...e }) => {
37
- const d = n[r] ? n[r] : i || (o === "multiDropdown" ? [] : "");
38
- return /* @__PURE__ */ m(
39
- w,
36
+ F && /* @__PURE__ */ n(C, { children: h.map(({ key: r, label: e, type: o, limit: d, options: l, defaultValue: i, ...u }) => {
37
+ const m = c[r] !== void 0 ? c[r] : i !== void 0 ? i : o === "multiDropdown" ? [] : o === "checkbox" ? !1 : "";
38
+ return /* @__PURE__ */ n(
39
+ p,
40
40
  {
41
- onChange: (a) => h(r, a, o),
42
- label: t,
41
+ onChange: (t) => x(r, t, o),
42
+ label: e,
43
43
  type: o,
44
- limit: l,
45
- defaultValue: d,
46
- noLabel: F,
47
- options: u,
44
+ limit: d,
45
+ ...o === "checkbox" ? { checked: !!m } : { defaultValue: m },
46
+ noLabel: s,
47
+ options: l,
48
48
  multiple: o === "multiDropdown",
49
- ...e
49
+ ...u
50
50
  },
51
51
  r
52
52
  );
53
53
  }) })
54
54
  ] });
55
55
  export {
56
- $ as default
56
+ V as default
57
57
  };
@@ -2694,8 +2694,8 @@ const Et = ["colon", "borderFocusColor", "minWidth", "noLabel"], zt = Yo("div",
2694
2694
  {
2695
2695
  options: a,
2696
2696
  onChange: (m, w) => (
2697
- // @ts-ignore
2698
- h && h({ ...m, target: { ...m.target, value: `${w == null ? void 0 : w.value}` || "" } })
2697
+ // @ts-ignore - Fix: Use nullish coalescing to avoid "undefined" string
2698
+ h && h({ ...m, target: { ...m.target, value: (w == null ? void 0 : w.value) ?? "" } })
2699
2699
  ),
2700
2700
  ...P,
2701
2701
  renderInput: (m) => /* @__PURE__ */ T(zo, { ...m })
package/Toast/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default, type ToastProps } from './Toast';
2
2
  export { Toaster, type ToastItem } from './Toaster';
3
3
  export { handleDismissToast, showToast, saveToastForRedirect, showSavedToast } from './ToastHelpers';
4
+ export { useToast } from './useToast';
package/Toast/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { default as e } from "./Toast.js";
2
- import { Toaster as a } from "./Toaster.js";
3
- import { handleDismissToast as T, saveToastForRedirect as d, showSavedToast as f, showToast as m } from "./ToastHelpers.js";
2
+ import { Toaster as r } from "./Toaster.js";
3
+ import { handleDismissToast as T, saveToastForRedirect as f, showSavedToast as m, showToast as d } from "./ToastHelpers.js";
4
+ import { useToast as x } from "./useToast.js";
4
5
  export {
5
- a as Toaster,
6
+ r as Toaster,
6
7
  e as default,
7
8
  T as handleDismissToast,
8
- d as saveToastForRedirect,
9
- f as showSavedToast,
10
- m as showToast
9
+ f as saveToastForRedirect,
10
+ m as showSavedToast,
11
+ d as showToast,
12
+ x as useToast
11
13
  };
@@ -0,0 +1,23 @@
1
+ import { ToastProps } from './Toast';
2
+ import { ToastItem } from './Toaster';
3
+ /**
4
+ * Hook for managing toast notifications.
5
+ * Encapsulates toast state management and provides a simple API.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * const { toasts, toast, dismissToast } = useToast();
10
+ *
11
+ * // Show a toast
12
+ * toast({ type: 'success', message: 'Saved!', duration: 3000 });
13
+ *
14
+ * // In render
15
+ * <Toaster toastQueue={toasts} onDismiss={dismissToast} />
16
+ * ```
17
+ */
18
+ export declare const useToast: () => {
19
+ toasts: ToastItem[];
20
+ toast: (toastProps: ToastProps) => void;
21
+ dismissToast: (id: number) => void;
22
+ };
23
+ export default useToast;
@@ -0,0 +1,10 @@
1
+ import { useState as a } from "react";
2
+ import { handleDismissToast as e, showToast as i } from "./ToastHelpers.js";
3
+ const c = () => {
4
+ const [o, t] = a([]);
5
+ return { toasts: o, toast: (s) => i(s, t), dismissToast: (s) => e(s, t) };
6
+ };
7
+ export {
8
+ c as default,
9
+ c as useToast
10
+ };
package/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { default as LabeledValue } from './LabeledValue';
9
9
  export { default as ListButton } from './ListButton';
10
10
  export { default as Loading } from './Loading';
11
11
  export { default as ScrollContainer } from './ScrollContainer';
12
+ export * from './FormContext';
12
13
  export { default as Accordion } from './Accordion';
13
14
  export { default as ErrorBoundary } from './ErrorBoundary';
14
15
  export { default as InfoCard } from './InfoCard';
package/index.js CHANGED
@@ -1,52 +1,55 @@
1
- import { AvatarWithImage as t } from "./AvatarWithImage/AvatarWithImage.js";
1
+ import { AvatarWithImage as e } from "./AvatarWithImage/AvatarWithImage.js";
2
2
  import { Button as a } from "./Button/Button.js";
3
3
  import { Divider as p } from "./Divider/Divider.js";
4
- import { Flex as x } from "./Flex/Flex.js";
5
- import { Grid as l } from "./Grid/Grid.js";
4
+ import { Flex as m } from "./Flex/Flex.js";
5
+ import { Grid as n } from "./Grid/Grid.js";
6
6
  import { default as i } from "./Icon/Icon.js";
7
7
  import { Input as s } from "./Input/Input.js";
8
- import { LabeledValue as I } from "./LabeledValue/LabeledValue.js";
9
- import { default as b } from "./ListButton/ListButton.js";
8
+ import { LabeledValue as c } from "./LabeledValue/LabeledValue.js";
9
+ import { default as I } from "./ListButton/ListButton.js";
10
10
  import { default as g } from "./Loading/Loading.js";
11
- import { default as C } from "./ScrollContainer/ScrollContainer.js";
12
- import { Accordion as v } from "./Accordion/Accordion.js";
13
- import { default as E } from "./ErrorBoundary/ErrorBoundary.js";
11
+ import { default as B } from "./ScrollContainer/ScrollContainer.js";
12
+ import { createDobToAgeExtension as v, createFormContext as E } from "./FormContext/createFormContext.js";
13
+ import { Accordion as D } from "./Accordion/Accordion.js";
14
+ import { default as S } from "./ErrorBoundary/ErrorBoundary.js";
14
15
  import { InfoCard as y } from "./InfoCard/InfoCard.js";
15
- import { LabeledInput as F } from "./LabeledInput/LabeledInput.js";
16
- import { default as H } from "./List/List.js";
17
- import { default as T } from "./LogoLoop/LogoLoop.js";
18
- import { SiteBanner as j } from "./SiteBanner/SiteBanner.js";
19
- import { Tooltip as q } from "./Tooltip/Tooltip.js";
20
- import { default as z } from "./WithLabel/WithLabel.js";
21
- import { default as K } from "./WithLoading/WithLoading.js";
22
- import { A as O } from "./.chunks/AppHeader.js";
23
- import { EditableInfoCard as Q } from "./EditableInfoCard/EditableInfoCard.js";
24
- import { Nav as U } from "./Nav/Nav.js";
25
- import { default as Y } from "./AppContainer/AppContainer.js";
16
+ import { LabeledInput as H } from "./LabeledInput/LabeledInput.js";
17
+ import { default as V } from "./List/List.js";
18
+ import { default as k } from "./LogoLoop/LogoLoop.js";
19
+ import { SiteBanner as w } from "./SiteBanner/SiteBanner.js";
20
+ import { Tooltip as J } from "./Tooltip/Tooltip.js";
21
+ import { default as M } from "./WithLabel/WithLabel.js";
22
+ import { default as P } from "./WithLoading/WithLoading.js";
23
+ import { A as R } from "./.chunks/AppHeader.js";
24
+ import { EditableInfoCard as X } from "./EditableInfoCard/EditableInfoCard.js";
25
+ import { Nav as Z } from "./Nav/Nav.js";
26
+ import { default as $ } from "./AppContainer/AppContainer.js";
26
27
  export {
27
- v as Accordion,
28
- Y as AppContainer,
29
- O as AppHeader,
30
- t as AvatarWithImage,
28
+ D as Accordion,
29
+ $ as AppContainer,
30
+ R as AppHeader,
31
+ e as AvatarWithImage,
31
32
  a as Button,
32
33
  p as Divider,
33
- Q as EditableInfoCard,
34
- E as ErrorBoundary,
35
- x as Flex,
36
- l as Grid,
34
+ X as EditableInfoCard,
35
+ S as ErrorBoundary,
36
+ m as Flex,
37
+ n as Grid,
37
38
  i as Icon,
38
39
  y as InfoCard,
39
40
  s as Input,
40
- F as LabeledInput,
41
- I as LabeledValue,
42
- H as List,
43
- b as ListButton,
41
+ H as LabeledInput,
42
+ c as LabeledValue,
43
+ V as List,
44
+ I as ListButton,
44
45
  g as Loading,
45
- T as LogoLoop,
46
- U as Nav,
47
- C as ScrollContainer,
48
- j as SiteBanner,
49
- q as Tooltip,
50
- z as WithLabel,
51
- K as WithLoading
46
+ k as LogoLoop,
47
+ Z as Nav,
48
+ B as ScrollContainer,
49
+ w as SiteBanner,
50
+ J as Tooltip,
51
+ M as WithLabel,
52
+ P as WithLoading,
53
+ v as createDobToAgeExtension,
54
+ E as createFormContext
52
55
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jcicl",
3
3
  "private": false,
4
- "version": "1.0.28",
4
+ "version": "1.0.30",
5
5
  "description": "Component library for the websites of Johnson County Iowa",
6
6
  "license": "MIT",
7
7
  "homepage": "https://devops.jc.net/JCIT/Business%20Solutions%20Delivery/_git/JCComponentLibrary?path=%2FREADME.md&version=GBmaster",