Skip to content

Commit 52fd428

Browse files
committed
feat: improved notifications
Improved automated handling of the cancel option. Now errors with odd options like saying a notification is false but having "Cancel" in the options.
1 parent 6c7c1fd commit 52fd428

File tree

4 files changed

+80
-33
lines changed

4 files changed

+80
-33
lines changed

src/components/LibNotifications/LibNotification.vue

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,47 +105,50 @@ defineExpose({
105105
106106
& :deep(.group) {
107107
--groupPadding: var(--paddingM);
108-
@include flex-row(true, center, center);
108+
padding: 0;
109+
@include flex-row(wrap, center, center);
109110
}
110111
111112
@include border();
112-
margin: var(--paddingM);
113113
@include focusable();
114114
// @include flex-col(nowrap, null, center);
115115
@include flex-row(nowrap);
116116
position:relative;
117+
gap: var(--paddingM);
118+
margin: var(--paddingM);
117119
}
118120
119121
.upper {
120-
// @include flex(1,0,100%);
122+
margin: var(--paddingM);
123+
gap: var(--paddingM);
121124
@include flex-col(wrap);
122125
}
123126
124127
.message-container {
125128
@include flex(1, 1);
126129
@include flex-col(wrap);
127130
align-items: center;
128-
margin-right: calc((var(--paddingXS) + 2px) * 2 + 1em);
131+
margin-right: calc((var(--paddingXS) + 2px) + 1em);
129132
// margin-left: var(--paddingXS);
130133
margin-top: var(--paddingM);
134+
gap: var(--paddingM);
131135
// order:1;
132136
}
133137
134-
.message {
138+
.title {
139+
font-weight: bold;
140+
}
141+
142+
.message,
143+
.title {
135144
@include flex(1, 1);
136-
padding: var(--paddingM) 0 0 var(--paddingM);
137145
}
138146
139147
.code {
140148
@include flex(1, 0);
141-
padding: var(--paddingM) 0 0 var(--paddingM);
142149
font-size: var(--fontSizeSmall);
143150
}
144151
145-
.title {
146-
font-weight: bold;
147-
}
148-
149152
.actions {
150153
// @include flex(0,0);
151154
position: absolute;

src/components/LibNotifications/LibNotifications.stories.js

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,61 @@ const Template = args => ({
3232
const withTitle = ref(true)
3333
const disableTimeout = ref(false)
3434

35-
const notify = () => {
35+
const notifyRequiresAction = () => {
3636
count++
3737
handler.notify({
3838
title: withTitle.value ? `Notification(${count})` : undefined,
39-
message: `This is a notification. Pick an option:`,
39+
message: `This is a notification that requires action. Pick an option:`,
4040
requiresAction: true,
41+
options: ["Ok", "Default Answer", "Cancel"],
42+
default: "Default Answer",
43+
})
44+
}
45+
const notifyWithDangerousOption = () => {
46+
count++
47+
handler.notify({
48+
title: withTitle.value ? `Notification(${count})` : undefined,
49+
message: `This is a notification that has a dangerous option. Pick an option:`,
4150
options: ["Ok", "Default Answer", "Dangerous Option", "Cancel"],
4251
default: "Default Answer",
4352
dangerous: ["Dangerous Option"],
44-
cancellable: true,
53+
})
54+
}
55+
const notifyNonCancellable = () => {
56+
count++
57+
handler.notify({
58+
title: withTitle.value ? `Notification(${count})` : undefined,
59+
message: `This is a non-cancellable notification. Pick an option:`,
60+
options: ["Ok", "Default Answer"],
61+
default: "Default Answer",
62+
cancellable: false,
63+
})
64+
}
65+
const notifyNonCancellableRequiresAction = () => {
66+
count++
67+
handler.notify({
68+
title: withTitle.value ? `Notification(${count})` : undefined,
69+
message: `This is a non-cancellable notification. Pick an option:`,
70+
requiresAction: true,
71+
options: ["Ok", "Default Answer"],
72+
default: "Default Answer",
73+
cancellable: false,
4574
})
4675
}
4776
const notifyTimeoutable = () => {
4877
count++
4978
handler.notify({
5079
title: withTitle.value ? `Notification(${count})` : undefined,
5180
message: `This is a notification. No action required.`,
52-
cancellable: true,
5381
timeout: disableTimeout.value ? false : 2000,
5482
})
5583
}
5684
return {
57-
notify,
85+
notifyRequiresAction,
5886
notifyTimeoutable,
87+
notifyNonCancellable,
88+
notifyWithDangerousOption,
89+
notifyNonCancellableRequiresAction,
5990
handler,
6091
withTitle,
6192
disableTimeout,
@@ -69,8 +100,11 @@ const Template = args => ({
69100
// <lib-debug>{{args.handler}}</lib-debug>
70101
template: `
71102
<test-wrapper :outline="args.outline">
72-
<lib-button :label="'Notify'" @click="notify()"></lib-button>
73103
<lib-button :label="'Notify Timeoutable'" @click="notifyTimeoutable()"></lib-button>
104+
<lib-button :label="'Notify RequiresAction'" @click="notifyRequiresAction()"></lib-button>
105+
<lib-button :label="'Notify Non-Cancellable that RequiresAction'" @click="notifyNonCancellableRequiresAction()"></lib-button>
106+
<lib-button :label="'Notify With Dangerous Option'" @click="notifyWithDangerousOption()"></lib-button>
107+
<lib-button :label="'Notify Non-Cancellable'" @click="notifyNonCancellable()"></lib-button>
74108
<input type="checkbox" v-model="withTitle"/> With Title
75109
<input type="checkbox" v-model="disableTimeout"/> Disable Timeout
76110
<lib-notifications :handler="handler" />

src/components/LibNotifications/LibNotifications.vue

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
:notification="notification"
2424
v-for="(notification, i) of notifications"
2525
:key="notification.id"
26-
:ref="(el: any) => i==0 ? lastNotificationComp = el as any :''"
26+
:ref="(el: any) => i == 0 ? lastNotificationComp = el as any : ''"
2727
/>
2828
</TransitionGroup>
2929
</div>
@@ -87,7 +87,7 @@ const notificationListener = (entry: NotificationEntry, type: "added" | "resolve
8787
if (entry.requiresAction) {
8888
topNotifications.value.push(entry)
8989
// eslint-disable-next-line @typescript-eslint/no-floating-promises
90-
entry.promise.then(() => { removeIfIn(topNotifications.value, entry)})
90+
entry.promise.then(() => { removeIfIn(topNotifications.value, entry) })
9191
} else {
9292
notifications.value.splice(0, 0, entry)
9393
// eslint-disable-next-line @typescript-eslint/no-floating-promises
@@ -105,24 +105,24 @@ onBeforeUnmount(() => {
105105

106106
<style lang="scss" scoped>
107107
.top-notification-veil {
108-
position:absolute;
109-
@include pos(0,0,0,0);
108+
position: absolute;
109+
@include pos(0, 0, 0, 0);
110110
background: var(--opacity5);
111111
display: flex;
112112
@include flex-row(nowrap, center, center)
113113
}
114114
115115
.notifications {
116-
position:absolute;
116+
position: absolute;
117117
z-index: 1000;
118-
@include pos(0,0,0,null);
118+
@include pos(0, 0, 0, null);
119119
width: 300px;
120120
pointer-events: none;
121121
/* for animations */
122-
overflow:hidden;
123-
& > * {
124-
pointer-events:all;
122+
overflow: hidden;
123+
124+
&>* {
125+
pointer-events: all;
125126
}
126127
}
127-
128128
</style>

src/helpers/NotificationHandler.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,19 @@ export class NotificationHandler<
4141
`)
4242
}
4343
if (entry.cancellable) {
44-
if (!entry.options.includes(entry.cancellable)) {
44+
if (typeof entry.cancellable === "string" && !entry.options.includes(entry.cancellable)) {
4545
throw new Error(
4646
crop`Entry options does not include cancellable option "${entry.cancellable}":
4747
${indent(pretty(entry), 6)}
4848
`)
4949
}
50+
} else {
51+
if (entry.options.includes("Cancel")) {
52+
throw new Error(
53+
crop`You specified that the entry should not be cancellable, but the options include the "Cancel" option:
54+
${indent(pretty(entry), 6)}
55+
`)
56+
}
5057
}
5158
if (entry.timeout !== undefined && !entry.cancellable) {
5259
throw new Error(
@@ -73,8 +80,8 @@ export class NotificationHandler<
7380
requiresAction: false,
7481
options: ["Ok", "Cancel"],
7582
default: "Ok",
83+
cancellable: rawEntry.cancellable,
7684
...rawEntry,
77-
cancellable: rawEntry.cancellable ? "Cancel" : rawEntry.cancellable,
7885
dangerous: rawEntry.dangerous ?? [],
7986
timeout: rawEntry.timeout === true
8087
? this.timeout
@@ -83,8 +90,12 @@ export class NotificationHandler<
8390
: undefined,
8491
} as TEntry
8592

93+
if (rawEntry.cancellable === true || (rawEntry.cancellable === undefined && entry.options?.includes("Cancel"))) {
94+
entry.cancellable = "Cancel" as any
95+
}
8696

8797
this._checkEntry(entry)
98+
8899
castType<TEntry>(entry)
89100
this.id++
90101
entry.id = this.id
@@ -95,7 +106,7 @@ export class NotificationHandler<
95106

96107
if (entry.timeout !== undefined) {
97108
setTimeout(() => {
98-
entry.resolve(entry?.cancellable === true ? "Cancel" : entry.cancellable)
109+
entry.resolve(entry.cancellable)
99110
}, entry.timeout)
100111
}
101112
this.queue.push(entry)
@@ -118,8 +129,7 @@ export class NotificationHandler<
118129
this.queue.splice(this.queue.indexOf(entry), 1)
119130
return res
120131
}) as NotificationPromise
121-
}
122-
static resolveToDefault(notification: NotificationEntry): void {
132+
} static resolveToDefault(notification: NotificationEntry): void {
123133
notification.resolve(notification.default)
124134
}
125135
static dismiss(notification: NotificationEntry): void {
@@ -171,7 +181,7 @@ export type RawNotificationEntry<
171181

172182
export type NotificationEntry<
173183
TRawEntry extends RawNotificationEntry<any, any> = RawNotificationEntry<any, any>,
174-
> = MakeRequired<TRawEntry, "options" | "requiresAction" | "default" | "dangerous"> & {
184+
> = Omit<MakeRequired<TRawEntry, "options" | "requiresAction" | "default" | "dangerous">, "cancellable"> & {
175185
promise: NotificationPromise
176186
resolve: AnyFunction
177187
cancellable?: string

0 commit comments

Comments
 (0)