Skip to content

Commit e4d8dff

Browse files
author
KHOUBZA Younes
committed
update flash notification style, add transitions
1 parent dec2475 commit e4d8dff

17 files changed

+926
-592
lines changed

package.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,24 @@
3838
"url": "https://github.com/php-flasher/flasher-js.git"
3939
},
4040
"devDependencies": {
41-
"@lerna-lite/cli": "^1.1.1",
42-
"@lerna-lite/exec": "^1.1.1",
43-
"@lerna-lite/run": "^1.1.1",
41+
"@lerna-lite/cli": "^1.3.0",
42+
"@lerna-lite/exec": "^1.3.0",
43+
"@lerna-lite/run": "^1.3.0",
4444
"@rollup/plugin-babel": "^5.3.1",
4545
"@rollup/plugin-commonjs": "^22.0.0",
4646
"@rollup/plugin-node-resolve": "^13.3.0",
4747
"@rollup/plugin-typescript": "^8.3.2",
48-
"@types/node": "^17.0.31",
49-
"@typescript-eslint/eslint-plugin": "^5.22.0",
50-
"@typescript-eslint/parser": "^5.22.0",
48+
"@types/node": "^17.0.33",
49+
"@typescript-eslint/eslint-plugin": "^5.23.0",
50+
"@typescript-eslint/parser": "^5.23.0",
5151
"eslint": "^8.15.0",
5252
"eslint-config-airbnb": "^19.0.4",
5353
"eslint-config-airbnb-typescript": "^17.0.0",
5454
"eslint-plugin-import": "^2.26.0",
5555
"eslint-plugin-jsx-a11y": "^6.5.1",
5656
"eslint-plugin-react": "^7.29.4",
5757
"node-sass": "^7.0.1",
58-
"rollup": "^2.72.0",
58+
"rollup": "^2.73.0",
5959
"rollup-plugin-clear": "^2.0.7",
6060
"rollup-plugin-styles": "^4.0.0",
6161
"rollup-plugin-terser": "^7.0.2",

packages/flasher/src/interfaces.ts renamed to packages/flasher/src/common.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ export interface FlasherResponse {
3737
}
3838

3939
export interface NotificationFactoryInterface {
40-
success(message: string, title?: string, options?: FlasherOptions): void;
41-
info(message: string, title?: string, options?: FlasherOptions): void;
42-
warning(message: string, title?: string, options?: FlasherOptions): void;
43-
error(message: string, title?: string, options?: FlasherOptions): void;
44-
flash(notification: FlasherNotification): void;
40+
success(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void;
41+
info(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void;
42+
warning(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void;
43+
error(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void;
44+
flash(type: string|FlasherOptions, message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void;
4545
render(envelope: Envelope): void;
4646
renderOptions(options: FlasherOptions): void;
4747
}

packages/flasher/src/flasher.scss

+55-3
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,64 @@ $types: (
3535
error: (color: #DC2626FF, icon: 'M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z'),
3636
);
3737

38+
.fl-main-container {
39+
position: fixed;
40+
max-width: 304px;
41+
width: 100%;
42+
z-index: 99999;
43+
44+
&[data-position^="top-"] {
45+
top: 0.5rem;
46+
}
47+
48+
&[data-position^="bottom-"] {
49+
bottom: 0.5rem;
50+
}
51+
52+
&[data-position$="-right"] {
53+
right: 0.5rem;
54+
55+
.fl-container {
56+
transform: translateX(110%);
57+
}
58+
}
59+
60+
&[data-position$="-left"] {
61+
left: 0.5rem;
62+
63+
.fl-container {
64+
transform: translateX(-110%);
65+
}
66+
}
67+
68+
&[data-position$="-center"] {
69+
left: 50%;
70+
transform: translateX(-50%);
71+
}
72+
73+
&[data-position="top-center"] .fl-container {
74+
transform: translateY(-100vh);
75+
}
76+
77+
&[data-position="bottom-center"] .fl-container {
78+
transform: translateY(100vh);
79+
}
80+
81+
.fl-container {
82+
transition: transform 0.3s ease-in-out;
83+
overflow: hidden;
84+
85+
&.fl-show {
86+
transform: translate(0, 0);
87+
}
88+
}
89+
}
90+
3891
.fl-flasher.fl-container {
39-
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
92+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
4093
line-height: 1.5;
4194

42-
overflow: hidden;
4395
margin-top: 0.5rem;
44-
padding-right: 0.5rem;
4596
background-color: #ffffff;
4697
border-radius: 0.375rem 0 0 0.375rem;
4798
cursor: pointer;
@@ -86,6 +137,7 @@ $types: (
86137
color: #6B7280;
87138
font-size: 0.875rem;
88139
line-height: 1.25rem;
140+
text-transform: capitalize;
89141
}
90142

91143
.fl-progress-bar {

packages/flasher/src/flasher.ts

+110-58
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,89 @@ import {
88
QueueableInterface,
99
ResponseContext,
1010
Theme,
11-
} from './interfaces';
11+
} from './common';
1212

13-
import TemplateFactory from './template';
14-
import { parseFunction } from './parseFunction';
13+
import FlasherFactory from './flasherFactory';
1514

1615
export default class Flasher {
17-
private defaultHandler: string = 'template.flasher';
16+
private defaultHandler: string = 'theme.flasher';
1817

1918
private factories: Map<string, NotificationFactoryInterface> = new Map<string, NotificationFactoryInterface>();
2019

2120
private themes: Map<string, Theme> = new Map<string, Theme>();
2221

23-
success(message: string, title?: string, options?: FlasherOptions): void {
24-
this.flash({ type: 'success', message, title, options });
22+
success(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void {
23+
this.flash('success', message, title, options);
2524
}
2625

27-
info(message: string, title?: string, options?: FlasherOptions): void {
28-
this.flash({ type: 'info', message, title, options });
26+
info(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void {
27+
this.flash('info', message, title, options);
2928
}
3029

31-
warning(message: string, title?: string, options?: FlasherOptions): void {
32-
this.flash({ type: 'warning', message, title, options });
30+
warning(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void {
31+
this.flash('warning', message, title, options);
3332
}
3433

35-
error(message: string, title?: string, options?: FlasherOptions): void {
36-
this.flash({ type: 'error', message, title, options });
34+
error(message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void {
35+
this.flash('error', message, title, options);
3736
}
3837

39-
flash(notification: FlasherNotification): void {
38+
flash(type: string|FlasherOptions, message: string|FlasherOptions, title?: string|FlasherOptions, options?: FlasherOptions): void {
39+
const notification = this.createNotification(type, message, title, options);
4040
const factory = this.create(this.defaultHandler);
41+
42+
factory.renderOptions({});
43+
factory.render({ notification });
44+
}
45+
46+
createNotification(
47+
type: string|FlasherOptions,
48+
message?: string|FlasherOptions,
49+
title?: string|FlasherOptions,
50+
options?: FlasherOptions
51+
): FlasherNotification {
52+
if (typeof type === 'object') {
53+
options = type;
54+
type = options.type as unknown as string;
55+
message = options.message as unknown as string;
56+
title = options.title as unknown as string;
57+
} else if (typeof message === 'object') {
58+
options = message;
59+
message = options.message as unknown as string;
60+
title = options.title as unknown as string;
61+
} else if (typeof title === 'object') {
62+
options = title;
63+
title = options.title as unknown as string;
64+
}
65+
66+
if (undefined === message) {
67+
throw new Error('message option is required');
68+
}
69+
70+
return {
71+
type: type || 'info',
72+
message,
73+
title,
74+
options
75+
};
76+
}
77+
78+
create(alias: string): NotificationFactoryInterface {
79+
alias = this.resolveHandler(alias);
80+
this.resolveThemeHandler(alias);
81+
82+
let factory = this.factories.get(alias);
4183
if (!factory) {
42-
return;
84+
throw new Error(`Unable to resolve "${alias}" notification factory, did you forget to register it?`);
4385
}
4486

45-
notification.type = notification.type || 'info';
87+
return factory;
88+
}
4689

47-
factory.renderOptions({});
48-
factory.render({ notification });
90+
renderOptions(options: FlasherResponseOptions): void {
91+
Object.entries(options).forEach(([handler, option]) => {
92+
this.create(handler).renderOptions(option);
93+
});
4994
}
5095

5196
render(response: FlasherResponse): void {
@@ -67,19 +112,6 @@ export default class Flasher {
67112
this.themes.set(name, theme);
68113
}
69114

70-
create(alias: string): NotificationFactoryInterface | undefined {
71-
alias = this.resolveHandler(alias);
72-
73-
if (0 === alias.indexOf('template.') && !this.factories.has(alias)) {
74-
let viewFactory = this.themes.get(alias.replace('template.', ''));
75-
if (viewFactory) {
76-
this.addFactory(alias, new TemplateFactory(viewFactory));
77-
}
78-
}
79-
80-
return this.factories.get(alias);
81-
}
82-
83115
using(name: string): Flasher {
84116
this.defaultHandler = name;
85117

@@ -131,38 +163,25 @@ export default class Flasher {
131163
document.body.appendChild(tag);
132164
}
133165

134-
renderOptions(options: FlasherResponseOptions): void {
135-
Object.entries(options).forEach(([handler, option]) => {
136-
const factory = this.create(handler);
137-
if (factory) {
138-
factory.renderOptions(option);
139-
}
140-
});
141-
}
142-
143166
renderEnvelopes(envelopes: Envelope[], context: ResponseContext): void {
144167
const queues = new Map<string, QueueableInterface>();
168+
145169
envelopes.forEach((envelope) => {
146-
envelope.context = context;
170+
envelope.context = Object.assign({}, envelope.context || {}, context);
147171
envelope.handler = this.resolveHandler(envelope.handler);
148172

149173
const factory = this.create(envelope.handler);
150-
if (undefined !== factory) {
151-
if (this.isQueueable(factory)) {
152-
if (!queues.get(envelope.handler)) {
153-
factory.resetQueue();
154-
}
155-
factory.addEnvelope(envelope);
156-
queues.set(envelope.handler, factory);
157-
} else {
158-
factory.render(envelope);
159-
}
174+
if (!this.isQueueable(factory)) {
175+
factory.render(envelope);
176+
return;
160177
}
161-
});
162178

163-
queues.forEach((factory) => {
164-
factory.renderQueue();
179+
queues.get(envelope.handler) || factory.resetQueue();
180+
factory.addEnvelope(envelope);
181+
queues.set(envelope.handler, factory);
165182
});
183+
184+
queues.forEach(factory => factory.renderQueue());
166185
}
167186

168187
isQueueable(object: any): object is QueueableInterface {
@@ -183,7 +202,6 @@ export default class Flasher {
183202

184203
response.envelopes.forEach(envelope => {
185204
envelope.handler = this.resolveHandler(envelope.handler);
186-
187205
envelope.notification.options = this.parseOptions(envelope.notification.options || {});
188206
this.pushStyles(response, envelope.handler);
189207
});
@@ -193,14 +211,33 @@ export default class Flasher {
193211

194212
parseOptions(options: FlasherOptions): FlasherOptions {
195213
Object.entries(options).forEach(([key, value]) => {
196-
options[key] = parseFunction(value);
214+
options[key] = this.parseFunction(value);
197215
});
198216

199217
return options;
200218
}
201219

220+
parseFunction(func: any): any {
221+
if (typeof func !== 'string') {
222+
return func;
223+
}
224+
225+
const match = func.match(/^function(?:.+)?(?:\s+)?\((.+)?\)(?:\s+|\n+)?{(?:\s+|\n+)?((?:.|\n)+)}$/m);
226+
227+
if (!match) {
228+
return func;
229+
}
230+
231+
const args = match[1]?.split(',').map(arg => arg.trim()) ?? [];
232+
const body = match[2];
233+
234+
// @ts-ignore
235+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
236+
return new Function(...args, body);
237+
}
238+
202239
pushStyles(response: FlasherResponse, handler: string): void {
203-
handler = handler.replace('template.', '');
240+
handler = handler.replace('theme.', '');
204241
const styles = this.themes.get(handler)?.styles || [];
205242

206243
response.styles = response.styles
@@ -211,10 +248,25 @@ export default class Flasher {
211248
resolveHandler(handler?: string): string {
212249
handler = handler || this.defaultHandler;
213250

214-
if ('template' === handler) {
215-
handler = 'template.flasher';
251+
if (['flasher', 'theme', 'template'].includes(handler)) {
252+
handler = 'theme.flasher';
216253
}
217254

255+
handler = handler.replace('template.', 'theme.');
256+
218257
return handler;
219258
}
259+
260+
resolveThemeHandler(alias: string): void {
261+
if (0 !== alias.indexOf('theme.')) {
262+
return;
263+
}
264+
265+
let viewFactory = this.themes.get(alias.replace('theme.', ''));
266+
if (!viewFactory) {
267+
return;
268+
}
269+
270+
this.addFactory(alias, new FlasherFactory(viewFactory));
271+
}
220272
}

0 commit comments

Comments
 (0)