@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,612 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+ import Logger from '$qui/lib/logger.module.js'
4
+
5
+ import {NotImplementedError} from '$qui/base/errors.js'
6
+ import {AssertionError} from '$qui/base/errors.js'
7
+ import {mix} from '$qui/base/mixwith.js'
8
+ import SingletonMixin from '$qui/base/singleton.js'
9
+ import StockIcon from '$qui/icons/stock-icon.js'
10
+ import * as MenuBar from '$qui/main-ui/menu-bar.js'
11
+ import * as TopBar from '$qui/main-ui/top-bar.js'
12
+ import * as Navigation from '$qui/navigation.js'
13
+ import {getCurrentContext} from '$qui/pages/pages.js'
14
+ import {setCurrentContext} from '$qui/pages/pages.js'
15
+ import * as Window from '$qui/window.js'
16
+
17
+ import * as Sections from './sections.js'
18
+
19
+
20
+ /**
21
+ * The base class for sections.
22
+ * @alias qui.sections.Section
23
+ * @mixes qui.base.SingletonMixin
24
+ */
25
+ class Section extends mix().with(SingletonMixin) {
26
+
27
+ /**
28
+ * @constructs
29
+ * @param {String} id section identifier
30
+ * @param {String} title section title
31
+ * @param {qui.icons.Icon} icon section icon
32
+ * @param {String} [buttonType] one of:
33
+ * * {@link qui.sections.BUTTON_TYPE_NONE}
34
+ * * {@link qui.sections.BUTTON_TYPE_MENU_BAR} (default)
35
+ * * {@link qui.sections.BUTTON_TYPE_TOP_BAR}
36
+ * @param {Boolean} [closeMainPageOnHide] set to `true` to close the main page when section is hidden
37
+ * {@link qui.sections.BUTTON_TYPE_TOP_BAR} and {@link qui.sections.BUTTON_TYPE_NONE}
38
+ * @param {Number} [index] sets the section position (ordering) in relation with other sections; by default,
39
+ * sections are positioned based on their registration order
40
+ */
41
+ constructor({
42
+ id,
43
+ title,
44
+ icon,
45
+ buttonType = Sections.BUTTON_TYPE_MENU_BAR,
46
+ index = 0,
47
+ closeMainPageOnHide = false
48
+ }) {
49
+
50
+ super()
51
+
52
+ this._id = id
53
+ this._title = title
54
+ this._icon = icon
55
+ this._buttonType = buttonType
56
+ this._index = index
57
+ this._closeMainPageOnHide = closeMainPageOnHide
58
+
59
+ this._mainPage = null
60
+ this._savedPagesContext = null
61
+ this._whenPreloaded = null
62
+ this._whenLoaded = null
63
+
64
+ this.logger = Logger.get(`qui.sections.${this._id}`)
65
+
66
+ this._button = this._makeSectionButton()
67
+ this._button.on('click', function () {
68
+
69
+ if (this.isCurrent()) {
70
+ return
71
+ }
72
+
73
+ Sections.switchTo(this, /* source = */ 'button', /* historyEntry = */ true)
74
+
75
+ }.bind(this))
76
+
77
+ /* Hide menu bar after selecting a menu entry */
78
+ this._button.on('pointerup', function () {
79
+ if (MenuBar.isOpened()) {
80
+ MenuBar.close()
81
+ }
82
+ })
83
+
84
+ this._applyIcon()
85
+ }
86
+
87
+ /**
88
+ * Return the id of this section.
89
+ * @returns {String}
90
+ */
91
+ getId() {
92
+ return this._id
93
+ }
94
+
95
+ /**
96
+ * Override this to implement how the section is preloaded (i.e. before pushing the main page).
97
+ *
98
+ * During the preload execution, this section should not be assumed to be the current section. Preloading blocks
99
+ * navigation by blocking switching to this section.
100
+ *
101
+ * By default, returns a resolved promise.
102
+ *
103
+ * @returns {Promise}
104
+ */
105
+ preload() {
106
+ return Promise.resolve()
107
+ }
108
+
109
+ /**
110
+ * Override this to implement how the section is loaded, after pushing the main page.
111
+ *
112
+ * Loading the main page and loading the section may happen concurrently.
113
+ *
114
+ * By default, returns a resolved promise.
115
+ *
116
+ * @returns {Promise}
117
+ */
118
+ load() {
119
+ return Promise.resolve()
120
+ }
121
+
122
+ /**
123
+ * Return a promise that settles as soon as the section is preloaded.
124
+ *
125
+ * This method calls {@link qui.sections.Section#preload} once per section instance.
126
+ *
127
+ * @returns {Promise}
128
+ */
129
+ whenPreloaded() {
130
+ if (!this._whenPreloaded) {
131
+ this._whenPreloaded = this.preload()
132
+ }
133
+
134
+ return this._whenPreloaded
135
+ }
136
+
137
+ /**
138
+ * Return a promise that settles as soon as the section is loaded.
139
+ *
140
+ * This method calls {@link qui.sections.Section#load} once per section instance.
141
+ *
142
+ * A loaded section can also be assumed to be preloaded.
143
+ *
144
+ * @returns {Promise}
145
+ */
146
+ whenLoaded() {
147
+ return this.whenPreloaded().then(function () {
148
+
149
+ if (!this._whenLoaded) {
150
+ this._whenLoaded = this.load()
151
+ }
152
+
153
+ return this._whenLoaded
154
+
155
+ }.bind(this))
156
+ }
157
+
158
+ /**
159
+ * Override this method to prevent accidental closing of the section, to the possible extent. Sections can be closed
160
+ * by default.
161
+ *
162
+ * A section is closed only when application window is closed or reloaded.
163
+ *
164
+ * @returns {Boolean}
165
+ */
166
+ canClose() {
167
+ return true
168
+ }
169
+
170
+ /**
171
+ * This method is called when {@link qui.sections.register} is called on this section and is responsible of section
172
+ * setting up after registration.
173
+ */
174
+ handleRegister() {
175
+ this._addSectionButton()
176
+ }
177
+
178
+ /**
179
+ * This method is called when {@link qui.sections.unregister} is called on this section and is responsible of
180
+ * cleaning up after section removal.
181
+ */
182
+ handleUnregister() {
183
+ this._button.detach()
184
+ }
185
+
186
+
187
+ /* Visibility & layout */
188
+
189
+ /**
190
+ * Called whenever the section becomes visible.
191
+ * @param {String} source the source why the section has been shown; known sources are:
192
+ * * `"button"` - the section button has been pressed
193
+ * * `"navigation"` - {@link qui.navigation.navigate} was used to switch to this section
194
+ * * `"home"` - {@link qui.sections.showHome} was called
195
+ * * `"program"` - {@link qui.sections.switchTo} called from program
196
+ * @param {?qui.sections.Section} prevSection the previous section
197
+ */
198
+ onShow(source, prevSection) {
199
+ }
200
+
201
+ /**
202
+ * Handle the event of section becoming visible.
203
+ * @param {String} source (see {@link qui.sections.Section#onShow})
204
+ * @param {?qui.sections.Section} prevSection the previous section
205
+ */
206
+ handleShow(source, prevSection) {
207
+ /* Inform all pages of event */
208
+ let context = this.getPagesContext()
209
+ context.getPages().forEach(page => page.handleSectionShow())
210
+
211
+ this.onShow(source, prevSection)
212
+ }
213
+
214
+ /**
215
+ * Called whenever the section is hidden.
216
+ */
217
+ onHide() {
218
+ }
219
+
220
+ /**
221
+ * Handle the event of section becoming hidden.
222
+ * @returns {Promise} a promise that resolves if the section can be hidden and rejected otherwise
223
+ */
224
+ handleHide() {
225
+ let promise = Promise.resolve()
226
+ if (this._mainPage && this._closeMainPageOnHide) {
227
+ promise = this._mainPage.close()
228
+ }
229
+
230
+ return promise.then(function () {
231
+
232
+ /* Inform all pages of event */
233
+ let context = this.getPagesContext()
234
+ context.getPages().forEach(page => page.handleSectionHide())
235
+
236
+ this.onHide()
237
+
238
+ }.bind(this))
239
+ }
240
+
241
+ /**
242
+ * Called when the screen layout changes.
243
+ * @param {Boolean} smallScreen whether the screen is now a small screen or not
244
+ * @param {Boolean} landscape whether the screen layout is now landscape or portrait
245
+ */
246
+ onScreenLayoutChange(smallScreen, landscape) {
247
+ }
248
+
249
+ /**
250
+ * Called whenever the screen size changes.
251
+ * @param {Number} width the new width of the screen
252
+ * @param {Number} height the new height of the screen
253
+ */
254
+ onWindowResize(width, height) {
255
+ }
256
+
257
+ /**
258
+ * Called when the window becomes active or inactive.
259
+ * @param {Boolean} active
260
+ * @param active
261
+ */
262
+ onWindowActiveChange(active) {
263
+ }
264
+
265
+ /**
266
+ * Called when the window becomes focused or unfocused.
267
+ * @param {Boolean} focused
268
+ * @param focused
269
+ */
270
+ onWindowFocusedChange(focused) {
271
+ }
272
+
273
+ /**
274
+ * Called when the options bar is opened or closed, while this section is the current section.
275
+ * @param {Boolean} opened `true` if the bar is opened, `false` otherwise
276
+ */
277
+ onOptionsBarOpenClose(opened) {
278
+ }
279
+
280
+ _makeAndPushMainPage() {
281
+ this.logger.debug('creating main page')
282
+ this._mainPage = this.makeMainPage()
283
+
284
+ let pagesContext = this.getPagesContext()
285
+
286
+ /* Overwrite path id with the section id */
287
+ this._mainPage._pathId = this._id
288
+
289
+ /* Unset mainPage on close */
290
+ pagesContext.popSignal.connect(function (page, index) {
291
+ if (index === 0) { /* Main page removed */
292
+ this._mainPage = null
293
+ }
294
+ }.bind(this))
295
+
296
+ this._mainPage.pushSelf(pagesContext)
297
+ }
298
+
299
+ _show(source, prevSection) {
300
+ this.logger.debug('showing section')
301
+
302
+ /* Restore the pages context, if present */
303
+ if (this._savedPagesContext) {
304
+ this.logger.debug('restoring pages context')
305
+ setCurrentContext(this._savedPagesContext)
306
+ this._savedPagesContext = null
307
+ }
308
+
309
+ /* Create and push the main page if not already created */
310
+ if (!this._mainPage) {
311
+ this._makeAndPushMainPage()
312
+ }
313
+
314
+ this._button.addClass('selected')
315
+ this.handleShow(source, prevSection)
316
+
317
+ return this.whenLoaded().then(function () {
318
+
319
+ /* Start loading the main page when pushed, but don't wait for result */
320
+ this._mainPage.whenLoaded()
321
+
322
+ }.bind(this))
323
+ }
324
+
325
+ _hide(historyEntry) {
326
+ this.logger.debug('hiding section')
327
+ return this.handleHide().then(function () {
328
+ this._savedPagesContext = getCurrentContext()
329
+ this._button.removeClass('selected')
330
+
331
+ if (historyEntry) {
332
+ Navigation.addHistoryEntry()
333
+ }
334
+
335
+ setCurrentContext(null)
336
+ }.bind(this)).catch(function (e) {
337
+ if (e == null) {
338
+ e = new Sections.HideCancelled()
339
+ }
340
+
341
+ if (e instanceof Sections.HideCancelled) {
342
+ this.logger.debug('hiding cancelled')
343
+ }
344
+
345
+ throw e
346
+ }.bind(this))
347
+ }
348
+
349
+ /**
350
+ * Tell if this section is the current section or not.
351
+ * @returns {Boolean}
352
+ */
353
+ isCurrent() {
354
+ return Sections.getCurrent() === this
355
+ }
356
+
357
+ /**
358
+ * Return the current page of this section.
359
+ * @returns {?qui.pages.PageMixin}
360
+ */
361
+ getCurrentPage() {
362
+ let context = this.getPagesContext()
363
+ return context ? context.getCurrentPage() : null
364
+ }
365
+
366
+ /**
367
+ * Return the pages context of this section.
368
+ * @returns {?qui.pages.PagesContext}
369
+ */
370
+ getPagesContext() {
371
+ /* A hidden section has its pages context stored in _savedPagesContext. A visible (current) section owns the
372
+ * current pages context. */
373
+
374
+ if (this.isCurrent()) {
375
+ return getCurrentContext()
376
+ }
377
+ else {
378
+ return this._savedPagesContext
379
+ }
380
+ }
381
+
382
+
383
+ /* Title, icon & button */
384
+
385
+ _makeSectionButton() {
386
+ let button = $('<div></div>', {class: 'qui-base-button qui-section-button'})
387
+ button.append($('<div></div>', {class: 'qui-icon'}))
388
+ button.append($('<span></span>', {class: 'label'}))
389
+
390
+ button.addClass(this._id)
391
+ button.css('order', this._index * 10)
392
+
393
+ switch (this._buttonType) {
394
+ case Sections.BUTTON_TYPE_MENU_BAR:
395
+ button.find('.label').html(this._title)
396
+ break
397
+
398
+ case Sections.BUTTON_TYPE_TOP_BAR:
399
+ button.find('.label').remove()
400
+ button.attr('title', this._title)
401
+ break
402
+ }
403
+
404
+ return button
405
+ }
406
+
407
+ _addSectionButton() {
408
+ switch (this._buttonType) {
409
+ case Sections.BUTTON_TYPE_MENU_BAR:
410
+ MenuBar.addButton(this._button)
411
+ break
412
+
413
+ case Sections.BUTTON_TYPE_TOP_BAR:
414
+ TopBar.addButton(this._button)
415
+ break
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Show or hide the section button.
421
+ * @param {Boolean} visible
422
+ */
423
+ setButtonVisibility(visible) {
424
+ this._button.toggle(visible)
425
+ }
426
+
427
+ /**
428
+ * Return the section icon.
429
+ * @returns {qui.icons.Icon}
430
+ */
431
+ getIcon() {
432
+ return this._icon
433
+ }
434
+
435
+ /**
436
+ * Set the section icon.
437
+ * @param {qui.icons.Icon} icon the new icon
438
+ */
439
+ setIcon(icon) {
440
+ this._icon = icon
441
+ this._applyIcon()
442
+ }
443
+
444
+ _applyIcon() {
445
+ let icon = this._icon
446
+
447
+ if (icon instanceof StockIcon) {
448
+ switch (this._buttonType) {
449
+ case Sections.BUTTON_TYPE_MENU_BAR:
450
+ icon = icon.alterDefault({
451
+ variant: 'interactive',
452
+ activeVariant: 'interactive',
453
+ selectedVariant: 'background'
454
+ })
455
+ break
456
+
457
+ case Sections.BUTTON_TYPE_TOP_BAR:
458
+ icon = icon.alterDefault({variant: Window.isSmallScreen() ? 'white' : 'interactive'})
459
+ break
460
+ }
461
+ }
462
+
463
+ /* Following css manipulations are a workaround for properly detecting decoration background */
464
+ let origSelected = this._button.hasClass('selected')
465
+
466
+ this._button.css('transition', 'none')
467
+ this._button.removeClass('selected')
468
+
469
+ this._button.find('.qui-icon').empty()
470
+ icon.applyTo(this._button.find('.qui-icon'))
471
+
472
+ this._button.css('transition', '')
473
+ if (origSelected) {
474
+ this._button.addClass('selected')
475
+ }
476
+
477
+ this._icon = icon
478
+ }
479
+
480
+ /**
481
+ * Return the title of this section.
482
+ * @returns {String}
483
+ */
484
+ getTitle() {
485
+ return this._title
486
+ }
487
+
488
+ /**
489
+ * Set the title of this section.
490
+ * @param {String} title
491
+ */
492
+ setTitle(title) {
493
+ this._title = title
494
+ if (this._button) {
495
+ this._button.find('.label').html(this._title)
496
+ this._button.attr('title', this._title)
497
+ }
498
+ }
499
+
500
+
501
+ /* Pages & navigation */
502
+
503
+ /**
504
+ * Override this method to create the main section page.
505
+ * @returns {qui.pages.PageMixin} the section's main page
506
+ */
507
+ makeMainPage() {
508
+ throw new NotImplementedError()
509
+ }
510
+
511
+ /**
512
+ * Return the main page of this section.
513
+ * @returns {?qui.pages.PageMixin}
514
+ */
515
+ getMainPage() {
516
+ return this._mainPage
517
+ }
518
+
519
+ /**
520
+ * Push a new page onto the section's context. The page is not guaranteed to be pushed by the time the function
521
+ * exists.
522
+ * @param {qui.pages.PageMixin} page the page to be pushed
523
+ * @param {Boolean} [historyEntry] whether to create a new history entry for current page before adding the new
524
+ * page, or not (determined automatically by default, using new page's `pathId`)
525
+ * @returns {Promise} a promise that resolves as soon as the page is pushed, or rejected if the page cannot be
526
+ * pushed
527
+ */
528
+ pushPage(page, historyEntry = null) {
529
+ /* By default, pages with pathId will add a new history entry */
530
+ if (historyEntry == null) {
531
+ historyEntry = !!page.getPathId()
532
+ }
533
+
534
+ let context = this.getPagesContext()
535
+ if (!context) {
536
+ throw new AssertionError('Attempt to push page to uninitialized section')
537
+ }
538
+
539
+ if (!context.isCurrent()) {
540
+ historyEntry = false
541
+ }
542
+
543
+ let currentPage = context.getCurrentPage()
544
+ if (currentPage) {
545
+ return currentPage.pushPage(page, historyEntry)
546
+ }
547
+ else {
548
+ page.pushSelf(context)
549
+ page.whenLoaded() /* Start loading the page automatically when pushed */
550
+
551
+ if (historyEntry) {
552
+ Navigation.addHistoryEntry()
553
+ }
554
+ else if (context.isCurrent()) {
555
+ Navigation.updateHistoryEntry()
556
+ }
557
+
558
+ return Promise.resolve()
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Reset the section to its initial state, closing all pages, including the main page.
564
+ * @returns {Promise} a promise that is resolved as soon as the section is reset and the main page is loaded
565
+ */
566
+ reset() {
567
+ this.logger.debug('resetting section')
568
+
569
+ let promise = Promise.resolve()
570
+ if (this._mainPage) {
571
+ /* Close main page and all following pages */
572
+ promise = this._mainPage.close(/* force = */ true)
573
+ }
574
+
575
+ this.onReset()
576
+
577
+ /* Also re-apply button icon, as theme might have been changed */
578
+ this._applyIcon()
579
+
580
+ return promise.then(function () {
581
+ this._mainPage = null
582
+ if (this.isCurrent()) {
583
+ this._makeAndPushMainPage()
584
+ return this.whenLoaded().then(function () {
585
+ /* Start loading the main page when pushed, but don't wait for result */
586
+ return this._mainPage.whenLoaded()
587
+ }.bind(this))
588
+ }
589
+ }.bind(this))
590
+ }
591
+
592
+ /**
593
+ * Called whenever the section is reset.
594
+ */
595
+ onReset() {
596
+ }
597
+
598
+ /**
599
+ * Override this to implement redirects from this section when switching to it using
600
+ * {@link qui.navigation.navigate}. By default it returns this section.
601
+ *
602
+ * @param {String[]} path the requested navigation path
603
+ * @returns {qui.sections.Section|Promise<qui.sections.Section>} the new section
604
+ */
605
+ navigate(path) {
606
+ return this
607
+ }
608
+
609
+ }
610
+
611
+
612
+ export default Section