@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.
Files changed (202) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +492 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. package/.github/ISSUE_TEMPLATE/improvement_proposal.md +20 -0
  6. package/.github/workflows/main.yml +74 -0
  7. package/.pre-commit-config.yaml +8 -0
  8. package/LICENSE.txt +177 -0
  9. package/README.md +4 -0
  10. package/font/dejavusans-bold.woff +0 -0
  11. package/font/dejavusans-bolditalic.woff +0 -0
  12. package/font/dejavusans-italic.woff +0 -0
  13. package/font/dejavusans-regular.woff +0 -0
  14. package/img/qui-icons.svg +1937 -0
  15. package/js/base/base.js +47 -0
  16. package/js/base/condition-variable.js +92 -0
  17. package/js/base/errors.js +36 -0
  18. package/js/base/i18n.js +20 -0
  19. package/js/base/mixwith.js +135 -0
  20. package/js/base/require-js-compat.js +78 -0
  21. package/js/base/signal.js +91 -0
  22. package/js/base/singleton.js +66 -0
  23. package/js/base/timer.js +126 -0
  24. package/js/config.js +184 -0
  25. package/js/forms/common-fields/check-field.js +42 -0
  26. package/js/forms/common-fields/choice-buttons-field.js +30 -0
  27. package/js/forms/common-fields/color-combo-field.js +37 -0
  28. package/js/forms/common-fields/combo-field.js +108 -0
  29. package/js/forms/common-fields/common-fields.js +23 -0
  30. package/js/forms/common-fields/composite-field.js +132 -0
  31. package/js/forms/common-fields/custom-html-field.js +51 -0
  32. package/js/forms/common-fields/email-field.js +30 -0
  33. package/js/forms/common-fields/file-picker-field.js +46 -0
  34. package/js/forms/common-fields/jquery-ui-field.js +111 -0
  35. package/js/forms/common-fields/labels-field.js +69 -0
  36. package/js/forms/common-fields/numeric-field.js +39 -0
  37. package/js/forms/common-fields/password-field.js +28 -0
  38. package/js/forms/common-fields/phone-field.js +26 -0
  39. package/js/forms/common-fields/progress-disk-field.js +69 -0
  40. package/js/forms/common-fields/push-button-field.js +138 -0
  41. package/js/forms/common-fields/slider-field.js +51 -0
  42. package/js/forms/common-fields/text-area-field.js +34 -0
  43. package/js/forms/common-fields/text-field.js +89 -0
  44. package/js/forms/common-fields/up-down-field.js +85 -0
  45. package/js/forms/common-forms/common-forms.js +16 -0
  46. package/js/forms/common-forms/options-form.js +77 -0
  47. package/js/forms/common-forms/page-form.js +115 -0
  48. package/js/forms/form-button.js +202 -0
  49. package/js/forms/form-field.js +1183 -0
  50. package/js/forms/form.js +1181 -0
  51. package/js/forms/forms.js +68 -0
  52. package/js/global-glass.js +100 -0
  53. package/js/icons/default-stock.js +173 -0
  54. package/js/icons/icon.js +64 -0
  55. package/js/icons/icons.js +16 -0
  56. package/js/icons/multi-state-sprites-icon.js +362 -0
  57. package/js/icons/stock-icon.js +219 -0
  58. package/js/icons/stock.js +98 -0
  59. package/js/icons/stocks.js +57 -0
  60. package/js/index.js +232 -0
  61. package/js/lib/jquery.longpress.js +79 -0
  62. package/js/lib/jquery.module.js +4 -0
  63. package/js/lib/logger.module.js +4 -0
  64. package/js/lib/pep.module.js +4 -0
  65. package/js/lists/common-items/common-items.js +5 -0
  66. package/js/lists/common-items/icon-label-list-item.js +86 -0
  67. package/js/lists/common-lists/common-lists.js +5 -0
  68. package/js/lists/common-lists/page-list.js +53 -0
  69. package/js/lists/list-item.js +147 -0
  70. package/js/lists/list.js +636 -0
  71. package/js/lists/lists.js +26 -0
  72. package/js/main-ui/main-ui.js +64 -0
  73. package/js/main-ui/menu-bar.js +144 -0
  74. package/js/main-ui/options-bar.js +181 -0
  75. package/js/main-ui/status.js +185 -0
  76. package/js/main-ui/top-bar.js +59 -0
  77. package/js/messages/common-message-forms/common-message-forms.js +7 -0
  78. package/js/messages/common-message-forms/confirm-message-form.js +81 -0
  79. package/js/messages/common-message-forms/simple-message-form.js +67 -0
  80. package/js/messages/common-message-forms/sticky-simple-message-form.js +27 -0
  81. package/js/messages/message-form.js +107 -0
  82. package/js/messages/messages.js +21 -0
  83. package/js/messages/sticky-modal-page.js +98 -0
  84. package/js/messages/sticky-modal-progress-message.js +27 -0
  85. package/js/messages/toast.js +164 -0
  86. package/js/navigation.js +654 -0
  87. package/js/pages/breadcrumbs.js +124 -0
  88. package/js/pages/common-pages/common-pages.js +6 -0
  89. package/js/pages/common-pages/modal-progress-page.js +83 -0
  90. package/js/pages/common-pages/structured-page.js +46 -0
  91. package/js/pages/page.js +1018 -0
  92. package/js/pages/pages-context.js +154 -0
  93. package/js/pages/pages.js +252 -0
  94. package/js/pwa.js +337 -0
  95. package/js/sections/section.js +612 -0
  96. package/js/sections/sections.js +300 -0
  97. package/js/tables/common-cells/common-cells.js +7 -0
  98. package/js/tables/common-cells/icon-label-table-cell.js +68 -0
  99. package/js/tables/common-cells/push-button-table-cell.js +133 -0
  100. package/js/tables/common-cells/simple-table-cell.js +37 -0
  101. package/js/tables/common-tables/common-tables.js +5 -0
  102. package/js/tables/common-tables/page-table.js +55 -0
  103. package/js/tables/table-cell.js +198 -0
  104. package/js/tables/table-row.js +126 -0
  105. package/js/tables/table.js +492 -0
  106. package/js/tables/tables.js +36 -0
  107. package/js/theme.js +304 -0
  108. package/js/utils/ajax.js +126 -0
  109. package/js/utils/array.js +194 -0
  110. package/js/utils/colors.js +445 -0
  111. package/js/utils/cookies.js +65 -0
  112. package/js/utils/crypto.js +439 -0
  113. package/js/utils/css.js +234 -0
  114. package/js/utils/date.js +300 -0
  115. package/js/utils/files.js +27 -0
  116. package/js/utils/gestures.js +165 -0
  117. package/js/utils/html.js +76 -0
  118. package/js/utils/misc.js +81 -0
  119. package/js/utils/object.js +324 -0
  120. package/js/utils/promise.js +49 -0
  121. package/js/utils/string.js +192 -0
  122. package/js/utils/url.js +187 -0
  123. package/js/utils/utils.js +3 -0
  124. package/js/utils/visibility-manager.js +211 -0
  125. package/js/views/common-views/common-views.js +7 -0
  126. package/js/views/common-views/icon-label-view.js +210 -0
  127. package/js/views/common-views/progress-view.js +89 -0
  128. package/js/views/common-views/structured-view.js +368 -0
  129. package/js/views/view.js +467 -0
  130. package/js/views/views.js +3 -0
  131. package/js/widgets/base-widget.js +23 -0
  132. package/js/widgets/common-widgets/check-button.js +109 -0
  133. package/js/widgets/common-widgets/choice-buttons.js +322 -0
  134. package/js/widgets/common-widgets/color-combo.js +104 -0
  135. package/js/widgets/common-widgets/combo.js +645 -0
  136. package/js/widgets/common-widgets/common-widgets.js +17 -0
  137. package/js/widgets/common-widgets/email-input.js +7 -0
  138. package/js/widgets/common-widgets/file-picker.js +133 -0
  139. package/js/widgets/common-widgets/labels.js +132 -0
  140. package/js/widgets/common-widgets/numeric-input.js +49 -0
  141. package/js/widgets/common-widgets/password-input.js +91 -0
  142. package/js/widgets/common-widgets/phone-input.js +7 -0
  143. package/js/widgets/common-widgets/progress-disk.js +174 -0
  144. package/js/widgets/common-widgets/push-button.js +155 -0
  145. package/js/widgets/common-widgets/slider.js +455 -0
  146. package/js/widgets/common-widgets/text-area.js +52 -0
  147. package/js/widgets/common-widgets/text-input.js +174 -0
  148. package/js/widgets/common-widgets/up-down.js +351 -0
  149. package/js/widgets/widgets.js +57 -0
  150. package/js/window.js +557 -0
  151. package/jsdoc.conf.json +20 -0
  152. package/less/base.less +123 -0
  153. package/less/forms/common-fields.less +101 -0
  154. package/less/forms/common-forms.less +5 -0
  155. package/less/forms/form-button.less +21 -0
  156. package/less/forms/form-field.less +266 -0
  157. package/less/forms/form.less +131 -0
  158. package/less/global-glass.less +64 -0
  159. package/less/icon-label-view.less +82 -0
  160. package/less/icons.less +144 -0
  161. package/less/lists.less +105 -0
  162. package/less/main-ui.less +328 -0
  163. package/less/messages.less +189 -0
  164. package/less/no-effects.less +24 -0
  165. package/less/pages/breadcrumbs.less +98 -0
  166. package/less/pages/common-pages.less +36 -0
  167. package/less/pages/page.less +70 -0
  168. package/less/progress-view.less +51 -0
  169. package/less/stock-icons.less +43 -0
  170. package/less/structured-view.less +245 -0
  171. package/less/tables.less +84 -0
  172. package/less/theme-dark.less +133 -0
  173. package/less/theme-light.less +132 -0
  174. package/less/theme.less +419 -0
  175. package/less/visibility-manager.less +11 -0
  176. package/less/widgets/check-button.less +96 -0
  177. package/less/widgets/choice-buttons.less +160 -0
  178. package/less/widgets/color-combo.less +33 -0
  179. package/less/widgets/combo.less +230 -0
  180. package/less/widgets/common-buttons.less +120 -0
  181. package/less/widgets/common.less +24 -0
  182. package/less/widgets/input.less +258 -0
  183. package/less/widgets/labels.less +81 -0
  184. package/less/widgets/progress-disk.less +70 -0
  185. package/less/widgets/slider.less +199 -0
  186. package/less/widgets/updown.less +115 -0
  187. package/less/widgets/various.less +36 -0
  188. package/package.json +47 -0
  189. package/pyproject.toml +45 -0
  190. package/qui/__init__.py +110 -0
  191. package/qui/constants.py +1 -0
  192. package/qui/exceptions.py +2 -0
  193. package/qui/j2template.py +71 -0
  194. package/qui/settings.py +60 -0
  195. package/qui/templates/manifest.json +25 -0
  196. package/qui/templates/qui.html +126 -0
  197. package/qui/templates/service-worker.js +188 -0
  198. package/qui/web/__init__.py +0 -0
  199. package/qui/web/tornado.py +220 -0
  200. package/scripts/postinstall.sh +10 -0
  201. package/webpack/webpack-adjust-css-urls-loader.js +36 -0
  202. package/webpack/webpack-common.js +384 -0
