@qtoggle/qui 0.0.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/.eslintignore +2 -0
- package/.eslintrc.json +492 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/ISSUE_TEMPLATE/improvement_proposal.md +20 -0
- package/.github/workflows/main.yml +74 -0
- package/.pre-commit-config.yaml +8 -0
- package/LICENSE.txt +177 -0
- package/README.md +4 -0
- package/font/dejavusans-bold.woff +0 -0
- package/font/dejavusans-bolditalic.woff +0 -0
- package/font/dejavusans-italic.woff +0 -0
- package/font/dejavusans-regular.woff +0 -0
- package/img/qui-icons.svg +1937 -0
- package/js/base/base.js +47 -0
- package/js/base/condition-variable.js +92 -0
- package/js/base/errors.js +36 -0
- package/js/base/i18n.js +20 -0
- package/js/base/mixwith.js +135 -0
- package/js/base/require-js-compat.js +78 -0
- package/js/base/signal.js +91 -0
- package/js/base/singleton.js +66 -0
- package/js/base/timer.js +126 -0
- package/js/config.js +184 -0
- package/js/forms/common-fields/check-field.js +42 -0
- package/js/forms/common-fields/choice-buttons-field.js +30 -0
- package/js/forms/common-fields/color-combo-field.js +37 -0
- package/js/forms/common-fields/combo-field.js +108 -0
- package/js/forms/common-fields/common-fields.js +23 -0
- package/js/forms/common-fields/composite-field.js +132 -0
- package/js/forms/common-fields/custom-html-field.js +51 -0
- package/js/forms/common-fields/email-field.js +30 -0
- package/js/forms/common-fields/file-picker-field.js +46 -0
- package/js/forms/common-fields/jquery-ui-field.js +111 -0
- package/js/forms/common-fields/labels-field.js +69 -0
- package/js/forms/common-fields/numeric-field.js +39 -0
- package/js/forms/common-fields/password-field.js +28 -0
- package/js/forms/common-fields/phone-field.js +26 -0
- package/js/forms/common-fields/progress-disk-field.js +69 -0
- package/js/forms/common-fields/push-button-field.js +138 -0
- package/js/forms/common-fields/slider-field.js +51 -0
- package/js/forms/common-fields/text-area-field.js +34 -0
- package/js/forms/common-fields/text-field.js +89 -0
- package/js/forms/common-fields/up-down-field.js +85 -0
- package/js/forms/common-forms/common-forms.js +16 -0
- package/js/forms/common-forms/options-form.js +77 -0
- package/js/forms/common-forms/page-form.js +115 -0
- package/js/forms/form-button.js +202 -0
- package/js/forms/form-field.js +1183 -0
- package/js/forms/form.js +1181 -0
- package/js/forms/forms.js +68 -0
- package/js/global-glass.js +100 -0
- package/js/icons/default-stock.js +173 -0
- package/js/icons/icon.js +64 -0
- package/js/icons/icons.js +16 -0
- package/js/icons/multi-state-sprites-icon.js +362 -0
- package/js/icons/stock-icon.js +219 -0
- package/js/icons/stock.js +98 -0
- package/js/icons/stocks.js +57 -0
- package/js/index.js +232 -0
- package/js/lib/jquery.longpress.js +79 -0
- package/js/lib/jquery.module.js +4 -0
- package/js/lib/logger.module.js +4 -0
- package/js/lib/pep.module.js +4 -0
- package/js/lists/common-items/common-items.js +5 -0
- package/js/lists/common-items/icon-label-list-item.js +86 -0
- package/js/lists/common-lists/common-lists.js +5 -0
- package/js/lists/common-lists/page-list.js +53 -0
- package/js/lists/list-item.js +147 -0
- package/js/lists/list.js +636 -0
- package/js/lists/lists.js +26 -0
- package/js/main-ui/main-ui.js +64 -0
- package/js/main-ui/menu-bar.js +144 -0
- package/js/main-ui/options-bar.js +181 -0
- package/js/main-ui/status.js +185 -0
- package/js/main-ui/top-bar.js +59 -0
- package/js/messages/common-message-forms/common-message-forms.js +7 -0
- package/js/messages/common-message-forms/confirm-message-form.js +81 -0
- package/js/messages/common-message-forms/simple-message-form.js +67 -0
- package/js/messages/common-message-forms/sticky-simple-message-form.js +27 -0
- package/js/messages/message-form.js +107 -0
- package/js/messages/messages.js +21 -0
- package/js/messages/sticky-modal-page.js +98 -0
- package/js/messages/sticky-modal-progress-message.js +27 -0
- package/js/messages/toast.js +164 -0
- package/js/navigation.js +654 -0
- package/js/pages/breadcrumbs.js +124 -0
- package/js/pages/common-pages/common-pages.js +6 -0
- package/js/pages/common-pages/modal-progress-page.js +83 -0
- package/js/pages/common-pages/structured-page.js +46 -0
- package/js/pages/page.js +1018 -0
- package/js/pages/pages-context.js +154 -0
- package/js/pages/pages.js +252 -0
- package/js/pwa.js +337 -0
- package/js/sections/section.js +612 -0
- package/js/sections/sections.js +300 -0
- package/js/tables/common-cells/common-cells.js +7 -0
- package/js/tables/common-cells/icon-label-table-cell.js +68 -0
- package/js/tables/common-cells/push-button-table-cell.js +133 -0
- package/js/tables/common-cells/simple-table-cell.js +37 -0
- package/js/tables/common-tables/common-tables.js +5 -0
- package/js/tables/common-tables/page-table.js +55 -0
- package/js/tables/table-cell.js +198 -0
- package/js/tables/table-row.js +126 -0
- package/js/tables/table.js +492 -0
- package/js/tables/tables.js +36 -0
- package/js/theme.js +304 -0
- package/js/utils/ajax.js +126 -0
- package/js/utils/array.js +194 -0
- package/js/utils/colors.js +445 -0
- package/js/utils/cookies.js +65 -0
- package/js/utils/crypto.js +439 -0
- package/js/utils/css.js +234 -0
- package/js/utils/date.js +300 -0
- package/js/utils/files.js +27 -0
- package/js/utils/gestures.js +165 -0
- package/js/utils/html.js +76 -0
- package/js/utils/misc.js +81 -0
- package/js/utils/object.js +324 -0
- package/js/utils/promise.js +49 -0
- package/js/utils/string.js +192 -0
- package/js/utils/url.js +187 -0
- package/js/utils/utils.js +3 -0
- package/js/utils/visibility-manager.js +211 -0
- package/js/views/common-views/common-views.js +7 -0
- package/js/views/common-views/icon-label-view.js +210 -0
- package/js/views/common-views/progress-view.js +89 -0
- package/js/views/common-views/structured-view.js +368 -0
- package/js/views/view.js +467 -0
- package/js/views/views.js +3 -0
- package/js/widgets/base-widget.js +23 -0
- package/js/widgets/common-widgets/check-button.js +109 -0
- package/js/widgets/common-widgets/choice-buttons.js +322 -0
- package/js/widgets/common-widgets/color-combo.js +104 -0
- package/js/widgets/common-widgets/combo.js +645 -0
- package/js/widgets/common-widgets/common-widgets.js +17 -0
- package/js/widgets/common-widgets/email-input.js +7 -0
- package/js/widgets/common-widgets/file-picker.js +133 -0
- package/js/widgets/common-widgets/labels.js +132 -0
- package/js/widgets/common-widgets/numeric-input.js +49 -0
- package/js/widgets/common-widgets/password-input.js +91 -0
- package/js/widgets/common-widgets/phone-input.js +7 -0
- package/js/widgets/common-widgets/progress-disk.js +174 -0
- package/js/widgets/common-widgets/push-button.js +155 -0
- package/js/widgets/common-widgets/slider.js +455 -0
- package/js/widgets/common-widgets/text-area.js +52 -0
- package/js/widgets/common-widgets/text-input.js +174 -0
- package/js/widgets/common-widgets/up-down.js +351 -0
- package/js/widgets/widgets.js +57 -0
- package/js/window.js +557 -0
- package/jsdoc.conf.json +20 -0
- package/less/base.less +123 -0
- package/less/forms/common-fields.less +101 -0
- package/less/forms/common-forms.less +5 -0
- package/less/forms/form-button.less +21 -0
- package/less/forms/form-field.less +266 -0
- package/less/forms/form.less +131 -0
- package/less/global-glass.less +64 -0
- package/less/icon-label-view.less +82 -0
- package/less/icons.less +144 -0
- package/less/lists.less +105 -0
- package/less/main-ui.less +328 -0
- package/less/messages.less +189 -0
- package/less/no-effects.less +24 -0
- package/less/pages/breadcrumbs.less +98 -0
- package/less/pages/common-pages.less +36 -0
- package/less/pages/page.less +70 -0
- package/less/progress-view.less +51 -0
- package/less/stock-icons.less +43 -0
- package/less/structured-view.less +245 -0
- package/less/tables.less +84 -0
- package/less/theme-dark.less +133 -0
- package/less/theme-light.less +132 -0
- package/less/theme.less +419 -0
- package/less/visibility-manager.less +11 -0
- package/less/widgets/check-button.less +96 -0
- package/less/widgets/choice-buttons.less +160 -0
- package/less/widgets/color-combo.less +33 -0
- package/less/widgets/combo.less +230 -0
- package/less/widgets/common-buttons.less +120 -0
- package/less/widgets/common.less +24 -0
- package/less/widgets/input.less +258 -0
- package/less/widgets/labels.less +81 -0
- package/less/widgets/progress-disk.less +70 -0
- package/less/widgets/slider.less +199 -0
- package/less/widgets/updown.less +115 -0
- package/less/widgets/various.less +36 -0
- package/package.json +47 -0
- package/pyproject.toml +45 -0
- package/qui/__init__.py +110 -0
- package/qui/constants.py +1 -0
- package/qui/exceptions.py +2 -0
- package/qui/j2template.py +71 -0
- package/qui/settings.py +60 -0
- package/qui/templates/manifest.json +25 -0
- package/qui/templates/qui.html +126 -0
- package/qui/templates/service-worker.js +188 -0
- package/qui/web/__init__.py +0 -0
- package/qui/web/tornado.py +220 -0
- package/scripts/postinstall.sh +10 -0
- package/webpack/webpack-adjust-css-urls-loader.js +36 -0
- package/webpack/webpack-common.js +384 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
import {gettext} from '$qui/base/i18n.js'
|
|
3
|
+
import FormButton from '$qui/forms/form-button.js'
|
|
4
|
+
import StockIcon from '$qui/icons/stock-icon.js'
|
|
5
|
+
import {asap} from '$qui/utils/misc.js'
|
|
6
|
+
import * as ObjectUtils from '$qui/utils/object.js'
|
|
7
|
+
|
|
8
|
+
import MessageForm from '../message-form.js'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A message form asking the user for confirmation.
|
|
13
|
+
* @alias qui.messages.commonmessageforms.ConfirmMessageForm
|
|
14
|
+
* @extends qui.messages.MessageForm
|
|
15
|
+
*/
|
|
16
|
+
class ConfirmMessageForm extends MessageForm {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @constructs
|
|
20
|
+
* @param {qui.messages.MessageForm.Callback} [onYes] an optional confirmation callback
|
|
21
|
+
* @param {qui.messages.MessageForm.Callback} [onNo] an optional decline callback
|
|
22
|
+
* @param {...*} args parent class parameters
|
|
23
|
+
*/
|
|
24
|
+
constructor({onYes = null, onNo = null, ...args}) {
|
|
25
|
+
ObjectUtils.assignDefault(args, {
|
|
26
|
+
icon: new StockIcon({name: 'qmark'}),
|
|
27
|
+
buttons: [
|
|
28
|
+
new FormButton({id: 'no', caption: gettext('No'), cancel: true}),
|
|
29
|
+
new FormButton({id: 'yes', caption: gettext('Yes'), def: true})
|
|
30
|
+
]
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
super(args)
|
|
34
|
+
|
|
35
|
+
this._onYes = onYes
|
|
36
|
+
this._onNo = onNo
|
|
37
|
+
|
|
38
|
+
this._resolve = null
|
|
39
|
+
this._reject = null
|
|
40
|
+
|
|
41
|
+
this._confirmed = false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
applyData() {
|
|
45
|
+
this._confirmed = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onClose() {
|
|
49
|
+
if (this._confirmed) {
|
|
50
|
+
if (this._onYes) {
|
|
51
|
+
asap(this._onYes)
|
|
52
|
+
}
|
|
53
|
+
if (this._resolve) {
|
|
54
|
+
asap(this._resolve)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
if (this._onNo) {
|
|
59
|
+
asap(this._onNo)
|
|
60
|
+
}
|
|
61
|
+
if (this._reject) {
|
|
62
|
+
asap(this._reject)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Return a promise resolves on positive answer and is rejected on negative answer.
|
|
69
|
+
* @returns {Promise}
|
|
70
|
+
*/
|
|
71
|
+
asPromise() {
|
|
72
|
+
return new Promise(function (resolve, reject) {
|
|
73
|
+
this._resolve = resolve
|
|
74
|
+
this._reject = reject
|
|
75
|
+
}.bind(this))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
export default ConfirmMessageForm
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
import {gettext} from '$qui/base/i18n.js'
|
|
3
|
+
import FormButton from '$qui/forms/form-button.js'
|
|
4
|
+
import StockIcon from '$qui/icons/stock-icon.js'
|
|
5
|
+
import {asap} from '$qui/utils/misc.js'
|
|
6
|
+
import * as ObjectUtils from '$qui/utils/object.js'
|
|
7
|
+
|
|
8
|
+
import MessageForm from '../message-form.js'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A simple message form.
|
|
13
|
+
* @alias qui.messages.commonmessageforms.SimpleMessageForm
|
|
14
|
+
* @extends qui.messages.MessageForm
|
|
15
|
+
*/
|
|
16
|
+
class SimpleMessageForm extends MessageForm {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @constructs
|
|
20
|
+
* @param {String} type message type (`"info"`, `"warning"` or `"error"`)
|
|
21
|
+
* @param {String} [buttonCaption] optional button caption (defaults to `"OK"`)
|
|
22
|
+
* @param {qui.messages.MessageForm.Callback} [onClose] a dismiss callback
|
|
23
|
+
* @param {...*} args parent class parameters
|
|
24
|
+
*/
|
|
25
|
+
constructor({type, buttonCaption = gettext('OK'), onClose = null, ...args}) {
|
|
26
|
+
let variant = null
|
|
27
|
+
let iconName = null
|
|
28
|
+
switch (type) {
|
|
29
|
+
case 'info':
|
|
30
|
+
variant = 'foreground'
|
|
31
|
+
iconName = 'info'
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
case 'warning':
|
|
35
|
+
variant = 'warning'
|
|
36
|
+
iconName = 'exclam'
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
case 'error':
|
|
40
|
+
variant = 'error'
|
|
41
|
+
iconName = 'exclam'
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (iconName && variant && args.icon == null) {
|
|
46
|
+
args.icon = new StockIcon({name: iconName, variant: variant})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ObjectUtils.setDefault(args, 'buttons', [
|
|
50
|
+
new FormButton({id: 'ok', caption: buttonCaption, def: true, style: 'interactive'})
|
|
51
|
+
])
|
|
52
|
+
|
|
53
|
+
super(args)
|
|
54
|
+
|
|
55
|
+
this._onClose = onClose
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onClose() {
|
|
59
|
+
if (this._onClose) {
|
|
60
|
+
asap(this._onClose)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
export default SimpleMessageForm
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
import {mix} from '$qui/base/mixwith.js'
|
|
3
|
+
|
|
4
|
+
import SimpleMessageForm from './simple-message-form.js'
|
|
5
|
+
import StickyModalPageMixin from '../sticky-modal-page.js'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A sticky modal variant of {@link qui.messages.commonmessageforms.SimpleMessageForm}.
|
|
10
|
+
* @alias qui.messages.commonmessageforms.StickySimpleMessageForm
|
|
11
|
+
* @extends qui.messages.commonmessageforms.SimpleMessageForm
|
|
12
|
+
* @mixes qui.messages.StickyModalPageMixin
|
|
13
|
+
*/
|
|
14
|
+
class StickySimpleMessageForm extends mix(SimpleMessageForm).with(StickyModalPageMixin) {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @constructs
|
|
18
|
+
* @param {...*} args parent class parameters
|
|
19
|
+
*/
|
|
20
|
+
constructor({...args}) {
|
|
21
|
+
super(args)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export default StickySimpleMessageForm
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
|
|
2
|
+
import $ from '$qui/lib/jquery.module.js'
|
|
3
|
+
|
|
4
|
+
import {AssertionError} from '$qui/base/errors.js'
|
|
5
|
+
import {PageForm} from '$qui/forms/common-forms/common-forms.js'
|
|
6
|
+
import StockIcon from '$qui/icons/stock-icon.js'
|
|
7
|
+
import * as Sections from '$qui/sections/sections.js'
|
|
8
|
+
import {asap} from '$qui/utils/misc.js'
|
|
9
|
+
import * as ObjectUtils from '$qui/utils/object.js'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Message form callback.
|
|
14
|
+
* @callback qui.messages.MessageForm.Callback
|
|
15
|
+
* @param {*} result
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A base class for message forms.
|
|
20
|
+
* @alias qui.messages.MessageForm
|
|
21
|
+
* @extends qui.forms.commonforms.PageForm
|
|
22
|
+
*/
|
|
23
|
+
class MessageForm extends PageForm {
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @constructs
|
|
27
|
+
* @param {String} message the message
|
|
28
|
+
* @param {qui.icons.Icon} [icon] an optional icon
|
|
29
|
+
* @param {...*} args parent class parameters
|
|
30
|
+
*/
|
|
31
|
+
constructor({message, icon = null, ...args}) {
|
|
32
|
+
ObjectUtils.assignDefault(args, {
|
|
33
|
+
popup: true,
|
|
34
|
+
transparent: true,
|
|
35
|
+
topless: true,
|
|
36
|
+
width: '30em',
|
|
37
|
+
valuesWidth: 100,
|
|
38
|
+
title: args.message
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
super(args)
|
|
42
|
+
|
|
43
|
+
this._message = message
|
|
44
|
+
this._icon = icon
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
makeBody() {
|
|
48
|
+
return super.makeBody().append(this.makeMessageBody())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Override this to indicate how the message body is built.
|
|
53
|
+
* @returns {jQuery}
|
|
54
|
+
*/
|
|
55
|
+
makeMessageBody() {
|
|
56
|
+
let bodyDiv = $('<div></div>', {class: 'qui-message-form-body'})
|
|
57
|
+
let labelSpan = $('<div></div>', {class: 'qui-message-form-label'})
|
|
58
|
+
|
|
59
|
+
labelSpan.html(this._message)
|
|
60
|
+
|
|
61
|
+
if (this._icon) {
|
|
62
|
+
let iconDiv = $('<div></div>', {class: 'qui-icon qui-message-form-icon'})
|
|
63
|
+
let icon = this._icon
|
|
64
|
+
if (icon instanceof StockIcon) {
|
|
65
|
+
icon = icon.alterDefault({variant: 'foreground'})
|
|
66
|
+
}
|
|
67
|
+
icon.applyTo(iconDiv)
|
|
68
|
+
bodyDiv.append(iconDiv)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
bodyDiv.append(labelSpan)
|
|
72
|
+
|
|
73
|
+
return bodyDiv
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
handleBecomeCurrent() {
|
|
77
|
+
super.handleBecomeCurrent()
|
|
78
|
+
|
|
79
|
+
/* Focus the default button */
|
|
80
|
+
asap(function () {
|
|
81
|
+
let button = this.getButtons().find(b => b.isDefault())
|
|
82
|
+
if (button) {
|
|
83
|
+
button.focus()
|
|
84
|
+
}
|
|
85
|
+
}.bind(this))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Show the message form by pushing it onto the current section context.
|
|
90
|
+
* @returns {qui.messages.MessageForm} this form
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
show() {
|
|
94
|
+
let currentSection = Sections.getCurrent()
|
|
95
|
+
if (!currentSection) {
|
|
96
|
+
throw new AssertionError('Attempt to show message form while no current section')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
currentSection.pushPage(this)
|
|
100
|
+
|
|
101
|
+
return this
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
export default MessageForm
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace qui.messages
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as Toast from './toast.js'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a text into label markup.
|
|
10
|
+
* @alias qui.messages.wrapLabel
|
|
11
|
+
* @param {String} text the text to wrap
|
|
12
|
+
* @returns {String} the label
|
|
13
|
+
*/
|
|
14
|
+
export function wrapLabel(text) {
|
|
15
|
+
return `<span class="qui-message-label">${text}</span>`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export function init() {
|
|
20
|
+
Toast.init()
|
|
21
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
import {Mixin} from '$qui/base/mixwith.js'
|
|
3
|
+
import * as GlobalGlass from '$qui/global-glass.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/** @lends qui.messages.StickyModalPageMixin */
|
|
7
|
+
const StickyModalPageMixin = Mixin((superclass = Object) => {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A mixin for pages that are stuck to the global glass until explicitly removed.
|
|
11
|
+
* @alias qui.messages.StickyModalPageMixin
|
|
12
|
+
* @mixin
|
|
13
|
+
*/
|
|
14
|
+
class StickyModalPageMixin extends superclass {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @constructs
|
|
18
|
+
* @param {...*} args parent class parameters
|
|
19
|
+
*/
|
|
20
|
+
constructor({...args} = {}) {
|
|
21
|
+
super(args)
|
|
22
|
+
|
|
23
|
+
this._showCount = 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
initPageHTML(html) {
|
|
27
|
+
super.initPageHTML(html)
|
|
28
|
+
html.addClass('sticky')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
close() {
|
|
32
|
+
/* Call the hide method instead of page's close */
|
|
33
|
+
this.hide()
|
|
34
|
+
this.onClose()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Override this method to indicate how the page is reset to its initial state, as soon as it is shown.
|
|
39
|
+
*/
|
|
40
|
+
reset() {
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Show this page.
|
|
45
|
+
*
|
|
46
|
+
* This is a reentrant method.
|
|
47
|
+
*
|
|
48
|
+
* @returns {qui.messages.StickyModalPageMixin} this page
|
|
49
|
+
*/
|
|
50
|
+
show() {
|
|
51
|
+
if (this._showCount === 0) {
|
|
52
|
+
this.attach()
|
|
53
|
+
this.getPageHTML().addClass('visible')
|
|
54
|
+
GlobalGlass.updateVisibility()
|
|
55
|
+
|
|
56
|
+
this.handleBecomeCurrent()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this._showCount++
|
|
60
|
+
this.reset()
|
|
61
|
+
|
|
62
|
+
return this
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Hide a previously shown page.
|
|
67
|
+
*
|
|
68
|
+
* This method should be called as many times as its counterpart {@link qui.messages.StickyModalPageMixin#show}
|
|
69
|
+
* method has been called, before it actually hides the page.
|
|
70
|
+
*/
|
|
71
|
+
hide() {
|
|
72
|
+
this._showCount--
|
|
73
|
+
|
|
74
|
+
if (this._showCount === 0) {
|
|
75
|
+
this.detach()
|
|
76
|
+
this.getPageHTML().removeClass('visible')
|
|
77
|
+
GlobalGlass.updateVisibility()
|
|
78
|
+
|
|
79
|
+
this.handleLeaveCurrent()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handleBecomeCurrent() {
|
|
84
|
+
this.onBecomeCurrent()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
handleLeaveCurrent() {
|
|
88
|
+
this.onLeaveCurrent()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return StickyModalPageMixin
|
|
94
|
+
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
export default StickyModalPageMixin
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
import {mix} from '$qui/base/mixwith.js'
|
|
3
|
+
import {ModalProgressPage} from '$qui/pages/common-pages/common-pages.js'
|
|
4
|
+
|
|
5
|
+
import StickyModalPageMixin from './sticky-modal-page.js'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A sticky modal progress page.
|
|
10
|
+
* @alias qui.messages.StickyModalProgressMessage
|
|
11
|
+
* @extends qui.pages.commonpages.ModalProgressPage
|
|
12
|
+
* @mixes qui.messages.StickyModalPageMixin
|
|
13
|
+
*/
|
|
14
|
+
class StickyModalProgressMessage extends mix(ModalProgressPage).with(StickyModalPageMixin) {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @constructs
|
|
18
|
+
* @param {...*} args parent class parameters
|
|
19
|
+
*/
|
|
20
|
+
constructor({...args} = {}) {
|
|
21
|
+
super(args)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export default StickyModalProgressMessage
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace qui.messages.toast
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import $ from '$qui/lib/jquery.module.js'
|
|
6
|
+
|
|
7
|
+
import StockIcon from '$qui/icons/stock-icon.js'
|
|
8
|
+
import * as Theme from '$qui/theme.js'
|
|
9
|
+
import {asap} from '$qui/utils/misc.js'
|
|
10
|
+
import * as Window from '$qui/window.js'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const TOAST_MESSAGE_TIMEOUT = 5000 /* 5 seconds */
|
|
14
|
+
|
|
15
|
+
let toastMessageTimeoutHandle = null
|
|
16
|
+
let toastMessageContainer = null
|
|
17
|
+
let toastMessageShowingTimeoutHandle = null
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Show a toast message for a limited period of time.
|
|
22
|
+
* @alias qui.messages.toast.show
|
|
23
|
+
* @param {?String|jQuery} message the message to show
|
|
24
|
+
* @param {?String} type message type (`"info"`, `"warning"` or `"error"`)
|
|
25
|
+
* @param {Number} [timeout] the message display timeout, in milliseconds (defaults to 5 seconds)
|
|
26
|
+
* @param {Boolean} [closeable] whether the message can be closed by the user (defaults to `true`)
|
|
27
|
+
*/
|
|
28
|
+
export function show({message, type, timeout = TOAST_MESSAGE_TIMEOUT, closeable = true}) {
|
|
29
|
+
let messageSpan = toastMessageContainer.find('span.qui-toast-message')
|
|
30
|
+
|
|
31
|
+
if (toastMessageTimeoutHandle) {
|
|
32
|
+
clearTimeout(toastMessageTimeoutHandle)
|
|
33
|
+
toastMessageTimeoutHandle = null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (message) {
|
|
37
|
+
Window.$body.addClass('toast-message-visible')
|
|
38
|
+
toastMessageContainer.removeClass('info warning error multiline closeable')
|
|
39
|
+
|
|
40
|
+
messageSpan.html(message)
|
|
41
|
+
if (type) {
|
|
42
|
+
toastMessageContainer.addClass(type)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Test if the message wraps */
|
|
46
|
+
let normalHeight = messageSpan.height()
|
|
47
|
+
toastMessageContainer.find('div.qui-toast-message').css('white-space', 'normal')
|
|
48
|
+
let wrappedHeight = messageSpan.height()
|
|
49
|
+
toastMessageContainer.find('div.qui-toast-message').css('white-space', '')
|
|
50
|
+
|
|
51
|
+
if (normalHeight < wrappedHeight) {
|
|
52
|
+
toastMessageContainer.addClass('multiline')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (timeout !== 0) {
|
|
56
|
+
toastMessageTimeoutHandle = setTimeout(function () {
|
|
57
|
+
show({message: null, type: 'info'})
|
|
58
|
+
}, timeout)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (closeable !== false) {
|
|
62
|
+
toastMessageContainer.addClass('closeable')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Showing flag timeout */
|
|
66
|
+
if (toastMessageShowingTimeoutHandle) {
|
|
67
|
+
clearTimeout(toastMessageShowingTimeoutHandle)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
toastMessageShowingTimeoutHandle = asap(function () {
|
|
71
|
+
toastMessageShowingTimeoutHandle = null
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
else if (Window.$body.hasClass('toast-message-visible')) { /* Hide */
|
|
75
|
+
Window.$body.removeClass('toast-message-visible')
|
|
76
|
+
Theme.afterTransition(function () {
|
|
77
|
+
if (Window.$body.hasClass('toast-message-visible')) {
|
|
78
|
+
return /* Reshown in the meantime */
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
messageSpan.html('')
|
|
82
|
+
toastMessageContainer.removeClass('info warning error multiline')
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Hide a currently displayed toast message.
|
|
89
|
+
* @alias qui.messages.toast.hide
|
|
90
|
+
*/
|
|
91
|
+
export function hide() {
|
|
92
|
+
show({message: null, type: 'info'})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Convenience function for {@link qui.messages.toast.show} with default timeout and type `"info"`.
|
|
97
|
+
* @alias qui.messages.toast.info
|
|
98
|
+
* @param {String|jQuery} message the message to show
|
|
99
|
+
*/
|
|
100
|
+
export function info(message) {
|
|
101
|
+
show({message: message, type: 'info'})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Convenience function for {@link qui.messages.toast.show} with default timeout and type `"warning"`.
|
|
106
|
+
* @alias qui.messages.toast.warning
|
|
107
|
+
* @param {String|jQuery} message the message to show
|
|
108
|
+
*/
|
|
109
|
+
export function warning(message) {
|
|
110
|
+
show({message: message, type: 'warning'})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Convenience function for {@link qui.messages.toast.show} with default timeout and type `"error"`.
|
|
115
|
+
* @alias qui.messages.toast.error
|
|
116
|
+
* @param {String|jQuery} message the message to show
|
|
117
|
+
*/
|
|
118
|
+
export function error(message) {
|
|
119
|
+
show({message: message, type: 'error'})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
export function init() {
|
|
124
|
+
toastMessageContainer = $('<div></div>', {class: 'qui-toast-message-container'})
|
|
125
|
+
toastMessageContainer.append($('<div></div>', {class: 'qui-toast-message-icon'}))
|
|
126
|
+
|
|
127
|
+
let messageDiv = $('<div></div>', {class: 'qui-toast-message'})
|
|
128
|
+
messageDiv.append($('<span></span>', {class: 'qui-toast-message'}))
|
|
129
|
+
messageDiv.append($('<div></div>', {class: 'qui-base-button qui-toast-message-close-button'}))
|
|
130
|
+
toastMessageContainer.append(messageDiv)
|
|
131
|
+
|
|
132
|
+
Window.$body.append(toastMessageContainer)
|
|
133
|
+
|
|
134
|
+
/* Close button */
|
|
135
|
+
let closeButton = toastMessageContainer.find('div.qui-toast-message-close-button')
|
|
136
|
+
new StockIcon({
|
|
137
|
+
name: 'close', variant: 'interactive', activeVariant: 'interactive-active'
|
|
138
|
+
}).alter({scale: 0.75}).applyTo(closeButton)
|
|
139
|
+
|
|
140
|
+
closeButton.on('click', function () {
|
|
141
|
+
show({message: null, type: null})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
/* Close when clicked anywhere */
|
|
145
|
+
Window.$body.on('click', function (e) {
|
|
146
|
+
if (toastMessageShowingTimeoutHandle) {
|
|
147
|
+
return /* Showing is in progress */
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!Window.$body.hasClass('toast-message-visible')) {
|
|
151
|
+
return /* Message not visible */
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!toastMessageContainer.hasClass('closeable')) {
|
|
155
|
+
return /* Message not closeable */
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (toastMessageContainer.has(e.target).length) {
|
|
159
|
+
return /* Clicked on the message container itself */
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
show({message: null, type: null})
|
|
163
|
+
})
|
|
164
|
+
}
|