@etsoo/materialui 1.1.39 → 1.1.42

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 (42) hide show
  1. package/lib/AuditDisplay.d.ts +5 -5
  2. package/lib/AuditDisplay.js +17 -19
  3. package/lib/BridgeCloseButton.d.ts +2 -2
  4. package/lib/BridgeCloseButton.js +7 -8
  5. package/lib/DataSteps.d.ts +4 -4
  6. package/lib/DataSteps.js +17 -14
  7. package/lib/DataTable.d.ts +33 -0
  8. package/lib/DataTable.js +56 -0
  9. package/lib/ListMoreDisplay.d.ts +5 -5
  10. package/lib/ListMoreDisplay.js +8 -10
  11. package/lib/OptionBool.js +2 -1
  12. package/lib/SelectBool.js +2 -1
  13. package/lib/ShowDataComparison.d.ts +1 -1
  14. package/lib/ShowDataComparison.js +27 -20
  15. package/lib/SwitchAnt.d.ts +3 -3
  16. package/lib/SwitchAnt.js +11 -8
  17. package/lib/SwitchField.d.ts +45 -0
  18. package/lib/SwitchField.js +33 -0
  19. package/lib/Tiplist.js +4 -4
  20. package/lib/UserAvatar.js +7 -8
  21. package/lib/app/ReactApp.d.ts +7 -7
  22. package/lib/app/ReactApp.js +26 -26
  23. package/lib/index.d.ts +2 -0
  24. package/lib/index.js +2 -0
  25. package/lib/pages/ViewPage.d.ts +7 -7
  26. package/lib/pages/ViewPage.js +30 -29
  27. package/package.json +3 -2
  28. package/src/AuditDisplay.tsx +92 -99
  29. package/src/BridgeCloseButton.tsx +48 -50
  30. package/src/DataSteps.tsx +188 -185
  31. package/src/DataTable.tsx +124 -0
  32. package/src/ListMoreDisplay.tsx +184 -188
  33. package/src/OptionBool.tsx +1 -1
  34. package/src/SelectBool.tsx +1 -1
  35. package/src/ShowDataComparison.tsx +88 -76
  36. package/src/SwitchAnt.tsx +82 -78
  37. package/src/SwitchField.tsx +133 -0
  38. package/src/Tiplist.tsx +5 -4
  39. package/src/UserAvatar.tsx +43 -45
  40. package/src/app/ReactApp.ts +485 -489
  41. package/src/index.ts +2 -0
  42. package/src/pages/ViewPage.tsx +272 -277
@@ -1,41 +1,41 @@
1
1
  import {
2
- BridgeUtils,
3
- CoreApp,
4
- createClient,
5
- IActionResult,
6
- IApp,
7
- IAppSettings,
8
- ICoreApp,
9
- IUser
10
- } from '@etsoo/appscript';
2
+ BridgeUtils,
3
+ CoreApp,
4
+ createClient,
5
+ IActionResult,
6
+ IApp,
7
+ IAppSettings,
8
+ ICoreApp,
9
+ IUser
10
+ } from "@etsoo/appscript";
11
11
  import {
12
- INotifier,
13
- NotificationMessageType,
14
- NotificationRenderProps,
15
- NotificationReturn
16
- } from '@etsoo/notificationbase';
17
- import { DataTypes, WindowStorage } from '@etsoo/shared';
18
- import React from 'react';
19
- import { NotifierMU } from '../NotifierMU';
20
- import { ProgressCount } from '../ProgressCount';
21
- import { Labels } from './Labels';
12
+ INotifier,
13
+ NotificationMessageType,
14
+ NotificationRenderProps,
15
+ NotificationReturn
16
+ } from "@etsoo/notificationbase";
17
+ import { DataTypes, WindowStorage } from "@etsoo/shared";
18
+ import React from "react";
19
+ import { NotifierMU } from "../NotifierMU";
20
+ import { ProgressCount } from "../ProgressCount";
21
+ import { Labels } from "./Labels";
22
22
  import {
23
- CultureAction,
24
- CultureState,
25
- INotificationReact,
26
- InputDialogProps,
27
- IPageData,
28
- IStateProps,
29
- NotificationReactCallProps,
30
- PageAction,
31
- PageActionType,
32
- PageState,
33
- UserAction,
34
- UserActionType,
35
- UserCalls,
36
- UserState
37
- } from '@etsoo/react';
38
- import { NavigateFunction, NavigateOptions } from 'react-router-dom';
23
+ CultureAction,
24
+ CultureState,
25
+ INotificationReact,
26
+ InputDialogProps,
27
+ IPageData,
28
+ IStateProps,
29
+ NotificationReactCallProps,
30
+ PageAction,
31
+ PageActionType,
32
+ PageState,
33
+ UserAction,
34
+ UserActionType,
35
+ UserCalls,
36
+ UserState
37
+ } from "@etsoo/react";
38
+ import { NavigateFunction, NavigateOptions } from "react-router-dom";
39
39
 
