mirror of
https://github.com/S2-/gitlit
synced 2025-08-26 05:30:05 +02:00
1182 lines
38 KiB
HTML
1182 lines
38 KiB
HTML
<!--
|
|
====== PNotify ======
|
|
|
|
http://sciactive.com/pnotify/
|
|
|
|
Copyright 2009-2019 Hunter Perrin
|
|
Copyright 2015 Google, Inc.
|
|
|
|
Licensed under Apache License, Version 2.0.
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
-->
|
|
|
|
<div ref:elem
|
|
class="
|
|
ui-pnotify
|
|
{icon !== false ? 'ui-pnotify-with-icon' : ''}
|
|
{_styles.element ? _styles.element : ''}
|
|
{addClass}
|
|
{_animatingClass}
|
|
{_moveClass}
|
|
{animation === 'fade' ? 'ui-pnotify-fade-'+animateSpeed : ''}
|
|
{stack && stack.modal ? 'ui-pnotify-modal' : ''}
|
|
{_moduleClasses.join(' ')}
|
|
"
|
|
aria-live="assertive"
|
|
role="alertdialog"
|
|
ui-pnotify
|
|
on:mouseover
|
|
on:mouseout
|
|
on:mouseenter
|
|
on:mouseleave
|
|
on:mousemove
|
|
on:mousedown
|
|
on:mouseup
|
|
on:click
|
|
on:dblclick
|
|
on:focus
|
|
on:blur
|
|
on:touchstart
|
|
on:touchmove
|
|
on:touchend
|
|
on:touchcancel>
|
|
<div ref:container
|
|
class="
|
|
ui-pnotify-container
|
|
{_styles.container ? _styles.container : ''}
|
|
{_styles[type] ? _styles[type] : ''}
|
|
{cornerClass}
|
|
{shadow ? 'ui-pnotify-shadow' : ''}
|
|
"
|
|
style="{_widthStyle} {_minHeightStyle}"
|
|
role="alert">
|
|
{#each _modulesPrependContainer as module (module.key)}
|
|
<svelte:component this={module} on:init="initModule(event.module)" />
|
|
{/each}
|
|
{#if icon !== false}
|
|
<div ref:iconContainer class="ui-pnotify-icon {_styles.icon ? _styles.icon : ''}">
|
|
<span class="{icon === true ? (_icons[type] ? _icons[type] : '') : icon}"></span>
|
|
</div>
|
|
{/if}
|
|
{#if title !== false}
|
|
<h4 ref:titleContainer class="ui-pnotify-title {_styles.title ? _styles.title : ''}">
|
|
{#if titleTrusted}
|
|
{@html title}
|
|
{:else}
|
|
{title}
|
|
{/if}
|
|
</h4>
|
|
{/if}
|
|
{#if text !== false}
|
|
<div ref:textContainer class="ui-pnotify-text {_styles.text ? _styles.text : ''}" role="alert">
|
|
{#if textTrusted}
|
|
{@html text}
|
|
{:else}
|
|
{text}
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
{#each _modulesAppendContainer as module (module.key)}
|
|
<svelte:component this={module} on:init="initModule(event.module)" />
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let PNotify;
|
|
|
|
let posTimer; // Position all timer.
|
|
|
|
// These actions need to be done once the DOM is ready.
|
|
let onDocumentLoaded = () => {
|
|
PNotify.defaultStack.context = document.body;
|
|
// Reposition the notices when the window resizes.
|
|
window.addEventListener('resize', () => {
|
|
if (posTimer) {
|
|
clearTimeout(posTimer);
|
|
}
|
|
posTimer = setTimeout(() => {
|
|
PNotify.positionAll();
|
|
}, 10);
|
|
});
|
|
};
|
|
|
|
// Creates the background overlay for modal stacks.
|
|
let createStackOverlay = (stack) => {
|
|
const overlay = document.createElement('div');
|
|
overlay.classList.add('ui-pnotify-modal-overlay');
|
|
if (stack.context !== document.body) {
|
|
overlay.style.height = stack.context.scrollHeight + 'px';
|
|
overlay.style.width = stack.context.scrollWidth + 'px';
|
|
}
|
|
// Close the notices on overlay click.
|
|
overlay.addEventListener('click', () => {
|
|
if (stack.overlayClose) {
|
|
PNotify.closeStack(stack);
|
|
}
|
|
});
|
|
stack.overlay = overlay;
|
|
};
|
|
|
|
let insertStackOverlay = (stack) => {
|
|
if (stack.overlay.parentNode !== stack.context) {
|
|
stack.overlay = stack.context.insertBefore(stack.overlay, stack.context.firstChild);
|
|
}
|
|
};
|
|
|
|
let removeStackOverlay = (stack) => {
|
|
if (stack.overlay.parentNode) {
|
|
stack.overlay.parentNode.removeChild(stack.overlay);
|
|
}
|
|
};
|
|
|
|
// Default arguments for the new notice helper functions.
|
|
const getDefaultArgs = (options, type) => {
|
|
if (typeof options !== 'object') {
|
|
options = { 'text': options };
|
|
}
|
|
|
|
// Only assign the type if it was requested, so we don't overwrite
|
|
// options.type if it has something assigned.
|
|
if (type) {
|
|
options.type = type;
|
|
}
|
|
|
|
return { target: document.body, data: options };
|
|
};
|
|
|
|
export default {
|
|
setup: (Component) => {
|
|
// Add static properties to the PNotify object.
|
|
PNotify = Component;
|
|
|
|
PNotify.VERSION = '4.0.0';
|
|
|
|
PNotify.defaultStack = {
|
|
dir1: 'down',
|
|
dir2: 'left',
|
|
firstpos1: 25,
|
|
firstpos2: 25,
|
|
spacing1: 36,
|
|
spacing2: 36,
|
|
push: 'bottom',
|
|
context: window && document.body
|
|
};
|
|
|
|
PNotify.defaults = {
|
|
// The notice's title.
|
|
title: false,
|
|
// Whether to trust the title or escape its contents. (Not allow HTML.)
|
|
titleTrusted: false,
|
|
// The notice's text.
|
|
text: false,
|
|
// Whether to trust the text or escape its contents. (Not allow HTML.)
|
|
textTrusted: false,
|
|
// What styling classes to use. (Can be 'brighttheme', 'bootstrap3', 'bootstrap4', or a styling object.)
|
|
styling: 'brighttheme',
|
|
// What icons to use (Can be 'brighttheme', 'bootstrap3', 'fontawesome4', 'fontawesome5', or an icon object.)
|
|
icons: 'brighttheme',
|
|
// Additional classes to be added to the notice. (For custom styling.)
|
|
addClass: '',
|
|
// Class to be added to the notice for corner styling.
|
|
cornerClass: '',
|
|
// Display the notice when it is created.
|
|
autoDisplay: true,
|
|
// Width of the notice.
|
|
width: '360px',
|
|
// Minimum height of the notice. It will expand to fit content.
|
|
minHeight: '16px',
|
|
// Type of the notice. 'notice', 'info', 'success', or 'error'.
|
|
type: 'notice',
|
|
// Set icon to true to use the default icon for the selected
|
|
// style/type, false for no icon, or a string for your own icon class.
|
|
icon: true,
|
|
// The animation to use when displaying and hiding the notice. 'none'
|
|
// and 'fade' are supported through CSS. Others are supported
|
|
// through the Animate module and Animate.css.
|
|
animation: 'fade',
|
|
// Speed at which the notice animates in and out. 'slow', 'normal',
|
|
// or 'fast'. Respectively, 400ms, 250ms, 100ms.
|
|
animateSpeed: 'normal',
|
|
// Display a drop shadow.
|
|
shadow: true,
|
|
// After a delay, remove the notice.
|
|
hide: true,
|
|
// Delay in milliseconds before the notice is removed.
|
|
delay: 8000,
|
|
// Reset the hide timer if the mouse moves over the notice.
|
|
mouseReset: true,
|
|
// Remove the notice's elements from the DOM after it is removed.
|
|
remove: true,
|
|
// Whether to remove the notice from the global array when it is closed.
|
|
destroy: true,
|
|
// The stack on which the notices will be placed. Also controls the
|
|
// direction the notices stack.
|
|
stack: PNotify.defaultStack,
|
|
// This is where options for modules should be defined.
|
|
modules: {}
|
|
};
|
|
|
|
// An array of all active notices.
|
|
PNotify.notices = [];
|
|
|
|
// This object holds all the PNotify modules. They are used to provide
|
|
// additional functionality.
|
|
PNotify.modules = {};
|
|
|
|
// Modules can add themselves to these to be rendered in the template.
|
|
PNotify.modulesPrependContainer = [];
|
|
PNotify.modulesAppendContainer = [];
|
|
|
|
// Helper function to create a new notice.
|
|
PNotify.alert = (options) => new PNotify(getDefaultArgs(options));
|
|
// Helper function to create a new notice (notice type).
|
|
PNotify.notice = (options) => new PNotify(getDefaultArgs(options, 'notice'));
|
|
// Helper function to create a new notice (info type).
|
|
PNotify.info = (options) => new PNotify(getDefaultArgs(options, 'info'));
|
|
// Helper function to create a new notice (success type).
|
|
PNotify.success = (options) => new PNotify(getDefaultArgs(options, 'success'));
|
|
// Helper function to create a new notice (error type).
|
|
PNotify.error = (options) => new PNotify(getDefaultArgs(options, 'error'));
|
|
|
|
PNotify.removeAll = () => {
|
|
PNotify.closeAll();
|
|
};
|
|
|
|
// Close all notices.
|
|
PNotify.closeAll = () => {
|
|
for (let i = 0; i < PNotify.notices.length; i++) {
|
|
if (PNotify.notices[i].close) {
|
|
PNotify.notices[i].close(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
PNotify.removeStack = (stack) => {
|
|
PNotify.closeStack(stack);
|
|
};
|
|
|
|
// Close all notices in a single stack.
|
|
PNotify.closeStack = (stack) => {
|
|
if (stack === false) {
|
|
return;
|
|
}
|
|
for (let i = 0; i < PNotify.notices.length; i++) {
|
|
if (PNotify.notices[i].close && PNotify.notices[i].get().stack === stack) {
|
|
PNotify.notices[i].close(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Position all notices.
|
|
PNotify.positionAll = () => {
|
|
// This timer is used for queueing this function so it doesn't run
|
|
// repeatedly.
|
|
if (posTimer) {
|
|
clearTimeout(posTimer);
|
|
}
|
|
posTimer = null;
|
|
// Reset the next position data.
|
|
if (PNotify.notices.length > 0) {
|
|
for (let i = 0; i < PNotify.notices.length; i++) {
|
|
let notice = PNotify.notices[i];
|
|
let { stack } = notice.get();
|
|
if (!stack) {
|
|
continue;
|
|
}
|
|
if (stack.overlay) {
|
|
removeStackOverlay(stack);
|
|
}
|
|
stack.nextpos1 = stack.firstpos1;
|
|
stack.nextpos2 = stack.firstpos2;
|
|
stack.addpos2 = 0;
|
|
}
|
|
for (let i = 0; i < PNotify.notices.length; i++) {
|
|
PNotify.notices[i].position();
|
|
}
|
|
} else {
|
|
delete PNotify.defaultStack.nextpos1;
|
|
delete PNotify.defaultStack.nextpos2;
|
|
}
|
|
};
|
|
|
|
PNotify.styling = {
|
|
brighttheme: {
|
|
// Bright Theme doesn't require any UI libraries.
|
|
container: 'brighttheme',
|
|
notice: 'brighttheme-notice',
|
|
info: 'brighttheme-info',
|
|
success: 'brighttheme-success',
|
|
error: 'brighttheme-error'
|
|
},
|
|
bootstrap3: {
|
|
container: 'alert',
|
|
notice: 'alert-warning',
|
|
info: 'alert-info',
|
|
success: 'alert-success',
|
|
error: 'alert-danger',
|
|
icon: 'ui-pnotify-icon-bs3'
|
|
},
|
|
bootstrap4: {
|
|
container: 'alert',
|
|
notice: 'alert-warning',
|
|
info: 'alert-info',
|
|
success: 'alert-success',
|
|
error: 'alert-danger',
|
|
icon: 'ui-pnotify-icon-bs4',
|
|
title: 'ui-pnotify-title-bs4'
|
|
}
|
|
};
|
|
|
|
// icons are separate from the style, since bs4 doesn't come with any
|
|
PNotify.icons = {
|
|
brighttheme: {
|
|
notice: 'brighttheme-icon-notice',
|
|
info: 'brighttheme-icon-info',
|
|
success: 'brighttheme-icon-success',
|
|
error: 'brighttheme-icon-error'
|
|
},
|
|
bootstrap3: {
|
|
notice: 'glyphicon glyphicon-exclamation-sign',
|
|
info: 'glyphicon glyphicon-info-sign',
|
|
success: 'glyphicon glyphicon-ok-sign',
|
|
error: 'glyphicon glyphicon-warning-sign'
|
|
},
|
|
// User must have Font Awesome v4.0+
|
|
fontawesome4: {
|
|
notice: 'fa fa-exclamation-circle',
|
|
info: 'fa fa-info-circle',
|
|
success: 'fa fa-check-circle',
|
|
error: 'fa fa-exclamation-triangle'
|
|
},
|
|
// User must have Font Awesome v5.0+
|
|
fontawesome5: {
|
|
notice: 'fas fa-exclamation-circle',
|
|
info: 'fas fa-info-circle',
|
|
success: 'fas fa-check-circle',
|
|
error: 'fas fa-exclamation-triangle'
|
|
}
|
|
};
|
|
|
|
// Run the deferred actions once the DOM is ready.
|
|
if (window && document.body) {
|
|
onDocumentLoaded();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', onDocumentLoaded);
|
|
}
|
|
},
|
|
|
|
oncreate () {
|
|
this.on('mouseenter', (e) => {
|
|
// Stop animation, reset the removal timer when the user mouses over.
|
|
if (this.get().mouseReset && this.get()._animating === 'out') {
|
|
if (!this.get()._timerHide) {
|
|
return;
|
|
}
|
|
this.cancelClose();
|
|
}
|
|
// Stop the close timer.
|
|
if (this.get().hide && this.get().mouseReset) {
|
|
this.cancelClose();
|
|
}
|
|
});
|
|
|
|
this.on('mouseleave', (e) => {
|
|
// Start the close timer.
|
|
if (this.get().hide && this.get().mouseReset && this.get()._animating !== 'out') {
|
|
this.queueClose();
|
|
}
|
|
PNotify.positionAll();
|
|
});
|
|
|
|
let { stack } = this.get();
|
|
|
|
// Add the notice to the notice array.
|
|
if (stack && stack.push === 'top') {
|
|
PNotify.notices.splice(0, 0, this);
|
|
} else {
|
|
PNotify.notices.push(this);
|
|
}
|
|
|
|
// Run the modules.
|
|
this.runModules('init');
|
|
|
|
// We're now initialized, but haven't been opened yet.
|
|
this.set({ '_state': 'closed' });
|
|
|
|
// Display the notice.
|
|
if (this.get().autoDisplay) {
|
|
this.open();
|
|
}
|
|
},
|
|
|
|
data () {
|
|
const data = Object.assign({
|
|
'_state': 'initializing', // The state can be 'initializing', 'opening', 'open', 'closing', and 'closed'.
|
|
'_timer': null, // Auto close timer.
|
|
'_animTimer': null, // Animation timer.
|
|
'_animating': false, // Stores what is currently being animated (in or out).
|
|
'_animatingClass': '', // Stores the class that adds entry/exit animation effects.
|
|
'_moveClass': '', // Stores the class that adds movement animation effects.
|
|
'_timerHide': false, // Stores whether the notice was hidden by a timer.
|
|
'_moduleClasses': [], // Modules can add classes here to be added to the notice element. (They should play nice and not remove classes that aren't theirs.)
|
|
'_moduleIsNoticeOpen': false, // Modules that change how the notice displays (causing the notice element to not appear) can set this to true to make PNotify assume the notice has opened.
|
|
'_modules': {}, // Stores the instances of the modules.
|
|
'_modulesPrependContainer': PNotify.modulesPrependContainer,
|
|
'_modulesAppendContainer': PNotify.modulesAppendContainer
|
|
}, PNotify.defaults);
|
|
data.modules = Object.assign({}, PNotify.defaults.modules);
|
|
return data;
|
|
},
|
|
|
|
computed: {
|
|
// Grab styles from the styling object or use the styles provided.
|
|
_styles: ({ styling }) => typeof styling === 'object' ? styling : PNotify.styling[styling],
|
|
// Grab the icons from the icons object or use provided icons
|
|
_icons: ({ icons }) => typeof icons === 'object' ? icons : PNotify.icons[icons],
|
|
_widthStyle: ({ width }) => typeof width === 'string' ? 'width: ' + width + ';' : '',
|
|
_minHeightStyle: ({ minHeight }) => typeof minHeight === 'string' ? 'min-height: ' + minHeight + ';' : ''
|
|
},
|
|
|
|
methods: {
|
|
// This runs an event on all the modules.
|
|
runModules (event) {
|
|
if (event === 'init') {
|
|
// Initializing a module should only be done if it has an init
|
|
// function, which means it's not rendered in the template.
|
|
for (let key in PNotify.modules) {
|
|
if (!PNotify.modules.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
if (typeof PNotify.modules[key].init === 'function') {
|
|
const module = PNotify.modules[key].init(this);
|
|
this.initModule(module);
|
|
}
|
|
}
|
|
} else {
|
|
const { _modules } = this.get();
|
|
for (let module in _modules) {
|
|
if (!_modules.hasOwnProperty(module)) {
|
|
continue;
|
|
}
|
|
const moduleOptions = Object.assign({
|
|
'_notice': this,
|
|
'_options': this.get()
|
|
}, this.get().modules[module]);
|
|
_modules[module].set(moduleOptions);
|
|
if (typeof _modules[module][event] === 'function') {
|
|
_modules[module][event]();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// This passes module options to a module.
|
|
initModule (module) {
|
|
const { modules } = this.get();
|
|
if (!modules.hasOwnProperty(module.constructor.key)) {
|
|
modules[module.constructor.key] = {};
|
|
}
|
|
const moduleOptions = Object.assign({
|
|
'_notice': this,
|
|
'_options': this.get()
|
|
}, modules[module.constructor.key]);
|
|
module.initModule(moduleOptions);
|
|
|
|
// Now save the module instance.
|
|
const { _modules } = this.get();
|
|
_modules[module.constructor.key] = module;
|
|
},
|
|
|
|
update (options) {
|
|
// Save old options.
|
|
const oldHide = this.get().hide;
|
|
const oldIcon = this.get().icon;
|
|
|
|
this.set(options);
|
|
|
|
// Run the modules.
|
|
this.runModules('update');
|
|
|
|
// Update the timed hiding.
|
|
if (!this.get().hide) {
|
|
this.cancelClose();
|
|
} else if (!oldHide) {
|
|
this.queueClose();
|
|
}
|
|
this.queuePosition();
|
|
|
|
// Font Awesome 5 replaces our lovely element with a gross SVG. In order
|
|
// to make it play nice with Svelte, we have to clear the element and
|
|
// make it again.
|
|
const { icon } = this.get();
|
|
if (
|
|
icon !== oldIcon &&
|
|
(
|
|
(icon === true && this.get().icons === 'fontawesome5') ||
|
|
(typeof icon === 'string' && icon.match(/(^| )fa[srlb]($| )/))
|
|
)
|
|
) {
|
|
this.set({ 'icon': false });
|
|
this.set({ 'icon': icon });
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// Display the notice.
|
|
open () {
|
|
const { _state, hide } = this.get();
|
|
if (_state === 'opening') {
|
|
return;
|
|
}
|
|
if (_state === 'open') {
|
|
if (hide) {
|
|
this.queueClose();
|
|
}
|
|
return;
|
|
}
|
|
this.set({
|
|
'_state': 'opening',
|
|
// This makes the notice visibity: hidden; so its dimensions can be
|
|
// determined.
|
|
'_animatingClass': 'ui-pnotify-initial-hidden'
|
|
});
|
|
// Run the modules.
|
|
this.runModules('beforeOpen');
|
|
|
|
let { stack } = this.get();
|
|
// If the notice is not in the DOM, or in the wrong context, append it.
|
|
if (
|
|
!this.refs.elem.parentNode ||
|
|
(
|
|
stack &&
|
|
stack.context &&
|
|
stack.context !== this.refs.elem.parentNode
|
|
)
|
|
) {
|
|
if (stack && stack.context) {
|
|
stack.context.appendChild(this.refs.elem);
|
|
} else if (document.body) {
|
|
document.body.appendChild(this.refs.elem);
|
|
} else {
|
|
throw new Error('No context to open this notice in.');
|
|
}
|
|
}
|
|
|
|
// Wait until the DOM is updated.
|
|
setTimeout(() => {
|
|
if (stack) {
|
|
// Mark the stack so it won't animate the new notice.
|
|
stack.animation = false;
|
|
// Now position all the notices.
|
|
PNotify.positionAll();
|
|
// Reset animation.
|
|
stack.animation = true;
|
|
}
|
|
|
|
this.animateIn(() => {
|
|
// Now set it to hide.
|
|
if (this.get().hide) {
|
|
this.queueClose();
|
|
}
|
|
|
|
this.set({ '_state': 'open' });
|
|
|
|
// Run the modules.
|
|
this.runModules('afterOpen');
|
|
});
|
|
}, 0);
|
|
|
|
return this;
|
|
},
|
|
|
|
remove (timerHide) {
|
|
return this.close(timerHide);
|
|
},
|
|
|
|
// Remove the notice.
|
|
close (timerHide) {
|
|
const { _state } = this.get();
|
|
if (_state === 'closing' || _state === 'closed') {
|
|
return;
|
|
}
|
|
this.set({ '_state': 'closing', '_timerHide': !!timerHide }); // Make sure it's a boolean.
|
|
// Run the modules.
|
|
this.runModules('beforeClose');
|
|
|
|
const { _timer } = this.get();
|
|
if (_timer && clearTimeout) {
|
|
clearTimeout(_timer);
|
|
this.set({ '_timer': null });
|
|
}
|
|
this.animateOut(() => {
|
|
this.set({ '_state': 'closed' });
|
|
// Run the modules.
|
|
this.runModules('afterClose');
|
|
this.queuePosition();
|
|
// If we're supposed to remove the notice from the DOM, do it.
|
|
if (this.get().remove) {
|
|
this.refs.elem.parentNode.removeChild(this.refs.elem);
|
|
}
|
|
// Run the modules.
|
|
this.runModules('beforeDestroy');
|
|
// Remove object from PNotify.notices to prevent memory leak (issue #49)
|
|
// unless destroy is off
|
|
if (this.get().destroy) {
|
|
if (PNotify.notices !== null) {
|
|
const idx = PNotify.notices.indexOf(this);
|
|
if (idx !== -1) {
|
|
PNotify.notices.splice(idx, 1);
|
|
}
|
|
}
|
|
}
|
|
// Run the modules.
|
|
this.runModules('afterDestroy');
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
// Animate the notice in.
|
|
animateIn (callback) {
|
|
// Declare that the notice is animating in.
|
|
this.set({ '_animating': 'in' });
|
|
const finished = () => {
|
|
this.refs.elem.removeEventListener('transitionend', finished);
|
|
const { _animTimer, _animating, _moduleIsNoticeOpen } = this.get();
|
|
if (_animTimer) {
|
|
clearTimeout(_animTimer);
|
|
}
|
|
if (_animating !== 'in') {
|
|
return;
|
|
}
|
|
let visible = _moduleIsNoticeOpen;
|
|
if (!visible) {
|
|
const domRect = this.refs.elem.getBoundingClientRect();
|
|
for (let prop in domRect) {
|
|
if (domRect[prop] > 0) {
|
|
visible = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (visible) {
|
|
if (callback) {
|
|
callback.call();
|
|
}
|
|
// Declare that the notice has completed animating.
|
|
this.set({ '_animating': false });
|
|
} else {
|
|
this.set({ '_animTimer': setTimeout(finished, 40) });
|
|
}
|
|
};
|
|
|
|
if (this.get().animation === 'fade') {
|
|
this.refs.elem.addEventListener('transitionend', finished);
|
|
this.set({ '_animatingClass': 'ui-pnotify-in' });
|
|
// eslint-disable-next-line no-unused-expressions
|
|
this.refs.elem.style.opacity; // This line is necessary for some reason. Some notices don't fade without it.
|
|
this.set({ '_animatingClass': 'ui-pnotify-in ui-pnotify-fade-in' });
|
|
// Just in case the event doesn't fire, call it after 650 ms.
|
|
this.set({ '_animTimer': setTimeout(finished, 650) });
|
|
} else {
|
|
this.set({ '_animatingClass': 'ui-pnotify-in' });
|
|
finished();
|
|
}
|
|
},
|
|
|
|
// Animate the notice out.
|
|
animateOut (callback) {
|
|
// Declare that the notice is animating out.
|
|
this.set({ '_animating': 'out' });
|
|
const finished = () => {
|
|
this.refs.elem.removeEventListener('transitionend', finished);
|
|
const { _animTimer, _animating, _moduleIsNoticeOpen } = this.get();
|
|
if (_animTimer) {
|
|
clearTimeout(_animTimer);
|
|
}
|
|
if (_animating !== 'out') {
|
|
return;
|
|
}
|
|
let visible = _moduleIsNoticeOpen;
|
|
if (!visible) {
|
|
const domRect = this.refs.elem.getBoundingClientRect();
|
|
for (let prop in domRect) {
|
|
if (domRect[prop] > 0) {
|
|
visible = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!this.refs.elem.style.opacity || this.refs.elem.style.opacity === '0' || !visible) {
|
|
this.set({ '_animatingClass': '' });
|
|
const { stack } = this.get();
|
|
if (stack && stack.overlay) {
|
|
// Go through the modal stack to see if any are left open.
|
|
// TODO: Rewrite this cause it sucks.
|
|
let stillOpen = false;
|
|
for (let i = 0; i < PNotify.notices.length; i++) {
|
|
const notice = PNotify.notices[i];
|
|
if (notice !== this && notice.get().stack === stack && notice.get()._state !== 'closed') {
|
|
stillOpen = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!stillOpen) {
|
|
removeStackOverlay(stack);
|
|
}
|
|
}
|
|
if (callback) {
|
|
callback.call();
|
|
}
|
|
// Declare that the notice has completed animating.
|
|
this.set({ '_animating': false });
|
|
} else {
|
|
// In case this was called before the notice finished animating.
|
|
this.set({ '_animTimer': setTimeout(finished, 40) });
|
|
}
|
|
};
|
|
|
|
if (this.get().animation === 'fade') {
|
|
this.refs.elem.addEventListener('transitionend', finished);
|
|
this.set({ '_animatingClass': 'ui-pnotify-in' });
|
|
// Just in case the event doesn't fire, call it after 650 ms.
|
|
this.set({ '_animTimer': setTimeout(finished, 650) });
|
|
} else {
|
|
this.set({ '_animatingClass': '' });
|
|
finished();
|
|
}
|
|
},
|
|
|
|
// Position the notice.
|
|
position () {
|
|
// Get the notice's stack.
|
|
let { stack } = this.get();
|
|
let elem = this.refs.elem;
|
|
if (!stack) {
|
|
return;
|
|
}
|
|
if (!stack.context) {
|
|
stack.context = document.body;
|
|
}
|
|
if (typeof stack.nextpos1 !== 'number') {
|
|
stack.nextpos1 = stack.firstpos1;
|
|
}
|
|
if (typeof stack.nextpos2 !== 'number') {
|
|
stack.nextpos2 = stack.firstpos2;
|
|
}
|
|
if (typeof stack.addpos2 !== 'number') {
|
|
stack.addpos2 = 0;
|
|
}
|
|
|
|
// Skip this notice if it's not shown.
|
|
if (
|
|
!elem.classList.contains('ui-pnotify-in') &&
|
|
!elem.classList.contains('ui-pnotify-initial-hidden')
|
|
) {
|
|
return this;
|
|
}
|
|
|
|
if (stack.modal) {
|
|
if (!stack.overlay) {
|
|
createStackOverlay(stack);
|
|
}
|
|
insertStackOverlay(stack);
|
|
}
|
|
|
|
// Read from the DOM to cause refresh.
|
|
elem.getBoundingClientRect();
|
|
|
|
if (stack.animation) {
|
|
// Add animate class.
|
|
this.set({ '_moveClass': 'ui-pnotify-move' });
|
|
}
|
|
|
|
let spaceY = (stack.context === document.body ? window.innerHeight : stack.context.scrollHeight);
|
|
let spaceX = (stack.context === document.body ? window.innerWidth : stack.context.scrollWidth);
|
|
|
|
let csspos1;
|
|
|
|
if (stack.dir1) {
|
|
csspos1 = {
|
|
'down': 'top',
|
|
'up': 'bottom',
|
|
'left': 'right',
|
|
'right': 'left'
|
|
}[stack.dir1];
|
|
|
|
// Calculate the current pos1 value.
|
|
let curpos1;
|
|
switch (stack.dir1) {
|
|
case 'down':
|
|
curpos1 = elem.offsetTop;
|
|
break;
|
|
case 'up':
|
|
curpos1 = spaceY - elem.scrollHeight - elem.offsetTop;
|
|
break;
|
|
case 'left':
|
|
curpos1 = spaceX - elem.scrollWidth - elem.offsetLeft;
|
|
break;
|
|
case 'right':
|
|
curpos1 = elem.offsetLeft;
|
|
break;
|
|
}
|
|
// Remember the first pos1, so the first notice goes there.
|
|
if (typeof stack.firstpos1 === 'undefined') {
|
|
stack.firstpos1 = curpos1;
|
|
stack.nextpos1 = stack.firstpos1;
|
|
}
|
|
}
|
|
|
|
if (stack.dir1 && stack.dir2) {
|
|
let csspos2 = {
|
|
'down': 'top',
|
|
'up': 'bottom',
|
|
'left': 'right',
|
|
'right': 'left'
|
|
}[stack.dir2];
|
|
|
|
// Calculate the current pos2 value.
|
|
let curpos2;
|
|
switch (stack.dir2) {
|
|
case 'down':
|
|
curpos2 = elem.offsetTop;
|
|
break;
|
|
case 'up':
|
|
curpos2 = spaceY - elem.scrollHeight - elem.offsetTop;
|
|
break;
|
|
case 'left':
|
|
curpos2 = spaceX - elem.scrollWidth - elem.offsetLeft;
|
|
break;
|
|
case 'right':
|
|
curpos2 = elem.offsetLeft;
|
|
break;
|
|
}
|
|
// Remember the first pos2, so the first notice goes there.
|
|
if (typeof stack.firstpos2 === 'undefined') {
|
|
stack.firstpos2 = curpos2;
|
|
stack.nextpos2 = stack.firstpos2;
|
|
}
|
|
|
|
// Check that it's not beyond the viewport edge.
|
|
const endY = stack.nextpos1 + elem.offsetHeight + (typeof stack.spacing1 === 'undefined' ? 25 : stack.spacing1);
|
|
const endX = stack.nextpos1 + elem.offsetWidth + (typeof stack.spacing1 === 'undefined' ? 25 : stack.spacing1);
|
|
if (
|
|
((stack.dir1 === 'down' || stack.dir1 === 'up') && endY > spaceY) ||
|
|
((stack.dir1 === 'left' || stack.dir1 === 'right') && endX > spaceX)
|
|
) {
|
|
// If it is, it needs to go back to the first pos1, and over on pos2.
|
|
stack.nextpos1 = stack.firstpos1;
|
|
stack.nextpos2 += stack.addpos2 + (typeof stack.spacing2 === 'undefined' ? 25 : stack.spacing2);
|
|
stack.addpos2 = 0;
|
|
}
|
|
|
|
// Move the notice on dir2.
|
|
if (typeof stack.nextpos2 === 'number') {
|
|
elem.style[csspos2] = stack.nextpos2 + 'px';
|
|
if (!stack.animation) {
|
|
// eslint-disable-next-line no-unused-expressions
|
|
elem.style[csspos2]; // Read from the DOM for update.
|
|
}
|
|
}
|
|
|
|
// Keep track of the widest/tallest notice in the column/row, so we can push the next column/row.
|
|
switch (stack.dir2) {
|
|
case 'down':
|
|
case 'up':
|
|
if (elem.offsetHeight + (parseFloat(elem.style.marginTop, 10) || 0) + (parseFloat(elem.style.marginBottom, 10) || 0) > stack.addpos2) {
|
|
stack.addpos2 = elem.offsetHeight;
|
|
}
|
|
break;
|
|
case 'left':
|
|
case 'right':
|
|
if (elem.offsetWidth + (parseFloat(elem.style.marginLeft, 10) || 0) + (parseFloat(elem.style.marginRight, 10) || 0) > stack.addpos2) {
|
|
stack.addpos2 = elem.offsetWidth;
|
|
}
|
|
break;
|
|
}
|
|
} else if (stack.dir1) {
|
|
// Center the notice along dir1 axis, because the stack has no dir2.
|
|
let cssMiddle, cssposCross;
|
|
switch (stack.dir1) {
|
|
case 'down':
|
|
case 'up':
|
|
cssposCross = ['left', 'right'];
|
|
cssMiddle = (stack.context.scrollWidth / 2) - (elem.offsetWidth / 2);
|
|
break;
|
|
case 'left':
|
|
case 'right':
|
|
cssposCross = ['top', 'bottom'];
|
|
cssMiddle = (spaceY / 2) - (elem.offsetHeight / 2);
|
|
break;
|
|
}
|
|
elem.style[cssposCross[0]] = cssMiddle + 'px';
|
|
elem.style[cssposCross[1]] = 'auto';
|
|
if (!stack.animation) {
|
|
// eslint-disable-next-line no-unused-expressions
|
|
elem.style[cssposCross[0]]; // Read from the DOM for update.
|
|
}
|
|
}
|
|
|
|
if (stack.dir1) {
|
|
// Move the notice on dir1.
|
|
if (typeof stack.nextpos1 === 'number') {
|
|
elem.style[csspos1] = stack.nextpos1 + 'px';
|
|
if (!stack.animation) {
|
|
// eslint-disable-next-line no-unused-expressions
|
|
elem.style[csspos1]; // Read from the DOM for update.
|
|
}
|
|
}
|
|
|
|
// Calculate the next dir1 position.
|
|
switch (stack.dir1) {
|
|
case 'down':
|
|
case 'up':
|
|
stack.nextpos1 += elem.offsetHeight + (typeof stack.spacing1 === 'undefined' ? 25 : stack.spacing1);
|
|
break;
|
|
case 'left':
|
|
case 'right':
|
|
stack.nextpos1 += elem.offsetWidth + (typeof stack.spacing1 === 'undefined' ? 25 : stack.spacing1);
|
|
break;
|
|
}
|
|
} else {
|
|
// Center the notice on the screen, because the stack has no dir1.
|
|
let cssMiddleLeft = (spaceX / 2) - (elem.offsetWidth / 2);
|
|
let cssMiddleTop = (spaceY / 2) - (elem.offsetHeight / 2);
|
|
elem.style.left = cssMiddleLeft + 'px';
|
|
elem.style.top = cssMiddleTop + 'px';
|
|
if (!stack.animation) {
|
|
// eslint-disable-next-line no-unused-expressions
|
|
elem.style.left; // Read from the DOM for update.
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
// Queue the position all function so it doesn't run repeatedly and
|
|
// use up resources.
|
|
queuePosition (milliseconds) {
|
|
if (posTimer) {
|
|
clearTimeout(posTimer);
|
|
}
|
|
if (!milliseconds) {
|
|
milliseconds = 10;
|
|
}
|
|
posTimer = setTimeout(() => {
|
|
PNotify.positionAll();
|
|
}, milliseconds);
|
|
return this;
|
|
},
|
|
|
|
cancelRemove () {
|
|
return this.cancelClose();
|
|
},
|
|
|
|
// Cancel any pending removal timer.
|
|
cancelClose () {
|
|
const { _timer, _animTimer, _state, animation } = this.get();
|
|
if (_timer) {
|
|
clearTimeout(_timer);
|
|
}
|
|
if (_animTimer) {
|
|
clearTimeout(_animTimer);
|
|
}
|
|
if (_state === 'closing') {
|
|
// If it's animating out, stop it.
|
|
this.set({
|
|
'_state': 'open',
|
|
'_animating': false,
|
|
'_animatingClass': animation === 'fade' ? 'ui-pnotify-in ui-pnotify-fade-in' : 'ui-pnotify-in'
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
|
|
queueRemove () {
|
|
return this.queueClose();
|
|
},
|
|
|
|
// Queue a close timer.
|
|
queueClose () {
|
|
// Cancel any current close timer.
|
|
this.cancelClose();
|
|
this.set({
|
|
'_timer': setTimeout(() => this.close(true), (isNaN(this.get().delay) ? 0 : this.get().delay))
|
|
});
|
|
return this;
|
|
},
|
|
|
|
addModuleClass (...classNames) {
|
|
const { _moduleClasses } = this.get();
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
let className = classNames[i];
|
|
if (_moduleClasses.indexOf(className) === -1) {
|
|
_moduleClasses.push(className);
|
|
}
|
|
}
|
|
this.set({ _moduleClasses });
|
|
},
|
|
|
|
removeModuleClass (...classNames) {
|
|
const { _moduleClasses } = this.get();
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
let className = classNames[i];
|
|
const idx = _moduleClasses.indexOf(className);
|
|
if (idx !== -1) {
|
|
_moduleClasses.splice(idx, 1);
|
|
}
|
|
}
|
|
this.set({ _moduleClasses });
|
|
},
|
|
|
|
hasModuleClass (...classNames) {
|
|
const { _moduleClasses } = this.get();
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
let className = classNames[i];
|
|
if (_moduleClasses.indexOf(className) === -1) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
/* -- Notice */
|
|
:global(body > .ui-pnotify) {
|
|
/* Notices in the body context should be fixed to the viewport. */
|
|
position: fixed;
|
|
/* Ensures notices are above everything */
|
|
z-index: 100040;
|
|
}
|
|
:global(body > .ui-pnotify.ui-pnotify-modal) {
|
|
z-index: 100042;
|
|
}
|
|
:global(.ui-pnotify) {
|
|
position: absolute;
|
|
height: auto;
|
|
z-index: 1;
|
|
display: none;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-modal) {
|
|
z-index: 3;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-in) {
|
|
display: block;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-initial-hidden) {
|
|
display: block;
|
|
visibility: hidden;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-move) {
|
|
transition: left .5s ease, top .5s ease, right .5s ease, bottom .5s ease;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-slow) {
|
|
transition: opacity .4s linear;
|
|
opacity: 0;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-slow.ui-pnotify.ui-pnotify-move) {
|
|
transition: opacity .4s linear, left .5s ease, top .5s ease, right .5s ease, bottom .5s ease;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-normal) {
|
|
transition: opacity .25s linear;
|
|
opacity: 0;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-normal.ui-pnotify.ui-pnotify-move) {
|
|
transition: opacity .25s linear, left .5s ease, top .5s ease, right .5s ease, bottom .5s ease;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-fast) {
|
|
transition: opacity .1s linear;
|
|
opacity: 0;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-fast.ui-pnotify.ui-pnotify-move) {
|
|
transition: opacity .1s linear, left .5s ease, top .5s ease, right .5s ease, bottom .5s ease;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-fade-in) {
|
|
opacity: 1;
|
|
}
|
|
:global(.ui-pnotify .ui-pnotify-shadow) {
|
|
-webkit-box-shadow: 0px 6px 28px 0px rgba(0,0,0,0.1);
|
|
-moz-box-shadow: 0px 6px 28px 0px rgba(0,0,0,0.1);
|
|
box-shadow: 0px 6px 28px 0px rgba(0,0,0,0.1);
|
|
}
|
|
:global(.ui-pnotify-container) {
|
|
background-position: 0 0;
|
|
padding: .8em;
|
|
height: 100%;
|
|
margin: 0;
|
|
}
|
|
:global(.ui-pnotify-container:after) {
|
|
content: " "; /* Older browser do not support empty content */
|
|
visibility: hidden;
|
|
display: block;
|
|
height: 0;
|
|
clear: both;
|
|
}
|
|
:global(.ui-pnotify-container.ui-pnotify-sharp) {
|
|
-webkit-border-radius: 0;
|
|
-moz-border-radius: 0;
|
|
border-radius: 0;
|
|
}
|
|
:global(.ui-pnotify-title) {
|
|
display: block;
|
|
white-space: pre-line;
|
|
margin-bottom: .4em;
|
|
margin-top: 0;
|
|
}
|
|
:global(.ui-pnotify.ui-pnotify-with-icon .ui-pnotify-title),
|
|
:global(.ui-pnotify.ui-pnotify-with-icon .ui-pnotify-text) {
|
|
margin-left: 24px;
|
|
}
|
|
:global([dir=rtl] .ui-pnotify.ui-pnotify-with-icon .ui-pnotify-title),
|
|
:global([dir=rtl] .ui-pnotify.ui-pnotify-with-icon .ui-pnotify-text) {
|
|
margin-right: 24px;
|
|
margin-left: 0;
|
|
}
|
|
/* Bootstrap 4: make title text a tad smaller. */
|
|
:global(.ui-pnotify-title-bs4) {
|
|
font-size: 1.2rem;
|
|
}
|
|
:global(.ui-pnotify-text) {
|
|
display: block;
|
|
white-space: pre-line;
|
|
}
|
|
:global(.ui-pnotify-icon),
|
|
:global(.ui-pnotify-icon span) {
|
|
display: block;
|
|
float: left;
|
|
}
|
|
:global([dir=rtl] .ui-pnotify-icon),
|
|
:global([dir=rtl] .ui-pnotify-icon span) {
|
|
float: right;
|
|
}
|
|
/* Bootstrap 3: correct positioning of icon. */
|
|
:global(.ui-pnotify-icon-bs3 > span) {
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
/* Bootstrap 4: correct positioning of icon. */
|
|
:global(.ui-pnotify-icon-bs4 > span) {
|
|
position: relative;
|
|
top: 4px;
|
|
}
|
|
/* Overlay */
|
|
:global(.ui-pnotify-modal-overlay) {
|
|
background-color: rgba(0, 0, 0, .4);
|
|
top: 0;
|
|
left: 0;
|
|
position: absolute;
|
|
height: 100%;
|
|
width: 100%;
|
|
z-index: 2;
|
|
}
|
|
:global(body > .ui-pnotify-modal-overlay) {
|
|
position: fixed;
|
|
z-index: 100041;
|
|
}
|
|
</style>
|