@mihcm/ui 0.15.0 → 0.16.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/MainSidebar/command.js +2 -2
- package/dist/MainSidebar/command.js.map +1 -1
- package/dist/MainSidebar/drilldown.js +1 -1
- package/dist/MainSidebar/drilldown.js.map +1 -1
- package/dist/MainSidebar/expanded.js +2 -2
- package/dist/MainSidebar/expanded.js.map +1 -1
- package/dist/MainSidebar/helpers.d.ts.map +1 -1
- package/dist/MainSidebar/helpers.js +11 -9
- package/dist/MainSidebar/helpers.js.map +1 -1
- package/dist/MainSidebar/rail.js +1 -1
- package/dist/MainSidebar/rail.js.map +1 -1
- package/dist/Toast.d.ts +95 -35
- package/dist/Toast.d.ts.map +1 -1
- package/dist/Toast.js +148 -138
- package/dist/Toast.js.map +1 -1
- package/package.json +2 -1
- package/src/MainSidebar/command.tsx +2 -2
- package/src/MainSidebar/drilldown.tsx +1 -1
- package/src/MainSidebar/expanded.tsx +6 -6
- package/src/MainSidebar/helpers.ts +9 -7
- package/src/MainSidebar/rail.tsx +2 -2
- package/src/Toast.tsx +301 -325
package/dist/Toast.js
CHANGED
|
@@ -1,31 +1,157 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* Toast (web variant — React DOM).
|
|
5
5
|
*
|
|
6
|
+
* Built on top of [sonner](https://sonner.emilkowal.ski/) so MiHCM apps get
|
|
7
|
+
* every feature the upstream library ships — promise toasts, loading state,
|
|
8
|
+
* action + cancel buttons, swipe-to-dismiss, hover-to-expand stacks, rich
|
|
9
|
+
* colours per type, theme switching, full a11y wiring — without forking.
|
|
10
|
+
*
|
|
11
|
+
* Surface is themed with MiHCM tokens (`bg-card`, `border-border`,
|
|
12
|
+
* `text-foreground`, `text-success-foreground`, etc.) via sonner's
|
|
13
|
+
* `toastOptions.classNames` so the toasts read as part of the rest of the
|
|
14
|
+
* design system regardless of light/dark mode or custom `colorScheme`s.
|
|
15
|
+
*
|
|
6
16
|
* Two-part API:
|
|
7
|
-
* 1. `<
|
|
8
|
-
* in a
|
|
9
|
-
*
|
|
17
|
+
* 1. `<Toaster />` — mount once near the app root. It renders the queue
|
|
18
|
+
* in a portal at the chosen `position`. Pass `richColors`, `expand`,
|
|
19
|
+
* `closeButton`, `visibleToasts`, `theme`, etc. to tune behaviour.
|
|
20
|
+
* 2. `toast()` — call from anywhere to enqueue. Sub-APIs:
|
|
10
21
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
22
|
+
* toast('Title')
|
|
23
|
+
* toast.success('Saved')
|
|
24
|
+
* toast.error('Failed', { description: '...' })
|
|
25
|
+
* toast.warning('Heads up')
|
|
26
|
+
* toast.info('Just so you know')
|
|
27
|
+
* toast.message('Plain')
|
|
28
|
+
* toast.loading('Working…')
|
|
29
|
+
* toast.promise(myPromise, {
|
|
30
|
+
* loading: 'Saving…',
|
|
31
|
+
* success: (data) => `Saved ${data.name}`,
|
|
32
|
+
* error: (err) => `Failed: ${err.message}`,
|
|
33
|
+
* })
|
|
34
|
+
* toast.custom((id) => <MyJSX onDismiss={() => toast.dismiss(id)} />)
|
|
35
|
+
* toast.dismiss() // dismiss all
|
|
36
|
+
* toast.dismiss(id) // dismiss one
|
|
14
37
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
38
|
+
* Backward-compat shims for the previous imperative MiHCM API are below —
|
|
39
|
+
* `ToastProvider` is a deprecated alias for `Toaster`; the legacy
|
|
40
|
+
* `Toast`/`ToastTitle`/`ToastDescription`/`ToastAction`/`ToastClose`
|
|
41
|
+
* sub-components remain so any pre-sonner consumer keeps rendering.
|
|
18
42
|
*
|
|
19
43
|
* Wiki: docs/components/Toast.md
|
|
20
44
|
*/
|
|
21
|
-
import { forwardRef,
|
|
45
|
+
import { forwardRef, } from 'react';
|
|
46
|
+
import { Toaster as SonnerToaster, toast as sonnerToast } from 'sonner';
|
|
22
47
|
import { cva } from 'class-variance-authority';
|
|
23
48
|
import { cn } from './internal/cn.js';
|
|
24
49
|
/* -------------------------------------------------------------------------- */
|
|
25
|
-
/*
|
|
50
|
+
/* Re-exports — the canonical sonner API */
|
|
51
|
+
/* -------------------------------------------------------------------------- */
|
|
52
|
+
/**
|
|
53
|
+
* Imperative toast API. Backed by sonner.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* toast('Saved');
|
|
57
|
+
* toast.success('Profile updated');
|
|
58
|
+
* toast.error('Network error', { description: 'Retrying in 5s…' });
|
|
59
|
+
* toast.loading('Compiling…');
|
|
60
|
+
* toast.promise(savePost(), {
|
|
61
|
+
* loading: 'Saving…',
|
|
62
|
+
* success: 'Saved!',
|
|
63
|
+
* error: 'Save failed',
|
|
64
|
+
* });
|
|
65
|
+
* toast.dismiss(id);
|
|
66
|
+
*/
|
|
67
|
+
export const toast = sonnerToast;
|
|
68
|
+
/**
|
|
69
|
+
* Mount `<Toaster />` once near the root of your app (e.g. in `layout.tsx`).
|
|
70
|
+
* Forwards every prop to sonner's `<Toaster />`; the only addition is the
|
|
71
|
+
* MiHCM token styling applied via `toastOptions.classNames`.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // app/layout.tsx
|
|
75
|
+
* import { Toaster } from '@mihcm/ui/Toast';
|
|
76
|
+
* <Toaster richColors closeButton position="bottom-right" />
|
|
77
|
+
*/
|
|
78
|
+
export function Toaster({ position = 'bottom-right', expand = false, richColors = false, closeButton = false, visibleToasts = 3, duration = 5000, theme = 'system', toastOptions, className, appearance, ...rest }) {
|
|
79
|
+
void appearance; /* reserved — sonner already handles visual variants via richColors */
|
|
80
|
+
/*
|
|
81
|
+
* Sonner injects its own CSS that uses these variables to colour each
|
|
82
|
+
* row. By mapping them to MiHCM design-system tokens here, the surface,
|
|
83
|
+
* border, and type tints inherit the brand palette AND light/dark mode
|
|
84
|
+
* flips automatically — no need to fight specificity with Tailwind.
|
|
85
|
+
*/
|
|
86
|
+
const sonnerCssVars = {
|
|
87
|
+
'--normal-bg': 'var(--color-card)',
|
|
88
|
+
'--normal-text': 'var(--color-card-foreground)',
|
|
89
|
+
'--normal-border': 'var(--color-border)',
|
|
90
|
+
'--success-bg': 'var(--color-success-50, color-mix(in oklab, var(--color-success) 12%, var(--color-card)))',
|
|
91
|
+
'--success-text': 'var(--color-success-700, var(--color-success))',
|
|
92
|
+
'--success-border': 'var(--color-success)',
|
|
93
|
+
'--error-bg': 'var(--color-destructive-50, color-mix(in oklab, var(--color-destructive) 12%, var(--color-card)))',
|
|
94
|
+
'--error-text': 'var(--color-destructive-700, var(--color-destructive))',
|
|
95
|
+
'--error-border': 'var(--color-destructive)',
|
|
96
|
+
'--warning-bg': 'var(--color-warning-50, color-mix(in oklab, var(--color-warning) 12%, var(--color-card)))',
|
|
97
|
+
'--warning-text': 'var(--color-warning-700, var(--color-warning))',
|
|
98
|
+
'--warning-border': 'var(--color-warning)',
|
|
99
|
+
'--info-bg': 'var(--color-primary-50, color-mix(in oklab, var(--color-primary) 12%, var(--color-card)))',
|
|
100
|
+
'--info-text': 'var(--color-primary-700, var(--color-primary))',
|
|
101
|
+
'--info-border': 'var(--color-primary)',
|
|
102
|
+
};
|
|
103
|
+
return (_jsx(SonnerToaster, { position: position, expand: expand, richColors: richColors, closeButton: closeButton, visibleToasts: visibleToasts, duration: duration, theme: theme, className: cn('mihcm-toaster', className), style: { ...sonnerCssVars, ...(rest.style ?? {}) }, toastOptions: {
|
|
104
|
+
...toastOptions,
|
|
105
|
+
classNames: {
|
|
106
|
+
/*
|
|
107
|
+
* Surface — the toast row itself. Polished default look:
|
|
108
|
+
* - bg-card / text-card-foreground (theme-aware)
|
|
109
|
+
* - shadow-mi-modal for a soft floating elevation
|
|
110
|
+
* - rounded-xl + slightly bigger padding for a modern feel
|
|
111
|
+
* - ring-1 ring-border for crisp edge separation in light mode
|
|
112
|
+
* (the border-border on its own can look washed out)
|
|
113
|
+
* - max-width capped so long messages don't sprawl
|
|
114
|
+
*/
|
|
115
|
+
toast: cn('group toast pointer-events-auto flex w-full items-start gap-3 rounded-xl border border-border bg-card p-4 pr-12', 'text-card-foreground shadow-mi-modal ring-1 ring-border/40', toastOptions?.classNames?.toast),
|
|
116
|
+
title: cn('text-sm font-semibold leading-snug text-foreground', toastOptions?.classNames?.title),
|
|
117
|
+
description: cn('text-sm text-muted-foreground leading-snug', toastOptions?.classNames?.description),
|
|
118
|
+
actionButton: cn('inline-flex shrink-0 items-center rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', toastOptions?.classNames?.actionButton),
|
|
119
|
+
cancelButton: cn('inline-flex shrink-0 items-center rounded-md border border-border bg-transparent px-3 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', toastOptions?.classNames?.cancelButton),
|
|
120
|
+
closeButton: cn('absolute right-2 top-2 grid size-6 place-items-center rounded-md border border-transparent bg-transparent text-foreground/60 transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', toastOptions?.classNames?.closeButton),
|
|
121
|
+
icon: cn('mt-0.5 shrink-0 [&_svg]:size-5', toastOptions?.classNames?.icon),
|
|
122
|
+
/*
|
|
123
|
+
* Per-type styling — sonner adds `data-type="success|error|…"`
|
|
124
|
+
* to the toast root. Use richColors-like surface treatment so
|
|
125
|
+
* tone is unmistakeable even without sonner's own richColors
|
|
126
|
+
* (which forces hard-coded colours).
|
|
127
|
+
*/
|
|
128
|
+
default: cn('border-border bg-card text-card-foreground', toastOptions?.classNames?.default),
|
|
129
|
+
success: cn('border-success/30 bg-success/10 text-foreground [&_[data-icon]]:text-success', toastOptions?.classNames?.success),
|
|
130
|
+
error: cn('border-destructive/30 bg-destructive/10 text-foreground [&_[data-icon]]:text-destructive', toastOptions?.classNames?.error),
|
|
131
|
+
warning: cn('border-warning/30 bg-warning/10 text-foreground [&_[data-icon]]:text-warning', toastOptions?.classNames?.warning),
|
|
132
|
+
info: cn('border-primary/30 bg-primary/10 text-foreground [&_[data-icon]]:text-primary', toastOptions?.classNames?.info),
|
|
133
|
+
loading: cn('border-border bg-card text-foreground', toastOptions?.classNames?.loading),
|
|
134
|
+
},
|
|
135
|
+
}, ...rest }));
|
|
136
|
+
}
|
|
137
|
+
/* -------------------------------------------------------------------------- */
|
|
138
|
+
/* Backward-compat layer */
|
|
139
|
+
/* */
|
|
140
|
+
/* The previous MiHCM Toast API exported `ToastProvider`, `Toast`, */
|
|
141
|
+
/* `ToastTitle`, `ToastDescription`, `ToastAction`, `ToastClose`. Keep them */
|
|
142
|
+
/* as thin shims so existing call sites stay green. New code should import */
|
|
143
|
+
/* `Toaster` + `toast` only. */
|
|
26
144
|
/* -------------------------------------------------------------------------- */
|
|
27
|
-
|
|
28
|
-
|
|
145
|
+
/**
|
|
146
|
+
* @deprecated Use `<Toaster />` instead. Kept as a wrapping component for
|
|
147
|
+
* pre-sonner consumers — renders children, then mounts the Toaster portal.
|
|
148
|
+
*/
|
|
149
|
+
export function ToastProvider({ children, ...rest }) {
|
|
150
|
+
return (_jsxs(_Fragment, { children: [children, _jsx(Toaster, { ...rest })] }));
|
|
151
|
+
}
|
|
152
|
+
/* The original `toastVariants` CVA — kept for any consumer that referenced
|
|
153
|
+
* its variant union types or used it to style a stand-alone Toast row. */
|
|
154
|
+
export const toastVariants = cva('pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-lg border p-4 shadow-lg transition-all duration-300 ease-out', {
|
|
29
155
|
variants: {
|
|
30
156
|
variant: {
|
|
31
157
|
default: 'border-border bg-card text-foreground',
|
|
@@ -55,140 +181,24 @@ export const toastVariants = cva('pointer-events-auto relative flex w-full items
|
|
|
55
181
|
],
|
|
56
182
|
defaultVariants: { variant: 'default', appearance: 'soft' },
|
|
57
183
|
});
|
|
58
|
-
|
|
59
|
-
function getStore() {
|
|
60
|
-
const g = globalThis;
|
|
61
|
-
if (!g[STORE_KEY]) {
|
|
62
|
-
g[STORE_KEY] = { toasts: [], listeners: new Set(), viewports: new Set(), counter: 0, viewportCounter: 0 };
|
|
63
|
-
}
|
|
64
|
-
return g[STORE_KEY];
|
|
65
|
-
}
|
|
66
|
-
function notify() {
|
|
67
|
-
for (const l of getStore().listeners)
|
|
68
|
-
l();
|
|
69
|
-
}
|
|
70
|
-
function getSnapshot() {
|
|
71
|
-
return getStore().toasts;
|
|
72
|
-
}
|
|
73
|
-
function getViewportSnapshot() {
|
|
74
|
-
return Array.from(getStore().viewports).at(-1);
|
|
75
|
-
}
|
|
76
|
-
function subscribe(listener) {
|
|
77
|
-
const store = getStore();
|
|
78
|
-
store.listeners.add(listener);
|
|
79
|
-
return () => store.listeners.delete(listener);
|
|
80
|
-
}
|
|
81
|
-
/** Imperative API — call from anywhere to show a toast. */
|
|
82
|
-
export function toast(data) {
|
|
83
|
-
const store = getStore();
|
|
84
|
-
const id = `toast-${++store.counter}-${Date.now()}`;
|
|
85
|
-
const entry = { id, ...data };
|
|
86
|
-
store.toasts = [...store.toasts, entry];
|
|
87
|
-
notify();
|
|
88
|
-
return id;
|
|
89
|
-
}
|
|
90
|
-
function dismiss(id) {
|
|
91
|
-
const store = getStore();
|
|
92
|
-
store.toasts = store.toasts.filter((t) => t.id !== id);
|
|
93
|
-
notify();
|
|
94
|
-
}
|
|
95
|
-
/* -------------------------------------------------------------------------- */
|
|
96
|
-
/* Sub-components */
|
|
97
|
-
/* -------------------------------------------------------------------------- */
|
|
98
|
-
const ACCENT = {
|
|
99
|
-
default: 'border-l-border',
|
|
100
|
-
success: 'border-l-success',
|
|
101
|
-
error: 'border-l-destructive',
|
|
102
|
-
warning: 'border-l-warning',
|
|
103
|
-
accent: 'border-l-accent',
|
|
104
|
-
};
|
|
184
|
+
/** @deprecated Sonner renders rows internally. Kept for stand-alone use. */
|
|
105
185
|
export const Toast = forwardRef(function Toast({ className, variant = 'default', appearance = 'soft', children, ...props }, ref) {
|
|
106
|
-
return (_jsx("div", { ref: ref, className: cn(toastVariants({ variant, appearance }),
|
|
186
|
+
return (_jsx("div", { ref: ref, className: cn(toastVariants({ variant, appearance }), className), ...props, children: children }));
|
|
107
187
|
});
|
|
188
|
+
/** @deprecated Use sonner's title slot in `toast()` instead. */
|
|
108
189
|
export const ToastTitle = forwardRef(function ToastTitle({ className, ...props }, ref) {
|
|
109
190
|
return _jsx("div", { ref: ref, className: cn('text-sm font-semibold', className), ...props });
|
|
110
191
|
});
|
|
192
|
+
/** @deprecated Use sonner's `description` option in `toast()` instead. */
|
|
111
193
|
export const ToastDescription = forwardRef(function ToastDescription({ className, ...props }, ref) {
|
|
112
194
|
return _jsx("div", { ref: ref, className: cn('text-sm text-muted-foreground', className), ...props });
|
|
113
195
|
});
|
|
196
|
+
/** @deprecated Use sonner's `action` option in `toast()` instead. */
|
|
114
197
|
export const ToastAction = forwardRef(function ToastAction({ className, ...props }, ref) {
|
|
115
|
-
return (_jsx("button", { ref: ref, type: "button", className: cn('inline-flex shrink-0 items-center rounded-md border border-border bg-transparent px-3 py-1 '
|
|
116
|
-
'text-sm font-medium transition-colors duration-150 hover:bg-muted focus-visible:outline-none ' +
|
|
117
|
-
'focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', className), ...props }));
|
|
198
|
+
return (_jsx("button", { ref: ref, type: "button", className: cn('inline-flex shrink-0 items-center rounded-md border border-border bg-transparent px-3 py-1 text-sm font-medium transition-colors duration-150 hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', className), ...props }));
|
|
118
199
|
});
|
|
200
|
+
/** @deprecated `closeButton` prop on `<Toaster />` renders this automatically. */
|
|
119
201
|
export const ToastClose = forwardRef(function ToastClose({ className, ...props }, ref) {
|
|
120
|
-
return (_jsx("button", { ref: ref, type: "button", "aria-label": "Close", className: cn('absolute right-2 top-2 rounded-md p-1 text-foreground/50 transition-opacity hover:text-foreground '
|
|
121
|
-
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', className), ...props, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("path", { d: "M18 6 6 18" }), _jsx("path", { d: "m6 6 12 12" })] }) }));
|
|
202
|
+
return (_jsx("button", { ref: ref, type: "button", "aria-label": "Close", className: cn('absolute right-2 top-2 rounded-md p-1 text-foreground/50 transition-opacity hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', className), ...props, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("path", { d: "M18 6 6 18" }), _jsx("path", { d: "m6 6 12 12" })] }) }));
|
|
122
203
|
});
|
|
123
|
-
/* -------------------------------------------------------------------------- */
|
|
124
|
-
/* Individual toast item (manages timer + animation) */
|
|
125
|
-
/* -------------------------------------------------------------------------- */
|
|
126
|
-
const DEFAULT_DURATION = 5000;
|
|
127
|
-
function ToastItem({ data, onDismiss, appearance, }) {
|
|
128
|
-
const [visible, setVisible] = useState(false);
|
|
129
|
-
const [exiting, setExiting] = useState(false);
|
|
130
|
-
const timerRef = useRef(undefined);
|
|
131
|
-
const remainingRef = useRef(data.duration ?? DEFAULT_DURATION);
|
|
132
|
-
// eslint-disable-next-line react-hooks/purity -- initial value only, never re-read during render
|
|
133
|
-
const startRef = useRef(Date.now());
|
|
134
|
-
const startTimer = useCallback(() => {
|
|
135
|
-
startRef.current = Date.now();
|
|
136
|
-
timerRef.current = setTimeout(() => {
|
|
137
|
-
setExiting(true);
|
|
138
|
-
setTimeout(() => onDismiss(data.id), 300);
|
|
139
|
-
}, remainingRef.current);
|
|
140
|
-
}, [data.id, onDismiss]);
|
|
141
|
-
const pauseTimer = useCallback(() => {
|
|
142
|
-
if (timerRef.current) {
|
|
143
|
-
clearTimeout(timerRef.current);
|
|
144
|
-
remainingRef.current -= Date.now() - startRef.current;
|
|
145
|
-
if (remainingRef.current < 0)
|
|
146
|
-
remainingRef.current = 0;
|
|
147
|
-
}
|
|
148
|
-
}, []);
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
// Trigger entry animation on next frame
|
|
151
|
-
const raf = requestAnimationFrame(() => setVisible(true));
|
|
152
|
-
startTimer();
|
|
153
|
-
return () => {
|
|
154
|
-
cancelAnimationFrame(raf);
|
|
155
|
-
if (timerRef.current)
|
|
156
|
-
clearTimeout(timerRef.current);
|
|
157
|
-
};
|
|
158
|
-
}, [startTimer]);
|
|
159
|
-
const handleClose = () => {
|
|
160
|
-
if (timerRef.current)
|
|
161
|
-
clearTimeout(timerRef.current);
|
|
162
|
-
setExiting(true);
|
|
163
|
-
setTimeout(() => onDismiss(data.id), 300);
|
|
164
|
-
};
|
|
165
|
-
const isError = data.variant === 'error';
|
|
166
|
-
return (_jsx("div", { role: "status", "aria-live": isError ? 'assertive' : 'polite', className: cn('transform transition-all duration-300', visible && !exiting ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'), onMouseEnter: pauseTimer, onMouseLeave: startTimer, children: _jsxs(Toast, { variant: data.variant, appearance: data.appearance ?? appearance, children: [data.icon ? _jsx("div", { className: "mt-0.5 shrink-0 text-muted-foreground", children: data.icon }) : null, _jsxs("div", { className: "flex-1 space-y-1 pr-6", children: [_jsx(ToastTitle, { children: data.title }), data.description !== undefined && (_jsx(ToastDescription, { children: data.description })), data.action !== undefined && (_jsx("div", { className: "mt-2", children: _jsx(ToastAction, { onClick: data.action.onClick, children: data.action.label }) }))] }), _jsx(ToastClose, { onClick: handleClose })] }) }));
|
|
167
|
-
}
|
|
168
|
-
/* -------------------------------------------------------------------------- */
|
|
169
|
-
/* ToastProvider */
|
|
170
|
-
/* -------------------------------------------------------------------------- */
|
|
171
|
-
const POSITION_CLASS = {
|
|
172
|
-
'bottom-right': 'bottom-4 right-4',
|
|
173
|
-
'bottom-left': 'bottom-4 left-4',
|
|
174
|
-
'top-right': 'right-4 top-4',
|
|
175
|
-
'top-left': 'left-4 top-4',
|
|
176
|
-
};
|
|
177
|
-
export function ToastProvider({ children, max = 5, position = 'bottom-right', appearance = 'soft' }) {
|
|
178
|
-
const [viewportId] = useState(() => ++getStore().viewportCounter);
|
|
179
|
-
useEffect(() => {
|
|
180
|
-
const store = getStore();
|
|
181
|
-
store.viewports.add(viewportId);
|
|
182
|
-
notify();
|
|
183
|
-
return () => {
|
|
184
|
-
store.viewports.delete(viewportId);
|
|
185
|
-
notify();
|
|
186
|
-
};
|
|
187
|
-
}, [viewportId]);
|
|
188
|
-
const items = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
189
|
-
const activeViewportId = useSyncExternalStore(subscribe, getViewportSnapshot, () => undefined);
|
|
190
|
-
const visible = items.slice(-max);
|
|
191
|
-
const isActiveViewport = viewportId === activeViewportId;
|
|
192
|
-
return (_jsxs(_Fragment, { children: [children, isActiveViewport ? (_jsx("div", { "aria-label": "Notifications", className: cn('pointer-events-none fixed z-[100] flex max-w-[420px] flex-col gap-2', POSITION_CLASS[position]), children: visible.map((t) => (_jsx(ToastItem, { data: t, onDismiss: dismiss, appearance: appearance }, t.id))) })) : null] }));
|
|
193
|
-
}
|
|
194
204
|
//# sourceMappingURL=Toast.js.map
|
package/dist/Toast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb
|
|
1
|
+
{"version":3,"file":"Toast.js","sourceRoot":"","sources":["../src/Toast.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mihcm/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Universal primitives (Button, Input, ...) for React, Next.js, and React Native. Tailwind 4 + NativeWind v5.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -454,6 +454,7 @@
|
|
|
454
454
|
"react-resizable-panels": "4.11.1",
|
|
455
455
|
"react-select": "^5.10.2",
|
|
456
456
|
"recharts": "^3.8.1",
|
|
457
|
+
"sonner": "2.0.7",
|
|
457
458
|
"tailwind-merge": "^3.6.0",
|
|
458
459
|
"tailwind-variants": "3.2.2",
|
|
459
460
|
"vaul": "1.1.2",
|
|
@@ -337,7 +337,7 @@ export const CommandSidebar = forwardRef<HTMLElement, MainSidebarProps>(function
|
|
|
337
337
|
</div>
|
|
338
338
|
{m.item.badge != null && m.item.badge !== false ? (
|
|
339
339
|
shouldWrapBadge(m.item.badge) ? (
|
|
340
|
-
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-
|
|
340
|
+
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-current/15 px-1.5 text-xs font-medium text-current">
|
|
341
341
|
{m.item.badge}
|
|
342
342
|
</span>
|
|
343
343
|
) : (
|
|
@@ -375,7 +375,7 @@ export const CommandSidebar = forwardRef<HTMLElement, MainSidebarProps>(function
|
|
|
375
375
|
<span className="min-w-0 flex-1 truncate">{item.label}</span>
|
|
376
376
|
{item.badge != null && item.badge !== false ? (
|
|
377
377
|
shouldWrapBadge(item.badge) ? (
|
|
378
|
-
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-
|
|
378
|
+
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-current/15 px-1.5 text-xs font-medium text-current">
|
|
379
379
|
{item.badge}
|
|
380
380
|
</span>
|
|
381
381
|
) : (
|
|
@@ -51,7 +51,7 @@ function PanelRow({ item, active, onClick, showChevron, className, activeClassNa
|
|
|
51
51
|
<span className="min-w-0 flex-1 truncate">{item.label}</span>
|
|
52
52
|
{item.badge != null && item.badge !== false ? (
|
|
53
53
|
shouldWrapBadge(item.badge) ? (
|
|
54
|
-
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-
|
|
54
|
+
<span className="ml-auto inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-current/15 px-1.5 text-xs font-medium text-current">
|
|
55
55
|
{item.badge}
|
|
56
56
|
</span>
|
|
57
57
|
) : (
|
|
@@ -83,14 +83,14 @@ function ExpandedRow({
|
|
|
83
83
|
)}
|
|
84
84
|
>
|
|
85
85
|
{item.icon ? (
|
|
86
|
-
<span className="grid size-5 shrink-0 place-items-center text-
|
|
86
|
+
<span className="grid size-5 shrink-0 place-items-center text-inherit [&_svg]:size-full" aria-hidden="true">
|
|
87
87
|
{item.icon}
|
|
88
88
|
</span>
|
|
89
89
|
) : null}
|
|
90
90
|
<span className="min-w-0 flex-1 truncate">{item.label}</span>
|
|
91
91
|
{item.badge != null && item.badge !== false ? (
|
|
92
92
|
shouldWrapBadge(item.badge) ? (
|
|
93
|
-
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-
|
|
93
|
+
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-current/15 px-1.5 text-xs font-medium text-current">
|
|
94
94
|
{item.badge}
|
|
95
95
|
</span>
|
|
96
96
|
) : (
|
|
@@ -104,7 +104,7 @@ function ExpandedRow({
|
|
|
104
104
|
stroke="currentColor"
|
|
105
105
|
strokeWidth="2"
|
|
106
106
|
className={cn(
|
|
107
|
-
'size-3.5 shrink-0
|
|
107
|
+
'size-3.5 shrink-0 transition-transform duration-200',
|
|
108
108
|
isExpanded && 'rotate-90',
|
|
109
109
|
)}
|
|
110
110
|
aria-hidden="true"
|
|
@@ -170,21 +170,21 @@ function ExpandedSearchMatch({ item, breadcrumbs, activeKey, onSelect, itemClass
|
|
|
170
170
|
)}
|
|
171
171
|
>
|
|
172
172
|
{item.icon ? (
|
|
173
|
-
<span className="grid size-5 shrink-0 place-items-center text-
|
|
173
|
+
<span className="grid size-5 shrink-0 place-items-center text-inherit [&_svg]:size-full" aria-hidden="true">
|
|
174
174
|
{item.icon}
|
|
175
175
|
</span>
|
|
176
176
|
) : null}
|
|
177
177
|
<span className="min-w-0 flex-1">
|
|
178
178
|
<span className="block truncate">{item.label}</span>
|
|
179
179
|
{breadcrumbs.length ? (
|
|
180
|
-
<span className="block truncate text-[11px] text-
|
|
180
|
+
<span className="block truncate text-[11px] text-current/60">
|
|
181
181
|
{breadcrumbs.join(' › ')}
|
|
182
182
|
</span>
|
|
183
183
|
) : null}
|
|
184
184
|
</span>
|
|
185
185
|
{item.badge != null && item.badge !== false ? (
|
|
186
186
|
shouldWrapBadge(item.badge) ? (
|
|
187
|
-
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-
|
|
187
|
+
<span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-current/15 px-1.5 text-xs font-medium text-current">
|
|
188
188
|
{item.badge}
|
|
189
189
|
</span>
|
|
190
190
|
) : (
|
|
@@ -142,14 +142,16 @@ export function colorSchemeToStyle(
|
|
|
142
142
|
*/
|
|
143
143
|
if (scheme.hoverBg) out['--mihcm-sidebar-hover-bg'] = scheme.hoverBg;
|
|
144
144
|
/*
|
|
145
|
-
* Tooltip surface uses --color-foreground / --color-background.
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
145
|
+
* Tooltip surface uses --color-foreground / --color-background. To make
|
|
146
|
+
* tooltips automatically contrast with the themed menu surface, default
|
|
147
|
+
* tooltipBg to the menu's fg (so a dark menu gets a light tooltip and
|
|
148
|
+
* vice versa) and tooltipFg to the menu's bg. Explicit tooltipBg/Fg
|
|
149
|
+
* overrides take precedence.
|
|
150
150
|
*/
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
const tooltipBg = scheme.tooltipBg ?? scheme.fg ?? scheme.railFg ?? scheme.panelFg;
|
|
152
|
+
const tooltipFg = scheme.tooltipFg ?? scheme.bg ?? scheme.railBg ?? scheme.panelBg;
|
|
153
|
+
if (tooltipBg) out['--color-foreground'] = tooltipBg;
|
|
154
|
+
if (tooltipFg) out['--color-background'] = tooltipFg;
|
|
153
155
|
return out as CSSProperties;
|
|
154
156
|
}
|
|
155
157
|
|
package/src/MainSidebar/rail.tsx
CHANGED
|
@@ -99,14 +99,14 @@ export const Rail = forwardRef<HTMLElement, RailProps>(function Rail(
|
|
|
99
99
|
'relative grid place-items-center transition-colors outline-none',
|
|
100
100
|
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-foreground/60',
|
|
101
101
|
buttonSize,
|
|
102
|
-
'text-primary-foreground/85 hover:bg-primary-foreground/
|
|
102
|
+
'text-primary-foreground/85 hover:bg-primary-foreground/20 hover:text-primary-foreground',
|
|
103
103
|
'disabled:cursor-not-allowed disabled:opacity-40',
|
|
104
104
|
isActive && 'bg-accent text-accent-foreground hover:bg-accent',
|
|
105
105
|
isActive && activeItemClassName,
|
|
106
106
|
itemClassName,
|
|
107
107
|
)}
|
|
108
108
|
>
|
|
109
|
-
<span className="grid size-[18px] place-items-center [&_svg]:size-full" aria-hidden="true">
|
|
109
|
+
<span className="grid size-[18px] place-items-center text-inherit [&_svg]:size-full" aria-hidden="true">
|
|
110
110
|
{item.icon}
|
|
111
111
|
</span>
|
|
112
112
|
<span className="sr-only">{item.label}</span>
|