40
40
  /**
41
41
  * React Application Type
@@ -45,7 +45,7 @@ export type ReactAppType = IApp & IReactAppBase;
45
45
  /**
46
46
  * Global application
47
47
  */
48
- export let globalApp: ReactAppType;
48
+ export let globalApp: ReactAppType | null;
49
49
 
50
50
  /**
51
51
  * React app state detector
@@ -57,486 +57,482 @@ export let globalApp: ReactAppType;
57
57
  * @returns Component
58
58
  */
59
59
  export function ReactAppStateDetector(props: IStateProps) {
60
- // Destruct
61
- const { targetFields, update } = props;
62
-
63
- // Context
64
- const { state } =
65
- globalApp == null
66
- ? ({} as UserCalls<IUser>)
67
- : React.useContext(globalApp.userState.context);
68
-
69
- // Ready
70
- React.useEffect(() => {
71
- // Match fields
72
- const changedFields = state.lastChangedFields;
73
- let matchedFields: string[] | undefined;
74
- if (targetFields == null || changedFields == null) {
75
- matchedFields = changedFields;
76
- } else {
77
- matchedFields = [];
78
- targetFields.forEach((targetField) => {
79
- if (changedFields.includes(targetField))
80
- matchedFields?.push(targetField);
81
- });
82
- }
83
-
84
- // Callback
85
- update(state.authorized, matchedFields);
86
- }, [state]);
87
-
88
- // return
89
- return React.createElement(React.Fragment);
60
+ // Destruct
61
+ const { targetFields, update } = props;
62
+
63
+ // Context
64
+ const { state } =
65
+ globalApp == null
66
+ ? ({} as UserCalls<IUser>)
67
+ : React.useContext(globalApp.userState.context);
68
+
69
+ // Ready
70
+ React.useEffect(() => {
71
+ // Match fields
72
+ const changedFields = state.lastChangedFields;
73
+ let matchedFields: string[] | undefined;
74
+ if (targetFields == null || changedFields == null) {
75
+ matchedFields = changedFields;
76
+ } else {
77
+ matchedFields = [];
78
+ targetFields.forEach((targetField) => {
79
+ if (changedFields.includes(targetField))
80
+ matchedFields?.push(targetField);
81
+ });
82
+ }
83
+
84
+ // Callback
85
+ update(state.authorized, matchedFields);
86
+ }, [state]);
87
+
88
+ // return
89
+ return React.createElement(React.Fragment);
90
90
  }
91
91
 
92
92
  /**
93
93
  * React implemented base
94
94
  */
