@ryuzaki13/react-foundation-lib 2.3.1 → 2.4.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/README.md CHANGED
@@ -618,9 +618,9 @@ Pipeline — runtime форматирования ячейки. Он прини
618
618
 
619
619
  | API | Поведение |
620
620
  | -------------------------- | ------------------------------------------------------------------------------------------------- |
621
- | `createNotificationsStore` | Zustand vanilla store, максимум 6 уведомлений, TTL timers, `push/update/upsert/dismiss/clear`. |
622
- | `bindNotifications` | Привязка store к imperative `notify`. |
623
- | `notify` | `push/upsert/update/dismiss/clear`, short methods `success/info/warning/error`, progress pattern. |
621
+ | `createNotificationsStore` | Zustand store: до 6 активных toast, полная session-history и TTL timers. |
622
+ | `bindNotifications` | Привязка store к imperative `notify`. |
623
+ | `notify` | `push/upsert/update/dismiss/clear/clearHistory`, short methods и progress pattern. |
624
624
 
625
625
  `notify` требует bound store; без provider будет ошибка.
626
626
 
@@ -11,6 +11,7 @@ var t = null, n = (e) => (t = e, () => {
11
11
  update: (e, t) => r().getState().actions.update(e, t),
12
12
  dismiss: (e) => r().getState().actions.dismiss(e),
13
13
  clear: () => r().getState().actions.clear(),
14
+ clearHistory: () => r().getState().actions.clearHistory(),
14
15
  success: (e, t) => r().getState().actions.push({
15
16
  type: "success",
16
17
  message: e,
@@ -64,7 +65,8 @@ var t = null, n = (e) => (t = e, () => {
64
65
  dismiss: () => i.dismiss(o)
65
66
  };
66
67
  }
67
- }), a = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`, o = 6, s = () => {
68
+ }), a = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`, o = 6;
69
+ function s() {
68
70
  let t = /* @__PURE__ */ new Map(), n = (e) => {
69
71
  let n = t.get(e);
70
72
  n && (window.clearTimeout(n), t.delete(e));
@@ -76,30 +78,39 @@ var t = null, n = (e) => (t = e, () => {
76
78
  t.set(r, a);
77
79
  }, i = e()((e, s) => ({
78
80
  items: [],
81
+ history: [],
79
82
  actions: {
80
83
  push: (t) => {
81
- let n = t.id ?? a(), s = Date.now(), c = {
82
- id: n,
84
+ let c = t.id ?? a(), l = Date.now(), u = {
85
+ id: c,
83
86
  type: t.type,
84
87
  title: t.title,
85
88
  message: t.message,
86
- createdAt: s,
89
+ createdAt: l,
87
90
  ttlMs: t.ttlMs,
88
91
  dismissible: t.dismissible ?? !0,
89
92
  actions: t.actions
90
- };
91
- return e((e) => ({ items: [c, ...e.items.filter((e) => e.id !== n)].slice(0, o) })), r(i, n, c.ttlMs), n;
93
+ }, d = s().items, f = [u, ...d.filter((e) => e.id !== c)].slice(0, o), p = new Set(f.map((e) => e.id));
94
+ return d.forEach((e) => {
95
+ p.has(e.id) || n(e.id);
96
+ }), e((e) => ({
97
+ items: f,
98
+ history: [u, ...e.history.filter((e) => e.id !== c)]
99
+ })), r(i, c, u.ttlMs), c;
92
100
  },
93
101
  update: (t, n) => {
94
- let { items: a } = s(), o = a.findIndex((e) => e.id === t);
95
- if (o === -1) return !1;
96
- let c = a[o], l = {
97
- ...c,
102
+ let a = s().items.find((e) => e.id === t);
103
+ if (!a) return !1;
104
+ let o = {
105
+ ...a,
98
106
  ...n,
99
- id: c.id,
100
- createdAt: c.createdAt
107
+ id: a.id,
108
+ createdAt: a.createdAt
101
109
  };
102
- return e((e) => ({ items: e.items.map((e) => e.id === t ? l : e) })), "ttlMs" in n && r(i, t, l.ttlMs), !0;
110
+ return e((e) => ({
111
+ items: e.items.map((e) => e.id === t ? o : e),
112
+ history: e.history.map((e) => e.id === t ? o : e)
113
+ })), "ttlMs" in n && r(i, t, o.ttlMs), !0;
103
114
  },
104
115
  upsert: (e) => s().actions.update(e.id, {
105
116
  type: e.type,
@@ -113,11 +124,14 @@ var t = null, n = (e) => (t = e, () => {
113
124
  },
114
125
  clear: () => {
115
126
  t.forEach((e) => window.clearTimeout(e)), t.clear(), e({ items: [] });
127
+ },
128
+ clearHistory: () => {
129
+ e({ history: [] });
116
130
  }
117
131
  }
118
132
  }));
119
133
  return i;
120
- };
134
+ }
121
135
  //#endregion
122
136
  export { n as bindNotifications, s as createNotificationsStore, i as notify };
123
137
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/notifications/notify.ts","../../src/notifications/store.ts"],"sourcesContent":["import type { NotificationPushInput, NotificationsStoreApi, NotificationUpdatePatch } from \"./store\";\nimport type { NotificationId } from \"./types\";\n\nlet boundStore: NotificationsStoreApi | null = null;\n\nexport const bindNotifications = (store: NotificationsStoreApi) => {\n\tboundStore = store;\n\n\treturn () => {\n\t\tif (boundStore === store) {\n\t\t\tboundStore = null;\n\t\t}\n\t};\n};\n\nconst requireStore = (): NotificationsStoreApi => {\n\tif (!boundStore) throw new Error(\"Notifications store is not bound. Wrap app with NotificationsProvider.\");\n\treturn boundStore;\n};\n\nexport const notify = Object.freeze({\n\tpush: (input: NotificationPushInput) => requireStore().getState().actions.push(input),\n\tupsert: (input: NotificationPushInput & { id: NotificationId }) => requireStore().getState().actions.upsert(input),\n\tupdate: (id: NotificationId, patch: NotificationUpdatePatch) => requireStore().getState().actions.update(id, patch),\n\n\tdismiss: (id: NotificationId) => requireStore().getState().actions.dismiss(id),\n\tclear: () => requireStore().getState().actions.clear(),\n\n\tsuccess: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"success\", message, ...opts }),\n\tinfo: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"info\", message, ...opts }),\n\twarning: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"warning\", message, ...opts }),\n\terror: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"error\", message, ...opts }),\n\n\t/**\n\t * Удобный паттерн \"долгой операции\":\n\t * const op = notify.progress(\"Сохраняю...\");\n\t * ... op.success(\"Готово\") или op.error(\"Ошибка\")\n\t */\n\tprogress: (message: string, opts?: { id?: NotificationId; title?: string }) => {\n\t\tconst store = requireStore();\n\t\tconst id = opts?.id;\n\n\t\tif (id) {\n\t\t\tstore.getState().actions.upsert({\n\t\t\t\tid,\n\t\t\t\ttype: \"info\",\n\t\t\t\ttitle: opts?.title,\n\t\t\t\tmessage,\n\t\t\t\tdismissible: false\n\t\t\t});\n\t\t}\n\n\t\tconst notificationId =\n\t\t\tid ??\n\t\t\tstore.getState().actions.push({\n\t\t\t\ttype: \"info\",\n\t\t\t\ttitle: opts?.title,\n\t\t\t\tmessage,\n\t\t\t\tdismissible: false\n\t\t\t});\n\n\t\treturn {\n\t\t\tid: notificationId,\n\t\t\tupdate: (nextMessage: string) => notify.update(notificationId, { message: nextMessage }),\n\t\t\tsuccess: (nextMessage: string, ttlMs = 2500) =>\n\t\t\t\tnotify.update(notificationId, { type: \"success\", message: nextMessage, dismissible: true, ttlMs }),\n\t\t\terror: (nextMessage: string, ttlMs = 5000) =>\n\t\t\t\tnotify.update(notificationId, { type: \"error\", message: nextMessage, dismissible: true, ttlMs }),\n\t\t\tdismiss: () => notify.dismiss(notificationId)\n\t\t};\n\t}\n});\n","import { createStore, StoreApi } from \"zustand\";\n\nimport { Notification, NotificationAction, NotificationId, NotificationType } from \"./types\";\n\nexport type NotificationPushInput = {\n\tid?: NotificationId;\n\ttype: NotificationType;\n\ttitle?: string;\n\tmessage: string;\n\tttlMs?: number;\n\tdismissible?: boolean;\n\tactions?: NotificationAction[];\n};\n\nexport type NotificationUpdatePatch = Partial<Omit<Notification, \"id\" | \"createdAt\">> & {\n\t// ttlMs можно менять; createdAt — нет\n};\n\ntype NotificationsState = {\n\titems: Notification[];\n};\n\ntype NotificationsActions = {\n\tpush: (input: NotificationPushInput) => NotificationId;\n\tupdate: (id: NotificationId, patch: NotificationUpdatePatch) => boolean;\n\tupsert: (input: NotificationPushInput & { id: NotificationId }) => NotificationId;\n\tdismiss: (id: NotificationId) => void;\n\tclear: () => void;\n};\n\nexport type NotificationsStore = NotificationsState & {\n\tactions: NotificationsActions;\n};\n\nconst genId = (): NotificationId => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n\nconst MAX_ITEMS = 6;\n\nexport const createNotificationsStore = () => {\n\tconst timers = new Map<NotificationId, number>();\n\n\tconst clearTimer = (id: NotificationId) => {\n\t\tconst t = timers.get(id);\n\t\tif (t) {\n\t\t\twindow.clearTimeout(t);\n\t\t\ttimers.delete(id);\n\t\t}\n\t};\n\n\tconst armTimer = (store: StoreApi<NotificationsStore>, id: NotificationId, ttlMs: number = 10_000) => {\n\t\tclearTimer(id);\n\t\tif (ttlMs <= 0) return;\n\n\t\tconst t = window.setTimeout(() => {\n\t\t\tstore.getState().actions.dismiss(id);\n\t\t}, ttlMs);\n\n\t\ttimers.set(id, t);\n\t};\n\n\tconst store = createStore<NotificationsStore>()((set, get) => ({\n\t\titems: [],\n\n\t\tactions: {\n\t\t\tpush: (input) => {\n\t\t\t\tconst id = input.id ?? genId();\n\t\t\t\tconst now = Date.now();\n\n\t\t\t\tconst notif: Notification = {\n\t\t\t\t\tid,\n\t\t\t\t\ttype: input.type,\n\t\t\t\t\ttitle: input.title,\n\t\t\t\t\tmessage: input.message,\n\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\tttlMs: input.ttlMs,\n\t\t\t\t\tdismissible: input.dismissible ?? true,\n\t\t\t\t\tactions: input.actions\n\t\t\t\t};\n\n\t\t\t\tset((s) => ({\n\t\t\t\t\titems: [notif, ...s.items.filter((n) => n.id !== id)].slice(0, MAX_ITEMS)\n\t\t\t\t}));\n\n\t\t\t\tarmTimer(store, id, notif.ttlMs);\n\t\t\t\treturn id;\n\t\t\t},\n\n\t\t\tupdate: (id, patch) => {\n\t\t\t\tconst { items } = get();\n\t\t\t\tconst idx = items.findIndex((n) => n.id === id);\n\t\t\t\tif (idx === -1) return false;\n\n\t\t\t\tconst prev = items[idx];\n\t\t\t\tconst next: Notification = {\n\t\t\t\t\t...prev,\n\t\t\t\t\t...patch,\n\t\t\t\t\tid: prev.id,\n\t\t\t\t\tcreatedAt: prev.createdAt\n\t\t\t\t};\n\n\t\t\t\tset((s) => ({\n\t\t\t\t\titems: s.items.map((n) => (n.id === id ? next : n))\n\t\t\t\t}));\n\n\t\t\t\t// если ttlMs изменили — пере-армим таймер\n\t\t\t\tif (\"ttlMs\" in patch) {\n\t\t\t\t\tarmTimer(store, id, next.ttlMs);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\tupsert: (input) => {\n\t\t\t\tconst ok = get().actions.update(input.id, {\n\t\t\t\t\ttype: input.type,\n\t\t\t\t\ttitle: input.title,\n\t\t\t\t\tmessage: input.message,\n\t\t\t\t\tttlMs: input.ttlMs,\n\t\t\t\t\tdismissible: input.dismissible\n\t\t\t\t});\n\n\t\t\t\tif (ok) return input.id;\n\t\t\t\treturn get().actions.push(input);\n\t\t\t},\n\n\t\t\tdismiss: (id) => {\n\t\t\t\tclearTimer(id);\n\t\t\t\tset((s) => ({ items: s.items.filter((n) => n.id !== id) }));\n\t\t\t},\n\n\t\t\tclear: () => {\n\t\t\t\ttimers.forEach((t) => window.clearTimeout(t));\n\t\t\t\ttimers.clear();\n\t\t\t\tset({ items: [] });\n\t\t\t}\n\t\t}\n\t}));\n\n\treturn store;\n};\n\nexport type NotificationsStoreApi = StoreApi<NotificationsStore>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/notifications/notify.ts","../../src/notifications/store.ts"],"sourcesContent":["import { type NotificationPushInput, type NotificationsStoreApi, type NotificationUpdatePatch } from \"./store\";\nimport { type NotificationId } from \"./types\";\n\nlet boundStore: NotificationsStoreApi | null = null;\n\nexport const bindNotifications = (store: NotificationsStoreApi) => {\n\tboundStore = store;\n\n\treturn () => {\n\t\tif (boundStore === store) {\n\t\t\tboundStore = null;\n\t\t}\n\t};\n};\n\nconst requireStore = (): NotificationsStoreApi => {\n\tif (!boundStore) throw new Error(\"Notifications store is not bound. Wrap app with NotificationsProvider.\");\n\treturn boundStore;\n};\n\nexport const notify = Object.freeze({\n\tpush: (input: NotificationPushInput) => requireStore().getState().actions.push(input),\n\tupsert: (input: NotificationPushInput & { id: NotificationId }) => requireStore().getState().actions.upsert(input),\n\tupdate: (id: NotificationId, patch: NotificationUpdatePatch) => requireStore().getState().actions.update(id, patch),\n\n\tdismiss: (id: NotificationId) => requireStore().getState().actions.dismiss(id),\n\tclear: () => requireStore().getState().actions.clear(),\n\tclearHistory: () => requireStore().getState().actions.clearHistory(),\n\n\tsuccess: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"success\", message, ...opts }),\n\tinfo: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"info\", message, ...opts }),\n\twarning: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"warning\", message, ...opts }),\n\terror: (message: string, opts?: Omit<NotificationPushInput, \"type\" | \"message\">) =>\n\t\trequireStore()\n\t\t\t.getState()\n\t\t\t.actions.push({ type: \"error\", message, ...opts }),\n\n\t/**\n\t * Удобный паттерн \"долгой операции\":\n\t * const op = notify.progress(\"Сохраняю...\");\n\t * ... op.success(\"Готово\") или op.error(\"Ошибка\")\n\t */\n\tprogress: (message: string, opts?: { id?: NotificationId; title?: string }) => {\n\t\tconst store = requireStore();\n\t\tconst id = opts?.id;\n\n\t\tif (id) {\n\t\t\tstore.getState().actions.upsert({\n\t\t\t\tid,\n\t\t\t\ttype: \"info\",\n\t\t\t\ttitle: opts?.title,\n\t\t\t\tmessage,\n\t\t\t\tdismissible: false\n\t\t\t});\n\t\t}\n\n\t\tconst notificationId =\n\t\t\tid ??\n\t\t\tstore.getState().actions.push({\n\t\t\t\ttype: \"info\",\n\t\t\t\ttitle: opts?.title,\n\t\t\t\tmessage,\n\t\t\t\tdismissible: false\n\t\t\t});\n\n\t\treturn {\n\t\t\tid: notificationId,\n\t\t\tupdate: (nextMessage: string) => notify.update(notificationId, { message: nextMessage }),\n\t\t\tsuccess: (nextMessage: string, ttlMs = 2500) =>\n\t\t\t\tnotify.update(notificationId, { type: \"success\", message: nextMessage, dismissible: true, ttlMs }),\n\t\t\terror: (nextMessage: string, ttlMs = 5000) =>\n\t\t\t\tnotify.update(notificationId, { type: \"error\", message: nextMessage, dismissible: true, ttlMs }),\n\t\t\tdismiss: () => notify.dismiss(notificationId)\n\t\t};\n\t}\n});\n","import { createStore, type StoreApi } from \"zustand\";\n\nimport { type Notification, type NotificationAction, type NotificationId, type NotificationType } from \"./types\";\n\nexport type NotificationPushInput = {\n\tid?: NotificationId;\n\ttype: NotificationType;\n\ttitle?: string;\n\tmessage: string;\n\tttlMs?: number;\n\tdismissible?: boolean;\n\tactions?: NotificationAction[];\n};\n\nexport type NotificationUpdatePatch = Partial<Omit<Notification, \"id\" | \"createdAt\">> & {\n\t// ttlMs можно менять; createdAt — нет\n};\n\nexport type NotificationsState = {\n\t/** Активные toast-уведомления, которые должен отображать краткоживущий host. */\n\titems: Notification[];\n\t/** Полная история за время жизни store; TTL, dismiss и переполнение toast-стека её не сокращают. */\n\thistory: Notification[];\n};\n\nexport type NotificationsActions = {\n\tpush: (input: NotificationPushInput) => NotificationId;\n\tupdate: (id: NotificationId, patch: NotificationUpdatePatch) => boolean;\n\tupsert: (input: NotificationPushInput & { id: NotificationId }) => NotificationId;\n\tdismiss: (id: NotificationId) => void;\n\tclear: () => void;\n\tclearHistory: () => void;\n};\n\nexport type NotificationsStore = NotificationsState & {\n\tactions: NotificationsActions;\n};\n\nconst genId = (): NotificationId => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;\n\nconst MAX_VISIBLE_ITEMS = 6;\n\nexport function createNotificationsStore() {\n\tconst timers = new Map<NotificationId, number>();\n\n\tconst clearTimer = (id: NotificationId) => {\n\t\tconst t = timers.get(id);\n\t\tif (t) {\n\t\t\twindow.clearTimeout(t);\n\t\t\ttimers.delete(id);\n\t\t}\n\t};\n\n\tconst armTimer = (store: StoreApi<NotificationsStore>, id: NotificationId, ttlMs: number = 10_000) => {\n\t\tclearTimer(id);\n\t\tif (ttlMs <= 0) return;\n\n\t\tconst t = window.setTimeout(() => {\n\t\t\tstore.getState().actions.dismiss(id);\n\t\t}, ttlMs);\n\n\t\ttimers.set(id, t);\n\t};\n\n\tconst store = createStore<NotificationsStore>()((set, get) => ({\n\t\titems: [],\n\t\thistory: [],\n\n\t\tactions: {\n\t\t\tpush: (input) => {\n\t\t\t\tconst id = input.id ?? genId();\n\t\t\t\tconst now = Date.now();\n\n\t\t\t\tconst notif: Notification = {\n\t\t\t\t\tid,\n\t\t\t\t\ttype: input.type,\n\t\t\t\t\ttitle: input.title,\n\t\t\t\t\tmessage: input.message,\n\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\tttlMs: input.ttlMs,\n\t\t\t\t\tdismissible: input.dismissible ?? true,\n\t\t\t\t\tactions: input.actions\n\t\t\t\t};\n\n\t\t\t\tconst currentItems = get().items;\n\t\t\t\tconst nextItems = [notif, ...currentItems.filter((notification) => notification.id !== id)].slice(0, MAX_VISIBLE_ITEMS);\n\t\t\t\tconst visibleIds = new Set(nextItems.map((notification) => notification.id));\n\n\t\t\t\t// Для вытесненного toast таймер больше не нужен: запись уже останется в history.\n\t\t\t\tcurrentItems.forEach((notification) => {\n\t\t\t\t\tif (!visibleIds.has(notification.id)) clearTimer(notification.id);\n\t\t\t\t});\n\n\t\t\t\tset((state) => ({\n\t\t\t\t\titems: nextItems,\n\t\t\t\t\thistory: [notif, ...state.history.filter((notification) => notification.id !== id)]\n\t\t\t\t}));\n\n\t\t\t\tarmTimer(store, id, notif.ttlMs);\n\t\t\t\treturn id;\n\t\t\t},\n\n\t\t\tupdate: (id, patch) => {\n\t\t\t\tconst previous = get().items.find((notification) => notification.id === id);\n\t\t\t\tif (!previous) return false;\n\n\t\t\t\tconst next: Notification = {\n\t\t\t\t\t...previous,\n\t\t\t\t\t...patch,\n\t\t\t\t\tid: previous.id,\n\t\t\t\t\tcreatedAt: previous.createdAt\n\t\t\t\t};\n\n\t\t\t\tset((state) => ({\n\t\t\t\t\titems: state.items.map((notification) => (notification.id === id ? next : notification)),\n\t\t\t\t\thistory: state.history.map((notification) => (notification.id === id ? next : notification))\n\t\t\t\t}));\n\n\t\t\t\t// если ttlMs изменили — пере-армим таймер\n\t\t\t\tif (\"ttlMs\" in patch) {\n\t\t\t\t\tarmTimer(store, id, next.ttlMs);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\tupsert: (input) => {\n\t\t\t\tconst ok = get().actions.update(input.id, {\n\t\t\t\t\ttype: input.type,\n\t\t\t\t\ttitle: input.title,\n\t\t\t\t\tmessage: input.message,\n\t\t\t\t\tttlMs: input.ttlMs,\n\t\t\t\t\tdismissible: input.dismissible\n\t\t\t\t});\n\n\t\t\t\tif (ok) return input.id;\n\t\t\t\treturn get().actions.push(input);\n\t\t\t},\n\n\t\t\tdismiss: (id) => {\n\t\t\t\tclearTimer(id);\n\t\t\t\tset((s) => ({ items: s.items.filter((n) => n.id !== id) }));\n\t\t\t},\n\n\t\t\tclear: () => {\n\t\t\t\ttimers.forEach((t) => window.clearTimeout(t));\n\t\t\t\ttimers.clear();\n\t\t\t\tset({ items: [] });\n\t\t\t},\n\n\t\t\tclearHistory: () => {\n\t\t\t\tset({ history: [] });\n\t\t\t}\n\t\t}\n\t}));\n\n\treturn store;\n}\n\nexport type NotificationsStoreApi = StoreApi<NotificationsStore>;\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import type { NotificationPushInput, NotificationsStoreApi, NotificationUpdatePatch } from "./store";
2
- import type { NotificationId } from "./types";
1
+ import { type NotificationPushInput, type NotificationsStoreApi, type NotificationUpdatePatch } from "./store";
2
+ import { type NotificationId } from "./types";
3
3
  export declare const bindNotifications: (store: NotificationsStoreApi) => () => void;
4
4
  export declare const notify: Readonly<{
5
5
  push: (input: NotificationPushInput) => string;
@@ -9,6 +9,7 @@ export declare const notify: Readonly<{
9
9
  update: (id: NotificationId, patch: NotificationUpdatePatch) => boolean;
10
10
  dismiss: (id: NotificationId) => void;
11
11
  clear: () => void;
12
+ clearHistory: () => void;
12
13
  success: (message: string, opts?: Omit<NotificationPushInput, "type" | "message">) => string;
13
14
  info: (message: string, opts?: Omit<NotificationPushInput, "type" | "message">) => string;
14
15
  warning: (message: string, opts?: Omit<NotificationPushInput, "type" | "message">) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/notifications/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,eAAO,MAAM,iBAAiB,GAAI,OAAO,qBAAqB,eAQ7D,CAAC;AAOF,eAAO,MAAM,MAAM;kBACJ,qBAAqB;oBACnB,qBAAqB,GAAG;QAAE,EAAE,EAAE,cAAc,CAAA;KAAE;iBACjD,cAAc,SAAS,uBAAuB;kBAE7C,cAAc;;uBAGT,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;oBAIjE,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;uBAI3D,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;qBAIhE,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IAK/E;;;;OAIG;wBACiB,MAAM,SAAS;QAAE,EAAE,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;8BAyBlD,MAAM;+BACL,MAAM;6BAER,MAAM;;;EAK5B,CAAC"}
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/notifications/notify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAC/G,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,eAAO,MAAM,iBAAiB,GAAI,OAAO,qBAAqB,eAQ7D,CAAC;AAOF,eAAO,MAAM,MAAM;kBACJ,qBAAqB;oBACnB,qBAAqB,GAAG;QAAE,EAAE,EAAE,cAAc,CAAA;KAAE;iBACjD,cAAc,SAAS,uBAAuB;kBAE7C,cAAc;;;uBAIT,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;oBAIjE,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;uBAI3D,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;qBAIhE,MAAM,SAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IAK/E;;;;OAIG;wBACiB,MAAM,SAAS;QAAE,EAAE,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;8BAyBlD,MAAM;+BACL,MAAM;6BAER,MAAM;;;EAK5B,CAAC"}
@@ -1,5 +1,5 @@
1
- import { StoreApi } from "zustand";
2
- import { Notification, NotificationAction, NotificationId, NotificationType } from "./types";
1
+ import { type StoreApi } from "zustand";
2
+ import { type Notification, type NotificationAction, type NotificationId, type NotificationType } from "./types";
3
3
  export type NotificationPushInput = {
4
4
  id?: NotificationId;
5
5
  type: NotificationType;
@@ -10,10 +10,13 @@ export type NotificationPushInput = {
10
10
  actions?: NotificationAction[];
11
11
  };
12
12
  export type NotificationUpdatePatch = Partial<Omit<Notification, "id" | "createdAt">> & {};
13
- type NotificationsState = {
13
+ export type NotificationsState = {
14
+ /** Активные toast-уведомления, которые должен отображать краткоживущий host. */
14
15
  items: Notification[];
16
+ /** Полная история за время жизни store; TTL, dismiss и переполнение toast-стека её не сокращают. */
17
+ history: Notification[];
15
18
  };
16
- type NotificationsActions = {
19
+ export type NotificationsActions = {
17
20
  push: (input: NotificationPushInput) => NotificationId;
18
21
  update: (id: NotificationId, patch: NotificationUpdatePatch) => boolean;
19
22
  upsert: (input: NotificationPushInput & {
@@ -21,11 +24,11 @@ type NotificationsActions = {
21
24
  }) => NotificationId;
22
25
  dismiss: (id: NotificationId) => void;
23
26
  clear: () => void;
27
+ clearHistory: () => void;
24
28
  };
25
29
  export type NotificationsStore = NotificationsState & {
26
30
  actions: NotificationsActions;
27
31
  };
28
- export declare const createNotificationsStore: () => StoreApi<NotificationsStore>;
32
+ export declare function createNotificationsStore(): StoreApi<NotificationsStore>;
29
33
  export type NotificationsStoreApi = StoreApi<NotificationsStore>;
30
- export {};
31
34
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/notifications/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE7F,MAAM,MAAM,qBAAqB,GAAG;IACnC,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC,GAAG,EAEvF,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACzB,KAAK,EAAE,YAAY,EAAE,CAAC;CACtB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC3B,IAAI,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,cAAc,CAAC;IACvD,MAAM,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC;IACxE,MAAM,EAAE,CAAC,KAAK,EAAE,qBAAqB,GAAG;QAAE,EAAE,EAAE,cAAc,CAAA;KAAE,KAAK,cAAc,CAAC;IAClF,OAAO,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GAAG;IACrD,OAAO,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAMF,eAAO,MAAM,wBAAwB,oCAqGpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/notifications/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAErD,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEjH,MAAM,MAAM,qBAAqB,GAAG;IACnC,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC,GAAG,EAEvF,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAChC,gFAAgF;IAChF,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,oGAAoG;IACpG,OAAO,EAAE,YAAY,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,cAAc,CAAC;IACvD,MAAM,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC;IACxE,MAAM,EAAE,CAAC,KAAK,EAAE,qBAAqB,GAAG;QAAE,EAAE,EAAE,cAAc,CAAA;KAAE,KAAK,cAAc,CAAC;IAClF,OAAO,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,YAAY,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GAAG;IACrD,OAAO,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAMF,wBAAgB,wBAAwB,iCAmHvC;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuzaki13/react-foundation-lib",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
5
5
  "license": "MIT",
6
6
  "author": "Ryuzaki13",
@@ -4,7 +4,7 @@ import { Meta } from "@storybook/addon-docs/blocks";
4
4
 
5
5
  # Notifications
6
6
 
7
- Модуль `@ryuzaki13/react-foundation-lib/notifications` хранит очередь коротких уведомлений и предоставляет два способа управления ею:
7
+ Модуль `@ryuzaki13/react-foundation-lib/notifications` хранит активный toast-стек и полную историю уведомлений, а также предоставляет два способа управления ими:
8
8
 
9
9
  - Zustand-store — для React-интерфейса, который отображает уведомления;
10
10
  - объект `notify` — для императивных вызовов из обработчиков, сервисов и `try/catch`.
@@ -88,6 +88,8 @@ export function NotificationsViewport() {
88
88
  }
89
89
  ```
90
90
 
91
+ `items` содержит только активные toast-уведомления и ограничен шестью элементами. Для отдельного центра уведомлений, страницы или содержимого диалога подписывайтесь на `state.history`: эта коллекция не сокращается при TTL, ручном закрытии и переполнении toast-стека.
92
+
91
93
  ### 3. Показывайте уведомления
92
94
 
93
95
  ```ts
@@ -137,17 +139,21 @@ type Notification = {
137
139
  ```ts
138
140
  type NotificationsStore = {
139
141
  items: Notification[];
142
+ history: Notification[];
140
143
  actions: {
141
144
  push(input): NotificationId;
142
145
  update(id, patch): boolean;
143
146
  upsert(inputWithId): NotificationId;
144
147
  dismiss(id): void;
145
148
  clear(): void;
149
+ clearHistory(): void;
146
150
  };
147
151
  };
148
152
  ```
149
153
 
150
- Очередь ограничена шестью элементами. Новое уведомление помещается в начало. При добавлении с уже существующим `id` старый элемент удаляется, а новый получает новое `createdAt`.
154
+ `items` — стек видимых toast-уведомлений, ограниченный шестью элементами. `history` — полная история за время жизни store. Новое уведомление помещается в начало обеих коллекций. При добавлении с уже существующим `id` старый элемент заменяется, а новый получает новое `createdAt`.
155
+
156
+ История живёт в памяти и не является журналом аудита: она сбрасывается при пересоздании store или явном `clearHistory()`. Поскольку `actions` уведомлений содержат функции, модуль намеренно не сохраняет историю в `localStorage` и не сериализует её.
151
157
 
152
158
  ### `push(input)`
153
159
 
@@ -196,9 +202,11 @@ notificationsStore.getState().actions.upsert({
196
202
 
197
203
  Важная деталь: при обновлении существующего элемента `upsert` меняет `type`, `title`, `message`, `ttlMs` и `dismissible`, но не заменяет `actions`. Для явной смены действий используйте `update(id, { actions })`.
198
204
 
199
- ### `dismiss(id)` и `clear()`
205
+ ### `dismiss(id)`, `clear()` и `clearHistory()`
206
+
207
+ `dismiss(id)` скрывает один toast и очищает его таймер. `clear()` скрывает весь toast-стек и очищает все таймеры. Обе операции сохраняют записи в `history`.
200
208
 
201
- `dismiss(id)` удаляет один элемент и очищает его таймер. `clear()` удаляет всю очередь и все таймеры.
209
+ `clearHistory()` явно очищает историю, не закрывая активные toast-уведомления. Чтобы полностью сбросить обе коллекции, последовательно вызовите `clear()` и `clearHistory()`.
202
210
 
203
211
  ## Императивный API `notify`
204
212
 
@@ -223,6 +231,7 @@ notify.upsert(inputWithId);
223
231
  notify.update(id, patch);
224
232
  notify.dismiss(id);
225
233
  notify.clear();
234
+ notify.clearHistory();
226
235
 
227
236
  notify.success(message, options);
228
237
  notify.info(message, options);
@@ -258,7 +267,7 @@ async function importFile(file: File) {
258
267
  - `update(message)` — меняет текст, не перезапуская таймер;
259
268
  - `success(message, ttlMs = 2500)` — делает уведомление успешным и закрываемым;
260
269
  - `error(message, ttlMs = 5000)` — делает уведомление ошибкой и закрываемым;
261
- - `dismiss()` — удаляет уведомление.
270
+ - `dismiss()` — скрывает toast, сохраняя уведомление в истории.
262
271
 
263
272
  При переданном стабильном `id` повторный `progress` продолжит существующий элемент. Это удобно для операции, которая может стартовать из нескольких мест.
264
273
 
@@ -307,17 +316,19 @@ function App() {
307
316
 
308
317
  ### Использование уведомления вместо состояния операции
309
318
 
310
- Очередь ограничена шестью элементами и живёт в памяти страницы. Не используйте её как журнал аудита, источник истины о фоновой задаче или долговременное хранилище ошибок.
319
+ История живёт в памяти страницы. Не используйте её как журнал аудита, источник истины о фоновой задаче или долговременное хранилище ошибок.
311
320
 
312
321
  ## Краткая карта API
313
322
 
314
323
  | Экспорт | Назначение |
315
324
  | ---------------------------- | -------------------------------------------------------- |
316
- | `createNotificationsStore()` | Создать независимый Zustand-store уведомлений |
317
- | `bindNotifications(store)` | Привязать store к глобальному фасаду `notify` |
318
- | `notify` | Императивно добавлять, обновлять и закрывать уведомления |
319
- | `Notification` | Полная модель сохранённого элемента |
320
- | `NotificationPushInput` | Параметры добавления элемента |
321
- | `NotificationUpdatePatch` | Допустимые поля частичного обновления |
322
- | `NotificationsStoreApi` | Тип vanilla Zustand-store |
323
- | `NotificationAction` | Кнопка-действие уведомления |
325
+ | `createNotificationsStore()` | Создать независимый Zustand-store toast-стека и истории |
326
+ | `bindNotifications(store)` | Привязать store к глобальному фасаду `notify` |
327
+ | `notify` | Императивно добавлять, обновлять и закрывать уведомления |
328
+ | `Notification` | Полная модель сохранённого элемента |
329
+ | `NotificationPushInput` | Параметры добавления элемента |
330
+ | `NotificationUpdatePatch` | Допустимые поля частичного обновления |
331
+ | `NotificationsState` | Состояние с активными `items` и полной `history` |
332
+ | `NotificationsActions` | Действия toast-стека и явная очистка истории |
333
+ | `NotificationsStoreApi` | Тип vanilla Zustand-store |
334
+ | `NotificationAction` | Кнопка-действие уведомления |