From 16439a79388689df5c9cc9805e7a27eeeea431c6 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 21 Dec 2017 18:10:09 -0800 Subject: [PATCH] Add basic mechanism for surfacing toasts. --- .../public/chrome/directives/kbn_chrome.html | 11 +- src/ui/public/chrome/directives/kbn_chrome.js | 6 +- src/ui/public/notify/index.js | 1 + .../public/notify/toasts/global_toast_list.js | 121 ++++++++++++++++++ src/ui/public/notify/toasts/index.js | 2 + .../notify/toasts/toast_notifications.js | 21 +++ src/ui/public/react_components.js | 2 + 7 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/ui/public/notify/toasts/global_toast_list.js create mode 100644 src/ui/public/notify/toasts/index.js create mode 100644 src/ui/public/notify/toasts/toast_notifications.js diff --git a/src/ui/public/chrome/directives/kbn_chrome.html b/src/ui/public/chrome/directives/kbn_chrome.html index 1d6a34e84bfd6f0..10fb9cb73adb936 100644 --- a/src/ui/public/chrome/directives/kbn_chrome.html +++ b/src/ui/public/chrome/directives/kbn_chrome.html @@ -10,8 +10,17 @@
- + + + + +
{ + const lifeTime = isImmediate ? TOAST_FADE_OUT_MS : TOAST_LIFE_TIME_MS; + + this.timeoutIds.push(setTimeout(() => { + this.props.dismissToast(toast); + this.setState(prevState => { + const toastIdToDismissedMap = { ...this.state.toastIdToDismissedMap }; + delete toastIdToDismissedMap[toast.id]; + + return { + toastIdToDismissedMap, + }; + }); + }, lifeTime)); + + this.timeoutIds.push(setTimeout(() => { + this.startDismissingToast(toast); + }, lifeTime - TOAST_FADE_OUT_MS)); + }; + + startDismissingToast(toast) { + this.setState(prevState => { + const toastIdToDismissedMap = { ...this.state.toastIdToDismissedMap }; + toastIdToDismissedMap[toast.id] = true; + + return { + toastIdToDismissedMap, + }; + }); + } + + componentWillUnmount() { + this.timeoutIds.forEach(timeoutId => clearTimeout(timeoutId)); + } + + componentDidUpdate(prevProps) { + prevProps.toasts.forEach(toast => { + if (!this.toastsScheduledForDismissal[toast.id]) { + this.scheduleToastForDismissal(toast); + } + }); + } + + render() { + const { + toasts, + dismissToast, + } = this.props; + + const renderedToasts = toasts.map(toast => { + const { text, ...rest } = toast; + return ( + + + {text} + + + ); + }); + + return ( + + {renderedToasts} + + ); + } +} + +const app = uiModules.get('app/kibana', ['react']); + +app.directive('globalToastList', function (reactDirective) { + return reactDirective(GlobalToastList, [ + 'toasts', + ['dismissToast', { watchDepth: 'reference' }], + ]); +}); diff --git a/src/ui/public/notify/toasts/index.js b/src/ui/public/notify/toasts/index.js new file mode 100644 index 000000000000000..b6a3fe4364da346 --- /dev/null +++ b/src/ui/public/notify/toasts/index.js @@ -0,0 +1,2 @@ +import './global_toast_list'; +export { toastNotifications } from './toast_notifications'; diff --git a/src/ui/public/notify/toasts/toast_notifications.js b/src/ui/public/notify/toasts/toast_notifications.js new file mode 100644 index 000000000000000..fb02a2e70942a8b --- /dev/null +++ b/src/ui/public/notify/toasts/toast_notifications.js @@ -0,0 +1,21 @@ +let toastCounter = 0; + +class ToastNotifications { + constructor() { + this.list = []; + } + + add = toast => { + this.list.push({ + id: toastCounter++, + ...toast + }); + }; + + remove = toast => { + const index = this.list.indexOf(toast); + this.list.splice(index, 1); + }; +} + +export const toastNotifications = new ToastNotifications(); diff --git a/src/ui/public/react_components.js b/src/ui/public/react_components.js index e6e262e9b8b8946..f293f227a77ae22 100644 --- a/src/ui/public/react_components.js +++ b/src/ui/public/react_components.js @@ -8,9 +8,11 @@ import { import { uiModules } from 'ui/modules'; const app = uiModules.get('app/kibana', ['react']); + app.directive('toolBarSearchBox', function (reactDirective) { return reactDirective(KuiToolBarSearchBox); }); + app.directive('confirmModal', function (reactDirective) { return reactDirective(KuiConfirmModal); });