95
95
  export interface IReactAppBase {
96
- /**
97
- * Override Notifier as React specific
98
- */
99
- readonly notifier: INotifier<React.ReactNode, NotificationReactCallProps>;
100
-
101
- /**
102
- * User state
103
- */
104
- readonly userState: UserState<any>;
105
-
106
- /**
107
- * Is screen size down 'sm'
108
- */
109
- smDown?: boolean;
110
-
111
- /**
112
- * Is screen size up 'md'
113
- */
114
- mdUp?: boolean;
115
-
116
- /**
117
- * Get date format props
118
- * @returns Props
119
- */
120
- getDateFormatProps(): object;
121
-
122
- /**
123
- * Get money format props
124
- * @param currency Currency, if undefined, default currency applied
125
- * @returns Props
126
- */
127
- getMoneyFormatProps(currency?: string): object;
128
-
129
- /**
130
- * Set page data
131
- * @param data Page data
132
- */
133
- setPageData(data: IPageData): void;
134
-
135
- /**
136
- * Set page title and data
137
- * @param key Page title resource key
138
- */
139
- setPageKey(key: string): void;
140
-
141
- /**
142
- * Set page title and data
143
- * @param title Page title
144
- */
145
- setPageTitle(title: string): void;
146
-
147
- /**
148
- * Show input dialog
149
- * @param props Props
150
- */
151
- showInputDialog({
152
- title,
153
- message,
154
- callback,
155
- ...rest
156
- }: InputDialogProps): INotificationReact;
96
+ /**
97
+ * Override Notifier as React specific
98
+ */
99
+ readonly notifier: INotifier<React.ReactNode, NotificationReactCallProps>;
100
+
101
+ /**
102
+ * User state
103
+ */
104
+ readonly userState: UserState<any>;
105
+
106
+ /**
107
+ * Is screen size down 'sm'
108
+ */
109
+ smDown?: boolean;
110
+
111
+ /**
112
+ * Is screen size up 'md'
113
+ */
114
+ mdUp?: boolean;
115
+
116
+ /**
117
+ * Get date format props
118
+ * @returns Props
119
+ */
120
+ getDateFormatProps(): object;
121
+
122
+ /**
123
+ * Get money format props
124
+ * @param currency Currency, if undefined, default currency applied
125
+ * @returns Props
126
+ */
127
+ getMoneyFormatProps(currency?: string): object;
128
+
129
+ /**
130
+ * Set page data
131
+ * @param data Page data
132
+ */
133
+ setPageData(data: IPageData): void;
134
+
135
+ /**
136
+ * Set page title and data
137
+ * @param key Page title resource key
138
+ */
139
+ setPageKey(key: string): void;
140
+
141
+ /**
142
+ * Set page title and data
143
+ * @param title Page title
144
+ */
145
+ setPageTitle(title: string): void;
146
+
147
+ /**
148
+ * Show input dialog
149
+ * @param props Props
150
+ */
151
+ showInputDialog({
152
+ title,
153
+ message,
154
+ callback,
155
+ ...rest
156
+ }: InputDialogProps): INotificationReact;
157
157
  }
158
158
 
159
159
  /**
160
160
  * Core application interface
161
161
  */
162
162
  export interface IReactApp<
163
- S extends IAppSettings,
164
- D extends IUser,
165
- P extends IPageData
163
+ S extends IAppSettings,
164
+ D extends IUser,
165
+ P extends IPageData
166
166
  > extends ICoreApp<D, S, React.ReactNode, NotificationReactCallProps>,
