react-snackbar-glass 1.1.0 â 1.2.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/CHANGELOG.md +16 -0
- package/README.md +32 -2
- package/dist/index.cjs +71 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +112 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +71 -3
- package/dist/index.js.map +1 -1
- package/dist/styles.css +112 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - 2026-10-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `compact` snackbars: single-line pill, no title or countdown hairline.
|
|
13
|
+
Available per toast (`compact: true`), on the provider
|
|
14
|
+
(`<SnackbarProvider compact />`) and in the store config.
|
|
15
|
+
- Minimize API: `toast.minimize()`, `toast.expand()`, `toast.isMinimized()`
|
|
16
|
+
(also on `useSnackbar()`), collapsing the whole stack into one summary pill
|
|
17
|
+
that expands on click.
|
|
18
|
+
- `minimizedLabel` host prop to customise the collapsed pill text.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Demo homepage now defaults to the light theme.
|
|
23
|
+
|
|
8
24
|
## [1.1.0] - 2026-10-28
|
|
9
25
|
|
|
10
26
|
### Added
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ dependencies**.
|
|
|
8
8
|
- ð **Global `toast`** â call it from anywhere, no hook required
|
|
9
9
|
- âģ **Promise aware** â one call moves a snackbar through loading â success â error
|
|
10
10
|
- ð **Swipe to dismiss**, hover/focus/document-hidden pause, countdown hairline
|
|
11
|
+
- ðĶ **Compact + minimize** â one-line pills, or collapse the whole stack
|
|
11
12
|
- âŋ **Accessible** â polite/assertive live regions, keyboard reachable actions, `prefers-reduced-motion`
|
|
12
13
|
- ð§Đ **Typed** â written in TypeScript, ships ESM + CJS + `.d.ts`
|
|
13
14
|
- ðŠķ **Tiny** â React is the only peer dependency
|
|
@@ -160,6 +161,8 @@ the snackbar only closes after the handler settles (`closeOnClick`, default
|
|
|
160
161
|
| `maxVisible` | `number` | `3` | Simultaneously visible cards per corner. |
|
|
161
162
|
| `dismissible` | `boolean` | `true` | Show the close button. |
|
|
162
163
|
| `progress` | `boolean` | `true` | Show the countdown hairline. |
|
|
164
|
+
| `compact` | `boolean` | `false` | Single-line pill for every snackbar. |
|
|
165
|
+
| `minimizedLabel` | `(count: number) => ReactNode` | `"3 notifications"` | Text on the collapsed pill. |
|
|
163
166
|
| `pauseOnHover` | `boolean` | `true` | Freeze the timer while hovered. |
|
|
164
167
|
| `pauseOnFocus` | `boolean` | `true` | Freeze the timer while focus is inside. |
|
|
165
168
|
| `pauseOnDocumentHidden` | `boolean` | `true` | Pause when the tab is hidden. |
|
|
@@ -175,12 +178,39 @@ the snackbar only closes after the handler settles (`closeOnClick`, default
|
|
|
175
178
|
## `SnackbarOptions`
|
|
176
179
|
|
|
177
180
|
`id`, `title`, `message`, `variant`, `duration`, `position`, `icon`,
|
|
178
|
-
`action`, `dismissible`, `progress`, `tone`, `size`, `className`,
|
|
179
|
-
`onOpen`, `onClose`.
|
|
181
|
+
`action`, `dismissible`, `progress`, `compact`, `tone`, `size`, `className`,
|
|
182
|
+
`style`, `onOpen`, `onClose`.
|
|
180
183
|
|
|
181
184
|
Reusing an `id` updates (or revives) the existing snackbar instead of stacking
|
|
182
185
|
a duplicate â ideal for progress-style flows.
|
|
183
186
|
|
|
187
|
+
## Compact and minimized stacks
|
|
188
|
+
|
|
189
|
+
Two ways to reclaim screen space when snackbars would otherwise pile up.
|
|
190
|
+
|
|
191
|
+
**Compact** â a single-line pill per snackbar (no title, no hairline):
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
toast.success('Draft saved', { compact: true });
|
|
195
|
+
snackbar.show({ message: 'Synced', compact: true });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Set it once for every snackbar with `<SnackbarProvider compact />` or
|
|
199
|
+
`createSnackbarStore({ compact: true })`.
|
|
200
|
+
|
|
201
|
+
**Minimized** â collapse the whole stack into one summary pill
|
|
202
|
+
(`"3 notifications"`) that expands on click:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
toast.minimize(); // stack â one pill
|
|
206
|
+
toast.expand(); // back to full cards
|
|
207
|
+
toast.isMinimized(); // boolean
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`useSnackbar()` exposes the same three methods, and the store has
|
|
211
|
+
`setMinimized(true | false)` + `getMinimized()`. Customize the pill text with
|
|
212
|
+
`minimizedLabel={(count) => `${count} neue Meldungen`}`.
|
|
213
|
+
|
|
184
214
|
## Theming
|
|
185
215
|
|
|
186
216
|
Colors, radii, blur and motion are CSS custom properties on `.prs-root`
|
package/dist/index.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var DEFAULT_CONFIG = {
|
|
|
11
11
|
duration: 5e3,
|
|
12
12
|
dismissible: true,
|
|
13
13
|
progress: true,
|
|
14
|
+
compact: false,
|
|
14
15
|
tone: "glass",
|
|
15
16
|
size: "md",
|
|
16
17
|
maxVisible: 3
|
|
@@ -50,6 +51,7 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
50
51
|
} : null,
|
|
51
52
|
...initialConfig.dismissible !== void 0 ? { dismissible: initialConfig.dismissible } : null,
|
|
52
53
|
...initialConfig.progress !== void 0 ? { progress: initialConfig.progress } : null,
|
|
54
|
+
...initialConfig.compact !== void 0 ? { compact: initialConfig.compact } : null,
|
|
53
55
|
...initialConfig.tone !== void 0 ? { tone: initialConfig.tone } : null,
|
|
54
56
|
...initialConfig.size !== void 0 ? { size: initialConfig.size } : null,
|
|
55
57
|
...initialConfig.maxVisible !== void 0 ? { maxVisible: Math.max(1, Math.floor(initialConfig.maxVisible)) } : null
|
|
@@ -60,6 +62,7 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
60
62
|
const shown = /* @__PURE__ */ new Set();
|
|
61
63
|
const pauseReasons = /* @__PURE__ */ new Map();
|
|
62
64
|
let pageHidden = false;
|
|
65
|
+
let minimized = false;
|
|
63
66
|
const emit = () => {
|
|
64
67
|
for (const listener of listeners) listener();
|
|
65
68
|
};
|
|
@@ -233,6 +236,7 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
233
236
|
action: options.action ?? null,
|
|
234
237
|
dismissible: options.dismissible ?? config.dismissible,
|
|
235
238
|
progress: options.progress ?? config.progress,
|
|
239
|
+
compact: options.compact ?? config.compact,
|
|
236
240
|
className: options.className,
|
|
237
241
|
style: options.style,
|
|
238
242
|
createdAt: Date.now(),
|
|
@@ -261,6 +265,7 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
261
265
|
...options.action !== void 0 ? { action: options.action } : null,
|
|
262
266
|
...options.dismissible !== void 0 ? { dismissible: options.dismissible } : null,
|
|
263
267
|
...options.progress !== void 0 ? { progress: options.progress } : null,
|
|
268
|
+
...options.compact !== void 0 ? { compact: options.compact } : null,
|
|
264
269
|
...options.className !== void 0 ? { className: options.className } : null,
|
|
265
270
|
...options.style !== void 0 ? { style: options.style } : null,
|
|
266
271
|
...options.onOpen !== void 0 ? { onOpen: options.onOpen } : null,
|
|
@@ -318,6 +323,7 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
318
323
|
} : null,
|
|
319
324
|
...patch.dismissible !== void 0 ? { dismissible: patch.dismissible } : null,
|
|
320
325
|
...patch.progress !== void 0 ? { progress: patch.progress } : null,
|
|
326
|
+
...patch.compact !== void 0 ? { compact: patch.compact } : null,
|
|
321
327
|
...patch.tone !== void 0 ? { tone: patch.tone } : null,
|
|
322
328
|
...patch.size !== void 0 ? { size: patch.size } : null,
|
|
323
329
|
...patch.maxVisible !== void 0 ? { maxVisible: Math.max(1, Math.floor(patch.maxVisible)) } : null
|
|
@@ -372,7 +378,14 @@ function createSnackbarStore(initialConfig = {}) {
|
|
|
372
378
|
resume,
|
|
373
379
|
notifyShown,
|
|
374
380
|
notifyExited,
|
|
375
|
-
setPageHidden
|
|
381
|
+
setPageHidden,
|
|
382
|
+
getMinimized: () => minimized,
|
|
383
|
+
getServerMinimized: () => false,
|
|
384
|
+
setMinimized(next) {
|
|
385
|
+
if (minimized === next) return;
|
|
386
|
+
minimized = next;
|
|
387
|
+
emit();
|
|
388
|
+
}
|
|
376
389
|
};
|
|
377
390
|
}
|
|
378
391
|
|
|
@@ -617,7 +630,7 @@ function SnackbarItem({
|
|
|
617
630
|
};
|
|
618
631
|
const resolvedIcon = record.icon === void 0 ? icons?.[variant] ?? defaultIcons[variant] : record.icon;
|
|
619
632
|
const showIcon = record.icon !== false;
|
|
620
|
-
const showProgress = record.progress && Number.isFinite(duration) && duration > 0;
|
|
633
|
+
const showProgress = !record.compact && record.progress && Number.isFinite(duration) && duration > 0;
|
|
621
634
|
const style = {
|
|
622
635
|
...record.style,
|
|
623
636
|
"--prs-drag-x": `${dragX}px`,
|
|
@@ -637,6 +650,7 @@ function SnackbarItem({
|
|
|
637
650
|
"data-dragging": dragging ? "true" : "false",
|
|
638
651
|
"data-has-icon": showIcon ? "true" : "false",
|
|
639
652
|
"data-dismissible": record.dismissible ? "true" : "false",
|
|
653
|
+
"data-compact": record.compact ? "true" : "false",
|
|
640
654
|
"data-prs-id": record.id,
|
|
641
655
|
"data-position": record.position,
|
|
642
656
|
style,
|
|
@@ -736,6 +750,9 @@ var POSITIONS = [
|
|
|
736
750
|
"bottom-center",
|
|
737
751
|
"bottom-right"
|
|
738
752
|
];
|
|
753
|
+
function defaultMinimizedLabel(count) {
|
|
754
|
+
return `${count} notification${count === 1 ? "" : "s"}`;
|
|
755
|
+
}
|
|
739
756
|
function SnackbarHost({
|
|
740
757
|
store,
|
|
741
758
|
maxVisible = 3,
|
|
@@ -753,6 +770,7 @@ function SnackbarHost({
|
|
|
753
770
|
closeIcon,
|
|
754
771
|
closeLabel = "Dismiss notification",
|
|
755
772
|
regionLabel = "Notifications",
|
|
773
|
+
minimizedLabel = defaultMinimizedLabel,
|
|
756
774
|
className,
|
|
757
775
|
style,
|
|
758
776
|
zIndex = 2147483e3,
|
|
@@ -763,6 +781,11 @@ function SnackbarHost({
|
|
|
763
781
|
store.getSnapshot,
|
|
764
782
|
store.getServerSnapshot
|
|
765
783
|
);
|
|
784
|
+
const minimized = react.useSyncExternalStore(
|
|
785
|
+
store.subscribe,
|
|
786
|
+
store.getMinimized,
|
|
787
|
+
store.getServerMinimized
|
|
788
|
+
);
|
|
766
789
|
const [portalNode, setPortalNode] = react.useState(null);
|
|
767
790
|
const regionRefs = react.useRef(/* @__PURE__ */ new Map());
|
|
768
791
|
const topsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
@@ -826,6 +849,7 @@ function SnackbarHost({
|
|
|
826
849
|
else byPosition.set(record.position, [record]);
|
|
827
850
|
}
|
|
828
851
|
const limit = Math.max(1, Math.floor(maxVisible));
|
|
852
|
+
const minimizedPosition = records[0]?.position ?? null;
|
|
829
853
|
const rootStyle = {
|
|
830
854
|
"--prs-width": `${width}px`,
|
|
831
855
|
"--prs-offset": `${offset}px`,
|
|
@@ -844,6 +868,32 @@ function SnackbarHost({
|
|
|
844
868
|
children: POSITIONS.map((position) => {
|
|
845
869
|
const items = byPosition.get(position);
|
|
846
870
|
if (!items || items.length === 0) return null;
|
|
871
|
+
if (minimized) {
|
|
872
|
+
if (position !== minimizedPosition) return null;
|
|
873
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
874
|
+
"section",
|
|
875
|
+
{
|
|
876
|
+
className: "prs-region prs-region--minimized",
|
|
877
|
+
"data-position": position,
|
|
878
|
+
"aria-label": regionLabel,
|
|
879
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
880
|
+
"button",
|
|
881
|
+
{
|
|
882
|
+
type: "button",
|
|
883
|
+
className: "prs-minimized",
|
|
884
|
+
"data-count": items.length,
|
|
885
|
+
onClick: () => store.setMinimized(false),
|
|
886
|
+
title: "Expand notifications",
|
|
887
|
+
children: [
|
|
888
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "prs-minimized__dot", "aria-hidden": "true" }),
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "prs-minimized__count", children: minimizedLabel(items.length) })
|
|
890
|
+
]
|
|
891
|
+
}
|
|
892
|
+
)
|
|
893
|
+
},
|
|
894
|
+
position
|
|
895
|
+
);
|
|
896
|
+
}
|
|
847
897
|
const visible = items.slice(0, limit);
|
|
848
898
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
849
899
|
"section",
|
|
@@ -887,6 +937,7 @@ function SnackbarProvider({
|
|
|
887
937
|
duration,
|
|
888
938
|
dismissible,
|
|
889
939
|
progress,
|
|
940
|
+
compact,
|
|
890
941
|
tone,
|
|
891
942
|
size,
|
|
892
943
|
maxVisible,
|
|
@@ -908,11 +959,22 @@ function SnackbarProvider({
|
|
|
908
959
|
duration,
|
|
909
960
|
dismissible,
|
|
910
961
|
progress,
|
|
962
|
+
compact,
|
|
911
963
|
tone,
|
|
912
964
|
size,
|
|
913
965
|
maxVisible
|
|
914
966
|
});
|
|
915
|
-
}, [
|
|
967
|
+
}, [
|
|
968
|
+
store,
|
|
969
|
+
position,
|
|
970
|
+
duration,
|
|
971
|
+
dismissible,
|
|
972
|
+
progress,
|
|
973
|
+
compact,
|
|
974
|
+
tone,
|
|
975
|
+
size,
|
|
976
|
+
maxVisible
|
|
977
|
+
]);
|
|
916
978
|
const api = react.useMemo(() => {
|
|
917
979
|
const preset2 = (variant) => (message, options = {}) => store.show({ ...options, message, variant });
|
|
918
980
|
return {
|
|
@@ -933,6 +995,9 @@ function SnackbarProvider({
|
|
|
933
995
|
variant: "loading",
|
|
934
996
|
duration: options.duration !== void 0 ? options.duration : null
|
|
935
997
|
}),
|
|
998
|
+
minimize: () => store.setMinimized(true),
|
|
999
|
+
expand: () => store.setMinimized(false),
|
|
1000
|
+
isMinimized: () => store.getMinimized(),
|
|
936
1001
|
store
|
|
937
1002
|
};
|
|
938
1003
|
}, [store]);
|
|
@@ -997,6 +1062,9 @@ function createToast() {
|
|
|
997
1062
|
duration: options.duration !== void 0 ? options.duration : null
|
|
998
1063
|
});
|
|
999
1064
|
toast2.promise = (input, messages, options) => getDefaultStore().promise(input, messages, options);
|
|
1065
|
+
toast2.minimize = () => getDefaultStore().setMinimized(true);
|
|
1066
|
+
toast2.expand = () => getDefaultStore().setMinimized(false);
|
|
1067
|
+
toast2.isMinimized = () => getDefaultStore().getMinimized();
|
|
1000
1068
|
Object.defineProperty(toast2, "store", {
|
|
1001
1069
|
get: getDefaultStore,
|
|
1002
1070
|
enumerable: true
|