@bsuite/ui 1.0.3 → 1.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.
- package/dist/data-unavailable.d.ts +98 -0
- package/dist/data-unavailable.d.ts.map +1 -0
- package/dist/data-unavailable.js +162 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +8 -4
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* DataUnavailable — the honest state.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS
|
|
6
|
+
* ---------------
|
|
7
|
+
* Five live surfaces across crm7 and business-suite-unified shared one idiom:
|
|
8
|
+
*
|
|
9
|
+
* const rows = query.data ?? HARDCODED_FALLBACK
|
|
10
|
+
*
|
|
11
|
+
* That line turns every failure into a confident-looking answer. The worst
|
|
12
|
+
* instance rendered a fabricated 95% compliance score — including "Fair Work
|
|
13
|
+
* Compliance — Award rates verified" — when the underlying query was denied by
|
|
14
|
+
* RLS. An authorization failure was presented to the user as a compliance
|
|
15
|
+
* assurance. Another exported invented dollar figures to a file that then looks
|
|
16
|
+
* authoritative to whoever opens it.
|
|
17
|
+
*
|
|
18
|
+
* The operator's standing rule is: never display mock data in the UI,
|
|
19
|
+
* especially for financial or account-related information.
|
|
20
|
+
*
|
|
21
|
+
* THE CONTRACT
|
|
22
|
+
* ------------
|
|
23
|
+
* This component renders the ABSENCE of data. It therefore:
|
|
24
|
+
*
|
|
25
|
+
* 1. Takes no numeric prop and renders no number of its own. There is
|
|
26
|
+
* nothing here that can be misread as a measurement.
|
|
27
|
+
* 2. Never uses the success tone. A failure is never green.
|
|
28
|
+
* 3. Separates "nothing exists" from "we could not find out", and separates
|
|
29
|
+
* "you may not see this" from "this broke". Collapsing those is precisely
|
|
30
|
+
* the defect it replaces.
|
|
31
|
+
* 4. Surfaces the real reason verbatim, in a monospace diagnostic block, so
|
|
32
|
+
* the reader can tell a permission denial from a network fault.
|
|
33
|
+
* 5. Carries `data-slot="data-unavailable"` and `data-state`, so tests and
|
|
34
|
+
* lints can assert that a surface degraded honestly.
|
|
35
|
+
*
|
|
36
|
+
* Pair it with `resolveDataState` so the call site never re-derives the
|
|
37
|
+
* branching, and the `??` idiom has nowhere to come back.
|
|
38
|
+
*/
|
|
39
|
+
export type DataUnavailableState =
|
|
40
|
+
/** The query is in flight. Nothing is known yet. */
|
|
41
|
+
'loading'
|
|
42
|
+
/** The query succeeded and the answer is genuinely "none". */
|
|
43
|
+
| 'empty'
|
|
44
|
+
/** The query failed. We do not know the answer. */
|
|
45
|
+
| 'error'
|
|
46
|
+
/** The query was refused. The data may exist; this caller may not see it. */
|
|
47
|
+
| 'forbidden'
|
|
48
|
+
/** No queryable source is reachable from this surface at all. */
|
|
49
|
+
| 'unavailable';
|
|
50
|
+
export interface DataUnavailableProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
51
|
+
state: DataUnavailableState;
|
|
52
|
+
/**
|
|
53
|
+
* What could not be shown, as a specific noun — "Budgets", "Compliance
|
|
54
|
+
* checks", "OAuth clients". Overrides the generic preset heading.
|
|
55
|
+
*/
|
|
56
|
+
title?: ReactNode;
|
|
57
|
+
description?: ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* The real, verbatim failure reason — an error message, a Postgres code.
|
|
60
|
+
* Rendered as monospace diagnostics, never as prose. Pass the actual thing;
|
|
61
|
+
* a sanitised reason is how "unknown" becomes "fine" again.
|
|
62
|
+
*/
|
|
63
|
+
reason?: ReactNode;
|
|
64
|
+
/** Retry handler. Omit when retrying cannot help (`forbidden`). */
|
|
65
|
+
onRetry?: () => void;
|
|
66
|
+
retryLabel?: string;
|
|
67
|
+
/** Escape hatch for a site-specific action (e.g. "Request access"). */
|
|
68
|
+
action?: ReactNode;
|
|
69
|
+
/** `compact` suits an inline card slot; `block` a full page region. */
|
|
70
|
+
size?: 'compact' | 'block';
|
|
71
|
+
}
|
|
72
|
+
export declare function DataUnavailable({ state, title, description, reason, onRetry, retryLabel, action, size, className, ...props }: DataUnavailableProps): import("react").JSX.Element;
|
|
73
|
+
/**
|
|
74
|
+
* Structural subset of a TanStack Query result. Declared structurally on
|
|
75
|
+
* purpose: `@bsuite/ui` must not take a dependency on the data layer, and this
|
|
76
|
+
* shape is equally satisfiable by a hand-rolled hook.
|
|
77
|
+
*/
|
|
78
|
+
export interface QueryLike<T> {
|
|
79
|
+
data: T | undefined;
|
|
80
|
+
isPending?: boolean;
|
|
81
|
+
isLoading?: boolean;
|
|
82
|
+
isError?: boolean;
|
|
83
|
+
error?: unknown;
|
|
84
|
+
}
|
|
85
|
+
/** Human-readable reason string for the `reason` prop. */
|
|
86
|
+
export declare function describeError(error: unknown): string;
|
|
87
|
+
/**
|
|
88
|
+
* Map a query result to either `'ready'` (render the real data) or the honest
|
|
89
|
+
* state to render instead.
|
|
90
|
+
*
|
|
91
|
+
* The point is that there is no fourth answer. A call site cannot get a
|
|
92
|
+
* fallback out of this function, which is what makes the class fix stick.
|
|
93
|
+
*
|
|
94
|
+
* @param isEmpty how to decide the data is genuinely empty. Defaults to
|
|
95
|
+
* "an array with no elements". Pass explicitly for object-shaped payloads.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveDataState<T>(query: QueryLike<T>, isEmpty?: (data: T) => boolean): DataUnavailableState | 'ready';
|
|
98
|
+
//# sourceMappingURL=data-unavailable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-unavailable.d.ts","sourceRoot":"","sources":["../src/data-unavailable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAMH,MAAM,MAAM,oBAAoB;AAC9B,oDAAoD;AAClD,SAAS;AACX,8DAA8D;GAC5D,OAAO;AACT,mDAAmD;GACjD,OAAO;AACT,6EAA6E;GAC3E,WAAW;AACb,iEAAiE;GAC/D,aAAa,CAAA;AA0JjB,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzF,KAAK,EAAE,oBAAoB,CAAA;IAC3B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,uEAAuE;IACvE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,KAAK,EACL,WAAW,EACX,MAAM,EACN,OAAO,EACP,UAAwB,EACxB,MAAM,EACN,IAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,oBAAoB,+BAiEtB;AAMD;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAwBD,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAcpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,OAAO,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAmD,GACxE,oBAAoB,GAAG,OAAO,CAKhC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from './utils.js';
|
|
3
|
+
const PRESETS = {
|
|
4
|
+
loading: {
|
|
5
|
+
tone: 'info',
|
|
6
|
+
title: 'Loading…',
|
|
7
|
+
description: 'Fetching the current figures.',
|
|
8
|
+
icon: 'spinner',
|
|
9
|
+
live: 'polite',
|
|
10
|
+
role: 'status',
|
|
11
|
+
},
|
|
12
|
+
empty: {
|
|
13
|
+
tone: 'neutral',
|
|
14
|
+
title: 'Nothing recorded yet',
|
|
15
|
+
description: 'This is a real answer, not a failure — no records exist for the current selection.',
|
|
16
|
+
icon: 'empty',
|
|
17
|
+
live: 'polite',
|
|
18
|
+
role: 'status',
|
|
19
|
+
},
|
|
20
|
+
error: {
|
|
21
|
+
tone: 'destructive',
|
|
22
|
+
title: 'Could not load this',
|
|
23
|
+
description: 'The request failed, so nothing is shown. This is not an empty result — the real value is unknown.',
|
|
24
|
+
icon: 'alert',
|
|
25
|
+
live: 'assertive',
|
|
26
|
+
role: 'alert',
|
|
27
|
+
},
|
|
28
|
+
forbidden: {
|
|
29
|
+
tone: 'warning',
|
|
30
|
+
title: 'You do not have access to this',
|
|
31
|
+
description: 'The request was refused. Data may exist that this account is not permitted to read, so nothing is shown.',
|
|
32
|
+
icon: 'lock',
|
|
33
|
+
live: 'assertive',
|
|
34
|
+
role: 'alert',
|
|
35
|
+
},
|
|
36
|
+
unavailable: {
|
|
37
|
+
tone: 'warning',
|
|
38
|
+
title: 'Not readable from here',
|
|
39
|
+
description: 'No queryable source for this information is reachable from this surface, so nothing is shown.',
|
|
40
|
+
icon: 'unplugged',
|
|
41
|
+
live: 'polite',
|
|
42
|
+
role: 'status',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
// Role-bound only. `success` is deliberately absent: an honest state is never green.
|
|
46
|
+
const TONE_SURFACE = {
|
|
47
|
+
neutral: 'border-border bg-muted/30 text-muted-foreground',
|
|
48
|
+
info: 'border-primary/30 bg-primary/5 text-primary',
|
|
49
|
+
warning: 'border-status-warning/40 bg-status-warning/5 text-status-warning',
|
|
50
|
+
destructive: 'border-destructive/40 bg-destructive/5 text-destructive',
|
|
51
|
+
};
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Icons — inline SVG, `currentColor` only, so they inherit the tone token.
|
|
54
|
+
// (The package has no icon dependency; `loading-spinner.tsx` sets this
|
|
55
|
+
// precedent. Adding lucide-react here would push it onto every consumer.)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
function StateIcon({ icon }) {
|
|
58
|
+
const common = {
|
|
59
|
+
viewBox: '0 0 24 24',
|
|
60
|
+
fill: 'none',
|
|
61
|
+
stroke: 'currentColor',
|
|
62
|
+
strokeWidth: 1.75,
|
|
63
|
+
strokeLinecap: 'round',
|
|
64
|
+
strokeLinejoin: 'round',
|
|
65
|
+
className: 'h-6 w-6',
|
|
66
|
+
'aria-hidden': true,
|
|
67
|
+
};
|
|
68
|
+
switch (icon) {
|
|
69
|
+
case 'spinner':
|
|
70
|
+
return (_jsxs("svg", { ...common, className: cn(common.className, 'animate-spin'), children: [_jsx("circle", { cx: "12", cy: "12", r: "9", className: "opacity-25" }), _jsx("path", { d: "M21 12a9 9 0 0 0-9-9", className: "opacity-90" })] }));
|
|
71
|
+
case 'empty':
|
|
72
|
+
// An empty tray — "we looked, there was nothing".
|
|
73
|
+
return (_jsxs("svg", { ...common, children: [_jsx("path", { d: "M3 14h4l1.5 3h7L17 14h4" }), _jsx("path", { d: "M5 14 7 5h10l2 9v5H5z" })] }));
|
|
74
|
+
case 'alert':
|
|
75
|
+
return (_jsxs("svg", { ...common, children: [_jsx("path", { d: "M12 3.5 2.5 20h19L12 3.5z" }), _jsx("path", { d: "M12 10v4" }), _jsx("path", { d: "M12 17.5h.01" })] }));
|
|
76
|
+
case 'lock':
|
|
77
|
+
return (_jsxs("svg", { ...common, children: [_jsx("rect", { x: "4", y: "10", width: "16", height: "10", rx: "2" }), _jsx("path", { d: "M8 10V7a4 4 0 0 1 8 0v3" }), _jsx("path", { d: "M12 14v2" })] }));
|
|
78
|
+
case 'unplugged':
|
|
79
|
+
return (_jsxs("svg", { ...common, children: [_jsx("path", { d: "M7 3v5" }), _jsx("path", { d: "M13 3v5" }), _jsx("path", { d: "M4 8h12v3a6 6 0 0 1-6 6v4" }), _jsx("path", { d: "m3 3 18 18" })] }));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Diagonal hatch. This is the load-bearing visual: hatched ground is not a
|
|
84
|
+
* texture any populated surface in the estate uses, so "this panel is not
|
|
85
|
+
* data" is legible before a single word is read. `currentColor` keeps it on
|
|
86
|
+
* the tone token in both themes.
|
|
87
|
+
*/
|
|
88
|
+
function Hatch() {
|
|
89
|
+
return (_jsxs("svg", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 h-full w-full opacity-[0.07]", preserveAspectRatio: "none", children: [_jsx("defs", { children: _jsx("pattern", { id: "bs-du-hatch", width: "8", height: "8", patternUnits: "userSpaceOnUse", patternTransform: "rotate(45)", children: _jsx("line", { x1: "0", y1: "0", x2: "0", y2: "8", stroke: "currentColor", strokeWidth: "2" }) }) }), _jsx("rect", { width: "100%", height: "100%", fill: "url(#bs-du-hatch)" })] }));
|
|
90
|
+
}
|
|
91
|
+
export function DataUnavailable({ state, title, description, reason, onRetry, retryLabel = 'Try again', action, size = 'block', className, ...props }) {
|
|
92
|
+
const preset = PRESETS[state];
|
|
93
|
+
const showRetry = Boolean(onRetry) && state !== 'forbidden';
|
|
94
|
+
return (_jsxs("div", { "data-slot": "data-unavailable", "data-state": state, role: preset.role, "aria-live": preset.live, "aria-busy": state === 'loading' || undefined, className: cn(
|
|
95
|
+
// Dashed + hatched: deliberately unlike every populated surface.
|
|
96
|
+
'relative isolate flex flex-col items-center justify-center overflow-hidden', 'rounded-lg border border-dashed text-center', size === 'compact' ? 'min-h-32 gap-2 p-4' : 'min-h-48 gap-3 p-8', TONE_SURFACE[preset.tone], className), ...props, children: [_jsx(Hatch, {}), _jsxs("div", { className: "relative flex flex-col items-center gap-3", children: [_jsx(StateIcon, { icon: preset.icon }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-base font-semibold text-foreground", children: title ?? preset.title }), _jsx("p", { className: "mx-auto max-w-prose text-sm text-muted-foreground", children: description ?? preset.description })] }), reason ? (_jsxs("div", { className: "mx-auto w-full max-w-prose", children: [_jsx("p", { className: "mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Reason" }), _jsx("pre", { className: "overflow-x-auto whitespace-pre-wrap break-words rounded-md border border-border bg-card/80 px-3 py-2 text-left font-mono text-xs text-muted-foreground", children: reason })] })) : null, showRetry || action ? (_jsxs("div", { className: "mt-1 flex flex-wrap items-center justify-center gap-2", children: [showRetry ? (_jsx("button", { type: "button", onClick: onRetry, className: cn('inline-flex items-center rounded-md border border-border bg-card px-3 py-1.5', 'text-sm font-medium text-foreground', 'hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'), children: retryLabel })) : null, action] })) : null] })] }));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Postgres/PostgREST codes that mean "refused", not "broken".
|
|
100
|
+
*
|
|
101
|
+
* 42501 insufficient_privilege · PGRST301 JWT problem ·
|
|
102
|
+
* PGRST116 no rows returned where one was required under RLS.
|
|
103
|
+
*/
|
|
104
|
+
const FORBIDDEN_MARKERS = ['42501', 'PGRST301', 'PGRST116', 'permission denied', 'row-level security'];
|
|
105
|
+
function looksForbidden(error) {
|
|
106
|
+
if (!error)
|
|
107
|
+
return false;
|
|
108
|
+
const parts = [];
|
|
109
|
+
if (typeof error === 'string')
|
|
110
|
+
parts.push(error);
|
|
111
|
+
else if (typeof error === 'object') {
|
|
112
|
+
const e = error;
|
|
113
|
+
if (typeof e.message === 'string')
|
|
114
|
+
parts.push(e.message);
|
|
115
|
+
if (typeof e.code === 'string')
|
|
116
|
+
parts.push(e.code);
|
|
117
|
+
if (e.status === 401 || e.status === 403)
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
const haystack = parts.join(' ').toLowerCase();
|
|
121
|
+
return FORBIDDEN_MARKERS.some((m) => haystack.includes(m.toLowerCase()));
|
|
122
|
+
}
|
|
123
|
+
/** Human-readable reason string for the `reason` prop. */
|
|
124
|
+
export function describeError(error) {
|
|
125
|
+
if (!error)
|
|
126
|
+
return 'Unknown error';
|
|
127
|
+
if (typeof error === 'string')
|
|
128
|
+
return error;
|
|
129
|
+
if (error instanceof Error)
|
|
130
|
+
return error.message;
|
|
131
|
+
if (typeof error === 'object') {
|
|
132
|
+
const e = error;
|
|
133
|
+
const code = typeof e.code === 'string' ? `[${e.code}] ` : '';
|
|
134
|
+
if (typeof e.message === 'string')
|
|
135
|
+
return `${code}${e.message}`;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return JSON.stringify(error);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return String(error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Map a query result to either `'ready'` (render the real data) or the honest
|
|
146
|
+
* state to render instead.
|
|
147
|
+
*
|
|
148
|
+
* The point is that there is no fourth answer. A call site cannot get a
|
|
149
|
+
* fallback out of this function, which is what makes the class fix stick.
|
|
150
|
+
*
|
|
151
|
+
* @param isEmpty how to decide the data is genuinely empty. Defaults to
|
|
152
|
+
* "an array with no elements". Pass explicitly for object-shaped payloads.
|
|
153
|
+
*/
|
|
154
|
+
export function resolveDataState(query, isEmpty = (d) => Array.isArray(d) && d.length === 0) {
|
|
155
|
+
if (query.isError)
|
|
156
|
+
return looksForbidden(query.error) ? 'forbidden' : 'error';
|
|
157
|
+
if (query.isPending ?? query.isLoading ?? false)
|
|
158
|
+
return 'loading';
|
|
159
|
+
if (query.data === undefined || query.data === null)
|
|
160
|
+
return 'error';
|
|
161
|
+
return isEmpty(query.data) ? 'empty' : 'ready';
|
|
162
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { D2CDefaultLogo, type D2CDefaultLogoProps } from './default-logo.js';
|
|
|
7
7
|
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, } from './button.js';
|
|
8
8
|
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type DialogContentProps, type DialogOverlayProps, } from './dialog.js';
|
|
9
9
|
export { EmptyState, type EmptyStateProps } from './empty-state.js';
|
|
10
|
+
export { DataUnavailable, resolveDataState, describeError, type DataUnavailableProps, type DataUnavailableState, type QueryLike, } from './data-unavailable.js';
|
|
10
11
|
export { ErrorBoundary, type ErrorBoundaryProps } from './error-boundary.js';
|
|
11
12
|
export { LoadingSpinner, type LoadingSpinnerProps } from './loading-spinner.js';
|
|
12
13
|
export { StatusBadge, type StatusBadgeProps, type StatusBadgeTone } from './status-badge.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAC/B,OAAO,EACL,IAAI,EACJ,cAAc,EACd,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,OAAO,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG5F,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAC/B,OAAO,EACL,IAAI,EACJ,cAAc,EACd,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,OAAO,GACb,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,SAAS,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG5F,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { D2CDefaultLogo } from './default-logo.js';
|
|
|
7
7
|
export { Button, } from './button.js';
|
|
8
8
|
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from './dialog.js';
|
|
9
9
|
export { EmptyState } from './empty-state.js';
|
|
10
|
+
export { DataUnavailable, resolveDataState, describeError, } from './data-unavailable.js';
|
|
10
11
|
export { ErrorBoundary } from './error-boundary.js';
|
|
11
12
|
export { LoadingSpinner } from './loading-spinner.js';
|
|
12
13
|
export { StatusBadge } from './status-badge.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsuite/ui",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"publishConfig": {
|
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
"import": "./dist/empty-state.js",
|
|
52
52
|
"types": "./dist/empty-state.d.ts"
|
|
53
53
|
},
|
|
54
|
+
"./data-unavailable": {
|
|
55
|
+
"import": "./dist/data-unavailable.js",
|
|
56
|
+
"types": "./dist/data-unavailable.d.ts"
|
|
57
|
+
},
|
|
54
58
|
"./error-boundary": {
|
|
55
59
|
"import": "./dist/error-boundary.js",
|
|
56
60
|
"types": "./dist/error-boundary.d.ts"
|
|
@@ -89,8 +93,8 @@
|
|
|
89
93
|
}
|
|
90
94
|
},
|
|
91
95
|
"devDependencies": {
|
|
92
|
-
"@bsuite/theme": "^0.
|
|
93
|
-
"@storybook/react-vite": "^10.
|
|
96
|
+
"@bsuite/theme": "^0.11.0",
|
|
97
|
+
"@storybook/react-vite": "^10.5.8",
|
|
94
98
|
"@tailwindcss/vite": "^4.3.0",
|
|
95
99
|
"@testing-library/jest-dom": "^6.9.1",
|
|
96
100
|
"@testing-library/react": "^16.3.2",
|
|
@@ -101,7 +105,7 @@
|
|
|
101
105
|
"jsdom": "^29.1.1",
|
|
102
106
|
"react": "^19.2.7",
|
|
103
107
|
"react-dom": "^19.2.7",
|
|
104
|
-
"storybook": "^10.
|
|
108
|
+
"storybook": "^10.5.8",
|
|
105
109
|
"tailwindcss": "^4.3.0",
|
|
106
110
|
"typescript": "~6.0.3",
|
|
107
111
|
"vite": "^8.0.16",
|