167
- IReactAppBase {
168
- /**
169
- * User state
170
- */
171
- readonly userState: UserState<D>;
172
-
173
- /**
174
- * Set page data
175
- * @param data Page data
176
- */
177
- setPageData(data: P): void;
167
+ IReactAppBase {
168
+ /**
169
+ * User state
170
+ */
171
+ readonly userState: UserState<D>;
172
+
173
+ /**
174
+ * Set page data
175
+ * @param data Page data
176
+ */
177
+ setPageData(data: P): void;
178
178
  }
179
179
 
180
180
  /**
181
181
  * React application
182
182
  */
183
183
  export class ReactApp<
184
- S extends IAppSettings,
185
- D extends IUser,
186
- P extends IPageData
187
- >
188
- extends CoreApp<D, S, React.ReactNode, NotificationReactCallProps>
189
- implements IReactApp<S, D, P>
184
+ S extends IAppSettings,
185
+ D extends IUser,
186
+ P extends IPageData
187
+ >
188
+ extends CoreApp<D, S, React.ReactNode, NotificationReactCallProps>
189
+ implements IReactApp<S, D, P>
190
190
  {
191
- private static _notifierProvider: React.FunctionComponent<NotificationRenderProps>;
192
-
193
- /**
194
- * Get notifier provider
195
- */
196
- static get notifierProvider() {
197
- return this._notifierProvider;
198
- }
199
-
200
- private static createNotifier() {
201
- // Notifier
202
- ReactApp._notifierProvider = NotifierMU.setup();
203
-
204
- return NotifierMU.instance;
205
- }
206
-
207
- /**
208
- * Culture state
209
- */
210
- readonly cultureState: CultureState;
211
-
212
- /**
213
- * Page state
214
- */
215
- readonly pageState: PageState<P>;
216
-
217
- /**
218
- * User state
219
- */
220
- readonly userState = new UserState<D>();
221
-
222
- /**
223
- * Is screen size down 'sm'
224
- */
225
- smDown?: boolean;
226
-
227
- /**
228
- * Is screen size up 'md'
229
- */
230
- mdUp?: boolean;
231
-
232
- /**
233
- * Navigate function
234
- */
235
- navigateFunction?: NavigateFunction;
236
-
237
- /**
238
- * Page state dispatch
239
- */
240
- pageStateDispatch?: React.Dispatch<PageAction<P>>;
241
-
242
- /**
243
- * User state dispatch
244
- */
245
- userStateDispatch?: React.Dispatch<UserAction<D>>;
246
-
247
- /**
248
- * Constructor
249
- * @param settings Settings
250
- * @param name Application name
251
- */
252
- constructor(settings: S, name: string) {
253
- super(
254
- settings,
255
- createClient(),
256
- ReactApp.createNotifier(),
257
- new WindowStorage(),
258
- name
191
+ private static _notifierProvider: React.FunctionComponent<NotificationRenderProps>;
192
+
193
+ /**
194
+ * Get notifier provider
195
+ */
196
+ static get notifierProvider() {
197
+ return this._notifierProvider;
198
+ }
199
+
200
+ private static createNotifier() {
201
+ // Notifier
202
+ ReactApp._notifierProvider = NotifierMU.setup();
203
+
204
+ return NotifierMU.instance;
205
+ }
206
+
207
+ /**
208
+ * Culture state
209
+ */
210
+ readonly cultureState: CultureState;
211
+
212
+ /**
213
+ * Page state
214
+ */
215
+ readonly pageState: PageState<P>;
216
+
217
+ /**
218
+ * User state
219
+ */
220
+ readonly userState = new UserState<D>();
221
+
222
+ /**
223
+ * Is screen size down 'sm'
224
+ */
225
+ smDown?: boolean;
226
+
227
+ /**
228
+ * Is screen size up 'md'
229
+ */
230
+ mdUp?: boolean;
231
+
232
+ /**
233
+ * Navigate function
234
+ */
235
+ navigateFunction?: NavigateFunction;
236
+
237
+ /**
238
+ * Page state dispatch
239
+ */
240
+ pageStateDispatch?: React.Dispatch<PageAction<P>>;
241
+
242
+ /**
243
+ * User state dispatch
244
+ */
245
+ userStateDispatch?: React.Dispatch<UserAction<D>>;
246
+
247
+ /**
248
+ * Constructor
249
+ * @param settings Settings
250
+ * @param name Application name
251
+ */
252
+ constructor(settings: S, name: string) {
253
+ super(
254
+ settings,
255
+ createClient(),
256
+ ReactApp.createNotifier(),
257
+ new WindowStorage(),
258
+ name
259
+ );
260
+
261
+ if (BridgeUtils.host) {
262
+ BridgeUtils.host.onUpdate((app, version) => {
263
+ this.notifier.message(
264
+ NotificationMessageType.Success,
265
+ this.get("updateTip") + `(${[app, version].join(", ")})`,
266
+ this.get("updateReady")
259
267
  );
260
-
261
- if (BridgeUtils.host) {
262
- BridgeUtils.host.onUpdate((app, version) => {
263
- this.notifier.message(
264
- NotificationMessageType.Success,
265
- this.get('updateTip') + `(${[app, version].join(', ')})`,
266
- this.get('updateReady')
267
- );
268
- });
269
- }
270
-
271
- this.cultureState = new CultureState(settings.currentCulture);
272
- this.pageState = new PageState<P>();
273
-
274
- globalApp = this;
268
+ });
275
269
  }
276
270
 
277
- /**
278
- * Override alert action result
279
- * @param result Action result
280
- * @param callback Callback
281
- */
282
- override alertResult(
283
- result: IActionResult | string,
284
- callback?: NotificationReturn<void>
285
- ) {
286
- const message =
287
- typeof result === 'string' ? result : this.formatResult(result);
288
- if (message.endsWith(')')) {
289
- const startPos = message.lastIndexOf('(');
290
- if (startPos > 0) {
291
- const main = message.substring(0, startPos).trim();
292
- const tip = message.substring(startPos);
293
-
294
- const titleNode = React.createElement(
295
- React.Fragment,
296
- null,
297
- main,
298
- React.createElement('br'),
299
- React.createElement(
300
- 'span',
301
- { style: { fontSize: '9px' } },
302
- tip
303
- )
304
- );
305
- this.notifier.alert(titleNode, callback);
306
- return;
307
- }
308
- }
309
- super.alertResult(message, callback);
310
- }
311
-
312
- /**
313
- * Change culture
314
- * @param culture New culture definition
315
- */
316
- override changeCulture(culture: DataTypes.CultureDefinition) {
317
- // Super call to update cultrue
318
- super.changeCulture(culture);
319
-
320
- // Update component labels
321
- Labels.setLabels(culture.resources, {
322
- notificationMU: {
323
- alertTitle: 'warning',
324
- alertOK: 'ok',
325
- confirmTitle: 'confirm',
326
- confirmYes: 'ok',
327
- confirmNo: 'cancel',
328
- promptTitle: 'prompt',
329
- promptCancel: 'cancel',
330
- promptOK: 'ok'
331
- }
332
- });
333
-
334
- // Document title
335
- // Default is servier name's label or appName label
336
- const title = this.get(this.name) ?? this.get('appName') ?? this.name;
337
- const host = BridgeUtils.host;
338
- if (host) {
339
- // Notify host
340
- host.changeCulture(culture.name);
341
- host.setTitle(title);
342
- } else {
343
- document.title = title;
344
- }
345
- }
346
-
347
- /**
348
- * Change culture extended
349
- * @param dispatch Dispatch method
350
- * @param culture New culture definition
351
- */
352
- changeCultureEx(
353
- dispatch: React.Dispatch<CultureAction>,
354
- culture: DataTypes.CultureDefinition
355
- ): void {
356
- // Same?
357
- if (culture.name === this.culture) return;
358
-
359
- // Dispatch action
360
- dispatch(culture);
361
-
362
- // Super call
363
- this.changeCulture(culture);
364
- }
365
-
366
- /**
367
- * Get date format props
368
- * @returns Props
369
- */
370
- getDateFormatProps() {
371
- return { culture: this.culture, timeZone: this.getTimeZone() };
372
- }
373
-
374
- /**
375
- * Get money format props
376
- * @param currency Currency, if undefined, default currency applied
377
- * @returns Props
378
- */
379
- getMoneyFormatProps(currency?: string) {
380
- return { culture: this.culture, currency: currency ?? this.currency };
381
- }
382
-
383
- /**
384
- * Fresh countdown UI
385
- * @param callback Callback
386
- */
387
- freshCountdownUI(callback?: () => PromiseLike<unknown>) {
388
- // Labels
389
- const labels = this.getLabels('cancel', 'tokenExpiry');
390
-
391
- // Progress
392
- const progress = React.createElement(ProgressCount, {
393
- seconds: 30,
394
- valueUnit: 's',
395
- onComplete: () => {
396
- // Stop the progress
397
- return false;
398
- }
399
- });
400
-
401
- // Popup
402
- this.notifier.alert(
403
- labels.tokenExpiry,
404
- async () => {
405
- if (callback) await callback();
406
- else await this.tryLogin();
407
- },
408
- undefined,
409
- {
410
- okLabel: labels.cancel,
411
- primaryButton: { fullWidth: true, autoFocus: false },
412
- inputs: progress
413
- }
271
+ this.cultureState = new CultureState(settings.currentCulture);
272
+ this.pageState = new PageState<P>();
273
+
274
+ globalApp = this;
275
+ }
276
+
277
+ /**
278
+ * Override alert action result
279
+ * @param result Action result
280
+ * @param callback Callback
281
+ */
282
+ override alertResult(
283
+ result: IActionResult | string,
284
+ callback?: NotificationReturn<void>
285
+ ) {
286
+ const message =
287
+ typeof result === "string" ? result : this.formatResult(result);
288
+ if (message.endsWith(")")) {
289
+ const startPos = message.lastIndexOf("(");
290
+ if (startPos > 0) {
291
+ const main = message.substring(0, startPos).trim();
292
+ const tip = message.substring(startPos);
293
+
294
+ const titleNode = React.createElement(
295
+ React.Fragment,
296
+ null,
297
+ main,
298
+ React.createElement("br"),
299
+ React.createElement("span", { style: { fontSize: "9px" } }, tip)
414
300
  );
301
+ this.notifier.alert(titleNode, callback);
302
+ return;
303
+ }
415
304
  }
416
-
417
- /**
418
- * Set page data
419
- * @param data Page data
420
- */
421
- setPageData(data: P): void {
422
- // Dispatch the change
423
- if (this.pageStateDispatch != null) {
424
- this.pageStateDispatch({
425
- type: PageActionType.Data,
426
- data
427
- });
428
- }
429
- }
430
-
431
- /**
432
- * Set page title and data
433
- * @param title Page title
434
- */
435
- setPageTitle(title: string): void {
436
- // Data
437
- const data = { title } as P;
438
-
439
- // Dispatch the change
440
- if (this.pageStateDispatch != null) {
441
- this.pageStateDispatch({
442
- type: PageActionType.Title,
443
- data
444
- });
445
- }
446
- }
447
-
448
- /**
449
- * Navigate to Url or delta
450
- * @param url Url or delta
451
- * @param options Options
452
- */
453
- override navigate<T extends number | string | URL>(
454
- to: T,
455
- options?: T extends number ? never : NavigateOptions
456
- ) {
457
- if (this.navigateFunction == null) super.navigate(to, options);
458
- else if (typeof to === 'number') this.navigateFunction(to);
459
- else this.navigateFunction(to, options);
460
- }
461
-
462
- /**
463
- * Set page title and data
464
- * @param key Page title resource key
465
- */
466
- setPageKey(key: string): void {
467
- this.setPageTitle(this.get<string>(key) ?? '');
468
- }
469
-
470
- /**
471
- * Show input dialog
472
- * @param props Props
473
- */
474
- showInputDialog({
475
- title,
476
- message,
477
- callback,
478
- ...rest
479
- }: InputDialogProps): INotificationReact {
480
- return this.notifier.prompt<HTMLFormElement | undefined>(
481
- message,
482
- callback,
483
- title,
484
- rest
485
- );
305
+ super.alertResult(message, callback);
306
+ }
307
+
308
+ /**
309
+ * Change culture
310
+ * @param culture New culture definition
311
+ */
312
+ override changeCulture(culture: DataTypes.CultureDefinition) {
313
+ // Super call to update cultrue
314
+ super.changeCulture(culture);
315
+
316
+ // Update component labels
317
+ Labels.setLabels(culture.resources, {
318
+ notificationMU: {
319
+ alertTitle: "warning",
320
+ alertOK: "ok",
321
+ confirmTitle: "confirm",
322
+ confirmYes: "ok",
323
+ confirmNo: "cancel",
324
+ promptTitle: "prompt",
325
+ promptCancel: "cancel",
326
+ promptOK: "ok"
327
+ }
328
+ });
329
+
330
+ // Document title
331
+ // Default is servier name's label or appName label
332
+ const title = this.get(this.name) ?? this.get("appName") ?? this.name;
333
+ const host = BridgeUtils.host;
334
+ if (host) {
335
+ // Notify host
336
+ host.changeCulture(culture.name);
337
+ host.setTitle(title);
338
+ } else {
339
+ document.title = title;
486
340
  }
487
-
488
- /**
489
- * User login extended
490
- * @param user New user
491
- * @param refreshToken Refresh token
492
- * @param keep Keep in local storage or not
493
- * @param dispatch User state dispatch
494
- */
495
- override userLogin(
496
- user: D,
497
- refreshToken: string,
498
- keep?: boolean,
499
- dispatch?: boolean
500
- ): void {
501
- // Super call, set token
502
- super.userLogin(user, refreshToken, keep);
503
-
504
- // Dispatch action
505
- if (this.userStateDispatch != null && dispatch !== false)
506
- this.userStateDispatch({
507
- type: UserActionType.Login,
508
- user
509
- });
341
+ }
342
+
343
+ /**
344
+ * Change culture extended
345
+ * @param dispatch Dispatch method
346
+ * @param culture New culture definition
347
+ */
348
+ changeCultureEx(
349
+ dispatch: React.Dispatch<CultureAction>,
350
+ culture: DataTypes.CultureDefinition
351
+ ): void {
352
+ // Same?
353
+ if (culture.name === this.culture) return;
354
+
355
+ // Dispatch action
356
+ dispatch(culture);
357
+
358
+ // Super call
359
+ this.changeCulture(culture);
360
+ }
361
+
362
+ /**
363
+ * Get date format props
364
+ * @returns Props
365
+ */
366
+ getDateFormatProps() {
367
+ return { culture: this.culture, timeZone: this.getTimeZone() };
368
+ }
369
+
370
+ /**
371
+ * Get money format props
372
+ * @param currency Currency, if undefined, default currency applied
373
+ * @returns Props
374
+ */
375
+ getMoneyFormatProps(currency?: string) {
376
+ return { culture: this.culture, currency: currency ?? this.currency };
377
+ }
378
+
379
+ /**
380
+ * Fresh countdown UI
381
+ * @param callback Callback
382
+ */
383
+ freshCountdownUI(callback?: () => PromiseLike<unknown>) {
384
+ // Labels
385
+ const labels = this.getLabels("cancel", "tokenExpiry");
386
+
387
+ // Progress
388
+ const progress = React.createElement(ProgressCount, {
389
+ seconds: 30,
390
+ valueUnit: "s",
391
+ onComplete: () => {
392
+ // Stop the progress
393
+ return false;
394
+ }
395
+ });
396
+
397
+ // Popup
398
+ this.notifier.alert(
399
+ labels.tokenExpiry,
400
+ async () => {
401
+ if (callback) await callback();
402
+ else await this.tryLogin();
403
+ },
404
+ undefined,
405
+ {
406
+ okLabel: labels.cancel,
407
+ primaryButton: { fullWidth: true, autoFocus: false },
408
+ inputs: progress
409
+ }
410
+ );
411
+ }
412
+
413
+ /**
414
+ * Set page data
415
+ * @param data Page data
416
+ */
417
+ setPageData(data: P): void {
418
+ // Dispatch the change
419
+ if (this.pageStateDispatch != null) {
420
+ this.pageStateDispatch({
421
+ type: PageActionType.Data,
422
+ data
423
+ });
510
424
  }
511
-
512
- /**
513
- * User logout
514
- * @param clearToken Clear refresh token or not
515
- */
516
- override userLogout(clearToken: boolean = true): void {
517
- // Super call
518
- super.userLogout(clearToken);
519
-
520
- // Dispatch action
521
- if (this.userStateDispatch != null)
522
- this.userStateDispatch({
523
- type: UserActionType.Logout
524
- });
425
+ }
426
+
427
+ /**
428
+ * Set page title and data
429
+ * @param title Page title
430
+ */
431
+ setPageTitle(title: string): void {
432
+ // Data
433
+ const data = { title } as P;
434
+
435
+ // Dispatch the change
436
+ if (this.pageStateDispatch != null) {
437
+ this.pageStateDispatch({
438
+ type: PageActionType.Title,
439
+ data
440
+ });
525
441
  }
526
-
527
- /**
528
- * User unauthorized
529
- */
530
- override userUnauthorized() {
531
- // Super call
532
- super.userUnauthorized();
533
-
534
- if (this.userStateDispatch != null) {
535
- // There is delay during state update
536
- // Not a good idea to try login multiple times with API calls
537
- this.userStateDispatch({
538
- type: UserActionType.Unauthorized
539
- });
540
- }
442
+ }
443
+
444
+ /**
445
+ * Navigate to Url or delta
446
+ * @param url Url or delta
447
+ * @param options Options
448
+ */
449
+ override navigate<T extends number | string | URL>(
450
+ to: T,
451
+ options?: T extends number ? never : NavigateOptions
452
+ ) {
453
+ if (this.navigateFunction == null) super.navigate(to, options);
454
+ else if (typeof to === "number") this.navigateFunction(to);
455
+ else this.navigateFunction(to, options);
456
+ }
457
+
458
+ /**
459
+ * Set page title and data
460
+ * @param key Page title resource key
461
+ */
462
+ setPageKey(key: string): void {
463
+ this.setPageTitle(this.get<string>(key) ?? "");
464
+ }
465
+
466
+ /**
467
+ * Show input dialog
468
+ * @param props Props
469
+ */
470
+ showInputDialog({
471
+ title,
472
+ message,
473
+ callback,
474
+ ...rest
475
+ }: InputDialogProps): INotificationReact {
476
+ return this.notifier.prompt<HTMLFormElement | undefined>(
477
+ message,
478
+ callback,
479
+ title,
480
+ rest
481
+ );
482
+ }
483
+
484
+ /**
485
+ * User login extended
486
+ * @param user New user
487
+ * @param refreshToken Refresh token
488
+ * @param keep Keep in local storage or not
489
+ * @param dispatch User state dispatch
490
+ */
491
+ override userLogin(
492
+ user: D,
493
+ refreshToken: string,
494
+ keep?: boolean,
495
+ dispatch?: boolean
496
+ ): void {
497
+ // Super call, set token
498
+ super.userLogin(user, refreshToken, keep);
499
+
500
+ // Dispatch action
501
+ if (this.userStateDispatch != null && dispatch !== false)
502
+ this.userStateDispatch({
503
+ type: UserActionType.Login,
504
+ user
505
+ });
506
+ }
507
+
508
+ /**
509
+ * User logout
510
+ * @param clearToken Clear refresh token or not
511
+ */
512
+ override userLogout(clearToken: boolean = true): void {
513
+ // Super call
514
+ super.userLogout(clearToken);
515
+
516
+ // Dispatch action
517
+ if (this.userStateDispatch != null)
518
+ this.userStateDispatch({
519
+ type: UserActionType.Logout
520
+ });
521
+ }
522
+
523
+ /**
524
+ * User unauthorized
525
+ */
526
+ override userUnauthorized() {
527
+ // Super call
528
+ super.userUnauthorized();
529
+
530
+ if (this.userStateDispatch != null) {
531
+ // There is delay during state update
532
+ // Not a good idea to try login multiple times with API calls
533
+ this.userStateDispatch({
534
+ type: UserActionType.Unauthorized
535
+ });
541
536
  }
537
+ }
542
538
  }