@@ -0,0 +1,154 @@
1
+
2
+ import Signal from '$qui/base/signal.js'
3
+
4
+ import {getCurrentContext} from './pages.js'
5
+ import * as Window from '$qui/window.js'
6
+
7
+
8
+ /**
9
+ * A page stack context.
10
+ * @alias qui.pages.PagesContext
11
+ */
12
+ class PagesContext {
13
+
14
+ /**
15
+ * @constructs
16
+ */
17
+ constructor() {
18
+
19
+ /**
20
+ * Emitted whenever a page is pushed to the context. Handlers are called with the following parameters:
21
+ * * `page: {@link qui.pages.PageMixin}`, the pushed page
22
+ * * `index: Number`, page index in page stack
23
+ * @type {qui.base.Signal}
24
+ */
25
+ this.pushSignal = new Signal(this)
26
+
27
+ /**
28
+ * Emitted whenever a page is popped from the context. Handlers are called with the following parameters:
29
+ * * `page: {@link qui.pages.PageMixin}`, the popped page
30
+ * * `index: Number`, page index in page stack
31
+ * @type {qui.base.Signal}
32
+ */
33
+ this.popSignal = new Signal(this)
34
+
35
+ /**
36
+ * @type {qui.pages.PageMixin[]}
37
+ * @private
38
+ */
39
+ this._stack = []
40
+ }
41
+
42
+ /**
43
+ * Return the number of pages.
44
+ * @returns {Number}
45
+ */
46
+ getSize() {
47
+ return this._stack.length
48
+ }
49
+
50
+ /**
51
+ * Return the page at a given index.
52
+ * @param {Number} index
53
+ * @returns {?qui.pages.PageMixin}
54
+ */
55
+ getPageAt(index) {
56
+ return this._stack[index] || null
57
+ }
58
+
59
+ /**
60
+ * Return the stack of pages.
61
+ * @returns {qui.pages.PageMixin[]}
62
+ */
63
+ getPages() {
64
+ return this._stack.slice()
65
+ }
66
+
67
+ /**
68
+ * Return the current page (the tip of the stack).
69
+ * @returns {?qui.pages.PageMixin}
70
+ */
71
+ getCurrentPage() {
72
+ return this._stack[this._stack.length - 1] || null
73
+ }
74
+
75
+ /**
76
+ * Return the pages that are currently visible in this context.
77
+ * @returns {qui.pages.PageMixin[]}
78
+ */
79
+ getVisiblePages() {
80
+ if (!this._stack.length) {
81
+ return []
82
+ }
83
+
84
+ let currentPage = this._stack[this._stack.length - 1]
85
+ let visiblePages = [currentPage]
86
+ let visiblePagesNonPopupMaxCount = Window.isSmallScreen() ? 1 : 4
87
+ let visiblePagesPopupCount = currentPage.isPopup() ? 1 : 0
88
+ let visiblePagesNonPopupCount = currentPage.isPopup() ? 0 : 1
89
+
90
+ /* Allow at most 4 visible non-popup pages at most 1 visible popup page, starting from current page and going
91
+ * backwards */
92
+ for (let i = this._stack.length - 2; i >= 0; i--) {
93
+ let nextPage = this._stack[i + 1]
94
+ let thisPage = this._stack[i]
95
+
96
+ /* Stop at first page that wants to be alone on the visible container; popup pages have an implicit behavior
97
+ * of keep-previous-visible */
98
+ if (!nextPage.isPrevKeptVisible() && !nextPage.isPopup()) {
99
+ break
100
+ }
101
+
102
+ if (thisPage.isPopup()) {
103
+ /* Only one popup page can be visible */
104
+ if (visiblePagesPopupCount < 1) {
105
+ visiblePagesPopupCount++
106
+ visiblePages.push(thisPage)
107
+ }
108
+ }
109
+ else {
110
+ /* Limit non-popup visible pages */
111
+ if (visiblePagesNonPopupCount < visiblePagesNonPopupMaxCount) {
112
+ visiblePagesNonPopupCount++
113
+ visiblePages.push(thisPage)
114
+ }
115
+ }
116
+ }
117
+
118
+ return visiblePages
119
+ }
120
+
121
+ /**
122
+ * Push a page to the context.
123
+ * @param {qui.pages.PageMixin} page
124
+ */
125
+ push(page) {
126
+ this._stack.push(page)
127
+ this.pushSignal.emit(page, this._stack.length - 1)
128
+ }
129
+
130
+ /**
131
+ * Pop the current page from the stack, returning it.
132
+ * @returns {?qui.pages.PageMixin}
133
+ */
134
+ pop() {
135
+ let page = this._stack.pop() || null
136
+ if (page) {
137
+ this.popSignal.emit(page, this._stack.length)
138
+ }
139
+
140
+ return page
141
+ }
142
+
143
+ /**
144
+ * Tell if this context is the current pages context.
145
+ * @returns {Boolean}
146
+ */
147
+ isCurrent() {
148
+ return this === getCurrentContext()
149
+ }
150
+
151
+ }
152
+
153
+
154
+ export default PagesContext
@@ -0,0 +1,252 @@
1
+ /**
2
+ * @namespace qui.pages
3
+ */
4
+
5
+ import $ from '$qui/lib/jquery.module.js'
6
+
7
+ import {AssertionError} from '$qui/base/errors.js'
8
+ import * as GlobalGlass from '$qui/global-glass.js'
9
+ import * as OptionsBar from '$qui/main-ui/options-bar.js'
10
+ import {asap} from '$qui/utils/misc.js'
11
+ import * as Window from '$qui/window.js'
12
+
13
+ import PagesContext from './pages-context.js'
14
+ import * as Breadcrumbs from './breadcrumbs.js'
15
+
16
+
17
+ let pagesContainer = null
18
+ let currentContext = null
19
+
20
+
21
+ /* Page context */
22
+
23
+ function newContext() {
24
+ let context = new PagesContext()
25
+
26
+ context.pushSignal.connect(function (page) {
27
+ /* Whenever a page is added to a context, attach our UI handlers to it */
28
+ attachPageHTMLHandlers(page)
29
+ })
30
+
31
+ context.popSignal.connect(function (page) {
32
+ /* Whenever a page is removed from a context, detach our UI handlers from it */
33
+ detachPageHTMLHandlers(page)
34
+ })
35
+
36
+ return context
37
+ }
38
+
39
+ /**
40
+ * Return the current pages context.
41
+ * @alias qui.pages.getCurrentContext
42
+ * @returns {qui.pages.PagesContext}
43
+ */
44
+ export function getCurrentContext() {
45
+ return currentContext
46
+ }
47
+
48
+ /**
49
+ * Replace the current pages context.
50
+ * @alias qui.pages.setCurrentContext
51
+ * @param {?qui.pages.PagesContext} context new context, or `null` to replace with a brand-new context
52
+ */
53
+ export function setCurrentContext(context) {
54
+ let pagesToDetach = currentContext.getPages()
55
+ let currentPage = currentContext.getCurrentPage()
56
+ if (currentPage) {
57
+ currentPage.handleLeaveCurrent()
58
+ }
59
+
60
+ /* Hide & detach pages */
61
+ pagesToDetach.forEach(function (page) {
62
+ page.detach()
63
+ })
64
+
65
+ if (!context) {
66
+ context = newContext()
67
+ }
68
+
69
+ /* Restore the context */
70
+ currentContext = context
71
+
72
+ /* Attach new pages */
73
+ currentContext.getPages().forEach(function (page) {
74
+ page.attach()
75
+ })
76
+
77
+ currentPage = currentContext.getCurrentPage()
78
+ if (currentPage) {
79
+ currentPage.handleBecomeCurrent()
80
+ }
81
+
82
+ updateUI()
83
+ }
84
+
85
+ /**
86
+ * Return the current page.
87
+ * @alias qui.pages.getCurrentPage
88
+ * @returns {?qui.pages.PageMixin}
89
+ */
90
+ export function getCurrentPage() {
91
+ return currentContext.getCurrentPage()
92
+ }
93
+
94
+
95
+ /* Pages UI */
96
+
97
+ function updatePagesVisibility() {
98
+ /* Reset page classes/styles */
99
+ currentContext.getPages().forEach(function (page) {
100
+ page.getPageHTML().removeClass('visible current').css({width: '', left: ''})
101
+ })
102
+
103
+ /* Hide pages that are still part of the DOM but no longer part of current context */
104
+ $('div.qui-page').not('.sticky').removeClass('visible current').css({width: '', left: ''})
105
+
106
+ if (!currentContext.getSize()) {
107
+ GlobalGlass.updateVisibility()
108
+ return
109
+ }
110
+
111
+ let currentPage = currentContext.getCurrentPage()
112
+ currentPage.getPageHTML().addClass('current')
113
+
114
+ let visiblePages = currentContext.getVisiblePages()
115
+ let columnPages = visiblePages.filter(page => page.isColumnLayout())
116
+
117
+ /* The minimum accepted width is 25% (on a large screen) */
118
+ let expandWidth = 100 - columnPages.length * 25 /* As percent */
119
+ let left = 0
120
+ visiblePages.reverse().forEach(function (page) {
121
+ let html = page.getPageHTML()
122
+ html.addClass('visible')
123
+
124
+ let l
125
+ let w = null
126
+ /* The current popup page has special treatment and does not contribute to expanded width */
127
+ if (page.isPopup() && page === currentPage) {
128
+ l = 0
129
+ }
130
+ else {
131
+ l = left
132
+ if (!page.isColumnLayout()) { /* Expand */
133
+ w = expandWidth
134
+ left += expandWidth
135
+ }
136
+ else {
137
+ left += 25
138
+ }
139
+ }
140
+
141
+ if (l != null) {
142
+ html.css('left', `${l}%`)
143
+ }
144
+ if (w != null) {
145
+ html.css('width', `${w}%`)
146
+ }
147
+ })
148
+
149
+ pagesContainer.toggleClass('full', (left >= 100) || Window.isSmallScreen())
150
+
151
+ GlobalGlass.updateVisibility()
152
+ }
153
+
154
+ function updateContentScroll() {
155
+ /* Scrolled content */
156
+ let scrolled = currentContext.getPages().some(function (p) {
157
+ if (!p.isVisible()) {
158
+ return false
159
+ }
160
+
161
+ return p.getPageHTML()[0].scrollTop !== 0
162
+
163
+ })
164
+ Window.$body.toggleClass('content-scrolled', scrolled)
165
+ }
166
+
167
+ function handlePageScroll() {
168
+ updateContentScroll()
169
+
170
+ let $this = $(this)
171
+ let page = $this.data('page')
172
+ if (!page) {
173
+ throw AssertionError('page scroll event from a non-page HTML element')
174
+ }
175
+
176
+ page.handleVertScroll()
177
+ }
178
+
179
+ function triggerPageResize() {
180
+ asap(function () {
181
+ currentContext.getPages().forEach(function (page) {
182
+ page.handleResize()
183
+ })
184
+ })
185
+ }
186
+
187
+ function triggerPageResizeOnTransitionEnd(e) {
188
+ if (e.target !== this) {
189
+ return
190
+ }
191
+
192
+ if (e.originalEvent.propertyName !== 'width') {
193
+ return
194
+ }
195
+
196
+ triggerPageResize()
197
+ }
198
+
199
+ function attachPageHTMLHandlers(page) {
200
+ page.getPageHTML().on('scroll', handlePageScroll)
201
+ page.getPageHTML().on('transitionend', triggerPageResizeOnTransitionEnd)
202
+ }
203
+
204
+ function detachPageHTMLHandlers(page) {
205
+ page.getPageHTML().off('scroll', handlePageScroll)
206
+ page.getPageHTML().off('transitionend', triggerPageResizeOnTransitionEnd)
207
+ }
208
+
209
+ /**
210
+ * Return the page container HTML element.
211
+ * @alias qui.pages.getPagesContainer
212
+ * @returns {jQuery}
213
+ */
214
+ export function getPagesContainer() {
215
+ return pagesContainer
216
+ }
217
+
218
+ /**
219
+ * Update all UI elements depending on current page stack.
220
+ * @alias qui.pages.updateUI
221
+ */
222
+ export function updateUI() {
223
+ /* Use asap() because some pages might have just been attached to DOM and animations won't probably work */
224
+ asap(function () {
225
+ Breadcrumbs.update()
226
+ updatePagesVisibility()
227
+ updateContentScroll()
228
+ })
229
+ }
230
+
231
+
232
+ export function init() {
233
+ pagesContainer = $('div.qui-pages-container')
234
+ currentContext = newContext()
235
+
236
+ Breadcrumbs.init()
237
+
238
+ Window.resizeSignal.connect(function (width, height) {
239
+ triggerPageResize()
240
+ })
241
+
242
+ OptionsBar.openCloseSignal.connect(function (opened) {
243
+ /* Update _optionsBarOpen flag of current page */
244
+
245
+ let currentPage = currentContext.getCurrentPage()
246
+ if (!currentPage) {
247
+ return
248
+ }
249
+
250
+ currentPage._optionsBarOpen = opened
251
+ })
252
+ }