@beautinique/frontend-components 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -8,10 +8,10 @@ The components are styled with Tailwind classes and with the tokens (colours, sh
8
8
  ## Installation
9
9
 
10
10
  ```bash
11
- npm install @beautinique/frontend-components @beautinique/frontend-styles @iconify/react
11
+ npm install @beautinique/frontend-components @beautinique/frontend-stores @beautinique/frontend-styles @iconify/react
12
12
  ```
13
13
 
14
- Peer dependencies: `react`, `react-dom`, `@iconify/react`, `@beautinique/frontend-styles`.
14
+ Peer dependencies: `react`, `react-dom`, `@iconify/react`, `@beautinique/frontend-styles` and [`@beautinique/frontend-stores`](../stores/README.md) (the toasts are kept in a store that the app makes with it, see [Toasts](#toasts); that needs `zustand` in the app).
15
15
 
16
16
  ## Setup (once per app)
17
17
 
@@ -32,12 +32,12 @@ generated and the components come out unstyled. (The playground has a page that
32
32
  Components that most screens use live in `/ui` (and, later, `/layout`). Components that few screens
33
33
  use have a path of their own, so an app only bundles what it imports.
34
34
 
35
- | Import path | What it gives |
36
- | --------------------------------------------- | -------------------------------------------------------------------------------------------- |
37
- | `@beautinique/frontend-components` | Everything `/ui` and `/toast` give (the very same values), and the types (`IButton`, ...) |
38
- | `@beautinique/frontend-components/ui` | `Button`, `Tooltip` |
39
- | `@beautinique/frontend-components/toast` | `ToastContainer`, `toaster`, the toast store, and the helpers for toasts that follow uploads |
40
- | `@beautinique/frontend-components/source.css` | The `@source` line of the setup above |
35
+ | Import path | What it gives |
36
+ | --------------------------------------------- | ----------------------------------------------------------------------------------------- |
37
+ | `@beautinique/frontend-components` | Everything `/ui` and `/toast` give (the very same values), and the types (`IButton`, ...) |
38
+ | `@beautinique/frontend-components/ui` | `Button`, `Tooltip` |
39
+ | `@beautinique/frontend-components/toast` | `ToastContainer`, `createToaster`, and the helpers and types of toasts |
40
+ | `@beautinique/frontend-components/source.css` | The `@source` line of the setup above |
41
41
 
42
42
  `Button` can be imported from the root or from `/ui`, it is the same component either way:
43
43
 
@@ -46,25 +46,22 @@ import { Button } from '@beautinique/frontend-components'; // or
46
46
  import { Button } from '@beautinique/frontend-components/ui';
47
47
  ```
48
48
 
49
- The root only re-exports, it does not copy: a toast shown with `toaster` from one path appears in the
50
- `ToastContainer` of the other, and an app that imports only `Button` from the root bundles the same as
51
- with `/ui` (the bundler leaves out what is not used).
49
+ The root only re-exports, it does not copy: an app that imports only `Button` from the root bundles the
50
+ same as with `/ui` (the bundler leaves out what is not used).
52
51
 
53
52
  ```tsx
54
- import { Button, Tooltip } from '@beautinique/frontend-components/ui';
55
- import { ToastContainer, toaster } from '@beautinique/frontend-components/toast';
53
+ import { Button, Tooltip } from '@beautinique/frontend-components';
54
+
55
+ import { toaster } from '@/utils/toaster'; // made in the app, see Toasts
56
56
 
57
57
  const App = () => (
58
- <>
59
- <ToastContainer /> {/* once, near the root of the app */}
60
- <Tooltip title="Save" description="Ctrl + S">
61
- <Button
62
- pattern="primary"
63
- content="Save"
64
- buttonProps={{ onClick: () => toaster.success({ title: 'Saved' }) }}
65
- />
66
- </Tooltip>
67
- </>
58
+ <Tooltip title="Save" description="Ctrl + S">
59
+ <Button
60
+ pattern="primary"
61
+ content="Save"
62
+ buttonProps={{ onClick: () => toaster.success({ title: 'Saved' }) }}
63
+ />
64
+ </Tooltip>
68
65
  );
69
66
  ```
70
67
 
@@ -115,6 +112,38 @@ const hint = (
115
112
 
116
113
  ### Toasts
117
114
 
115
+ The toasts are kept in a store that **the app makes**, once, with the toasts of this package, and gives to the two things that need it: the `ToastContainer` that draws them, and `createToaster` that shows them. The package holds no store of its own, and exports none.
116
+
117
+ ```ts
118
+ // src/stores/toast.store.ts
119
+ import type { TToast } from '@beautinique/frontend-components';
120
+ import { createToastStore } from '@beautinique/frontend-stores';
121
+
122
+ export const useToastStore = createToastStore<TToast>();
123
+ ```
124
+
125
+ ```ts
126
+ // src/utils/toaster.ts - the toaster and the helpers, for the rest of the app to import
127
+ import { createToaster } from '@beautinique/frontend-components';
128
+
129
+ import { useToastStore } from '@/stores/toast.store';
130
+
131
+ export const {
132
+ toaster,
133
+ withProgressToast,
134
+ withUploadsToast,
135
+ runUploadsInToast,
136
+ createUploadsToast,
137
+ } = createToaster(useToastStore);
138
+ ```
139
+
140
+ ```tsx
141
+ // once, near the root of the app
142
+ import { ToastContainer } from '@beautinique/frontend-components';
143
+
144
+ <ToastContainer store={useToastStore} />;
145
+ ```
146
+
118
147
  `toaster` works from anywhere (an API client, a store), not only from components:
119
148
 
120
149
  ```tsx
@@ -127,20 +156,17 @@ toaster.custom({ type: 'custom', children: <p>Any React content</p> });
127
156
  ```
128
157
 
129
158
  Toasts that follow uploads (`withProgressToast` for one request, `createUploadsToast`,
130
- `withUploadsToast` and `runUploadsInToast` for several) are described in `src/toast/uploads.ts`.
159
+ `withUploadsToast` and `runUploadsInToast` for several) come out of `createToaster` too, and are described in `src/toast/uploads.ts`.
131
160
  `runUploadsInToast` shows one toast for the uploads of several fields: the overall progress on the
132
161
  left and a row (waiting, uploading, done or failed) for each field that has files.
133
162
 
163
+ **The store has to be one.** The container and `createToaster` must be given the same store: a toast added to a store that no container draws is never shown. Make it once (a module of the app, imported from there) and the two cannot differ. `createToaster` is also checked by the compiler: it takes a store of this package's `TToast`, nothing else.
164
+
134
165
  The icons of the toasts are bundled, so a toast shows its icon without a network.
135
166
 
136
167
  ## Rules for the code of this package
137
168
 
138
- - **State lives in exactly one import path.** The toast store is part of `/toast` only. A component
139
- of another path that needs it must import it from `@beautinique/frontend-components/toast`, never
140
- copy it: every entry is built as one file, so a copy would be a second store, and a toast shown
141
- through it would never reach the `ToastContainer`. The root is such a path: its built file only
142
- imports `/ui` and `/toast` (`tsup.config.ts` keeps them external, and `built-files.test.ts` checks
143
- the built files after `npm run build`).
169
+ - **The package holds no state.** The toast store is made by the app (`createToastStore` of `@beautinique/frontend-stores`, with this package's `TToast`) and given in, to the `ToastContainer` as a prop and to `createToaster`. Nothing here makes a store or exports one, so the package can never be the cause of two of them. The root only re-exports `/ui` and `/toast` (`tsup.config.ts` keeps them external, and `built-files.test.ts` checks the built files after `npm run build`).
144
170
  - **The root has what most screens use** (`/ui`, later `/layout`, and `/toast`). A component that few
145
171
  screens use gets a path of its own and is not in the root.
146
172
  - **A component's own css lives next to the component** and is imported by it, not by the styles
@@ -153,6 +179,7 @@ The icons of the toasts are bundled, so a toast shows its icon without a network
153
179
 
154
180
  ```bash
155
181
  npm run build # dist (needed by the playground and by the type check of the playground)
182
+ # `@beautinique/frontend-stores` has to be built first (`npm run build` at the root of the repo does it in order)
156
183
  npm run playground # http://localhost:4200 - the components, in a real browser
157
184
  npm test
158
185
  ```
@@ -1,5 +1,7 @@
1
1
  import type { IClassName } from '../types/component.js';
2
- import type { TToastItem } from './types.js';
3
- export declare const Toaster: import("react").MemoExoticComponent<(props: TToastItem & IClassName) => import("react").JSX.Element | null>;
4
- export declare const ToastContainer: () => import("react").JSX.Element | null;
2
+ import type { IToastContainer, TToastItem } from './types.js';
3
+ export declare const Toaster: import("react").MemoExoticComponent<(props: TToastItem & IClassName & {
4
+ onRemove?: (id: string) => void;
5
+ }) => import("react").JSX.Element | null>;
6
+ export declare const ToastContainer: ({ store: useToastStore }: IToastContainer) => import("react").JSX.Element | null;
5
7
  //# sourceMappingURL=ToastContainer.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToastContainer.d.cts","sourceRoot":"","sources":["../../src/toast/ToastContainer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,OAAO,KAAK,EAAuB,UAAU,EAAqB,MAAM,YAAY,CAAC;AA4UrF,eAAO,MAAM,OAAO,8CA1JQ,UAAU,GAAG,UAAU,wCA0JX,CAAC;AAEzC,eAAO,MAAM,cAAc,0CAY1B,CAAC"}
1
+ {"version":3,"file":"ToastContainer.d.cts","sourceRoot":"","sources":["../../src/toast/ToastContainer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EACV,eAAe,EAGf,UAAU,EAEX,MAAM,YAAY,CAAC;AA4UpB,eAAO,MAAM,OAAO,8CAzJQ,UAAU,GAAG,UAAU,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,wCAyJjD,CAAC;AAEzC,eAAO,MAAM,cAAc,GAAI,0BAA0B,eAAe,uCAavE,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import type { IClassName } from '../types/component.js';
2
- import type { TToastItem } from './types.js';
3
- export declare const Toaster: import("react").MemoExoticComponent<(props: TToastItem & IClassName) => import("react").JSX.Element | null>;
4
- export declare const ToastContainer: () => import("react").JSX.Element | null;
2
+ import type { IToastContainer, TToastItem } from './types.js';
3
+ export declare const Toaster: import("react").MemoExoticComponent<(props: TToastItem & IClassName & {
4
+ onRemove?: (id: string) => void;
5
+ }) => import("react").JSX.Element | null>;
6
+ export declare const ToastContainer: ({ store: useToastStore }: IToastContainer) => import("react").JSX.Element | null;
5
7
  //# sourceMappingURL=ToastContainer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToastContainer.d.ts","sourceRoot":"","sources":["../../src/toast/ToastContainer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,OAAO,KAAK,EAAuB,UAAU,EAAqB,MAAM,YAAY,CAAC;AA4UrF,eAAO,MAAM,OAAO,8CA1JQ,UAAU,GAAG,UAAU,wCA0JX,CAAC;AAEzC,eAAO,MAAM,cAAc,0CAY1B,CAAC"}
1
+ {"version":3,"file":"ToastContainer.d.ts","sourceRoot":"","sources":["../../src/toast/ToastContainer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EACV,eAAe,EAGf,UAAU,EAEX,MAAM,YAAY,CAAC;AA4UpB,eAAO,MAAM,OAAO,8CAzJQ,UAAU,GAAG,UAAU,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,wCAyJjD,CAAC;AAEzC,eAAO,MAAM,cAAc,GAAI,0BAA0B,eAAe,uCAavE,CAAC"}
@@ -1,58 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var nanoid = require('nanoid');
4
- var zustand = require('zustand');
5
3
  var react$1 = require('@iconify/react');
6
4
  var react = require('react');
7
5
  var jsxRuntime = require('react/jsx-runtime');
8
6
 
9
7
  var __defProp = Object.defineProperty;
10
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
11
-
12
- // src/lib/constants.ts
13
- var TOAST_TYPES = [
14
- "success",
15
- "error",
16
- "warning",
17
- "progress",
18
- "uploads",
19
- "loading",
20
- "default",
21
- "custom"
22
- ];
23
- var TOAST_TYPE = Object.fromEntries(TOAST_TYPES.map((type) => [type, type]));
24
-
25
- // src/toast/toast.store.ts
26
- var useToastStore = zustand.create((set) => ({
27
- toasts: [],
28
- add: /* @__PURE__ */ __name((toast) => {
29
- const id = nanoid.nanoid();
30
- set((state) => ({ toasts: [...state.toasts, { ...toast, id }] }));
31
- return id;
32
- }, "add"),
33
- update: {
34
- progress: /* @__PURE__ */ __name((id, progress) => {
35
- set((state) => ({
36
- toasts: state.toasts.map((toast) => toast.id === id ? { ...toast, progress } : toast)
37
- }));
38
- }, "progress"),
39
- uploadItem: /* @__PURE__ */ __name((id, itemId, patch) => {
40
- set((state) => ({
41
- toasts: state.toasts.map(
42
- (toast) => toast.id === id && toast.type === TOAST_TYPE.uploads ? {
43
- ...toast,
44
- items: toast.items.map(
45
- (item) => item.id === itemId ? { ...item, ...patch } : item
46
- )
47
- } : toast
48
- )
49
- }));
50
- }, "uploadItem")
51
- },
52
- remove: /* @__PURE__ */ __name((id) => {
53
- set((state) => ({ toasts: state.toasts.filter((t) => t.id !== id) }));
54
- }, "remove")
55
- }));
56
9
  var TOAST_ICON_SETS = [
57
10
  {
58
11
  prefix: "solar",
@@ -114,6 +67,19 @@ var TOAST_ICON_NAMES = TOAST_ICON_SETS.flatMap(
114
67
  ({ prefix, icons }) => Object.keys(icons).map((name) => `${prefix}:${name}`)
115
68
  );
116
69
 
70
+ // src/lib/constants.ts
71
+ var TOAST_TYPES = [
72
+ "success",
73
+ "error",
74
+ "warning",
75
+ "progress",
76
+ "uploads",
77
+ "loading",
78
+ "default",
79
+ "custom"
80
+ ];
81
+ var TOAST_TYPE = Object.fromEntries(TOAST_TYPES.map((type) => [type, type]));
82
+
117
83
  // src/lib/button.ts
118
84
  var PATTERN_CSS = {
119
85
  primary: "text-white bg-sky-blue-burst shadow-primary-btn hover:shadow-primary-btn-hover",
@@ -151,52 +117,6 @@ var Button = /* @__PURE__ */ __name(({
151
117
  )
152
118
  ), "Button");
153
119
 
154
- // src/toast/toaster.ts
155
- var { add, update, remove } = useToastStore.getState();
156
- var toaster = {
157
- success: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.success }), "success"),
158
- error: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.error }), "error"),
159
- warning: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.warning }), "warning"),
160
- default: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.default }), "default"),
161
- loading: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.loading }), "loading"),
162
- custom: /* @__PURE__ */ __name((data) => add(data), "custom"),
163
- progress: {
164
- start: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.progress }), "start"),
165
- update: /* @__PURE__ */ __name((toastId, progress) => {
166
- update.progress(toastId, progress);
167
- }, "update"),
168
- end: /* @__PURE__ */ __name((toastId) => {
169
- remove(toastId);
170
- }, "end")
171
- },
172
- uploads: {
173
- start: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.uploads }), "start"),
174
- update: /* @__PURE__ */ __name((toastId, itemId, patch) => {
175
- update.uploadItem(toastId, itemId, patch);
176
- }, "update")
177
- },
178
- remove: /* @__PURE__ */ __name((toastId) => {
179
- remove(toastId);
180
- }, "remove")
181
- };
182
- var withProgressToast = /* @__PURE__ */ __name(async ({
183
- title,
184
- description,
185
- request
186
- }) => {
187
- const toastId = toaster.progress.start({ title, description, progress: 0 });
188
- try {
189
- const response = await request((event) => {
190
- if (!event.total) return;
191
- toaster.progress.update(toastId, Math.round(event.loaded * 100 / event.total));
192
- });
193
- toaster.progress.update(toastId, 100);
194
- return response;
195
- } finally {
196
- toaster.progress.end(toastId);
197
- }
198
- }, "withProgressToast");
199
-
200
120
  // src/toast/uploads.ts
201
121
  var sum = /* @__PURE__ */ __name((numbers) => numbers.reduce((total, value) => total + value, 0), "sum");
202
122
  var isUploadItemSettled = /* @__PURE__ */ __name(({ status }) => status === "success" || status === "error", "isUploadItemSettled");
@@ -246,92 +166,95 @@ var getUploadsSummary = /* @__PURE__ */ __name((items) => {
246
166
  };
247
167
  }, "getUploadsSummary");
248
168
  var getFormDataFiles = /* @__PURE__ */ __name((data, key = "files") => data.getAll(key).filter((value) => value instanceof File), "getFormDataFiles");
249
- var createUploadsToast = /* @__PURE__ */ __name(({
250
- title,
251
- description,
252
- items
253
- }) => {
254
- if (items.length === 0) return void 0;
255
- const statuses = new Map(items.map(({ id }) => [id, "pending"]));
256
- const toastId = toaster.uploads.start({
169
+ var createUploadsHelpers = /* @__PURE__ */ __name((toaster) => {
170
+ const createUploadsToast = /* @__PURE__ */ __name(({
257
171
  title,
258
172
  description,
259
- items: items.map(({ id, label, files }) => {
260
- const fileSizes = files.map(({ size }) => size);
261
- return { id, label, status: "pending", loaded: 0, total: sum(fileSizes), fileSizes };
262
- })
263
- });
264
- const patch = /* @__PURE__ */ __name((id, changes) => {
265
- if (changes.status) statuses.set(id, changes.status);
266
- toaster.uploads.update(toastId, id, changes);
267
- }, "patch");
268
- return {
269
- item: /* @__PURE__ */ __name((id) => ({
270
- run: /* @__PURE__ */ __name(async (request) => {
271
- patch(id, { status: "uploading" });
272
- try {
273
- const result = await request((event) => {
274
- if (!event.total) return;
275
- patch(id, { loaded: event.loaded, total: event.total });
276
- });
277
- patch(id, { status: "success" });
278
- return result;
279
- } catch (error) {
280
- patch(id, { status: "error" });
281
- throw error;
173
+ items
174
+ }) => {
175
+ if (items.length === 0) return void 0;
176
+ const statuses = new Map(items.map(({ id }) => [id, "pending"]));
177
+ const toastId = toaster.uploads.start({
178
+ title,
179
+ description,
180
+ items: items.map(({ id, label, files }) => {
181
+ const fileSizes = files.map(({ size }) => size);
182
+ return { id, label, status: "pending", loaded: 0, total: sum(fileSizes), fileSizes };
183
+ })
184
+ });
185
+ const patch = /* @__PURE__ */ __name((id, changes) => {
186
+ if (changes.status) statuses.set(id, changes.status);
187
+ toaster.uploads.update(toastId, id, changes);
188
+ }, "patch");
189
+ return {
190
+ item: /* @__PURE__ */ __name((id) => ({
191
+ run: /* @__PURE__ */ __name(async (request) => {
192
+ patch(id, { status: "uploading" });
193
+ try {
194
+ const result = await request((event) => {
195
+ if (!event.total) return;
196
+ patch(id, { loaded: event.loaded, total: event.total });
197
+ });
198
+ patch(id, { status: "success" });
199
+ return result;
200
+ } catch (error) {
201
+ patch(id, { status: "error" });
202
+ throw error;
203
+ }
204
+ }, "run")
205
+ }), "item"),
206
+ end: /* @__PURE__ */ __name(() => {
207
+ for (const [id, status] of statuses) {
208
+ if (status === "pending" || status === "uploading") patch(id, { status: "error" });
282
209
  }
283
- }, "run")
284
- }), "item"),
285
- end: /* @__PURE__ */ __name(() => {
286
- for (const [id, status] of statuses) {
287
- if (status === "pending" || status === "uploading") patch(id, { status: "error" });
288
- }
289
- }, "end")
290
- };
291
- }, "createUploadsToast");
292
- var withUploadsToast = /* @__PURE__ */ __name(async ({
293
- title,
294
- description,
295
- files,
296
- request
297
- }) => {
298
- const group = createUploadsToast({
210
+ }, "end")
211
+ };
212
+ }, "createUploadsToast");
213
+ const withUploadsToast = /* @__PURE__ */ __name(async ({
299
214
  title,
300
215
  description,
301
- items: files.length > 0 ? [{ id: "files", label: description ?? title, files }] : []
302
- });
303
- if (!group) return request(() => void 0);
304
- try {
305
- return await group.item("files").run(request);
306
- } finally {
307
- group.end();
308
- }
309
- }, "withUploadsToast");
310
- var runUploadsInToast = /* @__PURE__ */ __name(async ({
311
- title,
312
- description,
313
- uploads,
314
- run
315
- }) => {
316
- const group = createUploadsToast({
216
+ files,
217
+ request
218
+ }) => {
219
+ const group = createUploadsToast({
220
+ title,
221
+ description,
222
+ items: files.length > 0 ? [{ id: "files", label: description ?? title, files }] : []
223
+ });
224
+ if (!group) return request(() => void 0);
225
+ try {
226
+ return await group.item("files").run(request);
227
+ } finally {
228
+ group.end();
229
+ }
230
+ }, "withUploadsToast");
231
+ const runUploadsInToast = /* @__PURE__ */ __name(async ({
317
232
  title,
318
233
  description,
319
- items: uploads.filter(({ files }) => files.length > 0)
320
- });
321
- const settled = await Promise.allSettled(uploads.map(({ id }) => run(id, group?.item(id))));
322
- group?.end();
323
- const results = {};
324
- settled.forEach((result, index) => {
325
- if (result.status === "fulfilled") results[uploads[index].id] = result.value;
326
- });
327
- const failure = settled.find(
328
- (result) => result.status === "rejected"
329
- );
330
- if (failure) {
331
- throw failure.reason instanceof Error ? failure.reason : new Error("Upload failed");
332
- }
333
- return results;
334
- }, "runUploadsInToast");
234
+ uploads,
235
+ run
236
+ }) => {
237
+ const group = createUploadsToast({
238
+ title,
239
+ description,
240
+ items: uploads.filter(({ files }) => files.length > 0)
241
+ });
242
+ const settled = await Promise.allSettled(uploads.map(({ id }) => run(id, group?.item(id))));
243
+ group?.end();
244
+ const results = {};
245
+ settled.forEach((result, index) => {
246
+ if (result.status === "fulfilled") results[uploads[index].id] = result.value;
247
+ });
248
+ const failure = settled.find(
249
+ (result) => result.status === "rejected"
250
+ );
251
+ if (failure) {
252
+ throw failure.reason instanceof Error ? failure.reason : new Error("Upload failed");
253
+ }
254
+ return results;
255
+ }, "runUploadsInToast");
256
+ return { createUploadsToast, withUploadsToast, runUploadsInToast };
257
+ }, "createUploadsHelpers");
335
258
  registerToastIcons();
336
259
  var CircularProgress = /* @__PURE__ */ __name(({ progress, ...props }) => {
337
260
  const radius = 10;
@@ -478,9 +401,9 @@ var ToasterItem = /* @__PURE__ */ __name((props) => {
478
401
  buttonProps,
479
402
  isClosable = true,
480
403
  autoClose = true,
481
- closeTimer = 5e3
404
+ closeTimer = 5e3,
405
+ onRemove
482
406
  } = props;
483
- const remove2 = useToastStore((s) => s.remove);
484
407
  const [visible, setVisible] = react.useState(false);
485
408
  const [mounted, setMounted] = react.useState(true);
486
409
  const timeoutRef = react.useRef(null);
@@ -502,9 +425,9 @@ var ToasterItem = /* @__PURE__ */ __name((props) => {
502
425
  setVisible(false);
503
426
  timeoutRef.current = setTimeout(() => {
504
427
  setMounted(false);
505
- remove2(props.id);
428
+ onRemove?.(props.id);
506
429
  }, 300);
507
- }, [remove2, props.id]);
430
+ }, [onRemove, props.id]);
508
431
  react.useEffect(() => {
509
432
  const enterTimer = setTimeout(() => {
510
433
  setVisible(true);
@@ -590,25 +513,73 @@ var ToasterItem = /* @__PURE__ */ __name((props) => {
590
513
  );
591
514
  }, "ToasterItem");
592
515
  var Toaster = react.memo(ToasterItem);
593
- var ToastContainer = /* @__PURE__ */ __name(() => {
516
+ var ToastContainer = /* @__PURE__ */ __name(({ store: useToastStore }) => {
594
517
  const toasts = useToastStore((s) => s.toasts);
518
+ const remove = useToastStore((s) => s.remove);
595
519
  if (toasts.length === 0) return null;
596
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "base:max-w-sm fixed right-2 bottom-2 z-999 flex w-full max-w-xs flex-col gap-2 sm:max-w-md md:right-4 md:bottom-4 md:max-w-lg lg:right-8 lg:bottom-8 lg:max-w-xl", children: toasts.map((toast) => /* @__PURE__ */ jsxRuntime.jsx(Toaster, { ...toast, id: toast.id }, toast.id)) });
520
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "base:max-w-sm fixed right-2 bottom-2 z-999 flex w-full max-w-xs flex-col gap-2 sm:max-w-md md:right-4 md:bottom-4 md:max-w-lg lg:right-8 lg:bottom-8 lg:max-w-xl", children: toasts.map((toast) => /* @__PURE__ */ jsxRuntime.jsx(Toaster, { ...toast, id: toast.id, onRemove: remove }, toast.id)) });
597
521
  }, "ToastContainer");
598
522
 
523
+ // src/toast/toaster.ts
524
+ var createToasterApi = /* @__PURE__ */ __name((store) => {
525
+ const { add, update, remove } = store.getState();
526
+ return {
527
+ success: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.success }), "success"),
528
+ error: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.error }), "error"),
529
+ warning: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.warning }), "warning"),
530
+ default: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.default }), "default"),
531
+ loading: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.loading }), "loading"),
532
+ custom: /* @__PURE__ */ __name((data) => add(data), "custom"),
533
+ progress: {
534
+ start: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.progress }), "start"),
535
+ update: /* @__PURE__ */ __name((toastId, progress) => {
536
+ update.progress(toastId, progress);
537
+ }, "update"),
538
+ end: /* @__PURE__ */ __name((toastId) => {
539
+ remove(toastId);
540
+ }, "end")
541
+ },
542
+ uploads: {
543
+ start: /* @__PURE__ */ __name((data) => add({ ...data, type: TOAST_TYPE.uploads }), "start"),
544
+ update: /* @__PURE__ */ __name((toastId, itemId, patch) => {
545
+ update.uploadItem(toastId, itemId, patch);
546
+ }, "update")
547
+ },
548
+ remove: /* @__PURE__ */ __name((toastId) => {
549
+ remove(toastId);
550
+ }, "remove")
551
+ };
552
+ }, "createToasterApi");
553
+ var createToaster = /* @__PURE__ */ __name((store) => {
554
+ const toaster = createToasterApi(store);
555
+ const withProgressToast = /* @__PURE__ */ __name(async ({
556
+ title,
557
+ description,
558
+ request
559
+ }) => {
560
+ const toastId = toaster.progress.start({ title, description, progress: 0 });
561
+ try {
562
+ const response = await request((event) => {
563
+ if (!event.total) return;
564
+ toaster.progress.update(toastId, Math.round(event.loaded * 100 / event.total));
565
+ });
566
+ toaster.progress.update(toastId, 100);
567
+ return response;
568
+ } finally {
569
+ toaster.progress.end(toastId);
570
+ }
571
+ }, "withProgressToast");
572
+ return { toaster, withProgressToast, ...createUploadsHelpers(toaster) };
573
+ }, "createToaster");
574
+
599
575
  exports.TOAST_ICON_NAMES = TOAST_ICON_NAMES;
600
576
  exports.ToastContainer = ToastContainer;
601
577
  exports.Toaster = Toaster;
602
- exports.createUploadsToast = createUploadsToast;
578
+ exports.createToaster = createToaster;
603
579
  exports.getFormDataFiles = getFormDataFiles;
604
580
  exports.getUploadItemFilesDone = getUploadItemFilesDone;
605
581
  exports.getUploadItemPercent = getUploadItemPercent;
606
582
  exports.getUploadsSummary = getUploadsSummary;
607
583
  exports.isUploadItemSettled = isUploadItemSettled;
608
- exports.runUploadsInToast = runUploadsInToast;
609
- exports.toaster = toaster;
610
- exports.useToastStore = useToastStore;
611
- exports.withProgressToast = withProgressToast;
612
- exports.withUploadsToast = withUploadsToast;
613
584
  //# sourceMappingURL=index.cjs.map
614
585
  //# sourceMappingURL=index.cjs.map