jcicl 1.1.5 → 1.2.2
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/Button/Button.d.ts +1 -0
- package/Button/Button.js +73 -70
- package/DesktopStepper/DesktopStepper.d.ts +24 -0
- package/DesktopStepper/DesktopStepper.js +204 -0
- package/DesktopStepper/index.d.ts +1 -0
- package/DesktopStepper/index.js +5 -0
- package/FieldGroup/FieldGroup.js +15 -12
- package/FieldVisibilityWrapper/FieldVisibilityWrapper.d.ts +11 -0
- package/FieldVisibilityWrapper/FieldVisibilityWrapper.js +48 -0
- package/FieldVisibilityWrapper/index.d.ts +1 -0
- package/FieldVisibilityWrapper/index.js +4 -0
- package/FormContext/createFormContext.js +91 -72
- package/FormContext/types.d.ts +8 -0
- package/FormFields/FormFields.d.ts +2 -2
- package/FormFields/FormFields.js +18 -18
- package/FormInput/FormInput.d.ts +4 -0
- package/FormInput/FormInput.js +39 -14
- package/LabeledCheckbox/LabeledCheckbox.js +4 -4
- package/LabeledCurrencyInput/LabeledCurrencyInput.js +33 -33
- package/LabeledInput/LabeledInput.d.ts +2 -0
- package/LabeledInput/LabeledInput.js +16 -14
- package/LabeledRadio/LabeledRadio.js +12 -12
- package/Nav/Nav.js +174 -151
- package/SelectableItemCard/SelectableItemCard.d.ts +9 -0
- package/SelectableItemCard/SelectableItemCard.js +24 -0
- package/SelectableItemCard/index.d.ts +1 -0
- package/SelectableItemCard/index.js +4 -0
- package/Stepper/Stepper.js +184 -226
- package/Table/Table.js +236 -234
- package/Tabs/Tabs.d.ts +42 -0
- package/Tabs/Tabs.js +121 -0
- package/Tabs/index.d.ts +1 -0
- package/Tabs/index.js +4 -0
- package/api.d.ts +16 -5
- package/api.js +42 -28
- package/assets/style.css +1 -1
- package/assets/tailwind.css +2 -2
- package/constants.js +1 -1
- package/formatters.d.ts +1 -0
- package/formatters.js +30 -17
- package/index.d.ts +2 -0
- package/index.js +64 -62
- package/package.json +1 -1
- package/problemDetails.d.ts +20 -0
- package/problemDetails.js +19 -0
- package/utils.d.ts +2 -2
- package/utils.js +35 -29
- package/validators.d.ts +8 -0
- package/validators.js +19 -9
package/Tabs/Tabs.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface TabItem {
|
|
2
|
+
/** Text shown in the tab header. */
|
|
3
|
+
label: string;
|
|
4
|
+
/** Optional element rendered before the label (e.g. an icon). */
|
|
5
|
+
icon?: React.ReactNode;
|
|
6
|
+
/** Content rendered in the panel when this tab is active. */
|
|
7
|
+
content: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export type TabHeaderAlignment = 'left' | 'center' | 'right';
|
|
10
|
+
export interface TabsProps {
|
|
11
|
+
/** Ordered list of tabs. The first tab is shown by default. */
|
|
12
|
+
tabs: TabItem[];
|
|
13
|
+
/** Initially active tab index when uncontrolled. Defaults to 0. */
|
|
14
|
+
defaultIndex?: number;
|
|
15
|
+
/** Active tab index. Provide together with onChange for controlled use. */
|
|
16
|
+
activeIndex?: number;
|
|
17
|
+
/** Called with the next index whenever a tab is selected. */
|
|
18
|
+
onChange?: (index: number) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Opt-in URL persistence (uncontrolled mode only). When set, the active tab's
|
|
21
|
+
* label is stored in the URL query param of this name via the History API —
|
|
22
|
+
* no router/dependency required. Give each Tabs on a page a UNIQUE key so they
|
|
23
|
+
* don't collide; persistence is per browser window since each window has its
|
|
24
|
+
* own URL. Tab labels should be unique, since the active tab is stored by label.
|
|
25
|
+
*/
|
|
26
|
+
persistKey?: string;
|
|
27
|
+
/** Horizontal alignment of the tab headers. Defaults to 'left'. */
|
|
28
|
+
tabHeaderAlignment?: TabHeaderAlignment;
|
|
29
|
+
/** Header label font size override (CSS length; number is treated as px). Defaults to 27px. */
|
|
30
|
+
headerFontSize?: string | number;
|
|
31
|
+
/** Extra class(es) merged onto each tab header button (consumer override). */
|
|
32
|
+
headerClassName?: string;
|
|
33
|
+
/** Inline style merged onto each tab header button (consumer override). */
|
|
34
|
+
headerStyle?: React.CSSProperties;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A horizontal tab layout: a header row of selectable tabs and a panel showing
|
|
38
|
+
* the active tab's content. Works uncontrolled (manages its own active tab,
|
|
39
|
+
* seeded by `defaultIndex`) or controlled (pass `activeIndex` plus `onChange`).
|
|
40
|
+
*/
|
|
41
|
+
export declare const Tabs: React.FC<TabsProps>;
|
|
42
|
+
export default Tabs;
|
package/Tabs/Tabs.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsxs as h, jsx as v } from "react/jsx-runtime";
|
|
2
|
+
import { useId as E, useState as S, useRef as T, useEffect as k } from "react";
|
|
3
|
+
import { cn as y } from "../cn.js";
|
|
4
|
+
const A = {
|
|
5
|
+
left: "justify-start",
|
|
6
|
+
center: "justify-between",
|
|
7
|
+
// spread headers across the full width
|
|
8
|
+
right: "justify-end"
|
|
9
|
+
}, x = (e, i) => {
|
|
10
|
+
if (typeof window > "u") return null;
|
|
11
|
+
const r = new URLSearchParams(window.location.search).get(e);
|
|
12
|
+
if (r == null) return null;
|
|
13
|
+
const l = i.findIndex((d) => d.label === r);
|
|
14
|
+
return l >= 0 ? l : null;
|
|
15
|
+
}, N = (e, i) => {
|
|
16
|
+
if (typeof window > "u") return;
|
|
17
|
+
const r = new URLSearchParams(window.location.search);
|
|
18
|
+
r.set(e, i), window.history.replaceState(
|
|
19
|
+
window.history.state,
|
|
20
|
+
"",
|
|
21
|
+
`${window.location.pathname}?${r}${window.location.hash}`
|
|
22
|
+
);
|
|
23
|
+
}, M = ({
|
|
24
|
+
tabs: e,
|
|
25
|
+
defaultIndex: i = 0,
|
|
26
|
+
activeIndex: r,
|
|
27
|
+
onChange: l,
|
|
28
|
+
persistKey: d,
|
|
29
|
+
tabHeaderAlignment: j = "left",
|
|
30
|
+
headerFontSize: f,
|
|
31
|
+
headerClassName: $,
|
|
32
|
+
headerStyle: I
|
|
33
|
+
}) => {
|
|
34
|
+
const s = E(), u = r !== void 0, o = !u && d, [g, b] = S(
|
|
35
|
+
() => (o ? x(o, e) : null) ?? i
|
|
36
|
+
), L = u ? r : g, p = T(e);
|
|
37
|
+
if (p.current = e, k(() => {
|
|
38
|
+
if (!o) return;
|
|
39
|
+
const t = () => {
|
|
40
|
+
const n = x(o, p.current);
|
|
41
|
+
n != null && b(n);
|
|
42
|
+
};
|
|
43
|
+
return window.addEventListener("popstate", t), () => window.removeEventListener("popstate", t);
|
|
44
|
+
}, [o]), e.length === 0) return null;
|
|
45
|
+
const c = Math.min(Math.max(L, 0), e.length - 1), w = (t) => {
|
|
46
|
+
u || b(t), o && N(o, e[t].label), l == null || l(t);
|
|
47
|
+
}, P = {
|
|
48
|
+
...f != null ? { fontSize: f } : {},
|
|
49
|
+
...I
|
|
50
|
+
}, R = (t) => {
|
|
51
|
+
var m;
|
|
52
|
+
if (t.key !== "ArrowRight" && t.key !== "ArrowLeft") return;
|
|
53
|
+
t.preventDefault();
|
|
54
|
+
const n = t.key === "ArrowRight" ? 1 : -1, a = (c + n + e.length) % e.length;
|
|
55
|
+
w(a), (m = document.getElementById(`${s}-tab-${a}`)) == null || m.focus();
|
|
56
|
+
};
|
|
57
|
+
return /* @__PURE__ */ h("div", { className: "jcTabs flex w-full flex-col", children: [
|
|
58
|
+
/* @__PURE__ */ v(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
role: "tablist",
|
|
62
|
+
className: y(
|
|
63
|
+
"jcTabList flex flex-wrap gap-1 border-b-2 border-jc-gray-2",
|
|
64
|
+
A[j]
|
|
65
|
+
),
|
|
66
|
+
children: e.map((t, n) => {
|
|
67
|
+
const a = n === c;
|
|
68
|
+
return /* @__PURE__ */ h(
|
|
69
|
+
"button",
|
|
70
|
+
{
|
|
71
|
+
id: `${s}-tab-${n}`,
|
|
72
|
+
type: "button",
|
|
73
|
+
role: "tab",
|
|
74
|
+
"aria-selected": a,
|
|
75
|
+
"aria-controls": `${s}-panel-${n}`,
|
|
76
|
+
tabIndex: a ? 0 : -1,
|
|
77
|
+
"data-active": a,
|
|
78
|
+
onClick: () => w(n),
|
|
79
|
+
onKeyDown: R,
|
|
80
|
+
style: P,
|
|
81
|
+
className: y(
|
|
82
|
+
// The lib's CSS omits Tailwind preflight, so reset the native button
|
|
83
|
+
// border explicitly (none on top/sides) and keep only the bottom indicator.
|
|
84
|
+
"jcTab -mb-0.5 inline-flex cursor-pointer appearance-none items-center gap-2 bg-transparent px-5 py-3",
|
|
85
|
+
"border-solid border-t-0 border-x-0 border-b-[3px] border-b-[color:transparent]",
|
|
86
|
+
"text-[27px] font-normal text-jc-black transition-[color,border-color] duration-200",
|
|
87
|
+
// Active indicator + focus ring track the app's selected theme color
|
|
88
|
+
// (--jc-theme-color, injected by DefaultTemplate); jc-gold (#fab62d) is the
|
|
89
|
+
// fallback outside a themed app. Keep these strings literal — Tailwind only
|
|
90
|
+
// scans complete class strings, so the color can't be interpolated.
|
|
91
|
+
"data-[active=true]:font-semibold data-[active=true]:border-b-[color:var(--jc-theme-color,#fab62d)]",
|
|
92
|
+
"focus-visible:rounded-[4px] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--jc-theme-color,#fab62d)]",
|
|
93
|
+
$
|
|
94
|
+
),
|
|
95
|
+
children: [
|
|
96
|
+
t.icon,
|
|
97
|
+
t.label
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
n
|
|
101
|
+
);
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
/* @__PURE__ */ v(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
role: "tabpanel",
|
|
109
|
+
id: `${s}-panel-${c}`,
|
|
110
|
+
"aria-labelledby": `${s}-tab-${c}`,
|
|
111
|
+
tabIndex: 0,
|
|
112
|
+
className: "jcTabPanel pt-6",
|
|
113
|
+
children: e[c].content
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
] });
|
|
117
|
+
};
|
|
118
|
+
export {
|
|
119
|
+
M as Tabs,
|
|
120
|
+
M as default
|
|
121
|
+
};
|
package/Tabs/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, type TabsProps, type TabItem } from './Tabs';
|
package/Tabs/index.js
ADDED
package/api.d.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
export interface ApiResponse<T> {
|
|
2
2
|
success: boolean;
|
|
3
3
|
data?: T | null;
|
|
4
|
-
error?:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
error?: ApiError;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Error shape for a failed request. `message` is always a best-effort human-readable string
|
|
8
|
+
* (back-compat for `message`-only consumers). The remaining fields mirror an RFC 7807 / 9457
|
|
9
|
+
* ProblemDetails body when the server sends one — `errors` carries field-level validation
|
|
10
|
+
* messages (field key → messages), e.g. for surfacing inline form errors.
|
|
11
|
+
*/
|
|
12
|
+
export interface ApiError {
|
|
13
|
+
status: number;
|
|
14
|
+
statusText: string;
|
|
15
|
+
message: string;
|
|
16
|
+
detail?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
traceId?: string;
|
|
19
|
+
errors?: Record<string, string[]>;
|
|
9
20
|
}
|
|
10
21
|
/**
|
|
11
22
|
* Base API client for JCIT applications.
|
package/api.js
CHANGED
|
@@ -1,70 +1,84 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
1
|
+
var E = Object.defineProperty;
|
|
2
|
+
var w = (a, e, i) => e in a ? E(a, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : a[e] = i;
|
|
3
|
+
var o = (a, e, i) => w(a, typeof e != "symbol" ? e + "" : e, i);
|
|
4
|
+
import { firstProblemMessage as v } from "./problemDetails.js";
|
|
5
|
+
class I {
|
|
5
6
|
constructor(e) {
|
|
6
7
|
// `protected` (not `private`): subclasses may need to read or override the URL
|
|
7
8
|
// (e.g., versioned-API subclass that prepends '/v2'). Not `public`: external
|
|
8
9
|
// code shouldn't be able to read or mutate the base URL after construction.
|
|
9
|
-
|
|
10
|
+
o(this, "baseUrl");
|
|
10
11
|
if (!e)
|
|
11
12
|
throw new Error(
|
|
12
13
|
"BaseApiClient: baseUrl is required. Pass import.meta.env.VITE_API_BASE_URL or equivalent."
|
|
13
14
|
);
|
|
14
15
|
this.baseUrl = e.endsWith("/") ? e.slice(0, -1) : e;
|
|
15
16
|
}
|
|
16
|
-
async request(e,
|
|
17
|
-
const
|
|
17
|
+
async request(e, i = "GET", l, c) {
|
|
18
|
+
const f = (c == null ? void 0 : c.timeoutMs) ?? 3e4, m = new AbortController(), T = setTimeout(() => m.abort(), f);
|
|
18
19
|
try {
|
|
19
|
-
const
|
|
20
|
-
method:
|
|
20
|
+
const g = {
|
|
21
|
+
method: i,
|
|
21
22
|
headers: {
|
|
22
|
-
...
|
|
23
|
-
...
|
|
23
|
+
...l !== void 0 ? { "Content-Type": "application/json" } : {},
|
|
24
|
+
...c == null ? void 0 : c.headers
|
|
24
25
|
},
|
|
25
26
|
credentials: "include",
|
|
26
27
|
// Required for Windows Authentication with CORS
|
|
27
|
-
signal:
|
|
28
|
+
signal: m.signal
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
if (clearTimeout(
|
|
30
|
+
l !== void 0 && (g.body = JSON.stringify(l));
|
|
31
|
+
const r = await fetch(`${this.baseUrl}${e}`, g);
|
|
32
|
+
if (clearTimeout(T), !r.ok) {
|
|
33
|
+
const h = await r.text();
|
|
34
|
+
let t;
|
|
35
|
+
try {
|
|
36
|
+
t = h ? JSON.parse(h) : void 0;
|
|
37
|
+
} catch {
|
|
38
|
+
t = void 0;
|
|
39
|
+
}
|
|
40
|
+
const x = v(t == null ? void 0 : t.errors);
|
|
32
41
|
return {
|
|
33
42
|
success: !1,
|
|
34
43
|
error: {
|
|
35
|
-
status:
|
|
36
|
-
statusText: t.
|
|
37
|
-
message: `HTTP ${
|
|
44
|
+
status: r.status,
|
|
45
|
+
statusText: r.statusText || (t == null ? void 0 : t.title) || "",
|
|
46
|
+
message: (t == null ? void 0 : t.message) || (t == null ? void 0 : t.detail) || (t == null ? void 0 : t.title) || x || `HTTP ${r.status}: ${r.statusText}`,
|
|
47
|
+
detail: t == null ? void 0 : t.detail,
|
|
48
|
+
title: t == null ? void 0 : t.title,
|
|
49
|
+
traceId: t == null ? void 0 : t.traceId,
|
|
50
|
+
errors: t == null ? void 0 : t.errors
|
|
38
51
|
}
|
|
39
52
|
};
|
|
40
|
-
|
|
53
|
+
}
|
|
54
|
+
if (r.status === 204)
|
|
41
55
|
return {
|
|
42
56
|
success: !0,
|
|
43
57
|
data: void 0
|
|
44
58
|
};
|
|
45
|
-
const
|
|
46
|
-
if (!
|
|
59
|
+
const s = await r.text();
|
|
60
|
+
if (!s || s === "null")
|
|
47
61
|
return {
|
|
48
62
|
success: !0,
|
|
49
63
|
data: null
|
|
50
64
|
};
|
|
51
|
-
let
|
|
65
|
+
let d;
|
|
52
66
|
try {
|
|
53
|
-
|
|
67
|
+
d = JSON.parse(s);
|
|
54
68
|
} catch {
|
|
55
|
-
|
|
69
|
+
d = s;
|
|
56
70
|
}
|
|
57
71
|
return {
|
|
58
72
|
success: !0,
|
|
59
|
-
data:
|
|
73
|
+
data: d
|
|
60
74
|
};
|
|
61
75
|
} catch (u) {
|
|
62
|
-
return clearTimeout(
|
|
76
|
+
return clearTimeout(T), u instanceof Error && u.name === "AbortError" ? {
|
|
63
77
|
success: !1,
|
|
64
78
|
error: {
|
|
65
79
|
status: 0,
|
|
66
80
|
statusText: "Timeout",
|
|
67
|
-
message: `Request timed out after ${
|
|
81
|
+
message: `Request timed out after ${f}ms`
|
|
68
82
|
}
|
|
69
83
|
} : {
|
|
70
84
|
success: !1,
|
|
@@ -78,5 +92,5 @@ class x {
|
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
export {
|
|
81
|
-
|
|
95
|
+
I as BaseApiClient
|
|
82
96
|
};
|